mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-14 07:01:56 +00:00
fix: restrict already invoiced qty in intercompany purchase invoice (#55768)
This commit is contained in:
@@ -179,12 +179,31 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
: "Inter Company Purchase Invoice";
|
: "Inter Company Purchase Invoice";
|
||||||
|
|
||||||
me.frm.add_custom_button(
|
me.frm.add_custom_button(
|
||||||
button_label,
|
__(button_label),
|
||||||
function () {
|
function () {
|
||||||
me.make_inter_company_invoice();
|
me.make_inter_company_invoice();
|
||||||
},
|
},
|
||||||
__("Create")
|
__("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"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2793,7 +2793,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
|||||||
"rate": "rate",
|
"rate": "rate",
|
||||||
},
|
},
|
||||||
"postprocess": update_item,
|
"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"]:
|
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||||
@@ -2834,10 +2834,19 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
|||||||
set_missing_values,
|
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
|
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"
|
reference_field = "inter_company_invoice_reference"
|
||||||
if doctype == "Purchase Order":
|
if doctype == "Purchase Order":
|
||||||
reference_field = "inter_company_order_reference"
|
reference_field = "inter_company_order_reference"
|
||||||
@@ -2850,20 +2859,19 @@ def get_received_items(reference_name, doctype, reference_fieldname):
|
|||||||
target_doctypes = frappe.get_all(
|
target_doctypes = frappe.get_all(
|
||||||
doctype,
|
doctype,
|
||||||
filters=filters,
|
filters=filters,
|
||||||
as_list=True,
|
pluck="name",
|
||||||
)
|
)
|
||||||
|
received_items_map = {}
|
||||||
if target_doctypes:
|
if target_doctypes:
|
||||||
target_doctypes = list(target_doctypes[0])
|
received_items_data = frappe.get_all(
|
||||||
|
|
||||||
received_items_map = frappe._dict(
|
|
||||||
frappe.get_all(
|
|
||||||
doctype + " Item",
|
doctype + " Item",
|
||||||
filters={"parent": ("in", target_doctypes)},
|
filters={"parent": ("in", target_doctypes)},
|
||||||
fields=[reference_fieldname, "qty"],
|
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
|
return received_items_map
|
||||||
|
|
||||||
|
|||||||
@@ -3000,6 +3000,67 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory
|
frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory
|
||||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", old_negative_stock)
|
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", old_negative_stock)
|
||||||
|
|
||||||
|
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_sle_for_target_warehouse(self):
|
def test_sle_for_target_warehouse(self):
|
||||||
se = make_stock_entry(
|
se = make_stock_entry(
|
||||||
item_code="138-CMS Shoe",
|
item_code="138-CMS Shoe",
|
||||||
|
|||||||
Reference in New Issue
Block a user