From 93d0db29100735cca01228ccf943a5abeb772bf9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:36:39 +0530 Subject: [PATCH] fix: Calculate gross margin on update of project costing from invoices (backport #43876) (#43900) * fix: Calculate gross margin on update of project costing from invoices (#43876) * fix: Calculate gross margin on update of project costing from invoices * chore: linter issues (cherry picked from commit 0bba6442c0f64bbf36f79d0df27bb922275d96af) # Conflicts: # erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json * fix: merge conflict --------- Co-authored-by: Nabin Hait --- .../purchase_invoice/purchase_invoice.py | 6 ++- .../purchase_invoice_item.json | 5 ++- .../doctype/sales_invoice/sales_invoice.py | 6 ++- .../sales_invoice_item.json | 5 ++- erpnext/projects/doctype/project/project.py | 42 +++++-------------- 5 files changed, 26 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c9f3b220cc6..b46fb9e9b30 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1593,7 +1593,11 @@ class PurchaseInvoice(BuyingController): for proj, value in projects.items(): res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run() current_purchase_cost = res and res[0][0] or 0 - frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) + # frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) + project_doc = frappe.get_doc("Project", proj) + project_doc.total_purchase_cost = current_purchase_cost + value + project_doc.calculate_gross_margin() + project_doc.db_update() def validate_supplier_invoice(self): if self.bill_date: diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 8a2ba36cf62..258cc10d4ec 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -505,7 +505,8 @@ "fieldtype": "Link", "label": "Project", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "allow_on_submit": 1, @@ -974,7 +975,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-07-19 12:12:42.449298", + "modified": "2024-10-28 15:06:19.246141", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ebdbb01fdc2..eb43de47a54 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1734,9 +1734,11 @@ class SalesInvoice(SellingController): ) def update_project(self): - if self.project: - project = frappe.get_doc("Project", self.project) + unique_projects = list(set([d.project for d in self.get("items") if d.project])) + for p in unique_projects: + project = frappe.get_doc("Project", p) project.update_billed_amount() + project.calculate_gross_margin() project.db_update() def verify_payment_amount_is_positive(self): diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 932bc8e49d4..8b93f56a4c2 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -812,7 +812,8 @@ "fieldname": "project", "fieldtype": "Link", "label": "Project", - "options": "Project" + "options": "Project", + "search_index": 1 }, { "depends_on": "eval:parent.update_stock == 1", @@ -927,7 +928,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-05-23 16:36:18.970862", + "modified": "2024-10-28 15:06:40.980995", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 7870a2ace73..f1512501e76 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -93,14 +93,14 @@ class Project(Document): def validate(self): if not self.is_new(): - self.copy_from_template() + self.copy_from_template() # nosemgrep self.send_welcome_email() self.update_costing() self.update_percent_complete() self.validate_from_to_dates("expected_start_date", "expected_end_date") self.validate_from_to_dates("actual_start_date", "actual_end_date") - def copy_from_template(self): + def copy_from_template(self): # nosemgrep """ Copy tasks from template """ @@ -205,7 +205,7 @@ class Project(Document): self.db_update() def after_insert(self): - self.copy_from_template() + self.copy_from_template() # nosemgrep if self.sales_order: frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) @@ -324,9 +324,13 @@ class Project(Document): self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0 def update_billed_amount(self): + # nosemgrep total_billed_amount = frappe.db.sql( - """select sum(base_net_total) - from `tabSales Invoice` where project = %s and docstatus=1""", + """select sum(base_net_amount) + from `tabSales Invoice Item` si_item, `tabSales Invoice` si + where si_item.parent = si.name + and if(si_item.project, si_item.project, si.project) = %s + and si.docstatus=1""", self.name, ) @@ -676,31 +680,8 @@ def update_project_sales_billing(): return # Else simply fallback to Daily - exists_query = "(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)" - project_map = {} - for project_details in frappe.db.sql( - """ - SELECT name, 1 as order_exists, null as invoice_exists from `tabProject` where - exists {order_exists} - union - SELECT name, null as order_exists, 1 as invoice_exists from `tabProject` where - exists {invoice_exists} - """.format( - order_exists=exists_query.format(doctype="Sales Order"), - invoice_exists=exists_query.format(doctype="Sales Invoice"), - ), - as_dict=True, - ): - project = project_map.setdefault( - project_details.name, frappe.get_doc("Project", project_details.name) - ) - if project_details.order_exists: - project.update_sales_amount() - if project_details.invoice_exists: - project.update_billed_amount() - - for project in project_map.values(): - project.save() + for project in frappe.get_all("Project", filters={"status": ["!=", "Cancelled"]}): + frappe.get_doc("Project", project.name).save() @frappe.whitelist() @@ -751,7 +732,6 @@ def get_users_email(doc): def calculate_total_purchase_cost(project: str | None = None): if project: pitem = qb.DocType("Purchase Invoice Item") - frappe.qb.DocType("Purchase Invoice Item") total_purchase_cost = ( qb.from_(pitem) .select(Sum(pitem.base_net_amount))