From 3815ebff8ae2dc29c956783174c7b05bcf147037 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 8 Sep 2016 18:17:43 +0530 Subject: [PATCH] Added %billed in timesheet --- .../doctype/sales_invoice/sales_invoice.js | 22 +++-- .../doctype/sales_invoice/sales_invoice.json | 83 +++++++------------ .../doctype/sales_invoice/sales_invoice.py | 17 ++-- .../sales_invoice_timesheet.json | 31 ++++++- .../projects/doctype/timesheet/timesheet.js | 4 +- .../projects/doctype/timesheet/timesheet.json | 70 ++++++++++++++-- .../projects/doctype/timesheet/timesheet.py | 61 +++++++++----- .../timesheet_detail/timesheet_detail.json | 82 +++++++++++------- 8 files changed, 240 insertions(+), 130 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 47ebc3da78..47f1a5c1c3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -481,16 +481,22 @@ frappe.ui.form.on('Sales Invoice', { }) frappe.ui.form.on('Sales Invoice Timesheet', { - time_sheet: function(frm){ - frm.call({ - method: "calculate_billing_amount_from_timesheet", - doc: frm.doc, + time_sheet: function(frm, cdt, cdn){ + var d = locals[cdt][cdn]; + frappe.call({ + method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data", + args: { + 'name': d.time_sheet, + 'project': frm.doc.project || null + }, callback: function(r, rt) { - refresh_field('total_billing_amount') + if(r.message){ + data = r.message; + frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours); + frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount); + frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail); + } } }) } }) - -cur_frm.add_fetch("time_sheet", "total_billing_hours", "billing_hours"); -cur_frm.add_fetch("time_sheet", "total_billing_amount", "billing_amount"); \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index ffe5237b7d..99b479348a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -337,6 +337,34 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "project", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_list_view": 0, + "label": "Project", + "length": 0, + "no_copy": 0, + "oldfieldname": "project_name", + "oldfieldtype": "Link", + "options": "Project", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1079,59 +1107,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "project_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "oldfieldname": "project_name", - "oldfieldtype": "Link", - "options": "Project", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -3892,7 +3867,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2016-09-07 03:04:15.927629", + "modified": "2016-09-08 09:05:02.895682", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 34296eabe8..f75a9af8ab 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -222,17 +222,19 @@ class SalesInvoice(SellingController): for d in self.timesheets: if d.time_sheet: timesheet = frappe.get_doc("Timesheet", d.time_sheet) - self.update_time_sheet_detail(timesheet, d) - timesheet.sales_invoice = sales_invoice + self.update_time_sheet_detail(timesheet, d, sales_invoice) + timesheet.calculate_total_amounts() + timesheet.calculate_percentage_billed() timesheet.flags.ignore_validate_update_after_submit = True timesheet.set_status() timesheet.save() - def update_time_sheet_detail(self, timesheet, args): + def update_time_sheet_detail(self, timesheet, args, sales_invoice): for data in timesheet.time_logs: - if (self.project and self.project == data.project) or \ - (not self.project and (data.billing_amount - data.billed_amount) > 0): - data.billed_amount = args.billing_amount + if (self.project and args.timesheet_detail == data.name) or \ + (not self.project and not data.sales_invoice) or \ + (not sales_invoice and data.sales_invoice == self.name): + data.sales_invoice = sales_invoice if self.project: return def on_update(self): @@ -480,7 +482,8 @@ class SalesInvoice(SellingController): self.append('timesheets', { 'time_sheet': data.parent, 'billing_hours': data.billing_hours, - 'billing_amount': data.billing_amt + 'billing_amount': data.billing_amt, + 'timesheet_detail': data.name }) self.calculate_billing_amount_from_timesheet() diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json index 25dd3cb845..e34accad9c 100644 --- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json +++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json @@ -14,6 +14,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "time_sheet", "fieldtype": "Link", "hidden": 0, @@ -40,6 +41,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "billing_hours", "fieldtype": "Float", "hidden": 0, @@ -65,6 +67,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "billing_amount", "fieldtype": "Currency", "hidden": 0, @@ -85,6 +88,32 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "timesheet_detail", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Timesheet Detail", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], "hide_heading": 0, @@ -97,7 +126,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-08-22 21:32:55.504103", + "modified": "2016-09-08 05:36:00.922319", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Timesheet", diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 42fe005dfc..a47b4ac44a 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -31,7 +31,7 @@ frappe.ui.form.on("Timesheet", { refresh: function(frm) { if(frm.doc.docstatus==1) { - if(!frm.doc.sales_invoice && frm.doc.total_billing_amount > 0){ + if(frm.doc.per_billed < 100){ frm.add_custom_button(__("Make Sales Invoice"), function() { frm.trigger("make_invoice") }, "icon-file-alt"); } @@ -42,7 +42,7 @@ frappe.ui.form.on("Timesheet", { } } - if(frm.doc.sales_invoice) { + if(frm.doc.per_billed > 0) { cur_frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false); } }, diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 1c174c81f8..caa3e7e1b0 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -571,23 +571,49 @@ "unique": 0 }, { - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "total_billed_amount", + "fieldname": "total_billed_hours", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Total Billed Amount", + "label": "Total Billed Hours", "length": 0, "no_copy": 0, "permlevel": 0, "precision": "", - "print_hide": 1, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_costing_amount", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Total Costing Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 1, "report_hide": 0, @@ -651,25 +677,51 @@ "unique": 0 }, { - "allow_on_submit": 0, + "allow_on_submit": 1, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "total_costing_amount", + "fieldname": "total_billed_amount", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Total Costing Amount", + "label": "Total Billed Amount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "per_billed", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "% Amount Billed", "length": 0, "no_copy": 0, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -765,7 +817,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-09-07 06:48:27.316087", + "modified": "2016-09-08 06:35:06.943066", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 68a8f4d555..867603b0a9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -25,10 +25,12 @@ class Timesheet(Document): self.validate_time_logs() self.update_cost() self.calculate_total_amounts() + self.calculate_percentage_billed() def calculate_total_amounts(self): self.total_hours = 0.0 self.total_billing_hours = 0.0 + self.total_billed_hours = 0.0 self.total_billing_amount = 0.0 self.total_costing_amount = 0.0 self.total_billed_amount = 0.0 @@ -37,11 +39,17 @@ class Timesheet(Document): self.update_billing_hours(d) self.total_hours += flt(d.hours) - self.total_billing_hours += flt(d.billing_hours) - if d.billable: + if d.billable: + self.total_billing_hours += flt(d.billing_hours) self.total_billing_amount += flt(d.billing_amount) self.total_costing_amount += flt(d.costing_amount) - self.total_billed_amount += flt(d.billed_amount) + self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0 + self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0 + + def calculate_percentage_billed(self): + self.per_billed = 0 + if self.total_billed_amount > 0 and self.total_billing_amount > 0: + self.per_billed = (self.total_billed_amount * 100) / self.total_billing_amount def update_billing_hours(self, args): if cint(args.billing_hours) == 0: @@ -54,7 +62,7 @@ class Timesheet(Document): "2": "Cancelled" }[str(self.docstatus or 0)] - if self.sales_invoice: + if self.per_billed == 100: self.status = "Billed" if self.salary_slip: @@ -253,9 +261,9 @@ def get_projectwise_timesheet_data(project, parent=None): if parent: cond = "and parent = %(parent)s" - return frappe.db.sql("""select parent, billing_hours, (billing_amount - billed_amount) as billing_amt + return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt from `tabTimesheet Detail` where docstatus=1 and project = %(project)s {0} - having billing_amt > 0""".format(cond), {'project': project, 'parent': parent}, as_dict=1) + and sales_invoice is null""".format(cond), {'project': project, 'parent': parent}, as_dict=1) @frappe.whitelist() def get_timesheet(doctype, txt, searchfield, start, page_len, filters): @@ -266,30 +274,41 @@ def get_timesheet(doctype, txt, searchfield, start, page_len, filters): condition = "and tsd.project = %(project)s" return frappe.db.sql("""select distinct tsd.parent from `tabTimesheet Detail` tsd, - `tabTimesheet` ts where ts.status in ('Submitted', 'Payslip') and - (tsd.billing_amount - tsd.billed_amount) > 0 and - tsd.docstatus = 1 and tsd.parent LIKE %(txt)s {condition} + `tabTimesheet` ts where + ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and + tsd.docstatus = 1 and ts.total_billing_amount > 0 + and tsd.parent LIKE %(txt)s {condition} order by tsd.parent limit %(start)s, %(page_len)s""" .format(condition=condition), { "txt": "%%%s%%" % frappe.db.escape(txt), "start": start, "page_len": page_len, 'project': filters.get("project") }) +@frappe.whitelist() +def get_timesheet_data(name, project): + if project and project!='': + data = get_projectwise_timesheet_data(project, name) + else: + data = frappe.get_all('Timesheet', + fields = ["(total_billing_amount - total_billed_amount) as billing_amt", "total_billing_hours as billing_hours"], filters = {'name': name}) + + return { + 'billing_hours': data[0].billing_hours, + 'billing_amount': data[0].billing_amt, + 'timesheet_detail': data[0].name if project and project!= '' else None + } + @frappe.whitelist() def make_sales_invoice(source_name, target=None): target = frappe.new_doc("Sales Invoice") + timesheet = frappe.get_doc('Timesheet', source_name) + + target.append('timesheets', { + 'time_sheet': timesheet.name, + 'billing_hours': flt(timesheet.total_billing_hours) - flt(timesheet.total_billed_hours), + 'billing_amount': flt(timesheet.total_billing_amount) - flt(timesheet.total_billed_amount) + }) - target.append("timesheets", get_mapped_doc("Timesheet", source_name, { - "Timesheet": { - "doctype": "Sales Invoice Timesheet", - "field_map": { - "total_billing_amount": "billing_amount", - "total_billing_hours": "billing_hours", - "name": "time_sheet" - }, - } - })) - target.run_method("calculate_billing_amount_from_timesheet") return target @@ -330,7 +349,7 @@ def get_activity_cost(employee=None, activity_type=None): ["costing_rate", "billing_rate"], as_dict=True) return rate[0] if rate else {} - + @frappe.whitelist() def get_events(start, end, filters=None): """Returns events for Gantt / Calendar view rendering. diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json index 560ce9f7a8..300fed30d5 100644 --- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json +++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json @@ -377,33 +377,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "billed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Billed Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -618,6 +591,59 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_invoice", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Sales Invoice", + "length": 0, + "no_copy": 1, + "options": "Sales Invoice", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], "hide_heading": 0, @@ -630,7 +656,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-09-07 02:55:22.545715", + "modified": "2016-09-08 03:24:26.221661", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet Detail",