From 8a42601e99a298782f43f252c64b1d692875d202 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 17:31:07 +0530 Subject: [PATCH 1/4] fix: update gross profit for returned invoices --- .../report/gross_profit/gross_profit.py | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index a9039a9cada..12a4ca76aab 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -440,6 +440,7 @@ class GrossProfitGenerator: if grouped_by_invoice: buying_amount = 0 + base_amount = 0 for row in reversed(self.si_list): if self.filters.get("group_by") == "Monthly": @@ -480,12 +481,11 @@ class GrossProfitGenerator: else: row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision) - if grouped_by_invoice: - if row.indent == 1.0: - buying_amount += row.buying_amount - elif row.indent == 0.0: - row.buying_amount = buying_amount - buying_amount = 0 + if grouped_by_invoice and row.indent == 0.0: + row.buying_amount = buying_amount + row.base_amount = base_amount + buying_amount = 0 + base_amount = 0 # get buying rate if flt(row.qty): @@ -495,11 +495,19 @@ class GrossProfitGenerator: if self.is_not_invoice_row(row): row.buying_rate, row.base_rate = 0.0, 0.0 + if self.is_not_invoice_row(row): + self.update_return_invoices(row) + + if grouped_by_invoice and row.indent == 1.0: + buying_amount += row.buying_amount + base_amount += row.base_amount + # calculate gross profit row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision) if row.base_amount: row.gross_profit_percent = flt( - (row.gross_profit / row.base_amount) * 100.0, self.currency_precision + (row.gross_profit / row.base_amount) * 100.0, + self.currency_precision, ) else: row.gross_profit_percent = 0.0 @@ -510,33 +518,24 @@ class GrossProfitGenerator: if self.grouped: self.get_average_rate_based_on_group_by() + def update_return_invoices(self, row): + if row.parent in self.returned_invoices and row.item_code in self.returned_invoices[row.parent]: + returned_item_rows = self.returned_invoices[row.parent][row.item_code] + for returned_item_row in returned_item_rows: + # returned_items 'qty' should be stateful + if returned_item_row.qty != 0: + if row.qty >= abs(returned_item_row.qty): + row.qty += returned_item_row.qty + returned_item_row.qty = 0 + else: + row.qty = 0 + returned_item_row.qty += row.qty + row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) + row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) + def get_average_rate_based_on_group_by(self): for key in list(self.grouped): - if self.filters.get("group_by") == "Invoice": - for row in self.grouped[key]: - if row.indent == 1.0: - if ( - row.parent in self.returned_invoices - and row.item_code in self.returned_invoices[row.parent] - ): - returned_item_rows = self.returned_invoices[row.parent][row.item_code] - for returned_item_row in returned_item_rows: - # returned_items 'qty' should be stateful - if returned_item_row.qty != 0: - if row.qty >= abs(returned_item_row.qty): - row.qty += returned_item_row.qty - returned_item_row.qty = 0 - else: - row.qty = 0 - returned_item_row.qty += row.qty - row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) - row.buying_amount = flt( - flt(row.qty) * flt(row.buying_rate), self.currency_precision - ) - if flt(row.qty) or row.base_amount: - row = self.set_average_rate(row) - self.grouped_data.append(row) - elif self.filters.get("group_by") == "Payment Term": + if self.filters.get("group_by") == "Payment Term": for i, row in enumerate(self.grouped[key]): invoice_portion = 0 @@ -556,7 +555,7 @@ class GrossProfitGenerator: new_row = self.set_average_rate(new_row) self.grouped_data.append(new_row) - else: + elif self.filters.get("group_by") != "Invoice": for i, row in enumerate(self.grouped[key]): if i == 0: new_row = row From 00403515a8ba005e5ac5c0e7a5ff88a5bda5b170 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 18:45:17 +0530 Subject: [PATCH 2/4] fix: gp for return invoice --- erpnext/accounts/report/gross_profit/gross_profit.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 12a4ca76aab..5ba1e41b624 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -526,11 +526,16 @@ class GrossProfitGenerator: if returned_item_row.qty != 0: if row.qty >= abs(returned_item_row.qty): row.qty += returned_item_row.qty + row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) returned_item_row.qty = 0 + returned_item_row.base_amount = 0 + else: row.qty = 0 + row.base_amount = 0 returned_item_row.qty += row.qty - row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) + returned_item_row.base_amount += row.base_amount + row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) def get_average_rate_based_on_group_by(self): From af5a3e5a48c7da329a078c3d0e177593061d123b Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 25 Nov 2024 19:05:08 +0530 Subject: [PATCH 3/4] fix: test case --- erpnext/accounts/report/gross_profit/test_gross_profit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 8bb5bfa81ff..6d060db1d15 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -421,12 +421,12 @@ class TestGrossProfit(IntegrationTestCase): "item_name": self.item, "warehouse": "Stores - _GP", "qty": 0.0, - "avg._selling_rate": 0.0, + "avg._selling_rate": 100, "valuation_rate": 0.0, - "selling_amount": -100.0, + "selling_amount": 0.0, "buying_amount": 0.0, - "gross_profit": -100.0, - "gross_profit_%": 100.0, + "gross_profit": 0.0, + "gross_profit_%": 0.0, } gp_entry = [x for x in data if x.parent_invoice == sinv.name] # Both items of Invoice should have '0' qty From eff9cd10cd6dcf47ad05311c5730da267482b870 Mon Sep 17 00:00:00 2001 From: vishakhdesai Date: Wed, 27 Nov 2024 12:43:34 +0530 Subject: [PATCH 4/4] fix: remove field precision in SO and PO for percentage fields --- .../doctype/purchase_order/purchase_order.js | 22 ++++----- .../doctype/sales_order/sales_order.js | 45 ++++++------------- .../doctype/sales_order/sales_order_list.js | 10 ++--- 3 files changed, 26 insertions(+), 51 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ad2c5f5bcfd..ac2d962d075 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -93,10 +93,7 @@ frappe.ui.form.on("Purchase Order", { get_materials_from_supplier: function (frm) { let po_details = []; - if ( - frm.doc.supplied_items && - (flt(frm.doc.per_received, precision("per_received")) == 100 || frm.doc.status === "Closed") - ) { + if (frm.doc.supplied_items && (flt(frm.doc.per_received) == 100 || frm.doc.status === "Closed")) { frm.doc.supplied_items.forEach((d) => { if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { po_details.push(d.name); @@ -332,8 +329,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( if (!["Closed", "Delivered"].includes(doc.status)) { if ( this.frm.doc.status !== "Closed" && - flt(this.frm.doc.per_received, precision("per_received")) < 100 && - flt(this.frm.doc.per_billed, precision("per_billed")) < 100 + flt(this.frm.doc.per_received) < 100 && + flt(this.frm.doc.per_billed) < 100 ) { if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) { this.frm.add_custom_button(__("Update Items"), () => { @@ -347,10 +344,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } if (this.frm.has_perm("submit")) { - if ( - flt(doc.per_billed, precision("per_billed")) < 100 || - flt(doc.per_received, precision("per_received")) < 100 - ) { + if (flt(doc.per_billed) < 100 || flt(doc.per_received) < 100) { if (doc.status != "On Hold") { this.frm.add_custom_button( __("Hold"), @@ -388,7 +382,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } if (doc.status != "Closed") { if (doc.status != "On Hold") { - if (flt(doc.per_received, precision("per_received")) < 100 && allow_receipt) { + if (flt(doc.per_received) < 100 && allow_receipt) { this.frm.add_custom_button( __("Purchase Receipt"), () => { @@ -419,7 +413,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } // Please do not add precision in the below flt function - if (flt(doc.per_billed, precision("per_billed")) < 100) + if (flt(doc.per_billed) < 100) this.frm.add_custom_button( __("Purchase Invoice"), () => { @@ -428,7 +422,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( __("Create") ); - if (flt(doc.per_billed, precision("per_billed")) < 100 && doc.status != "Delivered") { + if (flt(doc.per_billed) < 100 && doc.status != "Delivered") { this.frm.add_custom_button( __("Payment"), () => this.make_payment_entry(), @@ -436,7 +430,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( ); } - if (flt(doc.per_billed, precision("per_billed")) < 100) { + if (flt(doc.per_billed) < 100) { this.frm.add_custom_button( __("Payment Request"), function () { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 54c691e32bb..e4a264f77f2 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -43,8 +43,8 @@ frappe.ui.form.on("Sales Order", { if (frm.doc.docstatus === 1) { if ( frm.doc.status !== "Closed" && - flt(frm.doc.per_delivered, precision("per_delivered")) < 100 && - flt(frm.doc.per_billed, precision("per_billed")) < 100 && + flt(frm.doc.per_delivered) < 100 && + flt(frm.doc.per_billed) < 100 && frm.has_perm("write") ) { frm.add_custom_button(__("Update Items"), () => { @@ -61,7 +61,7 @@ frappe.ui.form.on("Sales Order", { if ( frm.doc.__onload && frm.doc.__onload.has_unreserved_stock && - flt(frm.doc.per_picked, precision("per_picked")) === 0 + flt(frm.doc.per_picked) === 0 ) { frm.add_custom_button( __("Reserve"), @@ -590,10 +590,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex __("Status") ); - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 || - flt(doc.per_billed, precision("per_billed")) < 100 - ) { + if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) { // close this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status")); } @@ -616,10 +613,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex ) && !this.frm.doc.skip_delivery_note; if (this.frm.has_perm("submit")) { - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 || - flt(doc.per_billed, precision("per_billed")) < 100 - ) { + if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) { // hold this.frm.add_custom_button( __("Hold"), @@ -637,8 +631,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if ( (!doc.__onload || !doc.__onload.has_reserved_stock) && - flt(doc.per_picked, precision("per_picked")) < 100 && - flt(doc.per_delivered, precision("per_delivered")) < 100 && + flt(doc.per_picked) < 100 && + flt(doc.per_delivered) < 100 && frappe.model.can_create("Pick List") ) { this.frm.add_custom_button( @@ -656,7 +650,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex // delivery note if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && + flt(doc.per_delivered) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery ) { @@ -678,10 +672,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // sales invoice - if ( - flt(doc.per_billed, precision("per_billed")) < 100 && - frappe.model.can_create("Sales Invoice") - ) { + if (flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice")) { this.frm.add_custom_button( __("Sales Invoice"), () => me.make_sales_invoice(), @@ -692,8 +683,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex // material request if ( (!doc.order_type || - ((order_is_a_sale || order_is_a_custom_sale) && - flt(doc.per_delivered, precision("per_delivered")) < 100)) && + ((order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered) < 100)) && frappe.model.can_create("Material Request") ) { this.frm.add_custom_button( @@ -718,10 +708,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // maintenance - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && - (order_is_maintenance || order_is_a_custom_sale) - ) { + if (flt(doc.per_delivered) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { if (frappe.model.can_create("Maintenance Visit")) { this.frm.add_custom_button( __("Maintenance Visit"), @@ -739,10 +726,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // project - if ( - flt(doc.per_delivered, precision("per_delivered")) < 100 && - frappe.model.can_create("Project") - ) { + if (flt(doc.per_delivered) < 100 && frappe.model.can_create("Project")) { this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create")); } @@ -770,10 +754,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } // payment request - if ( - flt(doc.per_billed, precision("per_billed", doc)) < - 100 + frappe.boot.sysdefaults.over_billing_allowance - ) { + if (flt(doc.per_billed) < 100 + frappe.boot.sysdefaults.over_billing_allowance) { this.frm.add_custom_button( __("Payment Request"), () => this.make_payment_request(), diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 65301750c04..e48af32a479 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -23,14 +23,14 @@ frappe.listview_settings["Sales Order"] = { return [__("Completed"), "green", "status,=,Completed"]; } else if (doc.advance_payment_status === "Requested") { return [__("To Pay"), "gray", "advance_payment_status,=,Requested"]; - } else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) { + } else if (!doc.skip_delivery_note && flt(doc.per_delivered) < 100) { if (frappe.datetime.get_diff(doc.delivery_date) < 0) { // not delivered & overdue return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"]; } else if (flt(doc.grand_total) === 0) { // not delivered (zeroount order) return [__("To Deliver"), "orange", "per_delivered,<,100|grand_total,=,0|status,!=,Closed"]; - } else if (flt(doc.per_billed, 2) < 100) { + } else if (flt(doc.per_billed) < 100) { // not delivered & not billed return [ __("To Deliver and Bill"), @@ -42,13 +42,13 @@ frappe.listview_settings["Sales Order"] = { return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"]; } } else if ( - flt(doc.per_delivered, 2) === 100 && + flt(doc.per_delivered) === 100 && flt(doc.grand_total) !== 0 && - flt(doc.per_billed, 2) < 100 + flt(doc.per_billed) < 100 ) { // to bill return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"]; - } else if (doc.skip_delivery_note && flt(doc.per_billed, 2) < 100) { + } else if (doc.skip_delivery_note && flt(doc.per_billed) < 100) { return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"]; } },