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 00000000000..90cc6d6e869 Binary files /dev/null and b/erpnext/docs/assets/img/project/timesheet/make_invoice_from_timesheet.gif 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 9be2bad0e21..4dbdcbb6dc3 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 @@ In the Timesheet, if "Is Billable" is checked, you will find option to create Sa Sales Invoice +Sales Invoice timesheet + ####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 955a2b0fe34..2588d566d56 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -54,19 +54,18 @@ class TestTimesheet(unittest.TestCase): 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 9b330e7811b..62ae1ed62b6 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -57,10 +57,37 @@ frappe.ui.form.on("Timesheet", { }, 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 16abd2499cc..ad566d5ac11 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -323,17 +323,32 @@ def get_timesheet_data(name, project): } @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