fix: restrict already invoiced qty in intercompany purchase invoice (#55754)

This commit is contained in:
Pandiyan P
2026-06-09 13:13:23 +05:30
committed by GitHub
parent 2f5b93e308
commit a5c23a3d16
3 changed files with 98 additions and 12 deletions

View File

@@ -180,12 +180,31 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
: "Inter Company Purchase Invoice";
me.frm.add_custom_button(
button_label,
__(button_label),
function () {
me.make_inter_company_invoice();
},
__("Create")
);
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_received_items",
args: {
reference_name: me.frm.doc.name,
doctype: "Purchase Invoice",
reference_fieldname: "sales_invoice_item",
},
callback: function (r) {
if (r.exc) return;
const received_items = r.message || {};
const has_pending_qty = me.frm.doc.items.some(
(item) => flt(item.qty) - flt(received_items[item.name] || 0) > 0
);
if (!has_pending_qty) {
me.frm.remove_custom_button(__(button_label), __("Create"));
}
},
});
}
}

View File

@@ -2526,7 +2526,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
"rate": "rate",
},
"postprocess": update_item,
"condition": lambda doc: doc.qty > 0,
"condition": lambda doc: doc.qty - received_items.get(doc.name, 0.0) > 0,
}
if doctype in ["Sales Invoice", "Sales Order"]:
@@ -2566,11 +2566,18 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
target_doc,
set_missing_values,
)
if not doclist.get("items"):
frappe.throw(
_(
"Cannot create Intercompany {0}. All items in the source {1} have already been fully invoiced. "
"Please check the existing linked {2}s."
).format(target_doctype, doctype, target_doctype)
)
return doclist
def get_received_items(reference_name, doctype, reference_fieldname):
@frappe.whitelist()
def get_received_items(reference_name: str, doctype: str, reference_fieldname: str):
reference_field = "inter_company_invoice_reference"
if doctype == "Purchase Order":
reference_field = "inter_company_order_reference"
@@ -2583,20 +2590,19 @@ def get_received_items(reference_name, doctype, reference_fieldname):
target_doctypes = frappe.get_all(
doctype,
filters=filters,
as_list=True,
pluck="name",
)
received_items_map = {}
if target_doctypes:
target_doctypes = list(target_doctypes[0])
received_items_map = frappe._dict(
frappe.get_all(
received_items_data = frappe.get_all(
doctype + " Item",
filters={"parent": ("in", target_doctypes)},
fields=[reference_fieldname, "qty"],
as_list=1,
)
)
for item in received_items_data:
key = item.get(reference_fieldname)
if key:
received_items_map[key] = received_items_map.get(key, 0.0) + flt(item.qty)
return received_items_map

View File

@@ -2690,6 +2690,67 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
def test_restrict_inter_company_pi_when_sales_invoice_qty_fully_consumed(self):
item_code_1 = "_Test IC Item 1"
item_code_2 = "_Test IC Item 2"
create_item(item_code_1, is_stock_item=1)
create_item(item_code_2, is_stock_item=1)
si = create_sales_invoice(
company="Wind Power LLC",
customer="_Test Internal Customer",
item_code=item_code_1,
debit_to="Debtors - WP",
warehouse="Stores - WP",
income_account="Sales - WP",
expense_account="Cost of Goods Sold - WP",
cost_center="Main - WP",
currency="USD",
qty=3,
do_not_save=1,
)
si.selling_price_list = "_Test Price List Rest of the World"
si.append(
"items",
{
"item_code": item_code_2,
"item_name": item_code_2,
"description": item_code_2,
"warehouse": "Stores - WP",
"qty": 2,
"uom": "Nos",
"stock_uom": "Nos",
"rate": 100,
"price_list_rate": 100,
"income_account": "Sales - WP",
"expense_account": "Cost of Goods Sold - WP",
"cost_center": "Main - WP",
"conversion_factor": 1,
},
)
si.submit()
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
for item in target_doc.items:
item.update(
{
"expense_account": "Cost of Goods Sold - _TC1",
"cost_center": "Main - _TC1",
}
)
target_doc.submit()
self.assertEqual(len(target_doc.items), 2)
self.assertEqual([item.qty for item in target_doc.items], [3, 2])
with self.assertRaisesRegex(
frappe.ValidationError,
"already been fully invoiced",
):
make_inter_company_transaction("Sales Invoice", si.name)
def test_inter_company_transaction_without_default_warehouse(self):
"Check mapping (expense account) of inter company SI to PI in absence of default warehouse."
# setup