From 54030eb72110c65b496c49ebec0cf845d2578ac7 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Thu, 31 Jul 2025 11:42:41 +0530 Subject: [PATCH 1/4] fix: incorrect pending qty when creating sales invoice from sales order (cherry picked from commit a5138f48998cb77cfa7600a9f81717e255d61628) --- .../selling/doctype/sales_order/sales_order.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 75260a5a3fa..0118e89cd3c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1144,6 +1144,17 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a target.debit_to = get_party_account("Customer", source.customer, source.company) def update_item(source, target, source_parent): + def get_billed_qty(so_item_name): + from frappe.query_builder.functions import Sum + + table = frappe.qb.DocType("Sales Invoice Item") + query = ( + frappe.qb.from_(table) + .select(Sum(table.qty).as_("qty")) + .where((table.docstatus == 1) & (table.so_detail == so_item_name)) + ) + return query.run(pluck="qty")[0] or 0 + if source_parent.has_unit_price_items: # 0 Amount rows (as seen in Unit Price Items) should be mapped as it is pending_amount = flt(source.amount) - flt(source.billed_amt) @@ -1153,8 +1164,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a target.base_amount = target.amount * flt(source_parent.conversion_rate) target.qty = ( - target.amount / flt(source.rate) - if (source.rate and source.billed_amt) + source.qty - get_billed_qty(source.name) + if (source.qty and source.billed_amt) else (source.qty if is_unit_price_row(source) else source.qty - source.returned_qty) ) From b403b6c7b417dc1ed45770230d0ce5b117f73f04 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Mon, 25 Aug 2025 21:04:51 +0530 Subject: [PATCH 2/4] test: add pending quantity check for invoice creation (cherry picked from commit e5d4b4f0f02f9de83cda794b783791a194db6a44) # Conflicts: # erpnext/selling/doctype/sales_order/test_sales_order.py --- .../doctype/sales_order/test_sales_order.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index c5898a5ffaa..9556a7a9480 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2396,6 +2396,66 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): self.assertFalse(so.per_billed) self.assertEqual(so.status, "To Deliver and Bill") +<<<<<<< HEAD +======= + def test_item_tax_transfer_from_sales_to_purchase(self): + from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order + + item_tax = frappe.new_doc("Item Tax Template") + item_tax.title = "Test Item Tax Template" + item_tax.company = "_Test Company" + item_tax.append("taxes", {"tax_type": "_Test Account Service Tax - _TC", "tax_rate": 2}) + item_tax.save() + + item_group = frappe.get_doc("Item Group", "_Test Item Group") + item_group.append("taxes", {"item_tax_template": "Test Item Tax Template - _TC"}) + item_group.save() + + so = make_sales_order(item_code="_Test Item", qty=1, do_not_submit=1) + so.append( + "taxes", + { + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "TDS", + "doctype": "Sales Taxes and Charges", + "rate": 2, + }, + ) + so.submit() + + po = make_purchase_order(so.name, selected_items=so.items) + po.supplier = "_Test Supplier" + po.items[0].rate = 100 + po.submit() + self.assertEqual(po.taxes[0].tax_amount, 2) + + def test_pending_quantity_after_update_item_during_invoice_creation(self): + so = make_sales_order(qty=30, rate=100) + + si1 = make_sales_invoice(so.name) + si1.update_stock = 1 + si1.get("items")[0].qty = 10 + si1.insert() + si1.submit() + + first_item_of_so = so.get("items")[0] + trans_item = json.dumps( + [ + { + "item_code": first_item_of_so.item_code, + "rate": 1000, + "qty": first_item_of_so.qty, + "docname": first_item_of_so.name, + }, + ] + ) + update_child_qty_rate("Sales Order", trans_item, so.name) + + si2 = make_sales_invoice(so.name) + self.assertEqual(si2.items[0].qty, 20) + +>>>>>>> e5d4b4f0f0 (test: add pending quantity check for invoice creation) def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") From 1380ee21b62c3389184dfa55f2231807ede9a14a Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Mon, 25 Aug 2025 21:23:14 +0530 Subject: [PATCH 3/4] chore: remove update_stock (cherry picked from commit 368dbe3bbf138aa892248a9e269e29dde04e1263) --- erpnext/selling/doctype/sales_order/test_sales_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 9556a7a9480..ff5b8e07593 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2434,7 +2434,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): so = make_sales_order(qty=30, rate=100) si1 = make_sales_invoice(so.name) - si1.update_stock = 1 si1.get("items")[0].qty = 10 si1.insert() si1.submit() From 69835732432580b3623c65cb7879922198c7d530 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 26 Aug 2025 11:06:55 +0530 Subject: [PATCH 4/4] chore: resolve conflict --- .../doctype/sales_order/test_sales_order.py | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index ff5b8e07593..04a3a989cae 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2396,40 +2396,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): self.assertFalse(so.per_billed) self.assertEqual(so.status, "To Deliver and Bill") -<<<<<<< HEAD -======= - def test_item_tax_transfer_from_sales_to_purchase(self): - from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order - - item_tax = frappe.new_doc("Item Tax Template") - item_tax.title = "Test Item Tax Template" - item_tax.company = "_Test Company" - item_tax.append("taxes", {"tax_type": "_Test Account Service Tax - _TC", "tax_rate": 2}) - item_tax.save() - - item_group = frappe.get_doc("Item Group", "_Test Item Group") - item_group.append("taxes", {"item_tax_template": "Test Item Tax Template - _TC"}) - item_group.save() - - so = make_sales_order(item_code="_Test Item", qty=1, do_not_submit=1) - so.append( - "taxes", - { - "account_head": "_Test Account Service Tax - _TC", - "charge_type": "On Net Total", - "description": "TDS", - "doctype": "Sales Taxes and Charges", - "rate": 2, - }, - ) - so.submit() - - po = make_purchase_order(so.name, selected_items=so.items) - po.supplier = "_Test Supplier" - po.items[0].rate = 100 - po.submit() - self.assertEqual(po.taxes[0].tax_amount, 2) - def test_pending_quantity_after_update_item_during_invoice_creation(self): so = make_sales_order(qty=30, rate=100) @@ -2454,7 +2420,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): si2 = make_sales_invoice(so.name) self.assertEqual(si2.items[0].qty, 20) ->>>>>>> e5d4b4f0f0 (test: add pending quantity check for invoice creation) def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings")