[Enahance] Provision to select customer, item code while making an invoice from timesheet (#10616)

diff --git a/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif b/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif
new file mode 100644
index 0000000..90cc6d6
--- /dev/null
+++ b/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif
Binary files differ
diff --git a/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md b/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md
index 9be2bad..4dbdcbb 100644
--- a/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md
+++ b/erpnext/docs/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.md
@@ -48,6 +48,8 @@
 
 <img class="screenshot" alt="Sales Invoice" src="/docs/assets/img/project/timesheet/timesheet-invoice-1.png">
 
+<img class="screenshot" alt="Sales Invoice timesheet" src="{{docs_base_url}}/assets/img/project/timesheet/make_invoice_from_timesheet.gif">
+
 ####Sales Invoice
 
 Sales Invoice has dedicated table for the Timesheet table where Timesheet details will be updated. You can select more Timesheets in this table.
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 955a2b0..2588d56 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -54,19 +54,18 @@
 
 	def test_sales_invoice_from_timesheet(self):
 		timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1)
-		sales_invoice = make_sales_invoice(timesheet.name)
-		sales_invoice.customer = "_Test Customer"
+		sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
 		sales_invoice.due_date = nowdate()
-
-		item = sales_invoice.append('items', {})
-		item.item_code = '_Test Item'
-		item.qty = 2
-		item.rate = 100
-
 		sales_invoice.submit()
 		timesheet = frappe.get_doc('Timesheet', timesheet.name)
 		self.assertEquals(sales_invoice.total_billing_amount, 100)
 		self.assertEquals(timesheet.status, 'Billed')
+		self.assertEquals(sales_invoice.customer, '_Test Customer')
+
+		item = sales_invoice.items[0]
+		self.assertEquals(item.item_code, '_Test Item')
+		self.assertEquals(item.qty, 2.00)
+		self.assertEquals(item.rate, 50.00)
 
 	def test_timesheet_billing_based_on_project(self):
 		timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1, project = '_Test Project', company='_Test Company')
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 9b330e7..62ae1ed 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -57,10 +57,37 @@
 	},
 
 	make_invoice: function(frm) {
-		frappe.model.open_mapped_doc({
-			method: "erpnext.projects.doctype.timesheet.timesheet.make_sales_invoice",
-			frm: frm
+		let dialog = new frappe.ui.Dialog({
+			title: __("For Item"),
+			fields: [
+				{"fieldtype": "Link", "label": __("Item Code"), "fieldname": "item_code", "options":"Item"},
+				{"fieldtype": "Link", "label": __("Customer"), "fieldname": "customer", "options":"Customer"}
+			]
 		});
+
+		dialog.set_primary_action(__("Make Sales Invoice"), () => {
+			var args = dialog.get_values();
+			if(!args) return;
+			dialog.hide();
+			return frappe.call({
+				type: "GET",
+				method: "erpnext.projects.doctype.timesheet.timesheet.make_sales_invoice",
+				args: {
+					"source_name": frm.doc.name,
+					"item_code": args.item_code,
+					"customer": args.customer
+				},
+				freeze: true,
+				callback: function(r) {
+					if(!r.exc) {
+						frappe.model.sync(r.message);
+						frappe.set_route("Form", r.message.doctype, r.message.name);
+					}
+				}
+			})
+		})
+
+		dialog.show();
 	},
 
 	make_salary_slip: function(frm) {
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 16abd24..ad566d5 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -323,17 +323,32 @@
 	}
 
 @frappe.whitelist()
-def make_sales_invoice(source_name, target=None):
+def make_sales_invoice(source_name, item_code=None, customer=None):
 	target = frappe.new_doc("Sales Invoice")
 	timesheet = frappe.get_doc('Timesheet', source_name)
 
+	hours = flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours)
+	billing_amount = flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount)
+	billing_rate = billing_amount / hours
+
+	if customer:
+		target.customer = customer
+
+	if item_code:
+		target.append('items', {
+			'item_code': item_code,
+			'qty': hours,
+			'rate': billing_rate
+		})
+
 	target.append('timesheets', {
 		'time_sheet': timesheet.name,
-		'billing_hours': flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours),
-		'billing_amount': flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount)
+		'billing_hours': hours,
+		'billing_amount': billing_amount
 	})
 
 	target.run_method("calculate_billing_amount_for_timesheet")
+	target.run_method("set_missing_values")
 
 	return target