Merge pull request #38319 from frappe/mergify/bp/version-15-hotfix/pr-38298
perf: optimize update_purchase_cost method (backport #38298)
This commit is contained in:
		
						commit
						2d6b2f7ae3
					
				| @ -525,7 +525,11 @@ class PurchaseInvoice(BuyingController): | ||||
| 		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() | ||||
| 
 | ||||
| @ -1260,7 +1264,10 @@ class PurchaseInvoice(BuyingController): | ||||
| 		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) | ||||
| @ -1279,13 +1286,21 @@ class PurchaseInvoice(BuyingController): | ||||
| 		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: | ||||
|  | ||||
| @ -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", | ||||
| @ -172,6 +173,14 @@ | ||||
|    "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", | ||||
| @ -179,7 +188,7 @@ | ||||
|  "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", | ||||
|  | ||||
| @ -351,5 +351,6 @@ erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_d | ||||
| erpnext.patches.v15_0.set_reserved_stock_in_bin | ||||
| 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 | ||||
|  | ||||
| @ -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"); | ||||
| 
 | ||||
| 
 | ||||
| @ -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({ | ||||
|  | ||||
| @ -4,11 +4,11 @@ | ||||
| 
 | ||||
| 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 import Interval | ||||
| from frappe.query_builder.functions import Count, CurDate, Date, UnixTimestamp | ||||
| from frappe.query_builder.functions import Count, CurDate, Date, Sum, UnixTimestamp | ||||
| from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime, today | ||||
| from frappe.utils.user import is_website_user | ||||
| 
 | ||||
| @ -249,12 +249,7 @@ class Project(Document): | ||||
| 			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): | ||||
| @ -695,3 +690,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), | ||||
| 		) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user