diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index d77f4a87171..62069673447 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -796,7 +796,11 @@ frappe.ui.form.on("Sales Invoice", { } }, +<<<<<<< HEAD onload: function (frm) { +======= + onload: function(frm) { +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) frm.redemption_conversion_factor = null; }, @@ -910,18 +914,34 @@ frappe.ui.form.on("Sales Invoice", { }, <<<<<<< HEAD +<<<<<<< HEAD +======= + project: function(frm) { + if (frm.doc.project) { + frm.events.add_timesheet_data(frm, { + project: frm.doc.project + }); + } + }, + +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) async add_timesheet_data(frm, kwargs) { if (kwargs === "Sales Invoice") { // called via frm.trigger() kwargs = Object(); } +<<<<<<< HEAD if (!Object.prototype.hasOwnProperty.call(kwargs, "project") && frm.doc.project) { +======= + if (!kwargs.hasOwnProperty("project") && frm.doc.project) { +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) kwargs.project = frm.doc.project; } const timesheets = await frm.events.get_timesheet_data(frm, kwargs); return frm.events.set_timesheet_data(frm, timesheets); +<<<<<<< HEAD ======= add_timesheet_row: function(frm, row, exchange_rate) { frm.add_child('timesheets', { @@ -931,12 +951,63 @@ frappe.ui.form.on("Sales Invoice", { 'billing_hours': row.billing_hours, 'billing_amount': flt(row.billing_amount) * flt(exchange_rate), 'timesheet_detail': row.name +======= + }, + + async get_timesheet_data(frm, kwargs) { + return frappe.call({ + method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data", + args: kwargs + }).then(r => { + if (!r.exc && r.message.length > 0) { + return r.message + } else { + return [] + } +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) }); - frm.refresh_field('timesheets'); + }, + + set_timesheet_data: function(frm, timesheets) { + frm.clear_table("timesheets") + timesheets.forEach(timesheet => { + if (frm.doc.currency != timesheet.currency) { + frappe.call({ + method: "erpnext.setup.utils.get_exchange_rate", + args: { + from_currency: timesheet.currency, + to_currency: frm.doc.currency + }, + callback: function(r) { + if (r.message) { + exchange_rate = r.message; + frm.events.append_time_log(frm, timesheet, exchange_rate); + } + } + }); + } else { + frm.events.append_time_log(frm, timesheet, 1.0); + } + }); + }, + + append_time_log: function(frm, time_log, exchange_rate) { + const row = frm.add_child("timesheets"); + row.activity_type = time_log.activity_type; + row.description = time_log.description; + row.time_sheet = time_log.time_sheet; + row.from_time = time_log.from_time; + row.to_time = time_log.to_time; + row.billing_hours = time_log.billing_hours; + row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate); + row.timesheet_detail = time_log.name; + + frm.refresh_field("timesheets"); frm.trigger("calculate_timesheet_totals"); >>>>>>> b57521a337 (feat: add `total_billing_hours` to Sales Invoice) }, +<<<<<<< HEAD async get_timesheet_data(frm, kwargs) { return frappe .call({ @@ -1067,6 +1138,58 @@ frappe.ui.form.on("Sales Invoice", { }, __("Get Items From") ); +======= + calculate_timesheet_totals: function(frm) { + frm.set_value("total_billing_amount", + frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0)); + frm.set_value("total_billing_hours", + frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0)); + }, + + refresh: function(frm) { + if (frm.doc.docstatus===0 && !frm.doc.is_return) { + frm.add_custom_button(__("Fetch Timesheet"), function() { + let d = new frappe.ui.Dialog({ + title: __("Fetch Timesheet"), + fields: [ + { + "label" : __("From"), + "fieldname": "from_time", + "fieldtype": "Date", + "reqd": 1, + }, + { + fieldtype: "Column Break", + fieldname: "col_break_1", + }, + { + "label" : __("To"), + "fieldname": "to_time", + "fieldtype": "Date", + "reqd": 1, + }, + { + "label" : __("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "default": frm.doc.project + }, + ], + primary_action: function() { + const data = d.get_values(); + frm.events.add_timesheet_data(frm, { + from_time: data.from_time, + to_time: data.to_time, + project: data.project + }); + d.hide(); + }, + primary_action_label: __("Get Timesheets") + }); + d.show(); + }); +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) } if (frm.doc.is_debit_note) { @@ -1111,37 +1234,13 @@ var select_loyalty_program = function (frm, loyalty_programs) { method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_dunning", frm: frm }); - }, - - calculate_timesheet_totals: function(frm) { - frm.set_value("total_billing_amount", - frm.doc.timesheets.reduce((a, b) => a + (b["billing_amount"] || 0.0), 0.0)); - frm.set_value("total_billing_hours", - frm.doc.timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0)); } }); frappe.ui.form.on("Sales Invoice Timesheet", { - time_sheet: function(frm, cdt, cdn){ - var d = locals[cdt][cdn]; - if(d.time_sheet) { - frappe.call({ - method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data", - args: { - "name": d.time_sheet, - "project": frm.doc.project || null - }, - callback: function(r) { - if(r.message) { - frappe.model.set_value(cdt, cdn, "billing_hours", r.message.billing_hours); - frappe.model.set_value(cdt, cdn, "billing_amount", r.message.billing_amount); - frappe.model.set_value(cdt, cdn, "timesheet_detail", r.message.timesheet_detail); - frm.trigger("calculate_timesheet_totals"); - } - } - }); - } + timesheets_remove(frm, cdt, cdn) { + frm.trigger("calculate_timesheet_totals"); } }); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ae12c717345..07df7fcb688 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1110,6 +1110,7 @@ class SalesInvoice(SellingController): self.set("timesheets", []) if self.project: for data in get_projectwise_timesheet_data(self.project): +<<<<<<< HEAD self.append( "timesheets", { @@ -1121,6 +1122,16 @@ class SalesInvoice(SellingController): "description": data.description, }, ) +======= + self.append('timesheets', { + 'time_sheet': data.time_sheet, + 'billing_hours': data.billing_hours, + 'billing_amount': data.billing_amount, + 'timesheet_detail': data.name, + 'activity_type': data.activity_type, + 'description': data.description + }) +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) self.calculate_billing_amount_for_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 1302fd38b26..13c45e78c03 100644 --- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json +++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json @@ -103,6 +103,7 @@ "fieldname": "section_break_11", "fieldtype": "Section Break", "label": "Reference" +<<<<<<< HEAD }, { "fieldname": "project_name", @@ -113,11 +114,17 @@ { "fieldname": "column_break_13", "fieldtype": "Column Break" +======= +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) } ], "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-03-27 13:10:36.562795", +======= + "modified": "2021-08-02 23:03:08.084930", +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Timesheet", diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 050891f27d0..db38a777648 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -298,8 +298,14 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to if from_time and to_time: condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s" +<<<<<<< HEAD query = f""" SELECT +======= + return frappe.db.sql(""" + SELECT + +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) tsd.name as name, tsd.parent as time_sheet, tsd.from_time as from_time, @@ -308,6 +314,7 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to tsd.billing_amount as billing_amount, tsd.activity_type as activity_type, tsd.description as description, +<<<<<<< HEAD ts.currency as currency, tsd.project_name as project_name FROM `tabTimesheet Detail` tsd @@ -343,6 +350,25 @@ def get_timesheet_detail_rate(timelog, currency): return timelog_detail.billing_amount * exchange_rate return timelog_detail.billing_amount +======= + ts.currency as currency + + FROM `tabTimesheet Detail` tsd + + INNER JOIN `tabTimesheet` ts + ON ts.name = tsd.parent + + WHERE tsd.parenttype = 'Timesheet' + AND tsd.docstatus=1 {0} + AND tsd.is_billable = 1 + AND tsd.sales_invoice is null + """.format(condition), { + 'project': project, + 'parent': parent, + 'from_time': from_time, + 'to_time': to_time + }, as_dict=1) +>>>>>>> 1110f88e5a (feat: refactor and enhance sales invoice timesheet) @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs