mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-18 14:32:13 +00:00
* 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 0bba6442c0)
# Conflicts:
# erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
* fix: merge conflict
---------
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
@@ -1593,7 +1593,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
for proj, value in projects.items():
|
for proj, value in projects.items():
|
||||||
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
|
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
|
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):
|
def validate_supplier_invoice(self):
|
||||||
if self.bill_date:
|
if self.bill_date:
|
||||||
|
|||||||
@@ -505,7 +505,8 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project",
|
"options": "Project",
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@@ -974,7 +975,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-07-19 12:12:42.449298",
|
"modified": "2024-10-28 15:06:19.246141",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
|||||||
@@ -1734,9 +1734,11 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
if self.project:
|
unique_projects = list(set([d.project for d in self.get("items") if d.project]))
|
||||||
project = frappe.get_doc("Project", self.project)
|
for p in unique_projects:
|
||||||
|
project = frappe.get_doc("Project", p)
|
||||||
project.update_billed_amount()
|
project.update_billed_amount()
|
||||||
|
project.calculate_gross_margin()
|
||||||
project.db_update()
|
project.db_update()
|
||||||
|
|
||||||
def verify_payment_amount_is_positive(self):
|
def verify_payment_amount_is_positive(self):
|
||||||
|
|||||||
@@ -812,7 +812,8 @@
|
|||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:parent.update_stock == 1",
|
"depends_on": "eval:parent.update_stock == 1",
|
||||||
@@ -927,7 +928,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-05-23 16:36:18.970862",
|
"modified": "2024-10-28 15:06:40.980995",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -93,14 +93,14 @@ class Project(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.copy_from_template()
|
self.copy_from_template() # nosemgrep
|
||||||
self.send_welcome_email()
|
self.send_welcome_email()
|
||||||
self.update_costing()
|
self.update_costing()
|
||||||
self.update_percent_complete()
|
self.update_percent_complete()
|
||||||
self.validate_from_to_dates("expected_start_date", "expected_end_date")
|
self.validate_from_to_dates("expected_start_date", "expected_end_date")
|
||||||
self.validate_from_to_dates("actual_start_date", "actual_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
|
Copy tasks from template
|
||||||
"""
|
"""
|
||||||
@@ -205,7 +205,7 @@ class Project(Document):
|
|||||||
self.db_update()
|
self.db_update()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.copy_from_template()
|
self.copy_from_template() # nosemgrep
|
||||||
if self.sales_order:
|
if self.sales_order:
|
||||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
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
|
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
|
||||||
|
|
||||||
def update_billed_amount(self):
|
def update_billed_amount(self):
|
||||||
|
# nosemgrep
|
||||||
total_billed_amount = frappe.db.sql(
|
total_billed_amount = frappe.db.sql(
|
||||||
"""select sum(base_net_total)
|
"""select sum(base_net_amount)
|
||||||
from `tabSales Invoice` where project = %s and docstatus=1""",
|
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,
|
self.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -676,31 +680,8 @@ def update_project_sales_billing():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Else simply fallback to Daily
|
# Else simply fallback to Daily
|
||||||
exists_query = "(SELECT 1 from `tab{doctype}` where docstatus = 1 and project = `tabProject`.name)"
|
for project in frappe.get_all("Project", filters={"status": ["!=", "Cancelled"]}):
|
||||||
project_map = {}
|
frappe.get_doc("Project", project.name).save()
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -751,7 +732,6 @@ def get_users_email(doc):
|
|||||||
def calculate_total_purchase_cost(project: str | None = None):
|
def calculate_total_purchase_cost(project: str | None = None):
|
||||||
if project:
|
if project:
|
||||||
pitem = qb.DocType("Purchase Invoice Item")
|
pitem = qb.DocType("Purchase Invoice Item")
|
||||||
frappe.qb.DocType("Purchase Invoice Item")
|
|
||||||
total_purchase_cost = (
|
total_purchase_cost = (
|
||||||
qb.from_(pitem)
|
qb.from_(pitem)
|
||||||
.select(Sum(pitem.base_net_amount))
|
.select(Sum(pitem.base_net_amount))
|
||||||
|
|||||||
Reference in New Issue
Block a user