diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 8d6317438e2ad66ca9122a9b7449b9314a6d53a4..4cf83259377d50dd17ae132c9d6f190606f9bcf9 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 2849158e3c5a78f12c453ce5f2524c6e555e9cf4..c398c05d6af7f372a1c4f60f0b0e1a828271aca6 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 f2858b6e56a556e05c10d73e92dab48ff4658803..89d146797260a6a43d937d9e75ca44f352cf6754 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 @@ -314,7 +313,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 +1335,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 2595c78fac5fcf638221055073ad21e265bd3b76..f99eb3069d822494cc5d1d62485dd34cafa4e2d9 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 0000000000000000000000000000000000000000..09c1127fea502938a2cc1b2e58ecd5a244e166f7 --- /dev/null +++ b/erpnext/patches/dokos/v4_0/add_down_payment_invoice_heading.py @@ -0,0 +1,9 @@ +import frappe +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", lang=lang) + print_heading.insert(ignore_if_duplicate=True) diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 33819a842b0bc8115c1cedf7819d9508289329aa..acea0440ab085c82438021cb3dfc8abd763c7d36 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 0b3024b96d30c0f3df404e8983085cc1558a54e5..c9368f13c888396caea02df89d062de42586b4b5 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 }; }); } diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index c84d87c39dbc9a35ba5c934ddc85ef4456836f07..b663b3b8e2a724149590616acef5fc30a2d7218c 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"): diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 07225921435506b891546fbe29aa1e47eeb474df..a9758a92c44150a8177329f3423a3d28dcade256 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")},