Skip to content

Commit

Permalink
Merge pull request #38318 from frappe/mergify/bp/version-14-hotfix/pr…
Browse files Browse the repository at this point in the history
…-38298

perf: optimize update_purchase_cost method  (backport #38298)
  • Loading branch information
ruthra-kumar authored Nov 24, 2023
2 parents dfdaad0 + 1d1af26 commit 7601861
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 16 deletions.
31 changes: 23 additions & 8 deletions erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,11 @@ def on_submit(self):
if self.update_stock == 1:
self.repost_future_sle_and_gle()

self.update_project()
if (
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project()

update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.update_advance_tax_references()

Expand Down Expand Up @@ -1269,7 +1273,10 @@ def on_cancel(self):
if self.update_stock == 1:
self.repost_future_sle_and_gle()

self.update_project()
if (
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project()
self.db_set("status", "Cancelled")

unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
Expand All @@ -1287,13 +1294,21 @@ def on_cancel(self):
self.update_advance_tax_references(cancel=1)

def update_project(self):
project_list = []
projects = frappe._dict()
for d in self.items:
if d.project and d.project not in project_list:
project = frappe.get_doc("Project", d.project)
project.update_purchase_costing()
project.db_update()
project_list.append(d.project)
if d.project:
if self.docstatus == 1:
projects[d.project] = projects.get(d.project, 0) + d.base_net_amount
elif self.docstatus == 2:
projects[d.project] = projects.get(d.project, 0) - d.base_net_amount

pj = frappe.qb.DocType("Project")
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)

def validate_supplier_invoice(self):
if self.bill_date:
Expand Down
11 changes: 10 additions & 1 deletion erpnext/buying/doctype/buying_settings/buying_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"po_required",
"pr_required",
"blanket_order_allowance",
"project_update_frequency",
"column_break_12",
"maintain_same_rate",
"set_landed_cost_based_on_purchase_invoice_rate",
Expand Down Expand Up @@ -172,14 +173,22 @@
"fieldname": "blanket_order_allowance",
"fieldtype": "Float",
"label": "Blanket Order Allowance (%)"
},
{
"default": "Each Transaction",
"description": "How often should Project be updated of Total Purchase Cost ?",
"fieldname": "project_update_frequency",
"fieldtype": "Select",
"label": "Update frequency of Project",
"options": "Each Transaction\nManual"
}
],
"icon": "fa fa-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-10-25 14:03:32.520418",
"modified": "2023-11-24 10:55:51.287327",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
Expand Down
1 change: 1 addition & 0 deletions erpnext/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,5 +351,6 @@ erpnext.patches.v14_0.rename_depreciation_amount_based_on_num_days_in_month_to_d
erpnext.patches.v14_0.add_default_for_repost_settings
erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation
erpnext.patches.v14_0.update_zero_asset_quantity_field
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
20 changes: 20 additions & 0 deletions erpnext/projects/doctype/project/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ frappe.ui.form.on("Project", {
frm.events.create_duplicate(frm);
}, __("Actions"));

frm.add_custom_button(__('Update Total Purchase Cost'), () => {
frm.events.update_total_purchase_cost(frm);
}, __("Actions"));

frm.trigger("set_project_status_button");


Expand All @@ -92,6 +96,22 @@ frappe.ui.form.on("Project", {

},

update_total_purchase_cost: function(frm) {
frappe.call({
method: "erpnext.projects.doctype.project.project.recalculate_project_total_purchase_cost",
args: {project: frm.doc.name},
freeze: true,
freeze_message: __('Recalculating Purchase Cost against this Project...'),
callback: function(r) {
if (r && !r.exc) {
frappe.msgprint(__('Total Purchase Cost has been updated'));
frm.refresh();
}
}

});
},

set_project_status_button: function(frm) {
frm.add_custom_button(__('Set Project Status'), () => {
let d = new frappe.ui.Dialog({
Expand Down
36 changes: 29 additions & 7 deletions erpnext/projects/doctype/project/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

import frappe
from email_reply_parser import EmailReplyParser
from frappe import _
from frappe import _, qb
from frappe.desk.reportview import get_match_cond
from frappe.model.document import Document
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime, today

from erpnext import get_default_company
Expand Down Expand Up @@ -246,12 +247,7 @@ def calculate_gross_margin(self):
self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100

def update_purchase_costing(self):
total_purchase_cost = frappe.db.sql(
"""select sum(base_net_amount)
from `tabPurchase Invoice Item` where project = %s and docstatus=1""",
self.name,
)

total_purchase_cost = calculate_total_purchase_cost(self.name)
self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0

def update_sales_amount(self):
Expand Down Expand Up @@ -671,3 +667,29 @@ def get_holiday_list(company=None):

def get_users_email(doc):
return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")]


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))
.where((pitem.project == project) & (pitem.docstatus == 1))
.run(as_list=True)
)
return total_purchase_cost
return None


@frappe.whitelist()
def recalculate_project_total_purchase_cost(project: str | None = None):
if project:
total_purchase_cost = calculate_total_purchase_cost(project)
frappe.db.set_value(
"Project",
project,
"total_purchase_cost",
(total_purchase_cost and total_purchase_cost[0][0] or 0),
)

0 comments on commit 7601861

Please sign in to comment.