From 68bb663435bcfe398ba43ca5f69aa6145dfdded8 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 28 Sep 2025 11:15:49 +0200 Subject: [PATCH 1/4] feat: Auto add Down Payment Invoice as print heading for down payments --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 7 ++++++- erpnext/patches.txt | 1 + .../dokos/v4_0/add_down_payment_invoice_heading.py | 8 ++++++++ erpnext/setup/setup_wizard/operations/install_fixtures.py | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f2858b6e56..5e72fd1490 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -314,7 +314,8 @@ class SalesInvoice(SellingController): self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.validate_fixed_asset() self.set_income_account_for_fixed_assets() - self.set_income_account_for_down_payments() + self.set_income_account_for_down_payments() # @dokos + self.set_print_heading() # @dokos self.validate_item_cost_centers() self.check_conversion_rate() self.validate_accounts() @@ -1335,6 +1336,10 @@ class SalesInvoice(SellingController): for d in self.get("items"): d.income_account = debit_to + def set_print_heading(self): + if self.get("is_down_payment_invoice"): + self.select_print_heading = frappe.get_cached_value("Print Heading", _("Down Payment Invoice")) + def check_prev_docstatus(self): for d in self.get("items"): if ( diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2595c78fac..f99eb3069d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -397,6 +397,7 @@ erpnext.patches.dokos.v4_0.check_enable_pappers erpnext.patches.dokos.v4_0.suspense_accounts_for_vat_on_payments erpnext.patches.dokos.v4_0.move_bundle_price_determination_to_bundle execute:frappe.delete_doc_if_exists("Page", "booking-credits") +erpnext.patches.dokos.v4_0.add_down_payment_invoice_heading # @dokos diff --git a/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py b/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py new file mode 100644 index 0000000000..d4ba1f3b5c --- /dev/null +++ b/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py @@ -0,0 +1,8 @@ +import frappe +from frappe.printing.doctype.print_heading.print_heading import PrintHeading + + +def execute(): + print_heading: PrintHeading = frappe.new_doc("Print Heading") # type: ignore + print_heading.print_heading = frappe._("Down Payment Invoice") + print_heading.insert() diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 0722592143..a9758a92c4 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -301,6 +301,7 @@ def install(country=None): {"doctype": "Project Type", "project_type": _("Other")}, {"doctype": "Print Heading", "print_heading": frappe._("Credit Note")}, {"doctype": "Print Heading", "print_heading": frappe._("Debit Note")}, + {"doctype": "Print Heading", "print_heading": frappe._("Down Payment Invoice")}, # Share Management {"doctype": "Share Type", "title": frappe._("Equity")}, {"doctype": "Share Type", "title": frappe._("Preference")}, -- GitLab From aa97bf283978074506657870d68314304aded0c0 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 28 Sep 2025 11:18:06 +0200 Subject: [PATCH 2/4] fix: fetch lang for creation in patch --- .../patches/dokos/v4_0/add_down_payment_invoice_heading.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py b/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py index d4ba1f3b5c..09c1127fea 100644 --- a/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py +++ b/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py @@ -3,6 +3,7 @@ from frappe.printing.doctype.print_heading.print_heading import PrintHeading def execute(): + lang = frappe.get_system_settings("language") print_heading: PrintHeading = frappe.new_doc("Print Heading") # type: ignore - print_heading.print_heading = frappe._("Down Payment Invoice") - print_heading.insert() + print_heading.print_heading = frappe._("Down Payment Invoice", lang=lang) + print_heading.insert(ignore_if_duplicate=True) -- GitLab From 802a9c6717d7ca42c73455131943081491032343 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 28 Sep 2025 11:54:00 +0200 Subject: [PATCH 3/4] fix: Auto adjust discount amount on down payment creation --- .../doctype/sales_invoice/sales_invoice.js | 14 ++++++++++++++ .../doctype/sales_invoice/sales_invoice.json | 5 +++-- .../doctype/sales_invoice/sales_invoice.py | 1 - erpnext/selling/doctype/sales_order/sales_order.py | 14 +++++++++++--- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 8d6317438e..4cf8325937 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -1167,6 +1167,7 @@ frappe.ui.form.on("Sales Invoice", { .get_list("Item", { filters: { is_down_payment_item: 1 }, order_by: "down_payment_percentage DESC", + fields: ["name", "down_payment_percentage"], }) .then((r) => { if (!r.exc && r.length) { @@ -1176,6 +1177,8 @@ frappe.ui.form.on("Sales Invoice", { calculate_down_payment(line); }); }); + + adjust_discount_amount(frm, r[0].down_payment_percentage); } }); } @@ -1287,6 +1290,17 @@ const calculate_down_payment = (line) => { } }; +const adjust_discount_amount = (frm, down_payment_percentage) => { + if (frm.doc.is_down_payment_invoice) { + if (frm.doc.additional_discount_percentage === 0.0 && frm.doc.discount_amount > 0.0) { + frm.set_value( + "discount_amount", + (flt(down_payment_percentage) / 100.0) * flt(frm.doc.discount_amount) + ); + } + } +}; + frappe.ui.form.on("Sales Invoice Payment", { mode_of_payment: function (frm) { frappe.call({ diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 2849158e3c..c398c05d6a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1623,7 +1623,8 @@ "label": "Is Down Payment Invoice", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "set_only_once": 1 }, { "default": "0", @@ -1991,7 +1992,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-09-09 14:48:59.472826", + "modified": "2025-09-28 11:28:35.262629", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5e72fd1490..89d1467972 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -155,7 +155,6 @@ class SalesInvoice(SellingController): is_return: DF.Check items: DF.Table[SalesInvoiceItem] language: DF.Link | None - last_scanned_warehouse: DF.Link | None letter_head: DF.Link | None loyalty_amount: DF.Currency loyalty_points: DF.Int diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index c84d87c39d..b663b3b8e2 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -3,7 +3,7 @@ import json -from typing import Literal +from typing import TYPE_CHECKING, Literal import frappe import frappe.utils @@ -47,6 +47,9 @@ from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty form_grid_templates = {"items": "templates/form_grid/item_grid.html"} +if TYPE_CHECKING: + from erpnext.stock.doctype.item.item import Item + class WarehouseRequired(frappe.ValidationError): pass @@ -1279,13 +1282,13 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a def postprocess(source, target): if "is_down_payment_invoice" in (frappe.flags.args or {}): target.is_down_payment_invoice = True - down_payment_item = frappe.db.get_value( + down_payment_item: Item = frappe.db.get_value( "Item", filters={"is_down_payment_item": 1}, order_by="down_payment_percentage DESC", as_dict=True, fieldname=["name", "down_payment_percentage"], - ) + ) # type: ignore target.items = [] target.append( "items", @@ -1298,6 +1301,11 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a }, ) + if not target.additional_discount_percentage and target.discount_amount: + target.discount_amount = ( + target.discount_amount * flt(down_payment_item.down_payment_percentage) / 100.0 + ) + set_missing_values(source, target) # Get the advance paid Journal Entries in Sales Invoice Advance if target.get("allocate_advances_automatically"): -- GitLab From 77ba1dffc6df4de9ee824863d8a525d86d8ab9b4 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Sun, 28 Sep 2025 12:10:24 +0200 Subject: [PATCH 4/4] fix: filter for down payment items --- erpnext/public/js/controllers/buying.js | 11 ++++++++++- erpnext/public/js/utils/sales_common.js | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 33819a842b..acea0440ab 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -101,9 +101,18 @@ erpnext.buying = { filters: filters, }; } else { + const filters = { + supplier: me.frm.doc.supplier, + is_purchase_item: 1, + has_variants: 0, + }; // @dokos + if (me.frm.doc.is_down_payment_invoice) { + // @dokos + filters["is_down_payment_item"] = 1; + } return { query: "erpnext.controllers.queries.item_query", - filters: { supplier: me.frm.doc.supplier, is_purchase_item: 1, has_variants: 0 }, + filters: filters, // @dokos }; } }); diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js index 0b3024b96d..c9368f13c8 100644 --- a/erpnext/public/js/utils/sales_common.js +++ b/erpnext/public/js/utils/sales_common.js @@ -79,9 +79,14 @@ erpnext.sales_common = { if (me.frm.doc.doctype == "Quotation" && me.frm.doc.quotation_to == "Customer") { customer = me.frm.doc.party_name; } + const filters = { is_sales_item: 1, customer: customer, has_variants: 0 }; // @dokos + if (me.frm.doc.is_down_payment_invoice) { + // @dokos + filters["is_down_payment_item"] = 1; + } return { query: "erpnext.controllers.queries.item_query", - filters: { is_sales_item: 1, customer: customer, has_variants: 0 }, + filters: filters, // @dokos }; }); } -- GitLab