diff --git a/rmax_custom/api/delivery_note.py b/rmax_custom/api/delivery_note.py index 248c230..5298e87 100644 --- a/rmax_custom/api/delivery_note.py +++ b/rmax_custom/api/delivery_note.py @@ -390,6 +390,13 @@ def _build_consolidated_standard_si(dns, buckets): si.customer_name = head.customer_name si.company = head.company si.currency = head.currency + # Carry the source DN's price list (e.g. "Inter Company Price") onto the SI. + # Without this the SI falls back to the customer/default selling price list + # (e.g. "Retail Price"), which mismatches the DNs. Item rates are still set + # explicitly from the netted DN amounts below, so this only fixes the + # price-list field + price_list_rate, it does not re-price the invoice. + if head.get("selling_price_list"): + si.selling_price_list = head.selling_price_list si.posting_date = frappe.utils.today() si.set_posting_time = 1 si.update_stock = 0 diff --git a/rmax_custom/public/js/sales_invoice_doctype.js b/rmax_custom/public/js/sales_invoice_doctype.js index 791f848..8112b7a 100644 --- a/rmax_custom/public/js/sales_invoice_doctype.js +++ b/rmax_custom/public/js/sales_invoice_doctype.js @@ -234,3 +234,35 @@ function _rmax_apply_branch_payment_accounts(frm, cdt, cdn) { }); } +// --------------------------------------------------------------------------- +// Consolidated Delivery Notes link (SI → source DNs) +// --------------------------------------------------------------------------- +// Net-off consolidation links the source DNs to this SI via the custom field +// `custom_consolidated_si` (the standard SI→DN item link is intentionally +// omitted — merged/netted rows can't map to a single DN row), so the +// Connections tab cannot surface them. This button gives the SI → source-DN +// navigation. The DN → SI direction already works via the DN's +// "Consolidated Sales Invoice" field. +frappe.ui.form.on("Sales Invoice", { + refresh: function (frm) { + if (frm.is_new()) return; + frappe.db + .count("Delivery Note", { + filters: { custom_consolidated_si: frm.doc.name }, + }) + .then(function (n) { + if (n) { + frm.add_custom_button( + __("Delivery Notes ({0})", [n]), + function () { + frappe.set_route("List", "Delivery Note", { + custom_consolidated_si: frm.doc.name, + }); + }, + __("View") + ); + } + }); + }, +}); + diff --git a/rmax_custom/tests/test_dn_consolidation.py b/rmax_custom/tests/test_dn_consolidation.py index 36f239f..ade54b6 100644 --- a/rmax_custom/tests/test_dn_consolidation.py +++ b/rmax_custom/tests/test_dn_consolidation.py @@ -100,6 +100,24 @@ def test_clubbed_dn_billing_status_flips_on_submit_and_reverts(self): dn.reload() self.assertEqual(dn.status, "To Bill") + def test_consolidated_si_inherits_dn_price_list(self): + # Consolidated SI must carry the source DN's selling price list (e.g. + # "Inter Company Price"), not fall back to the customer/default list. + pl = _ensure_selling_price_list("RMAX Consol PL") + dn = frappe.new_doc("Delivery Note") + dn.customer = self.customer + dn.company = self.company + dn.set_warehouse = self.warehouse + dn.selling_price_list = pl + dn.append("items", { + "item_code": self.item, "qty": 3, "rate": 10, + "warehouse": self.warehouse, "uom": "Nos", + }) + dn.insert(ignore_permissions=True) + dn.submit() + si = frappe.get_doc("Sales Invoice", consolidate_dns_to_si([dn.name])) + self.assertEqual(si.selling_price_list, pl) + def test_draft_si_delete_clears_stamp_and_succeeds(self): # Reproduces the bug: a DRAFT consolidated SI stamps the DN via # custom_consolidated_si; deleting it must clear the stamp (on_trash) @@ -150,6 +168,17 @@ def _ensure_item(item_code): return i.name +def _ensure_selling_price_list(name): + if not frappe.db.exists("Price List", name): + pl = frappe.new_doc("Price List") + pl.price_list_name = name + pl.selling = 1 + pl.enabled = 1 + pl.currency = "SAR" + pl.insert(ignore_permissions=True) + return name + + def _pick_default_company(): return frappe.db.get_single_value("Global Defaults", "default_company") \ or frappe.db.get_value("Company", {}, "name")