diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 7162aef8f224674c8be37eb96bc76e24b14ab65f..25958692a22594b66ce1339ba52f86642dadba54 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -669,20 +669,20 @@ class GrossProfitGenerator(object): 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 + return flt(row.qty) * incoming_amount else: return flt(row.qty) * self.get_average_buying_rate(row, item_code) 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 + from frappe.query_builder.functions import Avg delivery_note_item = frappe.qb.DocType("Delivery Note Item") query = ( frappe.qb.from_(delivery_note_item) - .select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty)) + .select(Avg(delivery_note_item.incoming_rate)) .where(delivery_note_item.docstatus == 1) .where(delivery_note_item.item_code == item_code) .where(delivery_note_item.against_sales_order == sales_order) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index c679fba7e3753f257f94fc5ec9c402fe20fb5249..1484212c70d902388bb4c3af10c87a98061b374c 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -466,3 +466,95 @@ class TestGrossProfit(FrappeTestCase): } gp_entry = [x for x in data if x.parent_invoice == sinv.name] self.assertDictContainsSubset(expected_entry, gp_entry[0]) + + def test_different_rates_in_si_and_dn(self): + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + """ + Test gp calculation when invoice and delivery note differ in qty and aren't connected + SO -- INV + | + DN + """ + se = make_stock_entry( + company=self.company, + item_code=self.item, + target=self.warehouse, + qty=3, + basic_rate=700, + 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": 700, + "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, + rate=800, + qty=10, + do_not_save=False, + do_not_submit=False, + ) + + from erpnext.selling.doctype.sales_order.sales_order import ( + make_delivery_note, + make_sales_invoice, + ) + + dn1 = make_delivery_note(so.name) + dn1.items[0].qty = 4 + dn1.items[0].rate = 800 + dn1.save().submit() + + dn2 = make_delivery_note(so.name) + dn2.items[0].qty = 6 + dn2.items[0].rate = 800 + dn2.save().submit() + + sinv = make_sales_invoice(so.name) + sinv.items[0].qty = 4 + sinv.items[0].rate = 800 + sinv.save().submit() + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + columns, data = execute(filters=filters) + 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": 800.0, + "valuation_rate": 700.0, + "selling_amount": 3200.0, + "buying_amount": 2800.0, + "gross_profit": 400.0, + "gross_profit_%": 12.5, + } + gp_entry = [x for x in data if x.parent_invoice == sinv.name] + self.assertDictContainsSubset(expected_entry, gp_entry[0])