From be5edd329f3935444704ae12ac87ace339161952 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 19 Jan 2023 09:39:43 +0000 Subject: [PATCH 1/4] fix(gp): fetch buying amount from dn related to so (cherry picked from commit e8e20da78eeece6b5fad895c6aa6f50f57e561e4) --- .../report/gross_profit/gross_profit.py | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 6bd3ee5aa6b..cb0a0ed75b0 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -585,10 +585,36 @@ class GrossProfitGenerator(object): return self.calculate_buying_amount_from_sle( row, my_sle, parenttype, parent, item_row, item_code ) + elif row.sales_order and row.so_detail: + incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code) + if incoming_amount: + return incoming_amount else: return flt(row.qty) * self.get_average_buying_rate(row, item_code) - return 0.0 + return flt(row.qty) * self.get_average_buying_rate(row, item_code) + + def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code): + from frappe.query_builder.functions import Sum + delivery_note = frappe.qb.DocType("Delivery Note") + delivery_note_item = frappe.qb.DocType("Delivery Note Item") + + query = ( + frappe.qb.from_(delivery_note) + .inner_join(delivery_note_item) + .on(delivery_note.name == delivery_note_item.parent) + .select( + Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty) + ) + .where(delivery_note.docstatus == 1) + .where(delivery_note_item.item_code == item_code) + .where(delivery_note_item.against_sales_order == sales_order) + .where(delivery_note_item.so_detail == so_detail) + .groupby(delivery_note_item.item_code) + ) + + incoming_amount = query.run() + return flt(incoming_amount[0][0]) if incoming_amount else 0 def get_average_buying_rate(self, row, item_code): args = row @@ -665,7 +691,8 @@ class GrossProfitGenerator(object): `tabSales Invoice`.territory, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description, `tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, - `tabSales Invoice Item`.brand, `tabSales Invoice Item`.dn_detail, + `tabSales Invoice Item`.brand, `tabSales Invoice Item`.so_detail, + `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return, From 0d8a4bf936fab8c7bd50c53eecbf8a4d830f8702 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 19 Jan 2023 10:25:05 +0000 Subject: [PATCH 2/4] chore: linting issue (cherry picked from commit ef90e249312dce414c6dbe088b8880c47403b3cc) --- erpnext/accounts/report/gross_profit/gross_profit.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index cb0a0ed75b0..77bc82590b8 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -596,6 +596,7 @@ class GrossProfitGenerator(object): def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code): from frappe.query_builder.functions import Sum + delivery_note = frappe.qb.DocType("Delivery Note") delivery_note_item = frappe.qb.DocType("Delivery Note Item") @@ -603,9 +604,7 @@ class GrossProfitGenerator(object): frappe.qb.from_(delivery_note) .inner_join(delivery_note_item) .on(delivery_note.name == delivery_note_item.parent) - .select( - Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty) - ) + .select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty)) .where(delivery_note.docstatus == 1) .where(delivery_note_item.item_code == item_code) .where(delivery_note_item.against_sales_order == sales_order) From b72a35a622ac5abcca5e6bba4760571059612cb3 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 30 Jan 2023 10:35:43 +0000 Subject: [PATCH 3/4] feat(gp): test for inv and dn related via so (cherry picked from commit 1f6ab86a659d9745fa8971656a54c19bc784de2b) --- .../report/gross_profit/test_gross_profit.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index d9febb74fd4..027f6a842eb 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -301,3 +301,79 @@ class TestGrossProfit(FrappeTestCase): columns, data = execute(filters=filters) self.assertGreater(len(data), 0) + + def test_order_connected_dn_and_inv(self): + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + """ + Test gp calculation when invoice and delivery note aren't directly connected. + SO -- INV + | + DN + """ + se = make_stock_entry( + company=self.company, + item_code=self.item, + target=self.warehouse, + qty=3, + basic_rate=100, + do_not_submit=True, + ) + item = se.items[0] + se.append( + "items", + { + "item_code": item.item_code, + "s_warehouse": item.s_warehouse, + "t_warehouse": item.t_warehouse, + "qty": 10, + "basic_rate": 200, + "conversion_factor": item.conversion_factor or 1.0, + "transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0), + "serial_no": item.serial_no, + "batch_no": item.batch_no, + "cost_center": item.cost_center, + "expense_account": item.expense_account, + }, + ) + se = se.save().submit() + + so = make_sales_order( + customer=self.customer, + company=self.company, + warehouse=self.warehouse, + item=self.item, + qty=4, + do_not_save=False, + do_not_submit=False, + ) + + from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice + make_delivery_note(so.name).submit() + sinv = make_sales_invoice(so.name).submit() + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + columns, data = execute(filters=filters) + + # Without Delivery Note, buying rate should be 150 + expected_entry = { + "parent_invoice": sinv.name, + "currency": "INR", + "sales_invoice": self.item, + "customer": self.customer, + "posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()), + "item_code": self.item, + "item_name": self.item, + "warehouse": "Stores - _GP", + "qty": 4.0, + "avg._selling_rate": 100.0, + "valuation_rate": 125.0, + "selling_amount": 400.0, + "buying_amount": 500.0, + "gross_profit": -100.0, + "gross_profit_%": -25.0, + } + gp_entry = [x for x in data if x.parent_invoice == sinv.name] + self.assertDictContainsSubset(expected_entry, gp_entry[0]) \ No newline at end of file From ac6186e16f1e34f90f13e692dc36ed5a9a368df8 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 30 Jan 2023 10:45:08 +0000 Subject: [PATCH 4/4] chore: linting issues (cherry picked from commit d69c8393692fa8ecc08ff7f10ca66a1c5a9f3b0f) --- .../accounts/report/gross_profit/test_gross_profit.py | 11 +++++++---- 1 file changed, 7 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 027f6a842eb..06a173ee35a 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -304,6 +304,7 @@ class TestGrossProfit(FrappeTestCase): def test_order_connected_dn_and_inv(self): from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + """ Test gp calculation when invoice and delivery note aren't directly connected. SO -- INV @@ -347,7 +348,11 @@ class TestGrossProfit(FrappeTestCase): do_not_submit=False, ) - from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice + from erpnext.selling.doctype.sales_order.sales_order import ( + make_delivery_note, + make_sales_invoice, + ) + make_delivery_note(so.name).submit() sinv = make_sales_invoice(so.name).submit() @@ -356,8 +361,6 @@ class TestGrossProfit(FrappeTestCase): ) columns, data = execute(filters=filters) - - # Without Delivery Note, buying rate should be 150 expected_entry = { "parent_invoice": sinv.name, "currency": "INR", @@ -376,4 +379,4 @@ class TestGrossProfit(FrappeTestCase): "gross_profit_%": -25.0, } gp_entry = [x for x in data if x.parent_invoice == sinv.name] - self.assertDictContainsSubset(expected_entry, gp_entry[0]) \ No newline at end of file + self.assertDictContainsSubset(expected_entry, gp_entry[0])