[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