From d7da5b047d5b7cf69da15d4269681c1fbff19957 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 06:13:11 +0000 Subject: [PATCH 01/57] fix: add portal user ownership check to supplier quotation (backport #54298) (#54300) Co-authored-by: Mihir Kandoi fix: add portal user ownership check to supplier quotation (#54298) --- .../doctype/request_for_quotation/request_for_quotation.py | 5 +++++ .../request_for_quotation/test_request_for_quotation.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 09d7d30ab39..03254c30f6e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -477,6 +477,11 @@ def create_supplier_quotation(doc): if isinstance(doc, str): doc = json.loads(doc) + if frappe.session.user not in frappe.get_all( + "Portal User", {"parent": doc.get("supplier")}, pluck="user" + ): + frappe.throw(_("Not Permitted"), frappe.PermissionError) + try: sq_doc = frappe.get_doc( { diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index 9201f6c4f2d..36468a83dac 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -264,6 +264,13 @@ def make_request_for_quotation(**args) -> "RequestforQuotation": for data in supplier_data: rfq.append("suppliers", data) + frappe.new_doc( + "Portal User", + user="Administrator", + parent=data.get("supplier"), + parentfield="portal_users", + parenttype="Supplier", + ).insert() rfq.append( "items", From 47e78bd4b93c5f1452efd81cda0a4da50620821e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 14 Apr 2026 14:46:46 +0530 Subject: [PATCH 02/57] refactor(company): don't force set service expense account on save (cherry picked from commit 927f40b296d6dd6cf1d4337c81c9be4f65e61ccd) --- erpnext/setup/doctype/company/company.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 51eb71d6f79..e4e9c24aeaf 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -684,21 +684,6 @@ class Company(NestedSet): self.db_set("disposal_account", disposal_acct) - if not self.service_expense_account: - service_expense_acct = frappe.db.get_value( - "Account", - { - "account_name": _("Marketing Expenses"), - "company": self.name, - "is_group": 0, - "root_type": "Expense", - }, - "name", - ) - - if service_expense_acct: - self.db_set("service_expense_account", service_expense_acct) - def _set_default_account(self, fieldname, account_type): if self.get(fieldname): return From 6cc560a5794921daa8cbdcda045fa90b3f3889d6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 12:29:29 +0530 Subject: [PATCH 03/57] refactor(test): set dependant value in company master (cherry picked from commit 299e141cee28bb7e105ed82907ea63e622fc708d) --- .../test_repost_accounting_ledger.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py index 793bde5c99f..3200a83c122 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py @@ -203,6 +203,11 @@ class TestRepostAccountingLedger(ERPNextTestSuite): def test_06_repost_purchase_receipt(self): from erpnext.accounts.doctype.account.test_account import create_account + if not frappe.db.set_value("Company", "_Test Company", "service_expense_account"): + frappe.db.set_value( + "Company", "_Test Company", "service_expense_account", "Marketing Expenses - _TC" + ) + provisional_account = create_account( account_name="Provision Account", parent_account="Current Liabilities - _TC", From 101f68c8e8f13177f014f5ae84c793ef29eb07e5 Mon Sep 17 00:00:00 2001 From: PKSowmiya05 Date: Wed, 15 Apr 2026 17:17:51 +0530 Subject: [PATCH 04/57] fix: non-collapsible in customer quick entry (cherry picked from commit 53e120269dbe716eed0a6a7157fcf47fc332f788) --- erpnext/public/js/utils/contact_address_quick_entry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/contact_address_quick_entry.js b/erpnext/public/js/utils/contact_address_quick_entry.js index f6ce5d0d28b..8c28f3b85ea 100644 --- a/erpnext/public/js/utils/contact_address_quick_entry.js +++ b/erpnext/public/js/utils/contact_address_quick_entry.js @@ -39,7 +39,7 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm { fieldtype: "Section Break", label: __("Primary Contact Details"), - collapsible: 1, + collapsible: 0, }, { label: __("First Name"), @@ -71,7 +71,7 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm { fieldtype: "Section Break", label: __("Primary Address Details"), - collapsible: 1, + collapsible: 0, }, { label: __("Address Line 1"), From 45052ce8a74800b4ef4d2080bfba3c1dba30c928 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 10:39:17 +0530 Subject: [PATCH 05/57] fix: reset base_rounded_total when rounded_total resets (backport #54241) (#54304) * fix: reset base_rounded_total when rounded_total resets (cherry picked from commit f8d278b73309cbc02ed1474bff88453321c0e2fd) # Conflicts: # erpnext/controllers/tests/test_taxes_and_totals.py * chore: spelling mistake (cherry picked from commit e2ac4765877851bbf0a41864e3a0589840c58c27) * chore: resolve conflicts --------- Co-authored-by: ljain112 --- erpnext/controllers/taxes_and_totals.py | 17 ++++----- .../tests/test_taxes_and_totals.py | 37 +++++++++++++++++++ .../public/js/controllers/taxes_and_totals.js | 30 +++++++-------- 3 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 erpnext/controllers/tests/test_taxes_and_totals.py diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 21743e57f11..78b7f56b232 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -784,18 +784,17 @@ class calculate_taxes_and_totals: if self.doc.meta.get_field("rounded_total"): if self.doc.is_rounded_total_disabled(): self.doc.rounded_total = 0 - self.doc.base_rounded_total = 0 self.doc.rounding_adjustment = 0 - return - self.doc.rounded_total = round_based_on_smallest_currency_fraction( - self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total") - ) + else: + self.doc.rounded_total = round_based_on_smallest_currency_fraction( + self.doc.grand_total, self.doc.currency, self.doc.precision("rounded_total") + ) - # rounding adjustment should always be the difference vetween grand and rounded total - self.doc.rounding_adjustment = flt( - self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment") - ) + # rounding adjustment should always be the difference between grand and rounded total + self.doc.rounding_adjustment = flt( + self.doc.rounded_total - self.doc.grand_total, self.doc.precision("rounding_adjustment") + ) self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"]) diff --git a/erpnext/controllers/tests/test_taxes_and_totals.py b/erpnext/controllers/tests/test_taxes_and_totals.py new file mode 100644 index 00000000000..09d5231d7bc --- /dev/null +++ b/erpnext/controllers/tests/test_taxes_and_totals.py @@ -0,0 +1,37 @@ +import frappe + +from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.tests.utils import ERPNextTestSuite + + +class TestTaxesAndTotals(ERPNextTestSuite): + def test_disabling_rounded_total_resets_base_fields(self): + """Disabling rounded total should also clear base rounded values.""" + so = make_sales_order(do_not_save=True) + so.items[0].qty = 1 + so.items[0].rate = 1000.25 + so.items[0].price_list_rate = 1000.25 + so.items[0].discount_percentage = 0 + so.items[0].discount_amount = 0 + so.set("taxes", []) + + so.disable_rounded_total = 0 + calculate_taxes_and_totals(so) + + self.assertEqual(so.grand_total, 1000.25) + self.assertEqual(so.rounded_total, 1000.0) + self.assertEqual(so.rounding_adjustment, -0.25) + self.assertEqual(so.base_grand_total, 1000.25) + self.assertEqual(so.base_rounded_total, 1000.0) + self.assertEqual(so.base_rounding_adjustment, -0.25) + + # User toggles disable_rounded_total after values are already set. + so.disable_rounded_total = 1 + + calculate_taxes_and_totals(so) + + self.assertEqual(so.rounded_total, 0) + self.assertEqual(so.rounding_adjustment, 0) + self.assertEqual(so.base_rounded_total, 0) + self.assertEqual(so.base_rounding_adjustment, 0) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index c53c1f8aef5..ad1b64413ac 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -716,23 +716,21 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { disable_rounded_total = frappe.sys_defaults.disable_rounded_total; } - if (cint(disable_rounded_total)) { - this.frm.doc.rounded_total = 0; - this.frm.doc.base_rounded_total = 0; - this.frm.doc.rounding_adjustment = 0; - return; - } - if (frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { - this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction( - this.frm.doc.grand_total, - this.frm.doc.currency, - precision("rounded_total") - ); - this.frm.doc.rounding_adjustment = flt( - this.frm.doc.rounded_total - this.frm.doc.grand_total, - precision("rounding_adjustment") - ); + if (cint(disable_rounded_total)) { + this.frm.doc.rounded_total = 0; + this.frm.doc.rounding_adjustment = 0; + } else { + this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction( + this.frm.doc.grand_total, + this.frm.doc.currency, + precision("rounded_total") + ); + this.frm.doc.rounding_adjustment = flt( + this.frm.doc.rounded_total - this.frm.doc.grand_total, + precision("rounding_adjustment") + ); + } this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]); } From d506e574d26cee1d1027567ba676cada0d83fe2b Mon Sep 17 00:00:00 2001 From: Dharanidharan2813 Date: Tue, 24 Feb 2026 13:47:16 +0530 Subject: [PATCH 06/57] fix(taxes_and_totals): apply conversion_rate to taxable_amount in get_itemised_tax (cherry picked from commit 2e577ed25b891c349d42eb0622956aeaab2f716e) --- erpnext/controllers/taxes_and_totals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 78b7f56b232..2961650673c 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -1289,7 +1289,8 @@ def get_itemised_tax(doc, with_tax_account=False): ) tax_info.tax_amount += flt(row.amount, precision) - tax_info.taxable_amount += flt(row.taxable_amount, precision) + conversion_rate = doc.conversion_rate or 1 + tax_info.taxable_amount += flt(row.taxable_amount / conversion_rate, precision) if with_tax_account: tax_info.tax_account = tax.account_head From f287edd8c26583bc043c76316e12cfe38df3a200 Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Thu, 9 Apr 2026 16:27:54 +0530 Subject: [PATCH 07/57] fix: move make_dimension_in_accounting_doctypes from after_insert to on_update (cherry picked from commit ee067e6015909a27462a5e6a0093a22a45c03820) --- .../doctype/accounting_dimension/accounting_dimension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 70253c674c5..549449cce42 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -82,7 +82,7 @@ class AccountingDimension(Document): else: frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company))) - def after_insert(self): + def on_update(self): if frappe.in_test: make_dimension_in_accounting_doctypes(doc=self) else: From b487f69b594da886c5eb501ef60d076e51781528 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:16:34 +0530 Subject: [PATCH 08/57] feat: add option to create production plan from sales order (backport #53662) (#54323) Co-authored-by: sudarsan2001 Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com> Co-authored-by: Mihir Kandoi --- .../doctype/sales_order/sales_order.js | 16 ++++++++++ .../doctype/sales_order/sales_order.py | 31 +++++++++++++++++++ .../doctype/sales_order/test_sales_order.py | 24 ++++++++++++-- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 82926bd3855..bd41932aac4 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -18,6 +18,7 @@ frappe.ui.form.on("Sales Order", { Project: "Project", "Payment Entry": "Payment", "Work Order": "Work Order", + "Production Plan": "Production Plan", }; frm.add_fetch("customer", "tax_id", "tax_id"); @@ -1059,6 +1060,14 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex __("Create") ); } + + if (frappe.model.can_create("Production Plan") && !doc.is_subcontracted) { + this.frm.add_custom_button( + __("Production Plan"), + () => this.make_production_plan(), + __("Create") + ); + } } // sales invoice @@ -1339,6 +1348,13 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex }); } + make_production_plan() { + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_production_plan", + frm: this.frm, + }); + } + order_type() { this.toggle_delivery_date(); } diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index dcf2ed1ad9b..6e8696dbd8e 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -27,6 +27,7 @@ from erpnext.manufacturing.doctype.blanket_order.blanket_order import ( ) from erpnext.manufacturing.doctype.production_plan.production_plan import ( get_items_for_material_requests, + get_sales_orders, ) from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults @@ -1801,6 +1802,36 @@ def make_work_orders(items, sales_order, company, project=None): return [p.name for p in out] +def make_production_plan(source_name, target_doc=None): + sales_order = frappe.get_doc("Sales Order", source_name) + + production_plan = frappe.new_doc( + "Production Plan", + company=sales_order.company, + get_items_from="Sales Order", + posting_date=nowdate(), + ) + + open_so = [data.name for data in get_sales_orders(production_plan)] + if sales_order.name not in open_so: + frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name)) + + production_plan.append( + "sales_orders", + { + "sales_order": sales_order.name, + "sales_order_date": sales_order.transaction_date, + "customer": sales_order.customer, + "grand_total": sales_order.base_grand_total, + }, + ) + production_plan.get_items() + if not production_plan.get("po_items"): + frappe.throw(_("Sales Order {0} is not available for production").format(sales_order.name)) + + return production_plan + + @frappe.whitelist() def update_status(status, name): so = frappe.get_doc("Sales Order", name, check_permission="submit") diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 6616c52b720..6a8abea1543 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -8,7 +8,7 @@ import frappe import frappe.permissions from frappe.core.doctype.user_permission.test_user_permission import create_user from frappe.tests import change_settings -from frappe.utils import add_days, flt, nowdate, today +from frappe.utils import add_days, flt, getdate, nowdate, today from erpnext.controllers.accounts_controller import InvalidQtyError, get_due_date, update_child_qty_rate from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import ( @@ -24,6 +24,7 @@ from erpnext.selling.doctype.sales_order.sales_order import ( create_pick_list, make_delivery_note, make_material_request, + make_production_plan, make_raw_material_request, make_sales_invoice, make_work_orders, @@ -213,6 +214,26 @@ class TestSalesOrder(ERPNextTestSuite): self.assertEqual(dn.doctype, "Delivery Note") self.assertEqual(len(dn.get("items")), len(so.get("items"))) + def test_make_production_plan(self): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + fg_item = make_item("Test PP FG Item", {"is_stock_item": 1}).name + make_bom(item=fg_item, rate=100, raw_materials=["_Test Item"]) + + so = make_sales_order(item_code=fg_item, do_not_submit=True) + self.assertRaises(frappe.ValidationError, make_production_plan, so.name) + + so.submit() + pp = make_production_plan(so.name) + + self.assertEqual(pp.doctype, "Production Plan") + self.assertGreater(len(pp.get("po_items")), 0) + self.assertEqual(pp.get("po_items")[0].sales_order, so.name) + self.assertEqual(pp.get("sales_orders")[0].sales_order, so.name) + self.assertEqual(getdate(pp.get("sales_orders")[0].sales_order_date), getdate(so.transaction_date)) + self.assertEqual(pp.get("sales_orders")[0].customer, so.customer) + self.assertEqual(pp.get("sales_orders")[0].grand_total, so.base_grand_total) + def test_make_sales_invoice(self): so = make_sales_order(do_not_submit=True) @@ -2787,7 +2808,6 @@ def make_sales_order(**args): ) so.delivery_date = add_days(so.transaction_date, 10) - if not args.do_not_save: so.insert() if not args.do_not_submit: From ead7744f8167e6e3b6d60de7f85e84af67781947 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 10:16:28 +0000 Subject: [PATCH 09/57] refactor: add category field to uom (backport #54290) (#54325) * refactor: add category field to uom (#54290) (cherry picked from commit e04a2e6da2a91f733b228e6094eb39589aa4a3f6) # Conflicts: # erpnext/patches.txt * chore: resolve conflicts --------- Co-authored-by: Mihir Kandoi --- erpnext/patches.txt | 1 + erpnext/patches/v16_0/uom_category.py | 11 + erpnext/setup/doctype/uom/uom.json | 12 +- erpnext/setup/doctype/uom/uom.py | 1 + erpnext/setup/setup_wizard/data/uom_data.json | 719 ++++++++++++------ .../operations/install_fixtures.py | 6 +- .../doctype/uom_category/uom_category.json | 46 +- 7 files changed, 547 insertions(+), 249 deletions(-) create mode 100644 erpnext/patches/v16_0/uom_category.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d41924edb4a..a6651be8483 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -474,3 +474,4 @@ erpnext.patches.v16_0.update_requested_qty_packed_item erpnext.patches.v16_0.remove_payables_receivables_workspace erpnext.patches.v16_0.co_by_product_patch erpnext.patches.v16_0.depends_on_inv_dimensions +erpnext.patches.v16_0.uom_category diff --git a/erpnext/patches/v16_0/uom_category.py b/erpnext/patches/v16_0/uom_category.py new file mode 100644 index 00000000000..f26bca1d388 --- /dev/null +++ b/erpnext/patches/v16_0/uom_category.py @@ -0,0 +1,11 @@ +import json + +import frappe + + +def execute(): + uom_data = json.loads( + open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read() + ) + bulk_update_dict = {uom["uom_name"]: {"category": uom["category"]} for uom in uom_data} + frappe.db.bulk_update("UOM", bulk_update_dict) diff --git a/erpnext/setup/doctype/uom/uom.json b/erpnext/setup/doctype/uom/uom.json index 73c69643c89..f8031c8a421 100644 --- a/erpnext/setup/doctype/uom/uom.json +++ b/erpnext/setup/doctype/uom/uom.json @@ -13,6 +13,7 @@ "common_code", "description", "column_break_obth", + "category", "enabled", "must_be_whole_number" ], @@ -60,12 +61,21 @@ { "fieldname": "column_break_obth", "fieldtype": "Column Break" + }, + { + "allow_in_quick_entry": 1, + "fieldname": "category", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Category", + "options": "UOM Category" } ], "icon": "fa fa-compass", "idx": 1, "links": [], - "modified": "2025-08-21 18:59:27.900209", + "modified": "2026-04-14 21:20:16.983514", "modified_by": "Administrator", "module": "Setup", "name": "UOM", diff --git a/erpnext/setup/doctype/uom/uom.py b/erpnext/setup/doctype/uom/uom.py index d47028fea2e..39766c10eae 100644 --- a/erpnext/setup/doctype/uom/uom.py +++ b/erpnext/setup/doctype/uom/uom.py @@ -14,6 +14,7 @@ class UOM(Document): if TYPE_CHECKING: from frappe.types import DF + category: DF.Link | None common_code: DF.Data | None description: DF.SmallText | None enabled: DF.Check diff --git a/erpnext/setup/setup_wizard/data/uom_data.json b/erpnext/setup/setup_wizard/data/uom_data.json index 12f11ce5b85..f428044ec3e 100644 --- a/erpnext/setup/setup_wizard/data/uom_data.json +++ b/erpnext/setup/setup_wizard/data/uom_data.json @@ -1,1056 +1,1295 @@ [ { "uom_name": "Abampere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Acre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Acre (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Ampere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Ampere-Hour", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Ampere-Minute", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Ampere-Second", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Are", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Area", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": null }, { "uom_name": "Arshin", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Atmosphere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Bar", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Barleycorn", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Barrel (Oil)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Barrel(Beer)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Biot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Box", "must_be_whole_number": 1, - "common_code": "BX" + "common_code": "BX", + "category": null }, { "uom_name": "Btu (It)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Btu (Mean)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Btu (Th)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Btu/Hour", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Btu/Minutes", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Btu/Seconds", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Bushel (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Bushel (US Dry Level)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Caballeria", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Cable Length", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Cable Length (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Cable Length (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Calibre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Calorie (Food)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Calorie (It)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Calorie (Mean)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Calorie (Th)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Calorie/Seconds", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Carat", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Celsius", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Temperature" }, { "uom_name": "Cental", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Centiarea", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Centigram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Centilitre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Centimeter", "must_be_whole_number": 0, "common_code": "CMT", - "symbol": "cm" + "symbol": "cm", + "category": "Length" }, { "uom_name": "Chain", "must_be_whole_number": 0, "common_code": "M49", - "symbol": "ch (US survey)" + "symbol": "ch (US survey)", + "category": "Length" }, { "uom_name": "Coulomb", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Cubic Centimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Cubic Decimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Cubic Foot", "must_be_whole_number": 0, "common_code": "FTQ", - "symbol": "ft³" + "symbol": "ft\u00b3", + "category": "Volume" }, { "uom_name": "Cubic Inch", "must_be_whole_number": 0, "common_code": "INQ", - "symbol": "in³" + "symbol": "in\u00b3", + "category": "Volume" }, { "uom_name": "Cubic Meter", "must_be_whole_number": 0, "common_code": "MTQ", - "symbol": "m³" + "symbol": "m\u00b3", + "category": "Volume" }, { "uom_name": "Cubic Millimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Cubic Yard", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Cup", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Cycle/Second", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Day", "must_be_whole_number": 0, "common_code": "DAY", - "symbol": "d" + "symbol": "d", + "category": "Time" }, { "uom_name": "Decigram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Decilitre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Decimeter", "must_be_whole_number": 0, "common_code": "DMT", - "symbol": "dm" + "symbol": "dm", + "category": "Length" }, { "uom_name": "Dekagram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Dram", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Dyne", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "EMU Of Charge", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "EMU of current", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Ells (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Ems(Pica)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Erg", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Fahrenheit", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Temperature" }, { "uom_name": "Faraday", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Fathom", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Fluid Ounce (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Fluid Ounce (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Foot", "must_be_whole_number": 0, "common_code": "FOT", - "symbol": "ft" + "symbol": "ft", + "category": "Length" }, { "uom_name": "Foot Of Water", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Foot/Minute", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Speed" }, { "uom_name": "Foot/Second", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Speed" }, { "uom_name": "Furlong", "must_be_whole_number": 0, "common_code": "M50", - "symbol": "fur" + "symbol": "fur", + "category": "Length" }, { "uom_name": "Gallon (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Gallon Dry (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Gallon Liquid (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Gamma", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Magnetic Induction" }, { "uom_name": "Gauss", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Magnetic Induction" }, { "uom_name": "Grain", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Grain/Cubic Foot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Grain/Gallon (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Grain/Gallon (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Gram", "must_be_whole_number": 0, "common_code": "GRM", - "symbol": "g" + "symbol": "g", + "category": "Mass" }, { "uom_name": "Gram-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Gram/Cubic Centimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Gram/Cubic Meter", "must_be_whole_number": 0, "common_code": "A93", - "symbol": "g/m³" + "symbol": "g/m\u00b3", + "category": "Density" }, { "uom_name": "Gram/Cubic Millimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Gram/Litre", "must_be_whole_number": 0, "common_code": "GL", - "symbol": "g/l" + "symbol": "g/l", + "category": "Density" }, { "uom_name": "Hand", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Hectare", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Area" }, { "uom_name": "Hectogram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Hectometer", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Hectopascal", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Hertz", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Horsepower", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Horsepower-Hours", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Hour", "must_be_whole_number": 0, - "common_code": "HUR" + "common_code": "HUR", + "category": "Time" }, { "uom_name": "Hundredweight (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Hundredweight (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Iches Of Water", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Inch", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Inch Pound-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Inch/Minute", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Speed" }, { "uom_name": "Inch/Second", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Speed" }, { "uom_name": "Inches Of Mercury", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Joule", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Joule/Meter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Kelvin", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Temperature" }, { "uom_name": "Kg", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Kiloampere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Kilocalorie", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Kilocoulomb", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Kilogram-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Kilogram/Cubic Centimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Kilogram/Cubic Meter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Kilogram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Kilohertz", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Kilojoule", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Kilometer", "must_be_whole_number": 0, "common_code": "KMT", - "symbol": "km" + "symbol": "km", + "category": "Length" }, { "uom_name": "Kilometer/Hour", "must_be_whole_number": 0, "common_code": "KMH", - "symbol": "km/h" + "symbol": "km/h", + "category": "Speed" }, { "uom_name": "Kilopascal", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Kilopond", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Kilopound-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Kilowatt", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Kilowatt-Hour", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Kip", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Knot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Speed" }, { "uom_name": "Link", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Litre", "must_be_whole_number": 0, "common_code": "LTR", - "symbol": "l" + "symbol": "l", + "category": "Volume" }, { "uom_name": "Litre-Atmosphere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Megacoulomb", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Megagram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Megahertz", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Megajoule", "must_be_whole_number": 0, "common_code": "3B", - "symbol": "MJ" + "symbol": "MJ", + "category": "Energy" }, { "uom_name": "Megawatt", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Meter", "must_be_whole_number": 0, - "common_code": "MTR" + "common_code": "MTR", + "category": "Length" }, { "uom_name": "Meter Of Water", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Meter/Second", "must_be_whole_number": 0, "common_code": "MTS", - "symbol": "m/s" + "symbol": "m/s", + "category": "Speed" }, { "uom_name": "Microbar", "must_be_whole_number": 0, "common_code": "B85", - "symbol": "µbar" + "symbol": "\u00b5bar", + "category": "Pressure" }, { "uom_name": "Microgram", "must_be_whole_number": 0, "common_code": "MC", - "symbol": "µg" + "symbol": "\u00b5g", + "category": "Mass" }, { "uom_name": "Microgram/Litre", "must_be_whole_number": 0, "common_code": "H29", - "symbol": "µg/l" + "symbol": "\u00b5g/l", + "category": "Density" }, { "uom_name": "Micrometer", "must_be_whole_number": 0, "common_code": "4H", - "symbol": "µm" + "symbol": "\u00b5m", + "category": "Length" }, { "uom_name": "Microsecond", "must_be_whole_number": 0, "common_code": "B98", - "symbol": "µs" + "symbol": "\u00b5s", + "category": "Time" }, { "uom_name": "Mile", "must_be_whole_number": 0, "common_code": "SMI", - "symbol": "mile" + "symbol": "mile", + "category": "Length" }, { "uom_name": "Mile (Nautical)", "must_be_whole_number": 0, "common_code": "NMI", - "symbol": "n mile" + "symbol": "n mile", + "category": "Length" }, { "uom_name": "Mile/Hour", "must_be_whole_number": 0, "common_code": "HM", - "symbol": "mile/h" + "symbol": "mile/h", + "category": "Speed" }, { "uom_name": "Mile/Minute", "must_be_whole_number": 0, "common_code": "M57", - "symbol": "mi/min" + "symbol": "mi/min", + "category": "Speed" }, { "uom_name": "Mile/Second", "must_be_whole_number": 0, "common_code": "M58", - "symbol": "mi/s" + "symbol": "mi/s", + "category": "Speed" }, { "uom_name": "Milibar", "must_be_whole_number": 0, "common_code": "MBR", - "symbol": "mbar" + "symbol": "mbar", + "category": "Pressure" }, { "uom_name": "Milliampere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electric Current" }, { "uom_name": "Millicoulomb", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Milligram", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Milligram/Cubic Centimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Milligram/Cubic Meter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Milligram/Cubic Millimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Milligram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Millihertz", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Millilitre", "must_be_whole_number": 0, "common_code": "MLT", - "symbol": "ml" + "symbol": "ml", + "category": "Volume" }, { "uom_name": "Millimeter", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Millimeter Of Mercury", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Millimeter Of Water", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Millisecond", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Time" }, { "uom_name": "Minute", "must_be_whole_number": 0, - "common_code": "MIN" + "common_code": "MIN", + "category": "Time" }, { "uom_name": "Nanocoulomb", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Electrical Charge" }, { "uom_name": "Nanogram/Litre", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Nanohertz", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Nanometer", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Nanosecond", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Time" }, { "uom_name": "Newton", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Nos", "must_be_whole_number": 1, - "common_code": "C62" + "common_code": "C62", + "category": null }, { "uom_name": "Ounce", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Ounce-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Ounce/Cubic Foot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Ounce/Cubic Inch", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Ounce/Gallon (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Ounce/Gallon (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Pair", - "must_be_whole_number": 1 + "must_be_whole_number": 1, + "category": null }, { "uom_name": "Parts Per Million", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Agriculture" }, { "uom_name": "Pascal", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Peck (UK)", "must_be_whole_number": 0, "common_code": "L43", - "symbol": "pk (UK)" + "symbol": "pk (UK)", + "category": null }, { "uom_name": "Peck (US)", "must_be_whole_number": 0, "common_code": "G23", - "symbol": "pk (US)" + "symbol": "pk (US)", + "category": null }, { "uom_name": "Percent", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Agriculture" }, { "uom_name": "Pint (UK)", "must_be_whole_number": 0, "common_code": "PTI", - "symbol": "pt (UK)" + "symbol": "pt (UK)", + "category": "Volume" }, { "uom_name": "Pint (US)", "must_be_whole_number": 0, "common_code": "PT", - "symbol": "pt (US)" + "symbol": "pt (US)", + "category": null }, { "uom_name": "Pint, Dry (US)", "must_be_whole_number": 0, "common_code": "L61", - "symbol": "pt (US dry)" + "symbol": "pt (US dry)", + "category": "Volume" }, { "uom_name": "Pint, Liquid (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Pond", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Pood", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Pound", "must_be_whole_number": 0, "common_code": "LBR", - "symbol": "lb" + "symbol": "lb", + "category": "Mass" }, { "uom_name": "Pound-Force", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Pound/Cubic Foot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Pound/Cubic Inch", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Pound/Cubic Yard", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Pound/Gallon (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Pound/Gallon (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Poundal", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Psi/1000 Feet", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Quart (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Quart Dry (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Quart Liquid (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Quintal", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Rod", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Sazhen", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Second", "must_be_whole_number": 0, - "common_code": "SEC" + "common_code": "SEC", + "category": "Time" }, { "uom_name": "Set", "must_be_whole_number": 1, - "common_code": "SX" + "common_code": "SX", + "category": null }, { "uom_name": "Slug", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Slug/Cubic Foot", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Square Centimeter", "must_be_whole_number": 0, "common_code": "CMK", - "symbol": "cm²" + "symbol": "cm\u00b2", + "category": "Area" }, { "uom_name": "Square Foot", "must_be_whole_number": 0, "common_code": "FTK", - "symbol": "ft²" + "symbol": "ft\u00b2", + "category": "Area" }, { "uom_name": "Square Inch", "must_be_whole_number": 0, "common_code": "INK", - "symbol": "in²" + "symbol": "in\u00b2", + "category": "Area" }, { "uom_name": "Square Kilometer", "must_be_whole_number": 0, "common_code": "KMK", - "symbol": "km²" + "symbol": "km\u00b2", + "category": "Area" }, { "uom_name": "Square Meter", "must_be_whole_number": 0, "common_code": "MTK", - "symbol": "m²" + "symbol": "m\u00b2", + "category": "Area" }, { "uom_name": "Square Mile", "must_be_whole_number": 0, "common_code": "MIK", - "symbol": "mi²" + "symbol": "mi\u00b2", + "category": "Area" }, { "uom_name": "Square Yard", "must_be_whole_number": 0, "common_code": "YDK", - "symbol": "yd²" + "symbol": "yd\u00b2", + "category": "Area" }, { "uom_name": "Stone", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Mass" }, { "uom_name": "Tablespoon (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Teaspoon", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Volume" }, { "uom_name": "Technical Atmosphere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Tesla", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Magnetic Induction" }, { "uom_name": "Ton (Long)/Cubic Yard", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Ton (Short)/Cubic Yard", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Density" }, { "uom_name": "Ton-Force (UK)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Ton-Force (US)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Tonne", "must_be_whole_number": 0, "common_code": "TNE", - "symbol": "t" + "symbol": "t", + "category": "Mass" }, { "uom_name": "Tonne-Force(Metric)", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Force" }, { "uom_name": "Torr", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Pressure" }, { "uom_name": "Unit", "must_be_whole_number": 1, - "common_code": "C62" + "common_code": "C62", + "category": null }, { "uom_name": "Vara", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Versta", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Length" }, { "uom_name": "Volt-Ampere", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Watt", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Power" }, { "uom_name": "Watt-Hour", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Energy" }, { "uom_name": "Wavelength In Gigametres", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Wavelength In Kilometres", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Wavelength In Megametres", - "must_be_whole_number": 0 + "must_be_whole_number": 0, + "category": "Frequency and Wavelength" }, { "uom_name": "Week", "must_be_whole_number": 0, "common_code": "WEE", - "symbol": "wk" + "symbol": "wk", + "category": "Time" }, { "uom_name": "Yard", "must_be_whole_number": 0, "common_code": "YRD", - "symbol": "yd" + "symbol": "yd", + "category": "Length" } -] +] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 01b034f2826..806db071963 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -390,6 +390,9 @@ def add_uom_data(): open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read() ) for d in uoms: + if d.get("category") and not frappe.db.exists("UOM Category", d.get("category")): + frappe.get_doc({"doctype": "UOM Category", "category_name": d.get("category")}).db_insert() + if not frappe.db.exists("UOM", d.get("uom_name")): doc = frappe.new_doc("UOM") doc.update(d) @@ -402,9 +405,6 @@ def add_uom_data(): ).read() ) for d in uom_conversions: - if not frappe.db.exists("UOM Category", d.get("category")): - frappe.get_doc({"doctype": "UOM Category", "category_name": d.get("category")}).db_insert() - if not frappe.db.exists( "UOM Conversion Factor", {"from_uom": d.get("from_uom"), "to_uom": d.get("to_uom")}, diff --git a/erpnext/stock/doctype/uom_category/uom_category.json b/erpnext/stock/doctype/uom_category/uom_category.json index 1d2c42d727c..77a19dbd5f3 100644 --- a/erpnext/stock/doctype/uom_category/uom_category.json +++ b/erpnext/stock/doctype/uom_category/uom_category.json @@ -20,7 +20,7 @@ } ], "links": [], - "modified": "2024-03-27 13:10:57.525335", + "modified": "2026-04-14 21:31:18.260962", "modified_by": "Administrator", "module": "Stock", "name": "UOM Category", @@ -34,14 +34,50 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Item Manager", "share": 1, "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", - "states": [], - "track_changes": 1 -} \ No newline at end of file + "states": [] +} From d49c34389b579462eebb60f627e87877166b2241 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 16 Apr 2026 15:28:01 +0530 Subject: [PATCH 10/57] fix(test): missing repost allowed defaults (cherry picked from commit 257865deb2a22d10d5b98dcb853db0399eabfe11) --- erpnext/setup/install.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 2007a3bf84c..a45279e994c 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -22,6 +22,7 @@ def after_install(): frappe.get_doc({"doctype": "Role", "role_name": "Analytics"}).insert() set_single_defaults() + setup_repost_defaults() create_print_setting_custom_fields() create_marketing_campaign_custom_fields() create_custom_company_links() @@ -74,6 +75,13 @@ def set_single_defaults(): setup_currency_exchange() +def setup_repost_defaults(): + accounts_settings = frappe.get_doc("Accounts Settings") + for x in frappe.get_hooks("repost_allowed_doctypes"): + accounts_settings.append("repost_allowed_types", {"document_type": x}) + accounts_settings.save() + + def setup_currency_exchange(): ces = frappe.get_single("Currency Exchange Settings") try: From f785f36ad64b8d0c4dde9f2dc1eec29b021c8bbf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 13 Apr 2026 15:31:12 +0530 Subject: [PATCH 11/57] refactor: merge reposting settings to accounts settings (cherry picked from commit 89ebf4854489e1d87c882ed2c75cf93f9f596c10) --- .../accounts_settings/accounts_settings.json | 15 ++++++++++++++- .../accounts_settings/accounts_settings.py | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 29673e89b6c..0807f07d8d9 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -62,6 +62,8 @@ "reconciliation_queue_size", "column_break_resa", "exchange_gain_loss_posting_date", + "repost_section", + "repost_allowed_types", "payment_options_section", "enable_loyalty_point_program", "column_break_ctam", @@ -702,6 +704,17 @@ "fieldname": "fetch_payment_schedule_in_payment_request", "fieldtype": "Check", "label": "Fetch Payment Schedule In Payment Request" + }, + { + "fieldname": "repost_section", + "fieldtype": "Section Break", + "label": "Repost" + }, + { + "fieldname": "repost_allowed_types", + "fieldtype": "Table", + "label": "Allowed Doctypes", + "options": "Repost Allowed Types" } ], "grid_page_length": 50, @@ -711,7 +724,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-03-30 07:32:58.182018", + "modified": "2026-04-13 15:30:28.729627", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 94b35eba00a..279d7f8e627 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -44,6 +44,8 @@ class AccountsSettings(Document): if TYPE_CHECKING: from frappe.types import DF + from erpnext.accounts.doctype.repost_allowed_types.repost_allowed_types import RepostAllowedTypes + add_taxes_from_item_tax_template: DF.Check add_taxes_from_taxes_and_charges_template: DF.Check allow_multi_currency_invoices_against_single_party_account: DF.Check @@ -86,6 +88,7 @@ class AccountsSettings(Document): receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor", "Raw SQL"] receivable_payable_remarks_length: DF.Int reconciliation_queue_size: DF.Int + repost_allowed_types: DF.Table[RepostAllowedTypes] role_allowed_to_over_bill: DF.Link | None role_to_notify_on_depreciation_failure: DF.Link | None role_to_override_stop_action: DF.Link | None From 151864079bc1cede50da0ee2a5d101ad57647ea7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 14 Apr 2026 10:18:39 +0530 Subject: [PATCH 12/57] refactor: move allowed doctypes to accounts settings - dropped 'allowed' field (cherry picked from commit d5c58277cb8dc585c6cd1ff9b78982456d72d261) --- .../accounts_settings/accounts_settings.py | 24 +++++++++++++++++++ .../repost_allowed_types.json | 21 ++++------------ .../repost_allowed_types.py | 1 - erpnext/patches.txt | 1 + ...ge_repost_settings_to_accounts_settings.py | 10 ++++++++ 5 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 erpnext/patches/v16_0/merge_repost_settings_to_accounts_settings.py diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 279d7f8e627..693d0918d20 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -10,6 +10,9 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_ from frappe.model.document import Document from frappe.utils import cint +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.utils import sync_auto_reconcile_config SELLING_DOCTYPES = [ @@ -143,6 +146,7 @@ class AccountsSettings(Document): frappe.clear_cache() self.validate_and_sync_auto_reconcile_config() + self.update_property_for_accounting_dimension() def validate_stale_days(self): if not self.allow_stale and cint(self.stale_days) <= 0: @@ -189,6 +193,17 @@ class AccountsSettings(Document): title=_("Auto Tax Settings Error"), ) + def update_property_for_accounting_dimension(self): + doctypes = [entry.document_type for entry in self.repost_allowed_types] + if not doctypes: + return + + from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import get_child_docs + + doctypes += get_child_docs(doctypes) + + set_allow_on_submit_for_dimension_fields(doctypes) + @frappe.whitelist() def drop_ar_sql_procedures(self): from erpnext.accounts.report.accounts_receivable.accounts_receivable import InitSQLProceduresForAR @@ -228,3 +243,12 @@ def create_property_setter_for_hiding_field(doctype, field_name, hide): "Check", validate_fields_for_doctype=False, ) + + +def set_allow_on_submit_for_dimension_fields(doctypes): + for dt in doctypes: + meta = frappe.get_meta(dt) + for dimension in get_accounting_dimensions(): + df = meta.get_field(dimension) + if df and not df.allow_on_submit: + frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1) diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json index cb7e6bac5f8..e9206b3cf2e 100644 --- a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json @@ -6,9 +6,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "document_type", - "column_break_sfzb", - "allowed" + "document_type" ], "fields": [ { @@ -17,29 +15,20 @@ "in_list_view": 1, "label": "Doctype", "options": "DocType" - }, - { - "default": "0", - "fieldname": "allowed", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Allowed" - }, - { - "fieldname": "column_break_sfzb", - "fieldtype": "Column Break" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-03-27 13:10:32.415806", + "modified": "2026-04-14 16:53:16.806714", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Allowed Types", "owner": "Administrator", "permissions": [], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py index 8226a910171..7f0064cc3cf 100644 --- a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py @@ -14,7 +14,6 @@ class RepostAllowedTypes(Document): if TYPE_CHECKING: from frappe.types import DF - allowed: DF.Check document_type: DF.Link | None parent: DF.Data parentfield: DF.Data diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a6651be8483..61b93b292b1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -475,3 +475,4 @@ erpnext.patches.v16_0.remove_payables_receivables_workspace erpnext.patches.v16_0.co_by_product_patch erpnext.patches.v16_0.depends_on_inv_dimensions erpnext.patches.v16_0.uom_category +erpnext.patches.v16_0.merge_repost_settings_to_accounts_settings diff --git a/erpnext/patches/v16_0/merge_repost_settings_to_accounts_settings.py b/erpnext/patches/v16_0/merge_repost_settings_to_accounts_settings.py new file mode 100644 index 00000000000..55560e0c578 --- /dev/null +++ b/erpnext/patches/v16_0/merge_repost_settings_to_accounts_settings.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + if allowed := frappe.get_hooks("repost_allowed_doctypes"): + accounts_settings = frappe.get_doc("Accounts Settings") + for x in allowed: + if x not in [t.document_type for t in accounts_settings.repost_allowed_types]: + accounts_settings.append("repost_allowed_types", {"document_type": x}) + accounts_settings.save() From 5c064331cb64756f6b34eacf2bb7ba169f3fcabf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 10:47:55 +0530 Subject: [PATCH 13/57] refactor(test): use new source for repost setting (cherry picked from commit b8207d5ed17acd537f8d6df1967e33702ad591f4) --- .../accounts/doctype/journal_entry/test_journal_entry.py | 6 +++--- .../doctype/purchase_invoice/test_purchase_invoice.py | 6 +++--- .../test_repost_accounting_ledger.py | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 53d6013e1e2..833c6189c7d 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -413,9 +413,9 @@ class TestJournalEntry(ERPNextTestSuite): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center # Configure Repost Accounting Ledger for JVs - settings = frappe.get_doc("Repost Accounting Ledger Settings") - if not [x for x in settings.allowed_types if x.document_type == "Journal Entry"]: - settings.append("allowed_types", {"document_type": "Journal Entry", "allowed": True}) + settings = frappe.get_doc("Accounts Settings") + if "Journal Entry" not in [x.document_type for x in settings.repost_allowed_types]: + settings.append("repost_allowed_types", {"document_type": "Journal Entry"}) settings.save() # Create JV with defaut cost center - _Test Cost Center diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index b42574ee206..5cf3c6be879 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2256,9 +2256,9 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin): def test_repost_accounting_entries(self): # update repost settings - settings = frappe.get_doc("Repost Accounting Ledger Settings") - if not [x for x in settings.allowed_types if x.document_type == "Purchase Invoice"]: - settings.append("allowed_types", {"document_type": "Purchase Invoice", "allowed": True}) + settings = frappe.get_doc("Accounts Settings") + if "Purchase Invoice" not in [x.document_type for x in settings.repost_allowed_types]: + settings.append("repost_allowed_types", {"document_type": "Purchase Invoice"}) settings.save() pi = make_purchase_invoice( diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py index 3200a83c122..935047e2e35 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py @@ -280,7 +280,8 @@ def update_repost_settings(): "Journal Entry", "Purchase Receipt", ] - repost_settings = frappe.get_doc("Repost Accounting Ledger Settings") - for x in allowed_types: - repost_settings.append("allowed_types", {"document_type": x, "allowed": True}) - repost_settings.save() + settings = frappe.get_doc("Accounts Settings") + for _type in allowed_types: + if _type not in [x.document_type for x in settings.repost_allowed_types]: + settings.append("repost_allowed_types", {"document_type": _type}) + settings.save() From 3ba400a02bdb4e55d25611032b0e4befbb0c2389 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 10:48:37 +0530 Subject: [PATCH 14/57] refactor(ux): better error message (cherry picked from commit 3093409933d3634840a07d08425428503b72df7b) --- .../repost_accounting_ledger.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 819ced1c911..d0af5794e09 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -274,14 +274,13 @@ def validate_docs_for_voucher_types(doc_voucher_types): if disallowed_types := voucher_types.difference(allowed_types): message = "are" if len(disallowed_types) > 1 else "is" frappe.throw( - _("{0} {1} not allowed to be reposted. Modify {2} to enable reposting.").format( + _( + "{0} {1} not allowed to be reposted. You can enable it by adding it '{2}' table in {3}." + ).format( frappe.bold(comma_and(list(disallowed_types))), message, - frappe.bold( - frappe.utils.get_link_to_form( - "Repost Accounting Ledger Settings", "Repost Accounting Ledger Settings" - ) - ), + frappe.bold("Allowed Doctype"), + frappe.utils.get_link_to_form("Accounts Settings"), ) ) From fa5e4dee1758505f353ecae8c33d5460715735a7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 10:53:35 +0530 Subject: [PATCH 15/57] refactor: remove redundant field from filter (cherry picked from commit ece85c770ff62d13436f8b300d74f9eccf376c85) --- .../repost_accounting_ledger/repost_accounting_ledger.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index d0af5794e09..807a1789881 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -219,7 +219,6 @@ def get_allowed_types_from_settings(child_doc: bool = False): x.document_type for x in frappe.db.get_all( "Repost Allowed Types", - filters={"allowed": True}, fields=["document_type"], distinct=True, ) @@ -288,8 +287,6 @@ def validate_docs_for_voucher_types(doc_voucher_types): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters): - filters = {"allowed": True} - if txt: filters.update({"document_type": ("like", f"%{txt}%")}) From 5a2933df8ffbdc79ab013da3e251aa8eded22ad0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 12:15:31 +0530 Subject: [PATCH 16/57] refactor: limit reposting to only supported doctypes (cherry picked from commit 940d3cfe0ac03afa2546acce7e3e367a17cb8668) --- .../doctype/accounts_settings/accounts_settings.js | 10 +++++++++- erpnext/hooks.py | 7 +++++++ erpnext/startup/boot.py | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index 931e05a716b..2fda643640b 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -2,7 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on("Accounts Settings", { - refresh: function (frm) {}, + refresh: function (frm) { + frm.set_query("document_type", "repost_allowed_types", function (doc, cdt, cdn) { + return { + filters: { + name: ["in", frappe.boot.sysdefaults.repost_allowed_doctypes], + }, + }; + }); + }, enable_immutable_ledger: function (frm) { if (!frm.doc.enable_immutable_ledger) { return; diff --git a/erpnext/hooks.py b/erpnext/hooks.py index cc4a08d8b67..2fd4be3a510 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -692,3 +692,10 @@ fields_for_group_similar_items = ["qty", "amount"] # ------------ # List of apps whose translatable strings should be excluded from this app's translations. ignore_translatable_strings_from = ["frappe"] +repost_allowed_doctypes = [ + "Sales Invoice", + "Purchase Invoice", + "Journal Entry", + "Payment Entry", + "Purchase Receipt", +] diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index b2e66ed6114..08cf9154c2b 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -66,6 +66,7 @@ def boot_session(bootinfo): bootinfo.sysdefaults.default_ageing_range = frappe.db.get_single_value( "Accounts Settings", "default_ageing_range" ) + bootinfo.sysdefaults.repost_allowed_doctypes = frappe.get_hooks("repost_allowed_doctypes") def update_page_info(bootinfo): From 2e7c4776d4007a351ceabc9eea19ec54da973bbc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 15 Apr 2026 12:18:06 +0530 Subject: [PATCH 17/57] refactor: delete redundent repost setting (cherry picked from commit 6a04c159cac1b79a2de45ea4599f199450f06c4b) --- .../__init__.py | 0 .../repost_accounting_ledger_settings.js | 8 --- .../repost_accounting_ledger_settings.json | 53 ------------------- .../repost_accounting_ledger_settings.py | 45 ---------------- .../test_repost_accounting_ledger_settings.py | 11 ---- 5 files changed, 117 deletions(-) delete mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py delete mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js delete mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json delete mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py delete mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js deleted file mode 100644 index 8c83ca50431..00000000000 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -// frappe.ui.form.on("Repost Accounting Ledger Settings", { -// refresh(frm) { - -// }, -// }); diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json deleted file mode 100644 index 808986bba23..00000000000 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "actions": [], - "creation": "2023-11-07 09:57:20.619939", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "allowed_types" - ], - "fields": [ - { - "fieldname": "allowed_types", - "fieldtype": "Table", - "label": "Allowed Doctypes", - "options": "Repost Allowed Types" - } - ], - "grid_page_length": 50, - "hide_toolbar": 0, - "in_create": 1, - "issingle": 1, - "links": [], - "modified": "2026-03-16 13:28:21.312607", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Repost Accounting Ledger Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "Administrator", - "select": 1, - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "read": 1, - "role": "System Manager", - "select": 1, - "write": 1 - } - ], - "row_format": "Dynamic", - "sort_field": "creation", - "sort_order": "DESC", - "states": [], - "track_changes": 1 -} diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py deleted file mode 100644 index d6ef25cd1b7..00000000000 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe.model.document import Document - -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( - get_accounting_dimensions, -) -from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import get_child_docs - - -class RepostAccountingLedgerSettings(Document): - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - from erpnext.accounts.doctype.repost_allowed_types.repost_allowed_types import RepostAllowedTypes - - allowed_types: DF.Table[RepostAllowedTypes] - # end: auto-generated types - - def validate(self): - self.update_property_for_accounting_dimension() - - def update_property_for_accounting_dimension(self): - doctypes = [entry.document_type for entry in self.allowed_types if entry.allowed] - if not doctypes: - return - doctypes += get_child_docs(doctypes) - - set_allow_on_submit_for_dimension_fields(doctypes) - - -def set_allow_on_submit_for_dimension_fields(doctypes): - for dt in doctypes: - meta = frappe.get_meta(dt) - for dimension in get_accounting_dimensions(): - df = meta.get_field(dimension) - if df and not df.allow_on_submit: - frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py deleted file mode 100644 index 6a8698e96af..00000000000 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe - - -from erpnext.tests.utils import ERPNextTestSuite - - -class TestRepostAccountingLedgerSettings(ERPNextTestSuite): - pass From 10dbfd310f4c27ae17990c869766ea97f8aa1529 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:17:51 +0530 Subject: [PATCH 18/57] feat: make fg phantom-able in bom creator (backport #54332) (#54333) --- .../doctype/bom_creator/bom_creator.js | 44 ++++++++++++++++--- .../doctype/bom_creator/bom_creator.json | 13 +++++- .../doctype/bom_creator/bom_creator.py | 21 +++++++-- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js index 045aa5c7968..2c6dbd84fa6 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js @@ -64,6 +64,13 @@ frappe.ui.form.on("BOM Creator", { options: "Item", reqd: 1, }, + { + label: __("Is Phantom BOM"), + fieldtype: "Check", + fieldname: "is_phantom", + default: 0, + change: toggle_filter, + }, { fieldtype: "Column Break" }, { label: __("Quantity"), @@ -72,7 +79,7 @@ frappe.ui.form.on("BOM Creator", { reqd: 1, default: 1.0, }, - { fieldtype: "Section Break" }, + { fieldtype: "Section Break", depends_on: "eval:!doc.is_phantom" }, { label: __("Currency"), fieldtype: "Link", @@ -89,7 +96,7 @@ frappe.ui.form.on("BOM Creator", { reqd: 1, default: 1.0, }, - { fieldtype: "Section Break" }, + { fieldtype: "Section Break", depends_on: "eval:!doc.is_phantom" }, { label: __("Routing"), fieldtype: "Link", @@ -99,14 +106,39 @@ frappe.ui.form.on("BOM Creator", { ], primary_action_label: __("Create"), primary_action: (values) => { - values.doctype = frm.doc.doctype; - frappe.db.insert(values).then((doc) => { - frappe.set_route("Form", doc.doctype, doc.name); + frappe.db.get_value("Item", values.item_code, "is_stock_item").then((r) => { + if (r.message) { + if (r.message.is_stock_item && values.is_phantom) { + frappe.throw( + __("Phantom BOM cannot be created for stock item {0}.", [values.item_code]) + ); + } else if (!r.message.is_stock_item && !values.is_phantom) { + frappe.throw( + __("Non-phantom BOM cannot be created for non-stock item {0}.", [ + values.item_code, + ]) + ); + } else { + values.doctype = frm.doc.doctype; + frappe.db.insert(values).then((doc) => { + frappe.set_route("Form", doc.doctype, doc.name); + }); + } + } }); }, }); - dialog.fields_dict.item_code.get_query = "erpnext.controllers.queries.item_query"; + function toggle_filter() { + dialog.fields_dict.item_code.get_query = { + query: "erpnext.controllers.queries.item_query", + filters: { + is_stock_item: !dialog.fields_dict.is_phantom.value, + }, + }; + } + toggle_filter(); + dialog.show(); }, diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json index 96298a913e1..4bca0510e3e 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json @@ -13,6 +13,7 @@ "details_tab", "section_break_ylsl", "item_code", + "is_phantom", "item_name", "item_group", "column_break_ikj7", @@ -291,6 +292,13 @@ "fieldtype": "Link", "label": "Routing", "options": "Routing" + }, + { + "default": "0", + "fieldname": "is_phantom", + "fieldtype": "Check", + "label": "Is Phantom Item", + "read_only": 1 } ], "hide_toolbar": 1, @@ -302,7 +310,7 @@ "link_fieldname": "bom_creator" } ], - "modified": "2024-11-25 16:41:03.047835", + "modified": "2026-04-16 17:39:38.232864", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Creator", @@ -336,9 +344,10 @@ "write": 1 } ], + "row_format": "Dynamic", "show_name_in_global_search": 1, "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py index 97849b6f17e..54d4fd48611 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py @@ -52,6 +52,7 @@ class BOMCreator(Document): currency: DF.Link default_warehouse: DF.Link | None error_log: DF.Text | None + is_phantom: DF.Check item_code: DF.Link item_group: DF.Link | None item_name: DF.Data | None @@ -77,6 +78,7 @@ class BOMCreator(Document): self.set_rate_for_items() def validate(self): + self.validate_finished_good() self.validate_items() self.validate_duplicate_item() @@ -102,6 +104,15 @@ class BOMCreator(Document): else: item_map[key] = row.idx + def validate_finished_good(self): + is_stock_item = frappe.get_cached_value("Item", self.item_code, "is_stock_item") + if is_stock_item and self.is_phantom: + frappe.throw(_("Phantom BOM cannot be created for stock item {0}.").format(self.item_code)) + elif not is_stock_item and not self.is_phantom: + frappe.throw( + _("Non-phantom BOM cannot be created for non-stock item {0}.").format(self.item_code) + ) + def validate_items(self): for row in self.items: if row.is_expandable and row.item_code == self.item_code: @@ -334,10 +345,12 @@ class BOMCreator(Document): } ) - if row.item_code == self.item_code and (self.routing or self.has_operations()): - bom.routing = self.routing - bom.with_operations = 1 - bom.transfer_material_against = "Work Order" + if row.item_code == self.item_code: + bom.is_phantom_bom = self.is_phantom + if not self.is_phantom and (self.routing or self.has_operations()): + bom.routing = self.routing + bom.with_operations = 1 + bom.transfer_material_against = "Work Order" for field in BOM_FIELDS: if self.get(field): From b252ad49b7484486bbd1c465fe9200f373f134cc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:11:45 +0000 Subject: [PATCH 19/57] fix: hide operations field in bom creator if phantom (backport #54336) (#54337) --- erpnext/manufacturing/doctype/bom_creator/bom_creator.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json index 4bca0510e3e..42e4f0a17b7 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json @@ -283,6 +283,7 @@ "read_only": 1 }, { + "depends_on": "eval:!doc.is_phantom", "fieldname": "section_break_xvld", "fieldtype": "Section Break", "label": "Operations Routing" @@ -310,7 +311,7 @@ "link_fieldname": "bom_creator" } ], - "modified": "2026-04-16 17:39:38.232864", + "modified": "2026-04-16 20:43:30.820781", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Creator", From 46f5de0b1c4c2604bf54b21069b7de76e5aab099 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Apr 2026 13:16:38 +0530 Subject: [PATCH 20/57] fix: make Target Warehouse mandatory on UI (cherry picked from commit 2a8267e10a4410f53f281d49e716c6cdd7dc490e) --- erpnext/manufacturing/doctype/work_order/work_order.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 9840ed1c13d..4fa5c5d7e04 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -241,6 +241,11 @@ frappe.ui.form.on("Work Order", { frm.trigger("allow_alternative_item"); frm.trigger("hide_reserve_stock_button"); frm.trigger("toggle_items_editable"); + frm.trigger("set_fg_warehouse_mandatory"); + }, + + skip_transfer(frm) { + frm.trigger("set_fg_warehouse_mandatory"); }, toggle_items_editable(frm) { @@ -277,6 +282,11 @@ frappe.ui.form.on("Work Order", { return has_reserved_stock; }, + set_fg_warehouse_mandatory(frm) { + let mandatory = frm.doc.skip_transfer === 1 || frm.doc.track_semi_finished_goods === 1 ? false : true; + frm.toggle_reqd("fg_warehouse", mandatory); + }, + add_custom_button_to_return_components: function (frm) { if (frm.doc.docstatus === 1 && ["Closed", "Completed"].includes(frm.doc.status)) { let non_consumed_items = frm.doc.required_items.filter((d) => { From 36cc39ddc69a1ffe2fab1f01ea37ae7151955b50 Mon Sep 17 00:00:00 2001 From: nishkagosalia Date: Fri, 17 Apr 2026 14:31:42 +0530 Subject: [PATCH 21/57] refactor(UX): Batch Form Cleanup (cherry picked from commit de747fe625d5f15a498b4bdc88d98f6647db53eb) --- erpnext/stock/doctype/batch/batch.js | 3 ++- erpnext/stock/doctype/batch/batch.json | 30 ++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 7c9c0eeb09a..d0b444129ce 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -72,7 +72,8 @@ frappe.ui.form.on("Batch", { consider_negative_batches: 1, }, callback: (r) => { - if (!r.message) { + if (!r.message || r.message.length === 0) { + frm.dashboard.add_comment(__("No stock available for this batch."), "Blue", true); return; } diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json index 0520eb79e24..d312a491db6 100644 --- a/erpnext/stock/doctype/batch/batch.json +++ b/erpnext/stock/doctype/batch/batch.json @@ -7,10 +7,6 @@ "document_type": "Setup", "engine": "InnoDB", "field_order": [ - "sb_disabled", - "disabled", - "column_break_24", - "use_batchwise_valuation", "sb_batch", "batch_id", "item", @@ -22,11 +18,14 @@ "batch_qty", "stock_uom", "expiry_date", + "use_batchwise_valuation", + "disabled", "source", - "supplier", "column_break_9", "reference_doctype", "reference_name", + "column_break_xrll", + "supplier", "section_break_7", "description", "manufacturing_section", @@ -37,9 +36,11 @@ "fields": [ { "default": "0", + "depends_on": "eval:!doc.__islocal", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disabled" + "label": "Disabled", + "report_hide": 1 }, { "depends_on": "eval:doc.__islocal", @@ -68,7 +69,7 @@ "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, - "label": "image" + "label": "Image" }, { "depends_on": "eval:doc.parent_batch", @@ -137,10 +138,6 @@ "oldfieldtype": "Small Text", "width": "300px" }, - { - "fieldname": "sb_disabled", - "fieldtype": "Section Break" - }, { "fieldname": "sb_batch", "fieldtype": "Section Break", @@ -190,10 +187,6 @@ "label": "Produced Qty", "read_only": 1 }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, { "default": "0", "fieldname": "use_batchwise_valuation", @@ -201,6 +194,10 @@ "label": "Use Batch-wise Valuation", "read_only": 1, "set_only_once": 1 + }, + { + "fieldname": "column_break_xrll", + "fieldtype": "Column Break" } ], "icon": "fa fa-archive", @@ -208,7 +205,7 @@ "image_field": "image", "links": [], "max_attachments": 5, - "modified": "2024-03-27 13:06:38.677265", + "modified": "2026-04-17 12:45:34.810373", "modified_by": "Administrator", "module": "Stock", "name": "Batch", @@ -230,6 +227,7 @@ } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], From 5916e570af92109833da237ecd9a360516f9a976 Mon Sep 17 00:00:00 2001 From: nishkagosalia Date: Mon, 6 Apr 2026 19:09:20 +0530 Subject: [PATCH 22/57] fix: Table row in dialog should not have delete row option (cherry picked from commit eb89903decbda456e1ac08ddff00d596c7a23f4f) --- erpnext/public/js/controllers/transaction.js | 3 ++- erpnext/selling/doctype/sales_order/sales_order.js | 4 +++- erpnext/selling/doctype/sales_order/sales_order.py | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4971f914b1e..94e4cbb5e44 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -472,6 +472,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe frappe.msgprint(__("No pending payment schedules available.")); return; } + schedules.forEach((schedule) => (schedule.__checked = 1)); const dialog = new frappe.ui.Dialog({ title: __("Select Payment Schedule"), @@ -481,6 +482,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe fieldname: "payment_schedules", label: __("Payment Schedules"), cannot_add_rows: true, + cannot_delete_rows: true, in_place_edit: false, data: schedules, fields: [ @@ -526,7 +528,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe }); return; } - console.log(selected); dialog.hide(); let me = this; const payment_request_type = ["Sales Order", "Sales Invoice"].includes(this.frm.doc.doctype) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index bd41932aac4..5fe7fe6543b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -1405,6 +1405,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex make_raw_material_request_dialog(r) { var me = this; + r.message.forEach((item) => (item.__checked = 1)); var fields = [ { fieldtype: "Check", fieldname: "include_exploded_items", label: __("Include Exploded Items") }, { @@ -1415,7 +1416,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex { fieldtype: "Table", fieldname: "items", - description: __("Select BOM, Qty and For Warehouse"), + description: __("Finished Goods"), + cannot_delete_rows: true, fields: [ { fieldtype: "Read Only", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 6e8696dbd8e..619f5cfcf19 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -2050,14 +2050,14 @@ def get_work_order_items(sales_order, for_raw_material_request=0): if not pending_qty: pending_qty = stock_qty * overproduction_percentage_for_sales_order - if pending_qty > 0 and i.item_code not in product_bundle_parents: + if pending_qty > 0 and i.item_code not in product_bundle_parents and bom: items.append( dict( name=i.name, item_code=i.item_code, item_name=i.item_name, description=i.description, - bom=bom or "", + bom=bom, warehouse=i.warehouse, pending_qty=pending_qty, required_qty=pending_qty if for_raw_material_request else 0, From 493f36b3cee01bd1574a17d0dbbc5926384d080c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Apr 2026 18:18:02 +0530 Subject: [PATCH 23/57] fix: negative batch report showing same batch-warehouse multiple times (cherry picked from commit 700572980da92e8d28353661b3f7cf03963f87c7) --- .../negative_batch_report.py | 77 +++++++++++-------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/erpnext/stock/report/negative_batch_report/negative_batch_report.py b/erpnext/stock/report/negative_batch_report/negative_batch_report.py index b12bd87d538..e6b2c1a747d 100644 --- a/erpnext/stock/report/negative_batch_report/negative_batch_report.py +++ b/erpnext/stock/report/negative_batch_report/negative_batch_report.py @@ -90,45 +90,62 @@ def get_data(filters) -> list[dict]: batch_negative_data = [] flt_precision = frappe.db.get_default("float_precision") or 2 + distinct_batches = set() for company in companies: - for batch in batches: - _c, data = stock_ledger_execute( - frappe._dict( - { - "company": company, - "batch_no": batch, - "from_date": add_to_date(today(), years=-12), - "to_date": today(), - "segregate_serial_batch_bundle": 1, - "warehouse": filters.get("warehouse"), - "valuation_field_type": "Currency", - } - ) - ) - - previous_qty = 0 - for row in data: - if flt(row.get("qty_after_transaction"), flt_precision) < 0: - batch_negative_data.append( + warehouses = get_warehouses(filters, company) + for warehouse in warehouses: + for batch in batches: + _c, data = stock_ledger_execute( + frappe._dict( { - "posting_date": row.get("date"), - "batch_no": row.get("batch_no"), - "item_code": row.get("item_code"), - "item_name": row.get("item_name"), - "warehouse": row.get("warehouse"), - "actual_qty": row.get("actual_qty"), - "qty_after_transaction": row.get("qty_after_transaction"), - "previous_qty": previous_qty, - "voucher_type": row.get("voucher_type"), - "voucher_no": row.get("voucher_no"), + "company": company, + "batch_no": batch, + "from_date": add_to_date(today(), years=-12), + "to_date": today(), + "segregate_serial_batch_bundle": 1, + "warehouse": warehouse, + "valuation_field_type": "Currency", } ) + ) - previous_qty = row.get("qty_after_transaction") + previous_qty = 0 + for row in data: + key = (row.get("warehouse"), batch) + if key in distinct_batches: + continue + + if flt(row.get("qty_after_transaction"), flt_precision) < 0: + batch_negative_data.append( + { + "posting_date": row.get("date"), + "batch_no": row.get("batch_no"), + "item_code": row.get("item_code"), + "item_name": row.get("item_name"), + "warehouse": row.get("warehouse"), + "actual_qty": row.get("actual_qty"), + "qty_after_transaction": row.get("qty_after_transaction"), + "previous_qty": previous_qty, + "voucher_type": row.get("voucher_type"), + "voucher_no": row.get("voucher_no"), + } + ) + + distinct_batches.add(key) + + previous_qty = row.get("qty_after_transaction") return batch_negative_data +def get_warehouses(filters, company): + warehouse_filters = {"company": company, "disabled": 0} + if filters.get("warehouse"): + warehouse_filters["name"] = filters["warehouse"] + + return frappe.get_all("Warehouse", pluck="name", filters=warehouse_filters) + + def get_batches(filters): batch_filters = {} if filters.get("item_code"): From d2cc54969651d35df316fa4621fda6246f0cdc25 Mon Sep 17 00:00:00 2001 From: Pandiyan37 Date: Fri, 17 Apr 2026 18:26:34 +0530 Subject: [PATCH 24/57] fix(manufacturing): handle empty list in query builder (cherry picked from commit 9e5d94c1e6b4900a468f191151bcb1890d377f85) --- .../material_requirements_planning_report.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py index 9a17948362a..fc987d29f93 100644 --- a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py +++ b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py @@ -78,10 +78,12 @@ class MaterialRequirementsPlanningReport: (so.docstatus == 1) & (so.status.notin(["Closed", "Completed", "Stopped"])) & (so_item.docstatus == 1) - & (so_item.item_code.isin(items)) ) ) + if items: + query = query.where(so_item.item_code.isin(items)) + if self.filters.get("warehouse"): warehouses = [self.filters.get("warehouse")] if frappe.db.get_value("Warehouse", self.filters.get("warehouse"), "is_group"): From 52a4ca9c410fc320988a9ce1b90bd4782e215cdf Mon Sep 17 00:00:00 2001 From: Lakshit Jain Date: Sat, 18 Apr 2026 11:34:36 +0530 Subject: [PATCH 25/57] feat: add support for 'not applicable' tax in item tax templates (#50898) * feat: add support for 'not applicable' tax in item tax templates * refactor: remove unused imports * fix: import NOT_APPLICABLE_TAX in get_item_tax_map function * fix: add item wise tax details for not applicable taxes * test: added test case for `not_applicable` * fix: do not create item wise tax details for not applicable tax * fix: ensure tax rate is set to 0 for not applicable tax rows * refactor: changes as per review * test: update selling settings * test: correct settings * fix: return both net and current tax amounts for not applicable tax (cherry picked from commit 453fe376ab5f6e5f2cfafa4125ad2c3ca4be2ade) --- .../item_tax_template/item_tax_template.js | 9 + .../item_tax_template/item_tax_template.py | 7 + .../item_tax_template_detail.json | 19 +- .../item_tax_template_detail.py | 1 + erpnext/controllers/accounts_controller.py | 8 +- erpnext/controllers/buying_controller.py | 9 +- erpnext/controllers/taxes_and_totals.py | 22 +- .../tests/test_item_wise_tax_details.py | 235 ++++++++++++++++++ .../public/js/controllers/taxes_and_totals.js | 25 +- erpnext/stock/get_item_details.py | 7 +- erpnext/utilities/transaction_base.py | 5 +- 11 files changed, 331 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js index b608ccd3568..94c87fcae93 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js @@ -47,3 +47,12 @@ frappe.ui.form.on("Item Tax Template", { }); }, }); + +frappe.ui.form.on("Item Tax Template Detail", { + not_applicable: function (frm, cdt, cdn) { + let row = locals[cdt][cdn]; + if (row.not_applicable) { + frappe.model.set_value(cdt, cdn, "tax_rate", 0); + } + }, +}); diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py index 464fb0f8227..8a23331b3f6 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.py +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.py @@ -27,8 +27,15 @@ class ItemTaxTemplate(Document): # end: auto-generated types def validate(self): + self.set_zero_rate_for_not_applicable_tax() self.validate_tax_accounts() + def set_zero_rate_for_not_applicable_tax(self): + """Ensure tax_rate is 0 for any row marked as not applicable.""" + for row in self.get("taxes"): + if row.not_applicable: + row.tax_rate = 0 + def autoname(self): if self.company and self.title: abbr = frappe.get_cached_value("Company", self.company, "abbr") diff --git a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.json b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.json index 5092489c012..d11d249894d 100644 --- a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.json +++ b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.json @@ -6,7 +6,8 @@ "engine": "InnoDB", "field_order": [ "tax_type", - "tax_rate" + "tax_rate", + "not_applicable" ], "fields": [ { @@ -21,20 +22,30 @@ "fieldname": "tax_rate", "fieldtype": "Float", "in_list_view": 1, - "label": "Tax Rate" + "label": "Tax Rate", + "read_only_depends_on": "eval:doc.not_applicable" + }, + { + "default": "0", + "description": "Check if this tax is not applicable to items (distinct from 0% rate)", + "fieldname": "not_applicable", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Not Applicable" } ], "istable": 1, "links": [], - "modified": "2024-03-27 13:09:55.735360", + "modified": "2025-12-26 17:19:18.791891", "modified_by": "Administrator", "module": "Accounts", "name": "Item Tax Template Detail", "owner": "Administrator", "permissions": [], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py index 810235e3691..a98fbc6ba86 100644 --- a/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py +++ b/erpnext/accounts/doctype/item_tax_template_detail/item_tax_template_detail.py @@ -14,6 +14,7 @@ class ItemTaxTemplateDetail(Document): if TYPE_CHECKING: from frappe.types import DF + not_applicable: DF.Check parent: DF.Data parentfield: DF.Data parenttype: DF.Data diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e0bb6e85bab..bd4233c6350 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -68,6 +68,7 @@ from erpnext.setup.utils import get_exchange_rate from erpnext.stock.doctype.item.item import get_uom_conv_factor from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.get_item_details import ( + NOT_APPLICABLE_TAX, ItemDetailsCtx, _get_item_tax_template, get_conversion_factor, @@ -3684,8 +3685,11 @@ def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True): if child_item.get("item_tax_rate") and add_taxes_from_item_tax_template: tax_map = json.loads(child_item.get("item_tax_rate")) - for tax_type in tax_map: - tax_rate = flt(tax_map[tax_type]) + for tax_type, tax_rate in tax_map.items(): + if tax_rate == NOT_APPLICABLE_TAX: + continue + + tax_rate = flt(tax_rate) taxes = parent_doc.get("taxes") or [] # add new row for tax head only if missing found = any(tax.account_head == tax_type for tax in taxes) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index fd86291027e..5627dffea95 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -18,7 +18,11 @@ from erpnext.buying.utils import update_last_purchase_rate, validate_for_items from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.subcontracting_controller import SubcontractingController -from erpnext.stock.get_item_details import get_conversion_factor, get_item_defaults +from erpnext.stock.get_item_details import ( + NOT_APPLICABLE_TAX, + get_conversion_factor, + get_item_defaults, +) from erpnext.stock.utils import get_incoming_rate @@ -523,6 +527,9 @@ class BuyingController(SubcontractingController): if account not in tax_accounts: continue + if rate == NOT_APPLICABLE_TAX: + continue + net_rate = item.base_net_amount if item.sales_incoming_rate: net_rate = item.qty * item.sales_incoming_rate diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 2961650673c..6a0a41b1799 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -20,7 +20,12 @@ from erpnext.controllers.accounts_controller import ( validate_taxes_and_charges, ) from erpnext.deprecation_dumpster import deprecated -from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template, get_item_tax_map +from erpnext.stock.get_item_details import ( + NOT_APPLICABLE_TAX, + ItemDetailsCtx, + _get_item_tax_template, + get_item_tax_map, +) from erpnext.utilities.regional import temporary_flag @@ -358,6 +363,9 @@ class calculate_taxes_and_totals: if cint(tax.included_in_print_rate): tax_rate = self._get_tax_rate(tax, item_tax_map) + if tax_rate == NOT_APPLICABLE_TAX: + return current_tax_fraction, inclusive_tax_amount_per_qty + if tax.charge_type == "On Net Total": current_tax_fraction = tax_rate / 100.0 @@ -382,9 +390,12 @@ class calculate_taxes_and_totals: def _get_tax_rate(self, tax, item_tax_map): if tax.account_head in item_tax_map: - return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) - else: - return tax.rate + rate = item_tax_map[tax.account_head] + if rate == NOT_APPLICABLE_TAX: + return NOT_APPLICABLE_TAX + return flt(rate, self.doc.precision("rate", tax)) + + return tax.rate def calculate_net_total(self): self.doc.total_qty = ( @@ -594,6 +605,9 @@ class calculate_taxes_and_totals: current_tax_amount = 0.0 current_net_amount = 0.0 + if tax_rate == NOT_APPLICABLE_TAX: + return current_net_amount, current_tax_amount + if tax.charge_type == "Actual": current_net_amount = item.net_amount # distribute the tax amount proportionally to each item row diff --git a/erpnext/controllers/tests/test_item_wise_tax_details.py b/erpnext/controllers/tests/test_item_wise_tax_details.py index 7e19c1dc057..81dd92c8606 100644 --- a/erpnext/controllers/tests/test_item_wise_tax_details.py +++ b/erpnext/controllers/tests/test_item_wise_tax_details.py @@ -301,3 +301,238 @@ class TestTaxesAndTotals(ERPNextTestSuite): tax = doc.taxes[0] detail = doc.item_wise_tax_details[0] self.assertEqual(detail.amount, tax.base_tax_amount_after_discount_amount) + + @change_settings("Selling Settings", {"allow_multiple_items": 1}) + def test_not_applicable_tax_in_item_tax_template(self): + """Test that items with 'not applicable' tax don't contribute to net amount of that tax.""" + template_7pct = frappe.get_doc( + { + "doctype": "Item Tax Template", + "title": "_Test VAT 7% Template", + "company": "_Test Company", + "taxes": [ + { + "tax_type": "_Test Account VAT - _TC", + "tax_rate": 7, + }, + { + "tax_type": "_Test Account Service Tax - _TC", + "tax_rate": 0, + "not_applicable": 1, + }, + ], + } + ).insert(ignore_if_duplicate=True) + + template_19pct = frappe.get_doc( + { + "doctype": "Item Tax Template", + "title": "_Test VAT 19% Template", + "company": "_Test Company", + "taxes": [ + { + "tax_type": "_Test Account VAT - _TC", + "tax_rate": 0, + }, + { + "tax_type": "_Test Account Service Tax - _TC", + "tax_rate": 19, + }, + ], + } + ).insert(ignore_if_duplicate=True) + + self.doc.items[0].item_tax_template = template_7pct.name + + self.doc.append( + "items", + { + "item_code": "_Test Item", + "qty": 1, + "rate": 100, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + "item_tax_template": template_19pct.name, + }, + ) + + self.doc.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT 7%", + "rate": 7, + }, + ) + + self.doc.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT 19%", + "rate": 19, + }, + ) + + self.doc.save() + + # VAT 7%: Both items contribute (Item 2 has 0% rate, not "not applicable") + self.assertEqual(self.doc.taxes[0].net_amount, 200.0) + # Service Tax 19%: Only Item 2 contributes (Item 1 has not_applicable) + self.assertEqual(self.doc.taxes[1].net_amount, 100.0) + + expected_values = [ + { + "item_row": self.doc.items[0].name, + "tax_row": self.doc.taxes[0].name, + "rate": 7.0, + "amount": 7.0, + "taxable_amount": 100.0, + }, + { + "item_row": self.doc.items[1].name, + "tax_row": self.doc.taxes[0].name, + "rate": 0.0, + "amount": 0.0, + "taxable_amount": 100.0, + }, + { + "item_row": self.doc.items[1].name, + "tax_row": self.doc.taxes[1].name, + "rate": 19.0, + "amount": 19.0, + "taxable_amount": 100.0, + }, + ] + + actual_values = [ + { + "item_row": row.item_row, + "tax_row": row.tax_row, + "rate": row.rate, + "amount": row.amount, + "taxable_amount": row.taxable_amount, + } + for row in self.doc.item_wise_tax_details + ] + + self.assertEqual(actual_values, expected_values) + + def test_not_applicable_tax_in_item_tax_template_with_different_items(self): + """Test that items with 'not applicable' tax don't contribute to net amount of that tax.""" + template_7pct = frappe.get_doc( + { + "doctype": "Item Tax Template", + "title": "_Test VAT 7% Template", + "company": "_Test Company", + "taxes": [ + { + "tax_type": "_Test Account VAT - _TC", + "tax_rate": 7, + }, + { + "tax_type": "_Test Account Service Tax - _TC", + "tax_rate": 0, + "not_applicable": 1, + }, + ], + } + ).insert(ignore_if_duplicate=True) + + template_19pct = frappe.get_doc( + { + "doctype": "Item Tax Template", + "title": "_Test VAT 19% Template", + "company": "_Test Company", + "taxes": [ + { + "tax_type": "_Test Account VAT - _TC", + "tax_rate": 0, + "not_applicable": 1, + }, + { + "tax_type": "_Test Account Service Tax - _TC", + "tax_rate": 19, + }, + ], + } + ).insert(ignore_if_duplicate=True) + + self.doc.items[0].item_tax_template = template_7pct.name + + self.doc.append( + "items", + { + "item_code": "_Test Item 2", + "qty": 1, + "rate": 100, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": "_Test Cost Center - _TC", + "item_tax_template": template_19pct.name, + }, + ) + + self.doc.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT 7%", + "rate": 0, + }, + ) + + self.doc.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT 19%", + "rate": 0, + }, + ) + + self.doc.save() + + # VAT 7%: Only Item 1 contributes (Item 2 has not_applicable) + self.assertEqual(self.doc.taxes[0].net_amount, 100.0) + # Service Tax 19%: Only Item 2 contributes (Item 1 has not_applicable) + self.assertEqual(self.doc.taxes[1].net_amount, 100.0) + + expected_values = [ + { + "item_row": self.doc.items[0].name, + "tax_row": self.doc.taxes[0].name, + "rate": 7.0, + "amount": 7.0, + "taxable_amount": 100.0, + }, + { + "item_row": self.doc.items[1].name, + "tax_row": self.doc.taxes[1].name, + "rate": 19.0, + "amount": 19.0, + "taxable_amount": 100.0, + }, + ] + + actual_values = [ + { + "item_row": row.item_row, + "tax_row": row.tax_row, + "rate": row.rate, + "amount": row.amount, + "taxable_amount": row.taxable_amount, + } + for row in self.doc.item_wise_tax_details + ] + + self.assertEqual(actual_values, expected_values) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index ad1b64413ac..e3074f711ef 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -1,6 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +const NOT_APPLICABLE_TAX = "N/A"; + erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { setup() { this.fetch_round_off_accounts(); @@ -299,6 +301,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if (cint(tax.included_in_print_rate)) { var tax_rate = this._get_tax_rate(tax, item_tax_map); + if (tax_rate === NOT_APPLICABLE_TAX) { + return [current_tax_fraction, inclusive_tax_amount_per_qty]; + } + if (tax.charge_type == "On Net Total") { current_tax_fraction = tax_rate / 100.0; } else if (tax.charge_type == "On Previous Row Amount") { @@ -322,9 +328,14 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } _get_tax_rate(tax, item_tax_map) { - return Object.keys(item_tax_map).indexOf(tax.account_head) != -1 - ? flt(item_tax_map[tax.account_head], precision("rate", tax)) - : tax.rate; + if (tax.account_head in item_tax_map) { + let rate = item_tax_map[tax.account_head]; + if (rate === NOT_APPLICABLE_TAX) { + return NOT_APPLICABLE_TAX; + } + return flt(rate, precision("rate", tax)); + } + return tax.rate; } calculate_net_total() { @@ -368,6 +379,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } $.each(item_tax_map, function (tax, rate) { + if (rate === NOT_APPLICABLE_TAX) { + return; + } + let found = (me.frm.doc.taxes || []).find((d) => d.account_head === tax); if (!found) { let child = frappe.model.add_child(me.frm.doc, "taxes"); @@ -524,6 +539,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { var current_tax_amount = 0.0; var current_net_amount = 0.0; + if (tax_rate === NOT_APPLICABLE_TAX) { + return [current_net_amount, current_tax_amount]; + } + // To set row_id by default as previous row. if (["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { if (tax.idx === 1) { diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 2b9fd98dd53..c02018b2d1c 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -41,6 +41,8 @@ purchase_doctypes = [ "Purchase Invoice", ] +NOT_APPLICABLE_TAX = "N/A" + def _preprocess_ctx(ctx): if not ctx.price_list: @@ -836,7 +838,10 @@ def get_item_tax_map(*, doc: str | dict | Document, tax_template: str | None = N template = frappe.get_cached_doc("Item Tax Template", tax_template) for d in template.taxes: if frappe.get_cached_value("Account", d.tax_type, "company") == doc.get("company"): - item_tax_map[d.tax_type] = d.tax_rate + if d.get("not_applicable"): + item_tax_map[d.tax_type] = NOT_APPLICABLE_TAX + else: + item_tax_map[d.tax_type] = d.tax_rate return json.dumps(item_tax_map) if as_json else item_tax_map diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 55d60fb389d..86ead256f7d 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -9,7 +9,7 @@ from frappe.utils import cint, flt, get_time, now_datetime from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions from erpnext.controllers.status_updater import StatusUpdater -from erpnext.stock.get_item_details import get_item_details +from erpnext.stock.get_item_details import NOT_APPLICABLE_TAX, get_item_details from erpnext.stock.utils import get_incoming_rate @@ -367,6 +367,9 @@ class TransactionBase(StatusUpdater): ): item_tax_template = frappe.json.loads(item_details.item_tax_rate) for tax_head, _rate in item_tax_template.items(): + if _rate == NOT_APPLICABLE_TAX: + continue + found = [x for x in self.taxes if x.account_head == tax_head] if not found: self.append("taxes", {"charge_type": "On Net Total", "account_head": tax_head, "rate": 0}) From 3914d5d1b736738e9bdacd5f9f3c81b12491056f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 06:49:44 +0000 Subject: [PATCH 26/57] fix: fetch item tax template from item group when creating item (backport #54258) (#54368) fix: fetch item tax template from item group when creating item (#54258) (cherry picked from commit b93f2350eebf21e6e132eeff42e50f88c69e6998) Co-authored-by: Pandiyan P --- erpnext/stock/doctype/item/item.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 272595c8437..639d6a292ec 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -777,6 +777,19 @@ class Item(Document): {"company": defaults.get("company"), "default_warehouse": defaults.default_warehouse}, ) + if not self.taxes and item_group.taxes: + for tax in item_group.taxes: + self.append( + "taxes", + { + "item_tax_template": tax.item_tax_template, + "tax_category": tax.tax_category, + "valid_from": tax.valid_from, + "minimum_net_rate": tax.minimum_net_rate, + "maximum_net_rate": tax.maximum_net_rate, + }, + ) + def update_variants(self): if self.flags.dont_update_variants or frappe.db.get_single_value( "Item Variant Settings", "do_not_update_variants" From 104eac21e8f3c4a4fb01304b514fe2fdba30ff39 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:05:36 +0000 Subject: [PATCH 27/57] fix: zero valuation rate popup on SI (backport #54376) (#54377) fix: zero valuation rate popup on SI (#54376) (cherry picked from commit 3ef6c24f07c49506873fba6b16edec087eff24ec) Co-authored-by: Mihir Kandoi --- erpnext/controllers/stock_controller.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 05f2e18c878..5e883a6695c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -142,14 +142,19 @@ class StockController(AccountsController): ]: for item in self.get("items"): if ( - (item.get("valuation_rate") == 0 or item.get("incoming_rate") == 0) + ( + item.get("valuation_rate") == 0 + or (item.get("incoming_rate") == 0 and self.get("update_stock", 1)) + ) and item.get("allow_zero_valuation_rate") == 0 and frappe.get_cached_value("Item", item.item_code, "is_stock_item") ): frappe.toast( - _( - "Row #{0}: Item {1} has zero rate but 'Allow Zero Valuation Rate' is not enabled." - ).format(item.idx, frappe.bold(item.item_code)), + _("Row #{0}: Item {1} has zero rate but '{2}' is not enabled.").format( + item.idx, + frappe.bold(item.item_code), + item.meta.get_label("allow_zero_valuation_rate"), + ), indicator="orange", ) From 78aaf6c7e83cd92fe7feac42c661cb04d6a38267 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:37:43 +0000 Subject: [PATCH 28/57] =?UTF-8?q?fix:=20dropship=20logic=20should=20come?= =?UTF-8?q?=20above=20non=20stock=20logic=20in=20gross=20profit=E2=80=A6?= =?UTF-8?q?=20(backport=20#54383)=20(#54385)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: dropship logic should come above non stock logic in gross profit… (#54383) fix: dropship logic should come above non stock logic in gross profit report (cherry picked from commit 40bcaa7bc3702db2a8dad919b081953ada632ac5) Co-authored-by: Mihir Kandoi --- .../report/gross_profit/gross_profit.py | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 518b49f0ea5..ad692f0fbbd 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -774,7 +774,28 @@ class GrossProfitGenerator: # IMP NOTE # stock_ledger_entries should already be filtered by item_code and warehouse and # sorted by posting_date desc, posting_time desc - if item_code in self.non_stock_items and (row.project or row.cost_center): + if ( + row.delivered_by_supplier + and row.so_detail + and ( + po_details := frappe.get_all( + "Purchase Order Item", + filters={"sales_order_item": row.so_detail, "docstatus": 1}, + pluck="name", + ) + ) + ): + from frappe.query_builder.functions import Sum + + table = frappe.qb.DocType("Purchase Invoice Item") + query = ( + frappe.qb.from_(table) + .select(Sum(table.stock_qty * table.base_net_rate)) + .where((table.po_detail.isin(po_details)) & (table.docstatus == 1)) + ) + return flt(query.run()[0][0]) + + elif item_code in self.non_stock_items and (row.project or row.cost_center): # Issue 6089-Get last purchasing rate for non-stock item item_rate = self.get_last_purchase_rate(item_code, row) return flt(row.qty) * item_rate @@ -804,26 +825,6 @@ class GrossProfitGenerator: return self.calculate_buying_amount_from_sle( row, my_sle, parenttype, parent, item_row, item_code ) - elif ( - row.delivered_by_supplier - and row.so_detail - and ( - po_details := frappe.get_all( - "Purchase Order Item", - filters={"sales_order_item": row.so_detail, "docstatus": 1}, - pluck="name", - ) - ) - ): - from frappe.query_builder.functions import Sum - - table = frappe.qb.DocType("Purchase Invoice Item") - query = ( - frappe.qb.from_(table) - .select(Sum(table.stock_qty * table.base_net_rate)) - .where((table.po_detail.isin(po_details)) & (table.docstatus == 1)) - ) - return flt(query.run()[0][0]) elif row.sales_order and row.so_detail: incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code) if incoming_amount: From 7556550158d26f3744e4e3d2ed81218cd822668f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 12:52:15 +0530 Subject: [PATCH 29/57] fix: use qty instead of stock qty dropship gross profit report (backport #54389) (#54391) fix: use qty instead of stock qty dropship gross profit report (#54389) (cherry picked from commit d6b379b936d4d2861c040f61308484fef18af15f) Co-authored-by: Mihir Kandoi --- erpnext/accounts/report/gross_profit/gross_profit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index ad692f0fbbd..dfba16a77eb 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -790,7 +790,7 @@ class GrossProfitGenerator: table = frappe.qb.DocType("Purchase Invoice Item") query = ( frappe.qb.from_(table) - .select(Sum(table.stock_qty * table.base_net_rate)) + .select(Sum(table.qty * table.base_net_rate)) .where((table.po_detail.isin(po_details)) & (table.docstatus == 1)) ) return flt(query.run()[0][0]) From cac907383b9906496b9d8220a9be9bc863bc29ca Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:46:21 +0000 Subject: [PATCH 30/57] fix: Disallow negative rates in Purchase invoice (backport #54254) (#54393) fix: Disallow negative rates in Purchase invoice (#54254) (cherry picked from commit 23768ae0a5b7b1019aee251c9bd2d81f766e98e3) Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com> --- .../buying_settings/buying_settings.json | 9 ++++++++- .../buying_settings/buying_settings.py | 1 + erpnext/controllers/status_updater.py | 19 +++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 5a0edbdf5d1..3963bbb4959 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -24,6 +24,7 @@ "allow_zero_qty_in_supplier_quotation", "use_transaction_date_exchange_rate", "allow_zero_qty_in_request_for_quotation", + "allow_negative_rates_for_items", "column_break_12", "maintain_same_rate", "allow_multiple_items", @@ -279,6 +280,12 @@ "fieldname": "validate_consumed_qty", "fieldtype": "Check", "label": "Validate Consumed Qty (as per BOM)" + }, + { + "default": "0", + "fieldname": "allow_negative_rates_for_items", + "fieldtype": "Check", + "label": "Allow Negative rates for Items" } ], "grid_page_length": 50, @@ -288,7 +295,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-03-16 13:28:19.432589", + "modified": "2026-04-15 16:07:35.484787", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.py b/erpnext/buying/doctype/buying_settings/buying_settings.py index 3634f8a9069..8f358bb364b 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.py +++ b/erpnext/buying/doctype/buying_settings/buying_settings.py @@ -18,6 +18,7 @@ class BuyingSettings(Document): from frappe.types import DF allow_multiple_items: DF.Check + allow_negative_rates_for_items: DF.Check allow_zero_qty_in_purchase_order: DF.Check allow_zero_qty_in_request_for_quotation: DF.Check allow_zero_qty_in_supplier_quotation: DF.Check diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 96ca67c28df..95c25c497bf 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -272,6 +272,12 @@ class StatusUpdater(Document): continue items_to_validate = [] + selling_negative_rate_allowed = frappe.get_single_value( + "Selling Settings", "allow_negative_rates_for_items" + ) + buying_negative_rate_allowed = frappe.get_single_value( + "Buying Settings", "allow_negative_rates_for_items" + ) # get unique transactions to update for d in self.get_all_children(): @@ -281,7 +287,12 @@ class StatusUpdater(Document): if hasattr(d, "qty") and d.qty > 0 and self.get("is_return"): frappe.throw(_("For an item {0}, quantity must be negative number").format(d.item_code)) - if not frappe.get_single_value("Selling Settings", "allow_negative_rates_for_items"): + if ( + not selling_negative_rate_allowed and self.doctype in ["Sales Invoice", "Delivery Note"] + ) or ( + not buying_negative_rate_allowed + and self.doctype in ["Purchase Invoice", "Purchase Receipt"] + ): if hasattr(d, "item_code") and hasattr(d, "rate") and flt(d.rate) < 0: frappe.throw( _( @@ -289,7 +300,11 @@ class StatusUpdater(Document): ).format( frappe.bold(d.item_code), frappe.bold(_("`Allow Negative rates for Items`")), - get_link_to_form("Selling Settings", "Selling Settings"), + get_link_to_form( + "Selling Settings" + if self.doctype in ["Sales Invoice", "Delivery Note"] + else "Buying Settings" + ), ), ) From aa2cba97808cdebb227c067504317e41922ded3a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:57:13 +0000 Subject: [PATCH 31/57] fix: validate south africa company in vat audit report (backport #54030) (#54394) fix: validate south africa company in vat audit report (#54030) * fix: validate south africa company in vat audit report * fix: use qb to get invoice data * fix: validate company region in south africa vat settings (cherry picked from commit 1c65cc108828bfb166b64aa238135829ae366884) Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com> --- .../south_africa_vat_settings.py | 12 ++- .../vat_audit_report/vat_audit_report.js | 7 ++ .../vat_audit_report/vat_audit_report.py | 79 +++++++++---------- 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py index aa3cd4b05af..bb1b67e6e07 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py @@ -1,9 +1,12 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ from frappe.model.document import Document +from erpnext import get_region + class SouthAfricaVATSettings(Document): # begin: auto-generated types @@ -22,4 +25,9 @@ class SouthAfricaVATSettings(Document): vat_accounts: DF.Table[SouthAfricaVATAccount] # end: auto-generated types - pass + def validate(self): + self.validate_company_region() + + def validate_company_region(self): + if self.company and get_region(self.company) != "South Africa": + frappe.throw(_("Company {0} is not in South Africa.").format(frappe.bold(self.company))) diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js index de4fde596cd..b9aa11f8061 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.js +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js @@ -10,6 +10,13 @@ frappe.query_reports["VAT Audit Report"] = { options: "Company", reqd: 1, default: frappe.defaults.get_user_default("Company"), + get_query: function () { + return { + filters: { + country: "South Africa", + }, + }; + }, }, { fieldname: "from_date", diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index 78fdc62e29c..fdabdb4e9e5 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -8,6 +8,7 @@ import frappe from frappe import _ from frappe.utils import formatdate, get_link_to_form +from erpnext import get_region from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_details_query @@ -23,19 +24,10 @@ class VATAuditReport: self.doctypes = ["Purchase Invoice", "Sales Invoice"] def run(self): + self.validate_company_region() self.get_sa_vat_accounts() self.get_columns() for doctype in self.doctypes: - self.select_columns = """ - name as voucher_no, - posting_date, remarks""" - columns = ( - ", supplier as party, credit_to as account" - if doctype == "Purchase Invoice" - else ", customer as party, debit_to as account" - ) - self.select_columns += columns - self.get_invoice_data(doctype) if self.invoices: @@ -45,6 +37,14 @@ class VATAuditReport: return self.columns, self.data + def validate_company_region(self): + if self.filters.company and get_region(self.filters.company) != "South Africa": + frappe.throw( + _( + "The company {0} is not in South Africa. VAT Audit Report is only available for companies in South Africa." + ).format(frappe.bold(self.filters.company)) + ) + def get_sa_vat_accounts(self): self.sa_vat_accounts = frappe.get_all( "South Africa VAT Account", filters={"parent": self.filters.company}, pluck="account" @@ -56,27 +56,38 @@ class VATAuditReport: frappe.throw(_("Please set VAT Accounts in {0}").format(link_to_settings)) def get_invoice_data(self, doctype): - conditions = self.get_conditions() self.invoices = frappe._dict() - - invoice_data = frappe.db.sql( - f""" - SELECT - {self.select_columns} - FROM - `tab{doctype}` - WHERE - docstatus = 1 {conditions} - and is_opening = 'No' - ORDER BY - posting_date DESC - """, - self.filters, - as_dict=1, + invoice_doctype = frappe.qb.DocType(doctype) + party_field = invoice_doctype.supplier if doctype == "Purchase Invoice" else invoice_doctype.customer + account_field = ( + invoice_doctype.credit_to if doctype == "Purchase Invoice" else invoice_doctype.debit_to ) - for d in invoice_data: - self.invoices.setdefault(d.voucher_no, d) + query = ( + frappe.qb.from_(invoice_doctype) + .select( + invoice_doctype.name.as_("voucher_no"), + invoice_doctype.posting_date, + invoice_doctype.remarks, + party_field.as_("party"), + account_field.as_("account"), + ) + .where(invoice_doctype.docstatus == 1) + .where(invoice_doctype.is_opening == "No") + .orderby(invoice_doctype.posting_date, order=frappe.qb.desc) + ) + + if self.filters.get("company"): + query = query.where(invoice_doctype.company == self.filters.company) + if self.filters.get("from_date"): + query = query.where(invoice_doctype.posting_date >= self.filters.from_date) + if self.filters.get("to_date"): + query = query.where(invoice_doctype.posting_date <= self.filters.to_date) + + invoice_data = query.run(as_dict=True) + + for row in invoice_data: + self.invoices.setdefault(row.voucher_no, row) def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() @@ -129,18 +140,6 @@ class VATAuditReport: self.items_based_on_tax_rate[parent][row.rate]["net_amount"] += row.taxable_amount self.items_based_on_tax_rate[parent][row.rate]["gross_amount"] += row.amount + row.taxable_amount - def get_conditions(self): - conditions = "" - for opts in ( - ("company", " and company=%(company)s"), - ("from_date", " and posting_date>=%(from_date)s"), - ("to_date", " and posting_date<=%(to_date)s"), - ): - if self.filters.get(opts[0]): - conditions += opts[1] - - return conditions - def get_data(self, doctype): consolidated_data = self.get_consolidated_data(doctype) section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") From d542a72da59194f2923e8c622449853ab0a53d11 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 08:02:03 +0000 Subject: [PATCH 32/57] Fix : None handling in pricing rule free item quantity calculation (backport #54375) (#54396) Fix : None handling in pricing rule free item quantity calculation (#54375) * fix(pricing_rule): handle None qty in transaction_qty calculation * Update erpnext/accounts/doctype/pricing_rule/utils.py --------- (cherry picked from commit 82438d6c720f4a19423291af6d2b8008105bf4a1) Co-authored-by: Jaganath-Tridots Co-authored-by: Jagan Co-authored-by: Mihir Kandoi --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 56ba6a040dc..6df17e4e2cd 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -662,7 +662,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if pricing_rule.is_recursive: transaction_qty = sum( [ - row.qty + flt(row.qty) for row in doc.items if not row.is_free_item and row.item_code == args.item_code From cfcba1fcf24af5d10e05c24955024231432d39d5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 08:06:09 +0000 Subject: [PATCH 33/57] fix: recalculate operating costs if workstation type is changed (backport #54390) (#54398) fix: recalculate operating costs if workstation type is changed (#54390) * fix: recalculate operating costs if workstation type is changed * fix: do not overwrite op costs on every save (cherry picked from commit 28f3429a5409ced9afdbac748ecfacf7658c9109) Co-authored-by: Mihir Kandoi --- .../manufacturing/doctype/workstation/workstation.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index 1867b567da0..e9ee6fb49f0 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -82,7 +82,9 @@ class Workstation(Document): ) def before_save(self): - self.set_data_based_on_workstation_type() + if self.has_value_changed("workstation_type"): + self.set_data_based_on_workstation_type() + self.set_hour_rate() self.set_total_working_hours() self.disabled_workstation() @@ -112,9 +114,6 @@ class Workstation(Document): @frappe.whitelist() def set_data_based_on_workstation_type(self): - if self.workstation_costs: - return - if self.workstation_type: data = frappe.get_all( "Workstation Cost", @@ -123,6 +122,9 @@ class Workstation(Document): order_by="idx", ) + if data: + self.workstation_costs = [] + for row in data: self.append( "workstation_costs", From b1825c0cbeb5bfc69468c7684770bc8ecaf45f78 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:52:45 +0000 Subject: [PATCH 34/57] =?UTF-8?q?fix(dashboard-trends):=20set=20default=20?= =?UTF-8?q?fiscal=20year=20and=20company=20before=20val=E2=80=A6=20(backpo?= =?UTF-8?q?rt=20#54339)=20(#54400)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(dashboard-trends): set default fiscal year and company before val… (#54339) * fix(dashboard-trends): set default fiscal year and company before validating filters Ensure and are populated with default values * fix(dashboard-trends): ensure fiscal_year and company are properly set before validation to avoid empty filter issues * Update erpnext/controllers/trends.py --------- Co-authored-by: Mihir Kandoi (cherry picked from commit d61b5fd5f61dfdcf210600a2d72671ccfd5e406e) # Conflicts: # erpnext/controllers/trends.py * chore: fix conflicts --------- Co-authored-by: Ahmed AbuKhatwa <82771130+AhmedAbokhatwa@users.noreply.github.com> Co-authored-by: Mihir Kandoi --- erpnext/controllers/trends.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index bc4e2b346d4..f8e152f5299 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -4,7 +4,9 @@ import frappe from frappe import _ -from frappe.utils import getdate +from frappe.utils import DateTimeLikeObject, getdate, today + +from erpnext.accounts.utils import get_fiscal_year def get_columns(filters, trans): @@ -45,6 +47,10 @@ def get_columns(filters, trans): def validate_filters(filters): + if not filters.get("fiscal_year"): + filters["fiscal_year"] = get_fiscal_year(today())[0] + if not filters.get("company"): + filters["company"] = frappe.defaults.get_user_default("Company") for f in ["Fiscal Year", "Based On", "Period", "Company"]: if not filters.get(f.lower().replace(" ", "_")): frappe.throw(_("{0} is mandatory").format(_(f))) From fa76e8ac7f89fd43d54ab78cbe9ec96a925095d8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:54:04 +0530 Subject: [PATCH 35/57] fix: changed qty validation from qty field to stock_qty (backport #54352) (#54357) fix: changed qty validation from qty field to stock_qty (#54352) (cherry picked from commit ba01d66c24a7efe56dd433155ba5aece3d33bb63) Co-authored-by: Jatin3128 <140256508+Jatin3128@users.noreply.github.com> --- erpnext/selling/doctype/quotation/quotation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 4583b6cc1e5..3eb82af1b01 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -191,7 +191,7 @@ class Quotation(SellingController): ) for row in self._items: - if row.name not in ordered_items or row.qty > ordered_items[row.name]: + if row.name not in ordered_items or row.stock_qty > ordered_items[row.name]: return "Partially Ordered" return "Ordered" @@ -413,9 +413,9 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False, ar target.run_method("calculate_taxes_and_totals") def update_item(obj, target, source_parent): - balance_qty = obj.qty if is_unit_price_row(obj) else obj.qty - ordered_items.get(obj.name, 0.0) - target.qty = balance_qty if balance_qty > 0 else 0 - target.stock_qty = flt(target.qty) * flt(obj.conversion_factor) + balance_stock_qty = obj.stock_qty - ordered_items.get(obj.name, 0.0) + target.stock_qty = balance_stock_qty if balance_stock_qty > 0 else 0 + target.qty = flt(target.stock_qty) / flt(obj.conversion_factor) if obj.against_blanket_order: target.against_blanket_order = obj.against_blanket_order @@ -429,7 +429,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False, ar 2. If selections: Is Alternative Item/Has Alternative Item: Map if selected and adequate qty 3. If no selections: Simple row: Map if adequate qty """ - if not ((item.qty > ordered_items.get(item.name, 0.0)) or is_unit_price_row(item)): + if not ((item.stock_qty > ordered_items.get(item.name, 0.0)) or is_unit_price_row(item)): return False if not selected_rows: From 0d6d64ff0571e65734460e969bc16c2d0af62527 Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Tue, 24 Mar 2026 19:57:42 +0530 Subject: [PATCH 36/57] fix: default permission for HR User role (cherry picked from commit 7b0bfe76cc4ee573d2e9e8a2890f5fae5d67fcce) --- erpnext/accounts/doctype/account/account.json | 7 ++++++- erpnext/accounts/doctype/cost_center/cost_center.json | 9 +++++++-- .../doctype/mode_of_payment/mode_of_payment.json | 10 ++++++++-- erpnext/projects/doctype/project/project.json | 6 +++++- erpnext/projects/doctype/task/task.json | 6 +++++- erpnext/setup/doctype/company/company.json | 7 ++++++- erpnext/setup/doctype/holiday_list/holiday_list.json | 6 +++++- .../terms_and_conditions/terms_and_conditions.json | 9 +++++++-- 8 files changed, 49 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 8b74dd823ec..7908fe9cbab 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -203,7 +203,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2025-08-02 06:26:44.657146", + "modified": "2026-03-24 12:43:45.708738", "modified_by": "Administrator", "module": "Accounts", "name": "Account", @@ -256,6 +256,11 @@ "role": "Accounts Manager", "share": 1, "write": 1 + }, + { + "read": 1, + "report": 1, + "role": "HR User" } ], "row_format": "Dynamic", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 8a84e2c4c01..744fee4e003 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -126,7 +126,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2025-01-22 10:46:42.904001", + "modified": "2026-03-24 12:44:03.297998", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", @@ -173,11 +173,16 @@ "role": "Employee", "select": 1, "share": 1 + }, + { + "read": 1, + "role": "HR User" } ], + "row_format": "Dynamic", "search_fields": "parent_cost_center, is_group", "show_name_in_global_search": 1, "sort_field": "creation", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json index f7e7fc414bd..134fc3d1751 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json @@ -48,7 +48,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-08-16 19:22:42.942264", + "modified": "2026-03-24 12:48:15.343727", "modified_by": "Administrator", "module": "Accounts", "name": "Mode of Payment", @@ -68,12 +68,18 @@ "read": 1, "report": 1, "role": "Accounts User" + }, + { + "read": 1, + "report": 1, + "role": "HR User" } ], "quick_entry": 1, + "row_format": "Dynamic", "show_name_in_global_search": 1, "sort_field": "creation", "sort_order": "ASC", "states": [], "translated_doctype": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index e36e15f71f5..1525c07771e 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -480,7 +480,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2026-03-09 17:15:24.426294", + "modified": "2026-03-24 12:52:03.156389", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -524,6 +524,10 @@ "role": "Employee", "select": 1, "share": 1 + }, + { + "read": 1, + "role": "HR User" } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 9335a196989..c9071e01435 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -423,7 +423,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2026-03-04 11:47:10.454548", + "modified": "2026-03-24 12:57:46.063009", "modified_by": "Administrator", "module": "Projects", "name": "Task", @@ -441,6 +441,10 @@ "role": "Projects User", "share": 1, "write": 1 + }, + { + "read": 1, + "role": "HR User" } ], "quick_entry": 1, diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index ab64635c048..ddce64615e7 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -970,7 +970,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2026-03-09 17:15:33.819426", + "modified": "2026-03-24 12:44:15.614147", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -1030,6 +1030,11 @@ { "role": "Auditor", "select": 1 + }, + { + "read": 1, + "report": 1, + "role": "HR User" } ], "row_format": "Dynamic", diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index dd7955f4c40..9728d1a9306 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -153,7 +153,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2025-08-28 15:17:05.313383", + "modified": "2026-03-24 13:05:44.681123", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", @@ -170,6 +170,10 @@ "role": "HR Manager", "share": 1, "write": 1 + }, + { + "read": 1, + "role": "HR User" } ], "row_format": "Dynamic", diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index 335780a0b05..444b9aee85e 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -77,7 +77,7 @@ "icon": "icon-legal", "idx": 1, "links": [], - "modified": "2024-03-27 13:10:53.065872", + "modified": "2026-03-24 16:36:37.021007", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", @@ -130,11 +130,16 @@ { "read": 1, "role": "Stock User" + }, + { + "read": 1, + "role": "HR User" } ], "quick_entry": 1, + "row_format": "Dynamic", "show_name_in_global_search": 1, "sort_field": "creation", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} From 534891aac4b291bd2e510cada37367cc1dffb8ad Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Thu, 26 Mar 2026 17:23:54 +0530 Subject: [PATCH 37/57] fix: default permission for HR manager role (cherry picked from commit 5ec66169a7ad6873990c7a591f3a2f22da8815da) --- erpnext/accounts/doctype/account/account.json | 7 ++++- .../doctype/cost_center/cost_center.json | 6 +++- .../mode_of_payment/mode_of_payment.json | 7 +++-- erpnext/projects/doctype/project/project.json | 6 +++- erpnext/projects/doctype/task/task.json | 6 +++- erpnext/setup/doctype/company/company.json | 11 ++++++- .../doctype/designation/designation.json | 16 ++++++++-- erpnext/setup/doctype/driver/driver.json | 29 ++----------------- .../employee_group/employee_group.json | 21 ++++++++++++-- .../terms_and_conditions.json | 8 ++++- 10 files changed, 79 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 7908fe9cbab..d00c35bc489 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -203,7 +203,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2026-03-24 12:43:45.708738", + "modified": "2026-03-25 17:27:39.304619", "modified_by": "Administrator", "module": "Accounts", "name": "Account", @@ -261,6 +261,11 @@ "read": 1, "report": 1, "role": "HR User" + }, + { + "read": 1, + "report": 1, + "role": "HR Manager" } ], "row_format": "Dynamic", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 744fee4e003..1d640e76a03 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -126,7 +126,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2026-03-24 12:44:03.297998", + "modified": "2026-03-25 17:26:40.657146", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", @@ -177,6 +177,10 @@ { "read": 1, "role": "HR User" + }, + { + "read": 1, + "role": "HR Manager" } ], "row_format": "Dynamic", diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json index 134fc3d1751..86dc0f20dba 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json @@ -48,7 +48,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2026-03-24 12:48:15.343727", + "modified": "2026-03-25 17:34:25.721556", "modified_by": "Administrator", "module": "Accounts", "name": "Mode of Payment", @@ -71,8 +71,11 @@ }, { "read": 1, - "report": 1, "role": "HR User" + }, + { + "read": 1, + "role": "HR Manager" } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 1525c07771e..32b9174e916 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -480,7 +480,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2026-03-24 12:52:03.156389", + "modified": "2026-03-25 17:32:36.262980", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -528,6 +528,10 @@ { "read": 1, "role": "HR User" + }, + { + "read": 1, + "role": "HR Manager" } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index c9071e01435..db2fbe1695f 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -423,7 +423,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2026-03-24 12:57:46.063009", + "modified": "2026-03-25 17:35:27.323503", "modified_by": "Administrator", "module": "Projects", "name": "Task", @@ -445,6 +445,10 @@ { "read": 1, "role": "HR User" + }, + { + "read": 1, + "role": "HR Manager" } ], "quick_entry": 1, diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index ddce64615e7..e901593aada 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -970,7 +970,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2026-03-24 12:44:15.614147", + "modified": "2026-03-25 17:23:35.604282", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -1035,6 +1035,15 @@ "read": 1, "report": 1, "role": "HR User" + }, + { + "create": 1, + "export": 1, + "import": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "write": 1 } ], "row_format": "Dynamic", diff --git a/erpnext/setup/doctype/designation/designation.json b/erpnext/setup/doctype/designation/designation.json index ed37131fb55..8d767cde46e 100644 --- a/erpnext/setup/doctype/designation/designation.json +++ b/erpnext/setup/doctype/designation/designation.json @@ -31,7 +31,7 @@ "icon": "fa fa-bookmark", "idx": 1, "links": [], - "modified": "2024-03-27 13:06:51.361961", + "modified": "2026-03-26 14:59:26.816846", "modified_by": "Administrator", "module": "Setup", "name": "Designation", @@ -52,12 +52,24 @@ { "read": 1, "role": "Sales User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "show_name_in_global_search": 1, "sort_field": "creation", "sort_order": "ASC", "states": [], "translated_doctype": 1 -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/driver/driver.json b/erpnext/setup/doctype/driver/driver.json index 342ca730637..9fc83ef2ec9 100644 --- a/erpnext/setup/doctype/driver/driver.json +++ b/erpnext/setup/doctype/driver/driver.json @@ -129,7 +129,7 @@ ], "icon": "fa fa-user", "links": [], - "modified": "2024-03-27 13:08:18.825438", + "modified": "2026-03-26 14:45:09.568829", "modified_by": "Administrator", "module": "Setup", "name": "Driver", @@ -144,30 +144,6 @@ "role": "Fleet Manager", "share": 1 }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "write": 1 - }, { "print": 1, "read": 1, @@ -186,6 +162,7 @@ } ], "quick_entry": 1, + "row_format": "Dynamic", "search_fields": "full_name", "show_name_in_global_search": 1, "sort_field": "creation", @@ -193,4 +170,4 @@ "states": [], "title_field": "full_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/employee_group/employee_group.json b/erpnext/setup/doctype/employee_group/employee_group.json index 0114e93433e..7e5a504674b 100644 --- a/erpnext/setup/doctype/employee_group/employee_group.json +++ b/erpnext/setup/doctype/employee_group/employee_group.json @@ -32,7 +32,7 @@ } ], "links": [], - "modified": "2024-03-27 13:09:39.303885", + "modified": "2026-03-26 17:22:45.737955", "modified_by": "Administrator", "module": "Setup", "name": "Employee Group", @@ -49,11 +49,28 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "HR User", + "write": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index 444b9aee85e..7226ccb76a3 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -77,7 +77,7 @@ "icon": "icon-legal", "idx": 1, "links": [], - "modified": "2026-03-24 16:36:37.021007", + "modified": "2026-03-26 16:11:17.367505", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", @@ -134,6 +134,12 @@ { "read": 1, "role": "HR User" + }, + { + "create": 1, + "read": 1, + "role": "HR Manager", + "write": 1 } ], "quick_entry": 1, From a7b1fec21dcb4609cab5f62a02926efc7232227c Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Thu, 9 Apr 2026 19:15:45 +0530 Subject: [PATCH 38/57] fix: default perm for HR manager & HR user (cherry picked from commit f02b3b61663f7734a3a2c27d033c697e28645a9a) --- .../doctype/activity_type/activity_type.json | 21 ++++++++- erpnext/projects/doctype/task/task.json | 14 ++++-- .../projects/doctype/task_type/task_type.json | 16 ++++++- .../projects/doctype/timesheet/timesheet.json | 46 ++++++++++++------- erpnext/support/doctype/issue/issue.json | 11 ++++- 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/erpnext/projects/doctype/activity_type/activity_type.json b/erpnext/projects/doctype/activity_type/activity_type.json index ccca292ba77..299465439fd 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.json +++ b/erpnext/projects/doctype/activity_type/activity_type.json @@ -47,7 +47,7 @@ "icon": "icon-flag", "idx": 1, "links": [], - "modified": "2024-08-16 19:22:57.706521", + "modified": "2026-04-09 19:11:53.918325", "modified_by": "Administrator", "module": "Projects", "name": "Activity Type", @@ -79,11 +79,28 @@ { "read": 1, "role": "Employee" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "role": "HR User", + "select": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "ASC", "states": [], "translated_doctype": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index db2fbe1695f..1cf8aeee8a8 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -423,7 +423,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2026-03-25 17:35:27.323503", + "modified": "2026-04-09 19:12:27.153143", "modified_by": "Administrator", "module": "Projects", "name": "Task", @@ -443,12 +443,18 @@ "write": 1 }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, "read": 1, - "role": "HR Manager" + "role": "HR Manager", + "share": 1, + "write": 1 } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/task_type/task_type.json b/erpnext/projects/doctype/task_type/task_type.json index 86b5f17e8dd..340ef70e8cb 100644 --- a/erpnext/projects/doctype/task_type/task_type.json +++ b/erpnext/projects/doctype/task_type/task_type.json @@ -21,7 +21,7 @@ } ], "links": [], - "modified": "2024-03-27 13:10:51.823692", + "modified": "2026-04-09 19:13:08.572142", "modified_by": "Administrator", "module": "Projects", "name": "Task Type", @@ -60,11 +60,23 @@ "report": 1, "role": "Projects User", "share": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "role": "HR Manager", + "write": 1 + }, + { + "role": "HR User", + "select": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "ASC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index bd38d7c6414..a703e6cd07f 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -315,7 +315,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2026-03-04 11:56:51.438298", + "modified": "2026-04-08 12:43:30.658074", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", @@ -336,21 +336,6 @@ "submit": 1, "write": 1 }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - }, { "amend": 1, "cancel": 1, @@ -389,6 +374,35 @@ "read": 1, "role": "Accounts User", "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 } ], "row_format": "Dynamic", diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 958cab52be0..fed181386ff 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -393,7 +393,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2025-09-25 11:10:53.556731", + "modified": "2026-04-09 19:14:57.692236", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -410,9 +410,18 @@ "role": "Support Team", "share": 1, "write": 1 + }, + { + "role": "HR Manager", + "select": 1 + }, + { + "role": "HR User", + "select": 1 } ], "quick_entry": 1, + "row_format": "Dynamic", "search_fields": "status,customer,subject,raised_by", "sender_field": "raised_by", "sort_field": "creation", From 95213fb9b830b7de9e324d7724867633804fbca4 Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Tue, 14 Apr 2026 18:30:41 +0530 Subject: [PATCH 39/57] fix: default perm for HR manager & HR user (cherry picked from commit 41103a0622b72e2ce8bd76048659f99aeb523d30) --- erpnext/accounts/doctype/account/account.json | 12 +++++------- .../accounts/doctype/cost_center/cost_center.json | 10 +++++----- .../doctype/mode_of_payment/mode_of_payment.json | 10 +++++----- erpnext/projects/doctype/project/project.json | 10 +++++----- erpnext/setup/doctype/company/company.json | 7 +++---- erpnext/setup/doctype/designation/designation.json | 3 +-- erpnext/setup/doctype/holiday_list/holiday_list.json | 6 +++--- .../terms_and_conditions/terms_and_conditions.json | 6 +++--- 8 files changed, 30 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index d00c35bc489..a5f8d60cf4b 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -203,7 +203,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2026-03-25 17:27:39.304619", + "modified": "2026-04-14 18:14:42.202065", "modified_by": "Administrator", "module": "Accounts", "name": "Account", @@ -258,14 +258,12 @@ "write": 1 }, { - "read": 1, - "report": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { - "read": 1, - "report": 1, - "role": "HR Manager" + "role": "HR Manager", + "select": 1 } ], "row_format": "Dynamic", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 1d640e76a03..bb69300ff8c 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -126,7 +126,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2026-03-25 17:26:40.657146", + "modified": "2026-04-14 18:15:27.367298", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", @@ -175,12 +175,12 @@ "share": 1 }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { - "read": 1, - "role": "HR Manager" + "role": "HR Manager", + "select": 1 } ], "row_format": "Dynamic", diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json index 86dc0f20dba..ec03452e56a 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json @@ -48,7 +48,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2026-03-25 17:34:25.721556", + "modified": "2026-04-14 18:16:47.795986", "modified_by": "Administrator", "module": "Accounts", "name": "Mode of Payment", @@ -70,12 +70,12 @@ "role": "Accounts User" }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { - "read": 1, - "role": "HR Manager" + "role": "HR Manager", + "select": 1 } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 32b9174e916..95bf037c5e5 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -480,7 +480,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2026-03-25 17:32:36.262980", + "modified": "2026-04-14 18:17:40.676750", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -526,12 +526,12 @@ "share": 1 }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { - "read": 1, - "role": "HR Manager" + "role": "HR Manager", + "select": 1 } ], "quick_entry": 1, diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index e901593aada..a38310fb3ad 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -970,7 +970,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2026-03-25 17:23:35.604282", + "modified": "2026-04-14 18:13:20.811422", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -1032,9 +1032,8 @@ "select": 1 }, { - "read": 1, - "report": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { "create": 1, diff --git a/erpnext/setup/doctype/designation/designation.json b/erpnext/setup/doctype/designation/designation.json index 8d767cde46e..001fed22a9f 100644 --- a/erpnext/setup/doctype/designation/designation.json +++ b/erpnext/setup/doctype/designation/designation.json @@ -31,7 +31,7 @@ "icon": "fa fa-bookmark", "idx": 1, "links": [], - "modified": "2026-03-26 14:59:26.816846", + "modified": "2026-04-14 18:19:34.405635", "modified_by": "Administrator", "module": "Setup", "name": "Designation", @@ -40,7 +40,6 @@ "permissions": [ { "create": 1, - "delete": 1, "email": 1, "print": 1, "read": 1, diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index 9728d1a9306..fb4feba4872 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -153,7 +153,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2026-03-24 13:05:44.681123", + "modified": "2026-04-14 18:21:08.018741", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", @@ -172,8 +172,8 @@ "write": 1 }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 } ], "row_format": "Dynamic", diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index 7226ccb76a3..6539aa45861 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -77,7 +77,7 @@ "icon": "icon-legal", "idx": 1, "links": [], - "modified": "2026-03-26 16:11:17.367505", + "modified": "2026-04-14 18:22:49.285298", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", @@ -132,8 +132,8 @@ "role": "Stock User" }, { - "read": 1, - "role": "HR User" + "role": "HR User", + "select": 1 }, { "create": 1, From 4940aeb71212bb37726b0443dcced964be794e03 Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Fri, 17 Apr 2026 15:39:42 +0530 Subject: [PATCH 40/57] fix: remove unwanted perm for HR user role (cherry picked from commit d26cd69fe5fce3179477c5d2c097c1c70ea91cc9) --- erpnext/projects/doctype/activity_type/activity_type.json | 6 +----- erpnext/projects/doctype/task_type/task_type.json | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/erpnext/projects/doctype/activity_type/activity_type.json b/erpnext/projects/doctype/activity_type/activity_type.json index 299465439fd..8c1bf2d3392 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.json +++ b/erpnext/projects/doctype/activity_type/activity_type.json @@ -47,7 +47,7 @@ "icon": "icon-flag", "idx": 1, "links": [], - "modified": "2026-04-09 19:11:53.918325", + "modified": "2026-04-17 15:24:16.754519", "modified_by": "Administrator", "module": "Projects", "name": "Activity Type", @@ -91,10 +91,6 @@ "role": "HR Manager", "share": 1, "write": 1 - }, - { - "role": "HR User", - "select": 1 } ], "quick_entry": 1, diff --git a/erpnext/projects/doctype/task_type/task_type.json b/erpnext/projects/doctype/task_type/task_type.json index 340ef70e8cb..bc65b4f6a3e 100644 --- a/erpnext/projects/doctype/task_type/task_type.json +++ b/erpnext/projects/doctype/task_type/task_type.json @@ -21,7 +21,7 @@ } ], "links": [], - "modified": "2026-04-09 19:13:08.572142", + "modified": "2026-04-17 15:32:28.598095", "modified_by": "Administrator", "module": "Projects", "name": "Task Type", @@ -67,10 +67,6 @@ "read": 1, "role": "HR Manager", "write": 1 - }, - { - "role": "HR User", - "select": 1 } ], "quick_entry": 1, From 47abaf70b24b588aad481df30afe4b799934f556 Mon Sep 17 00:00:00 2001 From: iamkhanraheel Date: Fri, 17 Apr 2026 17:13:51 +0530 Subject: [PATCH 41/57] fix: default company perms for HR manager (cherry picked from commit 2018a90ad8f30d343a153d0c52ba5615118f2247) --- erpnext/setup/doctype/company/company.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index a38310fb3ad..37eda038e3b 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -970,7 +970,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2026-04-14 18:13:20.811422", + "modified": "2026-04-17 17:11:46.586135", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -1036,9 +1036,6 @@ "select": 1 }, { - "create": 1, - "export": 1, - "import": 1, "read": 1, "report": 1, "role": "HR Manager", From 2c73e37f8025caf483d7022f2d196de50146f8d4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Apr 2026 16:56:01 +0530 Subject: [PATCH 42/57] feat: backflush based on in BOM (cherry picked from commit 877d99c5a59ccb0d38cbcee2999dfadd1544ca5c) --- erpnext/manufacturing/doctype/bom/bom.json | 163 +++++++++++------- erpnext/manufacturing/doctype/bom/bom.py | 14 ++ .../production_plan/test_production_plan.py | 3 + .../doctype/work_order/test_work_order.py | 60 +++++++ .../doctype/work_order/work_order.js | 7 + .../doctype/work_order/work_order.json | 5 +- .../doctype/work_order/work_order.py | 4 + .../stock/doctype/stock_entry/stock_entry.py | 18 +- .../stock_entry_type/stock_entry_type.py | 4 + 9 files changed, 201 insertions(+), 77 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 8574e58a498..a322c61e0e5 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -7,31 +7,18 @@ "engine": "InnoDB", "field_order": [ "production_item_tab", + "final_product_section", "company", "item", + "column_break_ztxc", "quantity", - "cb0", - "is_active", - "is_default", - "allow_alternative_item", - "set_rate_of_sub_assembly_item_based_on_bom", - "is_phantom_bom", - "cost_allocation_section", + "uom", + "cost_allocation__process_loss_section", "cost_allocation_per", - "column_break_srby", "cost_allocation", - "process_loss_section", + "column_break_tgkb", "process_loss_percentage", - "column_break_ssj2", "process_loss_qty", - "currency_detail", - "rm_cost_as_per", - "buying_price_list", - "price_list_currency", - "plc_conversion_rate", - "column_break_ivyw", - "currency", - "conversion_rate", "operations_section_section", "with_operations", "track_semi_finished_goods", @@ -46,8 +33,27 @@ "operations", "materials_section", "items", - "secondary_items_tab", + "section_break_hygk", "secondary_items", + "bom_conf_tab", + "bom_configuration_section", + "column_break_zbzp", + "is_active", + "is_default", + "set_rate_of_sub_assembly_item_based_on_bom", + "cb0", + "is_phantom_bom", + "allow_alternative_item", + "quality_inspection_section_break", + "inspection_required", + "column_break_dxp7", + "quality_inspection_template", + "default_warehouse_section", + "default_source_warehouse", + "column_break_inep", + "default_target_warehouse", + "consume_components_section", + "backflush_based_on", "costing", "operating_cost", "raw_material_cost", @@ -59,23 +65,21 @@ "column_break_26", "total_cost", "base_total_cost", - "quality_inspection_tab", - "quality_inspection_section_break", - "inspection_required", - "column_break_dxp7", - "quality_inspection_template", "more_info_tab", + "currency_detail", + "rm_cost_as_per", + "buying_price_list", + "price_list_currency", + "plc_conversion_rate", + "column_break_ivyw", + "currency", + "conversion_rate", "production_item_info_section", "item_name", - "uom", "image", "column_break_27", "description", "has_variants", - "default_warehouse_section", - "default_source_warehouse", - "column_break_inep", - "default_target_warehouse", "section_break_ouuf", "project", "section_break0", @@ -99,17 +103,18 @@ ], "fields": [ { - "description": "Item to be manufactured or repacked", + "description": "The final item that will be produced using this BOM.", "fieldname": "item", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, - "label": "Item", + "label": "Item to Manufacture", "oldfieldname": "item", "oldfieldtype": "Link", "options": "Item", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_description_on_click": 1 }, { "fetch_from": "item.item_name", @@ -130,23 +135,26 @@ "read_only": 1 }, { + "depends_on": "item", "fetch_from": "item.stock_uom", "fieldname": "uom", "fieldtype": "Link", - "label": "Item UOM", + "label": "Unit Of Measure", "options": "UOM", "read_only": 1 }, { "default": "1", - "description": "Quantity of item obtained after manufacturing / repacking from given quantities of raw materials", + "depends_on": "item", + "description": "How many units of the final product this BOM makes.", "fieldname": "quantity", "fieldtype": "Float", - "label": "Quantity", + "label": "Quantity (Output Qty)", "non_negative": 1, "oldfieldname": "quantity", "oldfieldtype": "Currency", - "reqd": 1 + "reqd": 1, + "show_description_on_click": 1 }, { "fieldname": "cb0", @@ -288,14 +296,13 @@ { "fieldname": "materials_section", "fieldtype": "Section Break", - "label": "Raw Materials", "oldfieldtype": "Section Break" }, { "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", + "label": "Components", "oldfieldname": "bom_materials", "oldfieldtype": "Table", "options": "BOM Item", @@ -415,6 +422,7 @@ "depends_on": "eval:!doc.is_phantom_bom", "fieldname": "website_section", "fieldtype": "Tab Break", + "hidden": 1, "label": "Website" }, { @@ -528,11 +536,6 @@ "fieldtype": "Section Break", "label": "Operations" }, - { - "fieldname": "process_loss_section", - "fieldtype": "Section Break", - "label": "Process Loss" - }, { "fieldname": "process_loss_percentage", "fieldtype": "Percent", @@ -546,10 +549,6 @@ "non_negative": 1, "read_only": 1 }, - { - "fieldname": "column_break_ssj2", - "fieldtype": "Column Break" - }, { "fieldname": "more_info_tab", "fieldtype": "Tab Break", @@ -668,11 +667,6 @@ "fieldname": "section_break_ouuf", "fieldtype": "Section Break" }, - { - "fieldname": "quality_inspection_tab", - "fieldtype": "Tab Break", - "label": "Quality Inspection" - }, { "fieldname": "secondary_items", "fieldtype": "Table", @@ -697,20 +691,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "fieldname": "secondary_items_tab", - "fieldtype": "Tab Break", - "label": "Secondary Items" - }, - { - "fieldname": "cost_allocation_section", - "fieldtype": "Section Break", - "label": "Cost Allocation" - }, - { - "fieldname": "column_break_srby", - "fieldtype": "Column Break" - }, { "fieldname": "cost_allocation", "fieldtype": "Currency", @@ -725,6 +705,55 @@ "fieldtype": "Percent", "label": "% Cost Allocation", "non_negative": 1 + }, + { + "collapsible": 1, + "fieldname": "bom_configuration_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_zbzp", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_ztxc", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "cost_allocation__process_loss_section", + "fieldtype": "Section Break", + "label": "Cost Allocation / Process Loss" + }, + { + "fieldname": "column_break_tgkb", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_hygk", + "fieldtype": "Section Break" + }, + { + "fieldname": "final_product_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "bom_conf_tab", + "fieldtype": "Tab Break", + "label": "BOM Configuration" + }, + { + "fieldname": "consume_components_section", + "fieldtype": "Section Break", + "label": "Consume Components" + }, + { + "description": "Controls how raw materials are consumed during the \u2018Manufacture\u2019 stock entry.", + "fieldname": "backflush_based_on", + "fieldtype": "Select", + "label": "Based On", + "options": "\nBOM\nMaterial Transferred for Manufacture", + "show_description_on_click": 1 } ], "icon": "fa fa-sitemap", @@ -732,7 +761,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2026-02-26 14:13:34.040181", + "modified": "2026-04-17 15:22:33.598938", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 03aae5219bb..ea86472cf46 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -117,6 +117,7 @@ class BOM(WebsiteGenerator): allow_alternative_item: DF.Check amended_from: DF.Link | None + backflush_based_on: DF.Literal["", "BOM", "Material Transferred for Manufacture"] base_operating_cost: DF.Currency base_raw_material_cost: DF.Currency base_secondary_items_cost: DF.Currency @@ -1982,3 +1983,16 @@ def get_secondary_items_from_sub_assemblies(bom_no, company, qty, secondary_item get_secondary_items_from_sub_assemblies(row.bom_no, company, qty, secondary_items) return secondary_items + + +def get_backflush_based_on(bom_no): + backflush_based_on = None + if bom_no: + backflush_based_on = frappe.get_cached_value("BOM", bom_no, "backflush_based_on") + + if not backflush_based_on: + backflush_based_on = frappe.db.get_single_value( + "Manufacturing Settings", "backflush_raw_materials_based_on" + ) + + return backflush_based_on diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 5d7e2fa2b36..9ab329b8f19 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -2879,6 +2879,9 @@ def make_bom(**args): } ) + if args.backflush_based_on: + bom.backflush_based_on = args.backflush_based_on + if args.operating_cost_per_bom_quantity: bom.fg_based_operating_cost = 1 bom.operating_cost_per_bom_quantity = args.operating_cost_per_bom_quantity diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 628aa58b187..4e48224032b 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -4240,6 +4240,66 @@ class TestWorkOrder(ERPNextTestSuite): self.assertEqual(wo_order.operations[0].time_in_mins, 72) self.assertEqual(wo_order.operations[1].time_in_mins, 240) + def test_backflush_based_on_in_bom(self): + raw_material_1 = make_item(item_code="BOM RM 1", properties={"is_stock_item": 1}).name + raw_material_2 = make_item(item_code="BOM RM 2", properties={"is_stock_item": 1}).name + fg_item = make_item(item_code="BOM FG 1", properties={"is_stock_item": 1}).name + + frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM") + + backflush_based_on = frappe.db.get_single_value( + "Manufacturing Settings", "backflush_raw_materials_based_on" + ) + self.assertEqual(backflush_based_on, "BOM") + + for item_code in [raw_material_1, raw_material_2]: + test_stock_entry.make_stock_entry( + item_code=item_code, target="Stores - _TC", qty=1, basic_rate=100 + ) + + bom = make_bom( + item=fg_item, + quantity=1, + raw_materials=[raw_material_1], + backflush_based_on="Material Transferred for Manufacture", + ) + + wo_order = make_wo_order_test_record(item=fg_item, qty=1, source_warehouse="Stores - _TC") + + self.assertEqual(bom.name, wo_order.bom_no) + backflush_based_on = frappe.db.get_value("BOM", wo_order.bom_no, "backflush_based_on") + self.assertEqual(backflush_based_on, "Material Transferred for Manufacture") + + material_transfer_entry = frappe.get_doc( + make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 1) + ) + material_transfer_entry.save() + + # Add second raw material in the material transfer entry which is not in the BOM to simulate backflush based on material transfer scenario + material_transfer_entry.append( + "items", + { + "item_code": raw_material_2, + "item_name": raw_material_2, + "item_group": frappe.get_value("Item", raw_material_2, "item_group"), + "uom": frappe.get_value("Item", raw_material_2, "stock_uom"), + "conversion_factor": 1, + "s_warehouse": "Stores - _TC", + "t_warehouse": material_transfer_entry.items[0].t_warehouse, + "qty": 1, + }, + ) + + material_transfer_entry.submit() + + manufacture_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 1)) + manufacture_entry.save() + + self.assertEqual(len(manufacture_entry.items), 3) + for row in manufacture_entry.items: + if row.s_warehouse: + self.assertIn(row.item_code, [raw_material_1, raw_material_2]) + def get_reserved_entries(voucher_no, warehouse=None): doctype = frappe.qb.DocType("Stock Reservation Entry") diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 4fa5c5d7e04..d26f6040f4c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -242,6 +242,11 @@ frappe.ui.form.on("Work Order", { frm.trigger("hide_reserve_stock_button"); frm.trigger("toggle_items_editable"); frm.trigger("set_fg_warehouse_mandatory"); + frm.trigger("toggle_hide_fields"); + }, + + toggle_hide_fields(frm) { + frm.toggle_display("operations", frm.doc?.operations && frm.doc.operations.length > 0); }, skip_transfer(frm) { @@ -638,6 +643,8 @@ frappe.ui.form.on("Work Order", { if (r.message["set_scrap_wh_mandatory"]) { frm.toggle_reqd("scrap_warehouse", true); } + + frm.trigger("toggle_hide_fields"); }, }); }, diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 6e5295fbfa0..2d5145141ae 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -15,7 +15,6 @@ "column_break1", "qty", "sales_order", - "track_semi_finished_goods", "reserve_stock", "section_break_vrpa", "max_producible_qty", @@ -86,6 +85,7 @@ "product_bundle_item", "section_break_ynih", "status", + "track_semi_finished_goods", "column_break_cvuw", "amended_from", "connections_tab" @@ -608,6 +608,7 @@ "fetch_from": "bom_no.track_semi_finished_goods", "fieldname": "track_semi_finished_goods", "fieldtype": "Check", + "hidden": 1, "label": "Track Semi Finished Goods", "read_only": 1 }, @@ -705,7 +706,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2026-03-16 10:15:28.708688", + "modified": "2026-04-17 13:42:12.374055", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 007675f9d8f..df8d4ce266f 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -162,6 +162,10 @@ class WorkOrder(Document): frappe.db.get_single_value("Stock Settings", "enable_stock_reservation"), ) + if self.bom_no: + if based_on := frappe.get_cached_value("BOM", self.bom_no, "backflush_based_on"): + self.set_onload("backflush_raw_materials_based_on", based_on) + def show_create_job_card_button(self): operation_details = frappe._dict( frappe.get_all( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index d62ce4e2cb4..8a2f2206875 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2537,14 +2537,12 @@ class StockEntry(StockController, SubcontractingInwardController): frappe.throw(_("Posting date and posting time is mandatory")) self.set_work_order_details() - self.flags.backflush_based_on = frappe.db.get_single_value( + backflush_based_on = frappe.db.get_single_value( "Manufacturing Settings", "backflush_raw_materials_based_on" ) if self.bom_no: - backflush_based_on = frappe.db.get_single_value( - "Manufacturing Settings", "backflush_raw_materials_based_on" - ) + backflush_based_on = self.get_backflush_based_on() if self.purpose in [ "Material Issue", @@ -2569,7 +2567,7 @@ class StockEntry(StockController, SubcontractingInwardController): or self.purpose == "Material Consumption for Manufacture" ) and not self.pro_doc.skip_transfer - and self.flags.backflush_based_on == "Material Transferred for Manufacture" + and backflush_based_on == "Material Transferred for Manufacture" ): self.add_transfered_raw_materials_in_items() @@ -2579,7 +2577,7 @@ class StockEntry(StockController, SubcontractingInwardController): self.purpose == "Manufacture" or self.purpose == "Material Consumption for Manufacture" ) - and self.flags.backflush_based_on == "BOM" + and backflush_based_on == "BOM" and frappe.db.get_single_value("Manufacturing Settings", "material_consumption") == 1 ): self.get_unconsumed_raw_materials() @@ -2649,8 +2647,7 @@ class StockEntry(StockController, SubcontractingInwardController): if ( self.purpose not in ["Material Transfer for Manufacture"] - and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on") - != "BOM" + and self.get_backflush_based_on() != "BOM" and not skip_transfer ): return @@ -2727,6 +2724,11 @@ class StockEntry(StockController, SubcontractingInwardController): row.idx = idx self.set("items", sorted_items) + def get_backflush_based_on(self): + from erpnext.manufacturing.doctype.bom.bom import get_backflush_based_on + + return get_backflush_based_on(self.bom_no) + def get_available_reserved_materials(self): reserved_entries = self.get_reserved_materials() if not reserved_entries: diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index f02c06810f0..ba0354fd1c8 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -115,6 +115,10 @@ class ManufactureEntry: "Manufacturing Settings", "backflush_raw_materials_based_on" ) + if self.bom_no: + if based_on := frappe.get_cached_value("BOM", self.bom_no, "backflush_based_on"): + backflush_based_on = based_on + available_serial_batches = frappe._dict({}) if backflush_based_on != "BOM": available_serial_batches = self.get_transferred_serial_batches() From dd6d4d19103cefef56a3f34a6295a8fe7d5842bb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 11:47:15 +0000 Subject: [PATCH 43/57] fix(pos_invoice_item): fetch `grant_commission` from `item_code` (backport #54413) (#54418) Co-authored-by: diptanilsaha fix(pos_invoice_item): fetch `grant_commission` from `item_code` (#54413) --- .../accounts/doctype/pos_invoice_item/pos_invoice_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json index 9fefe251533..ad9f8751942 100644 --- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json +++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json @@ -811,6 +811,7 @@ }, { "default": "0", + "fetch_from": "item_code.grant_commission", "fieldname": "grant_commission", "fieldtype": "Check", "label": "Grant Commission", @@ -857,7 +858,7 @@ ], "istable": 1, "links": [], - "modified": "2025-11-12 18:11:11.818015", + "modified": "2026-04-20 16:16:12.322024", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice Item", From 4bb30a9157e9bef79c321d45ca4ddae8a67cc96a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:03:23 +0000 Subject: [PATCH 44/57] test(BootStrapTestData): create `sales_partner` test data while bootstrapping (backport #54416) (#54421) Co-authored-by: diptanilsaha --- erpnext/tests/utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index e2a2a7ab195..7ea4f10a423 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -239,6 +239,7 @@ class BootStrapTestData: self.make_finance_book() self.make_leads() self.make_sales_person() + self.make_sales_partner() self.make_activity_type() self.make_address() self.update_support_settings() @@ -657,6 +658,29 @@ class BootStrapTestData: ] self.make_records(["sales_person_name"], records) + def make_sales_partner(self): + records = [ + { + "doctype": "Sales Partner", + "partner_name": "_Test Sales Partner India - 1", + "commission_rate": 7, + "territory": "_Test Territory India", + }, + { + "doctype": "Sales Partner", + "partner_name": "_Test Sales Partner India - 2", + "commission_rate": 5, + "territory": "_Test Territory India", + }, + { + "doctype": "Sales Partner", + "partner_name": "_Test Sales Partner Global - 1", + "commission_rate": 8, + "territory": "_Test Territory Rest Of The World", + }, + ] + self.make_records(["partner_name"], records) + def make_leads(self): records = [ { From 78497336c7c71ca8c7f202441c9902e3609b07ff Mon Sep 17 00:00:00 2001 From: sarathibalamurugan Date: Mon, 20 Apr 2026 16:17:38 +0530 Subject: [PATCH 45/57] fix: clear conditions table when calculate_based_on is set to Fixed (cherry picked from commit d73920be12727c83c60dd79c5e3b8be6935702a7) --- erpnext/accounts/doctype/shipping_rule/shipping_rule.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js index 1ece3e6c3dd..5c02fd2f127 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js @@ -25,6 +25,10 @@ frappe.ui.form.on("Shipping Rule", { }, calculate_based_on: function (frm) { frm.trigger("toggle_reqd"); + if (frm.doc.calculate_based_on === "Fixed") { + frm.clear_table("conditions"); + frm.refresh_field("conditions"); + } }, toggle_reqd: function (frm) { frm.toggle_reqd("shipping_amount", frm.doc.calculate_based_on === "Fixed"); From 319d769c6f256338fd7e00cb4fbb3017080bcfb4 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Mon, 20 Apr 2026 18:58:06 +0530 Subject: [PATCH 46/57] fix: clear shipping rule conditions for fixed shipping rule (cherry picked from commit d6bb0ae093a15b4c5430d43d3eed9e6000c920d5) --- erpnext/accounts/doctype/shipping_rule/shipping_rule.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index 3ec11364afa..7b226560668 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -58,6 +58,11 @@ class ShippingRule(Document): self.validate_overlapping_shipping_rule_conditions() def validate_from_to_values(self): + if self.calculate_based_on == "Fixed": + if self.conditions: + self.set("conditions", []) + return + zero_to_values = [] for d in self.get("conditions"): From 27c5dab7e462f6db89d4c2e283dea92e5cd670d0 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 8 Apr 2026 18:01:13 +0530 Subject: [PATCH 47/57] feat: use single remark field with custom remark toggle (cherry picked from commit 697f521e1498635fd2fd44b8b07746e6749cbb79) # Conflicts: # erpnext/accounts/doctype/journal_entry/journal_entry.json --- .../doctype/journal_entry/journal_entry.js | 7 +++++-- .../doctype/journal_entry/journal_entry.json | 20 +++++++++++++++++-- .../doctype/journal_entry/journal_entry.py | 13 ++++++------ .../journal_entry/journal_entry_list.js | 2 +- .../journal_entry/test_journal_entry.py | 19 ++++++++++++++++-- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index cc3b616d601..a4f1a881246 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -648,7 +648,7 @@ $.extend(erpnext.journal_entry, { reqd: 1, default: frm.doc.posting_date, }, - { fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark") }, + { fieldtype: "Small Text", fieldname: "remark", label: __("Remark") }, { fieldtype: "Select", fieldname: "naming_series", @@ -665,8 +665,11 @@ $.extend(erpnext.journal_entry, { var values = dialog.get_values(); frm.set_value("posting_date", values.posting_date); - frm.set_value("user_remark", values.user_remark); frm.set_value("naming_series", values.naming_series); + if (values.remark) { + frm.set_value("custom_remark", 1); + frm.set_value("remark", values.remark); + } // clear table is used because there might've been an error while adding child // and cleanup didn't happen diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 0856f41bb09..e080c206ab5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -78,6 +78,7 @@ "from_template", "title", "column_break3", + "custom_remark", "remark", "mode_of_payment", "party_not_required" @@ -202,6 +203,7 @@ { "fieldname": "user_remark", "fieldtype": "Small Text", + "hidden": 1, "label": "User Remark", "no_copy": 1, "oldfieldname": "user_remark", @@ -315,7 +317,7 @@ "no_copy": 1, "oldfieldname": "remark", "oldfieldtype": "Small Text", - "read_only": 1 + "read_only_depends_on": "eval: !doc.custom_remark" }, { "depends_on": "eval:doc.voucher_type== \"Inter Company Journal Entry\"", @@ -651,6 +653,20 @@ "fieldname": "tax_withholding_tab", "fieldtype": "Tab Break", "label": "Tax Withholding" +<<<<<<< HEAD +======= + }, + { + "fieldname": "auto_repeat_section", + "fieldtype": "Section Break", + "label": "Auto Repeat" + }, + { + "default": "0", + "fieldname": "custom_remark", + "fieldtype": "Check", + "label": "Custom Remark" +>>>>>>> 697f521e14 (feat: use single remark field with custom remark toggle) } ], "icon": "fa fa-file-text", @@ -665,7 +681,7 @@ "table_fieldname": "payment_entries" } ], - "modified": "2026-03-09 17:15:26.569327", + "modified": "2026-04-08 14:19:30.870894", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 8b6cd3eee23..a7f901b6777 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -61,6 +61,7 @@ class JournalEntry(AccountsController): cheque_no: DF.Data | None clearance_date: DF.Date | None company: DF.Link + custom_remark: DF.Check difference: DF.Currency due_date: DF.Date | None finance_book: DF.Link | None @@ -1023,11 +1024,11 @@ class JournalEntry(AccountsController): def create_remarks(self): r = [] - if self.flags.skip_remarks_creation: + if self.get("custom_remark"): return - if self.user_remark: - r.append(_("Note: {0}").format(self.user_remark)) + if self.flags.skip_remarks_creation: + return if self.cheque_no: if self.cheque_date: @@ -1135,9 +1136,7 @@ class JournalEntry(AccountsController): for d in self.get("accounts"): if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"): - r = [d.user_remark, self.remark] - r = [x for x in r if x] - remarks = "\n".join(r) + remarks = self.remark row = { "account": d.account, @@ -1548,7 +1547,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters): frappe.qb.from_(JournalEntry) .join(JournalEntryAccount) .on(JournalEntryAccount.parent == JournalEntry.name) - .select(JournalEntry.name, JournalEntry.posting_date, JournalEntry.user_remark) + .select(JournalEntry.name, JournalEntry.posting_date, JournalEntry.remark) .where(JournalEntryAccount.account == filters.get("account")) .where(JournalEntryAccount.reference_type.isnull() | (JournalEntryAccount.reference_type == "")) .where(JournalEntry.docstatus == 1) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry_list.js b/erpnext/accounts/doctype/journal_entry/journal_entry_list.js index 4ef7b998b8a..6ea0df946f2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry_list.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry_list.js @@ -1,5 +1,5 @@ frappe.listview_settings["Journal Entry"] = { - add_fields: ["voucher_type", "posting_date", "total_debit", "company", "user_remark"], + add_fields: ["voucher_type", "posting_date", "total_debit", "company", "remark"], get_indicator: function (doc) { if (doc.docstatus === 1) { return [__(doc.voucher_type), "blue", `voucher_type,=,${doc.voucher_type}`]; diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 833c6189c7d..87ffda42bd5 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -523,7 +523,7 @@ class TestJournalEntry(ERPNextTestSuite): jv = frappe.new_doc("Journal Entry") jv.posting_date = nowdate() jv.company = "_Test Company" - jv.user_remark = "test" + jv.remark = "test" jv.extend( "accounts", [ @@ -592,6 +592,21 @@ class TestJournalEntry(ERPNextTestSuite): self.assertEqual(jv.pay_to_recd_from, "_Test Receiver 2") + def test_custom_remark(self): + # When custom_remark is enabled, remark should not be auto-overwritten on save + jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) + jv.custom_remark = 1 + jv.remark = "My custom remark text" + jv.insert() + self.assertEqual(jv.remark, "My custom remark text") + + # When custom_remark is disabled, remark should be auto-generated + jv2 = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) + jv2.custom_remark = 0 + jv2.remark = "Should be overwritten" + jv2.insert() + self.assertNotEqual(jv2.remark, "Should be overwritten") + def test_credit_limit_for_customer(self): customer = make_customer("_Test New Customer") set_credit_limit("_Test New Customer", "_Test Company", 50) @@ -620,7 +635,7 @@ def make_journal_entry( jv = frappe.new_doc("Journal Entry") jv.posting_date = posting_date or nowdate() jv.company = company or "_Test Company" - jv.user_remark = "test" + jv.remark = "test" jv.multi_currency = 1 jv.set( "accounts", From aa359aded4f7817b1a2830b4cbeabb99817f42c3 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 9 Apr 2026 01:20:31 +0530 Subject: [PATCH 48/57] fix: append row level user remarks in gl map (cherry picked from commit 84e5272f5d159ab4c3036ce480e7704ed24488c6) --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 3 +++ erpnext/accounts/doctype/journal_entry/journal_entry.py | 8 +++++--- .../accounts/doctype/journal_entry/test_journal_entry.py | 7 ------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index a4f1a881246..1e3ad530f72 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -669,6 +669,9 @@ $.extend(erpnext.journal_entry, { if (values.remark) { frm.set_value("custom_remark", 1); frm.set_value("remark", values.remark); + } else { + frm.set_value("custom_remark", 0); + frm.set_value("remark", ""); } // clear table is used because there might've been an error while adding child diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index a7f901b6777..7a30139796a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1024,10 +1024,10 @@ class JournalEntry(AccountsController): def create_remarks(self): r = [] - if self.get("custom_remark"): + if self.flags.skip_remarks_creation: return - if self.flags.skip_remarks_creation: + if self.get("custom_remark"): return if self.cheque_no: @@ -1136,7 +1136,9 @@ class JournalEntry(AccountsController): for d in self.get("accounts"): if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"): - remarks = self.remark + r = [d.user_remark, self.remark] + r = [x for x in r if x] + remarks = "\n".join(r) row = { "account": d.account, diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 87ffda42bd5..581a0866721 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -600,13 +600,6 @@ class TestJournalEntry(ERPNextTestSuite): jv.insert() self.assertEqual(jv.remark, "My custom remark text") - # When custom_remark is disabled, remark should be auto-generated - jv2 = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) - jv2.custom_remark = 0 - jv2.remark = "Should be overwritten" - jv2.insert() - self.assertNotEqual(jv2.remark, "Should be overwritten") - def test_credit_limit_for_customer(self): customer = make_customer("_Test New Customer") set_credit_limit("_Test New Customer", "_Test Company", 50) From 37d080bdb4f5cd67f6c60916df2ce0fda0320ff8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:21:10 +0530 Subject: [PATCH 49/57] refactor: `Sales Partner Commission Summary` and `Sales Partner Transaction Summary` report (backport #54268) (#54431) Co-authored-by: diptanilsaha --- .../sales_partner_commission_summary.py | 248 ++++++----- .../test_sales_partner_commission_summary.py | 395 ++++++++++++++++++ .../sales_partner_transaction_summary.js | 16 +- .../sales_partner_transaction_summary.py | 174 +++----- .../test_sales_partner_transaction_summary.py | 183 ++++++++ 5 files changed, 794 insertions(+), 222 deletions(-) create mode 100644 erpnext/selling/report/sales_partner_commission_summary/test_sales_partner_commission_summary.py create mode 100644 erpnext/selling/report/sales_partner_transaction_summary/test_sales_partner_transaction_summary.py diff --git a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py index 5e07eb5d8a8..5b98c4bf386 100644 --- a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py +++ b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.py @@ -1,122 +1,176 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt - import frappe -from frappe import _, msgprint +from frappe import _ +from frappe.query_builder import DocType, Field, Order +from frappe.query_builder.custom import ConstantColumn +from frappe.query_builder.utils import QueryBuilder +from frappe.utils.data import comma_or + +SALES_TRANSACTION_DOCTYPES = ["Sales Order", "Sales Invoice", "Delivery Note", "POS Invoice"] def execute(filters=None): if not filters: filters = {} - columns = get_columns(filters) - data = get_entries(filters) - - return columns, data + return SalesPartnerCommissionSummaryReport(filters).run() -def get_columns(filters): - if not filters.get("doctype"): - msgprint(_("Please select the document type first"), raise_exception=1) +class SalesPartnerSummaryReport: + """ + Base class to generate Sales Partner Summary related Reports. + """ - columns = [ - { - "label": _(filters["doctype"]), - "options": filters["doctype"], - "fieldname": "name", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Customer"), - "options": "Customer", - "fieldname": "customer", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Currency"), - "fieldname": "currency", - "fieldtype": "Data", - "width": 80, - }, - { - "label": _("Territory"), - "options": "Territory", - "fieldname": "territory", - "fieldtype": "Link", - "width": 100, - }, - {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100}, - { - "label": _("Amount"), - "fieldname": "amount", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Sales Partner"), - "options": "Sales Partner", - "fieldname": "sales_partner", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Commission Rate %"), - "fieldname": "commission_rate", - "fieldtype": "Data", - "width": 100, - }, - { - "label": _("Total Commission"), - "fieldname": "total_commission", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - ] + dt: DocType + date_field: str + date_label: str + columns: list + data: list + query: QueryBuilder + filters: dict - return columns + def __init__(self, filters: dict): + self.filters = filters + self.columns = [] + def run(self): + self.validate_filters() + self.prepare_columns() + self.get_data() -def get_entries(filters): - date_field = "transaction_date" if filters.get("doctype") == "Sales Order" else "posting_date" - company_currency = frappe.db.get_value("Company", filters.get("company"), "default_currency") - conditions = get_conditions(filters, date_field) - entries = frappe.db.sql( + return self.columns, self.data + + def validate_filters(self): + if not self.filters.get("doctype"): + frappe.throw(_("Please select the document type first.")) + + if self.filters.get("doctype") not in SALES_TRANSACTION_DOCTYPES: + frappe.throw(_("DocType can be one of them {0}").format(comma_or(SALES_TRANSACTION_DOCTYPES))) + + if not self.filters.get("company"): + frappe.throw(_("Please select a company.")) + + if ( + self.filters.get("from_date") + and self.filters.get("to_date") + and self.filters.get("from_date") > self.filters.get("to_date") + ): + frappe.throw(_("From Date cannot be greater than To Date.")) + + self._set_date_field_and_label() + + def _set_date_field_and_label(self): + self.date_field = ( + "transaction_date" if self.filters.get("doctype") == "Sales Order" else "posting_date" + ) + self.date_label = _("Order Date") if self.date_field == "transaction_date" else _("Posting Date") + + def prepare_columns(self): """ - SELECT - name, customer, territory, {} as posting_date, base_net_total as amount, - sales_partner, commission_rate, total_commission, '{}' as currency - FROM - `tab{}` - WHERE - {} and docstatus = 1 and sales_partner is not null - and sales_partner != '' order by name desc, sales_partner - """.format(date_field, company_currency, filters.get("doctype"), conditions), - filters, - as_dict=1, - ) + Extend this method to add columns on the report. Use `make_column` to add more columns. + """ + raise NotImplementedError - return entries + def get_data(self): + self.build_report_query() + + self.data = self.query.run(as_dict=1) + + def build_report_query(self): + self._build_report_base_query() + self.extend_report_query() + self._apply_common_filters() + self.apply_filters() + + def _build_report_base_query(self): + self.dt = DocType(self.filters.get("doctype")) + + company_currency = frappe.get_cached_value("Company", self.filters.get("company"), "default_currency") + + self.query = ( + frappe.qb.from_(self.dt) + .select( + self.dt.name, + self.dt.customer, + self.dt.territory, + Field(self.date_field, "posting_date", table=self.dt), + self.dt.sales_partner, + self.dt.commission_rate, + ConstantColumn(company_currency).as_("currency"), + ) + .where( + (self.dt.docstatus == 1) & (self.dt.sales_partner.notnull()) & (self.dt.sales_partner != "") + ) + .orderby(self.dt.name, order=Order.desc) + .orderby(self.dt.sales_partner) + ) + + def extend_report_query(self): + """ + Extend this method to select more columns on the query. + """ + pass + + def _apply_common_filters(self): + for field in ["company", "customer", "territory", "sales_partner"]: + if self.filters.get(field): + self.query = self.query.where(Field(field, table=self.dt) == self.filters.get(field)) + + if self.filters.get("from_date"): + self.query = self.query.where( + Field(self.date_field, table=self.dt) >= self.filters.get("from_date") + ) + + if self.filters.get("to_date"): + self.query = self.query.where( + Field(self.date_field, table=self.dt) <= self.filters.get("to_date") + ) + + def apply_filters(self): + """ + Extend this method to add more conditions on the query. + """ + pass + + def make_column( + self, label: str, fieldname: str, fieldtype: str, width: int = 140, options: str = "", hidden: int = 0 + ): + self.columns.append( + dict( + label=label, + fieldname=fieldname, + fieldtype=fieldtype, + options=options, + width=width, + hidden=hidden, + ) + ) -def get_conditions(filters, date_field): - conditions = "1=1" +class SalesPartnerCommissionSummaryReport(SalesPartnerSummaryReport): + def prepare_columns(self): + self.make_column(_(self.filters.get("doctype")), "name", "Link", options=self.filters.get("doctype")) - for field in ["company", "customer", "territory"]: - if filters.get(field): - conditions += f" and {field} = %({field})s" + self.make_column(_("Customer"), "customer", "Link", options="Customer") - if filters.get("sales_partner"): - conditions += " and sales_partner = %(sales_partner)s" + self.make_column(_("Currency"), "currency", "Data", 80, hidden=1) - if filters.get("from_date"): - conditions += f" and {date_field} >= %(from_date)s" + self.make_column(_("Territory"), "territory", "Link", 100, "Territory") - if filters.get("to_date"): - conditions += f" and {date_field} <= %(to_date)s" + self.make_column(self.date_label, "posting_date", "Date") - return conditions + self.make_column(_("Amount"), "amount", "Currency", 120, "currency") + + self.make_column(_("Sales Partner"), "sales_partner", "Link", options="Sales Partner") + + self.make_column(_("Commission Rate %"), "commission_rate", "Data", 100) + + self.make_column(_("Total Commission"), "total_commission", "Currency", 120, "currency") + + def extend_report_query(self): + self.query = self.query.select( + self.dt.base_net_total.as_("amount"), + self.dt.total_commission, + ) diff --git a/erpnext/selling/report/sales_partner_commission_summary/test_sales_partner_commission_summary.py b/erpnext/selling/report/sales_partner_commission_summary/test_sales_partner_commission_summary.py new file mode 100644 index 00000000000..9a46bcb85db --- /dev/null +++ b/erpnext/selling/report/sales_partner_commission_summary/test_sales_partner_commission_summary.py @@ -0,0 +1,395 @@ +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.desk.query_report import run +from frappe.utils.data import comma_or + +from erpnext.selling.report.sales_partner_commission_summary.sales_partner_commission_summary import ( + SALES_TRANSACTION_DOCTYPES, +) +from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry +from erpnext.tests.utils import ERPNextTestSuite + + +class SalesPartnerSummaryReportTestMixin(ERPNextTestSuite): + def assert_doctype_filters(self): + self.filters["doctype"] = "Purchase Invoice" + + with self.assertRaisesRegex( + frappe.ValidationError, + _("DocType can be one of them {0}").format(comma_or(SALES_TRANSACTION_DOCTYPES)), + ): + run(self.report_name, self.filters) + + def assert_posting_date_label(self): + data = run(self.report_name, self.filters) + + posting_date_column = next( + (column for column in data.get("columns") if column.fieldname == "posting_date"), None + ) + + self.assertNotEqual(posting_date_column.get("label"), "Posting Date") + self.assertEqual(posting_date_column.get("label"), "Order Date") + + self.filters["doctype"] = "Sales Invoice" + + data = run(self.report_name, self.filters) + + posting_date_column = next( + (column for column in data.get("columns") if column.fieldname == "posting_date"), None + ) + + self.assertEqual(posting_date_column.get("label"), "Posting Date") + self.assertNotEqual(posting_date_column.get("label"), "Order Date") + + def create_transactions(self, doctype): + from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import ( + POSInvoiceTestMixin, + create_pos_invoice, + ) + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + make_transaction_funcs = { + "Sales Order": make_sales_order, + "Sales Invoice": create_sales_invoice, + "Delivery Note": create_delivery_note, + "POS Invoice": create_pos_invoice, + } + self.date_field = "transaction_date" if doctype == "Sales Order" else "posting_date" + + self.make_transaction_func = make_transaction_funcs[doctype] + + make_stock_entry( + item_code="_Test Item 2", + qty=10, + company="_Test Company", + to_warehouse="_Test Warehouse - _TC", + purpose="Material Receipt", + posting_date="2026-01-01", + ) + + if doctype == "POS Invoice": + POSInvoiceTestMixin.setUp(self) + + from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry + + pos_opening_entry = create_opening_entry(self.pos_profile, self.test_user.name, get_obj=1) + + self.transaction_doc_with_7pc_commision() + self.transaction_doc_with_5pc_commission() + self.transaction_doc_with_no_sales_partner() + self.transaction_doc_date_out_of_range_of_filters() + self.transaction_doc_with_revoked_commission() + self.transaction_doc_not_submitted() + self.transaction_doc_cancelled() + + if doctype == "Sales Order": + return + + self.transaction_doc_returned() + + if doctype == "POS Invoice": + pos_opening_entry.cancel() + + def transaction_doc_with_7pc_commision(self): + args = {"rate": 100, "qty": 10, self.date_field: "2026-01-14", "do_not_save": 1} + self.seven_pc_doc = self.make_transaction_func(**args) + self.seven_pc_doc.sales_partner = "_Test Sales Partner India - 1" + if self.seven_pc_doc.doctype == "POS Invoice": + self.seven_pc_doc.append("payments", {"mode_of_payment": "Cash", "amount": 1000, "default": 1}) + + self.seven_pc_doc.save() + self.seven_pc_doc.submit() + + def transaction_doc_with_5pc_commission(self): + args = {"rate": 20, "qty": 6, self.date_field: "2026-01-15", "do_not_save": 1} + self.five_pc_doc = self.make_transaction_func(**args) + self.five_pc_doc.sales_partner = "_Test Sales Partner India - 2" + + self.five_pc_doc.append( + "items", + { + "item_code": "_Test Item 2", + "qty": 4, + "rate": 30, + }, + ) + + if self.five_pc_doc.doctype == "POS Invoice": + self.five_pc_doc.append("payments", {"mode_of_payment": "Cash", "amount": 500, "default": 1}) + + self.five_pc_doc.save() + self.five_pc_doc.submit() + + def transaction_doc_with_no_sales_partner(self): + args = { + "item_code": "_Test Item", + "rate": 50, + "qty": 10, + self.date_field: "2026-01-19", + "do_not_save": 1, + } + self.no_sp_doc = self.make_transaction_func(**args) + if self.no_sp_doc.doctype == "POS Invoice": + self.no_sp_doc.append("payments", {"mode_of_payment": "Cash", "amount": 500, "default": 1}) + + self.no_sp_doc.save() + self.no_sp_doc.submit() + + def transaction_doc_date_out_of_range_of_filters(self): + args = { + "item_code": "_Test Item", + "rate": 60, + "qty": 10, + self.date_field: "2026-02-04", + "do_not_save": 1, + } + self.date_out_of_range_doc = self.make_transaction_func(**args) + self.date_out_of_range_doc.sales_partner = "_Test Sales Partner India - 1" + if self.date_out_of_range_doc.doctype == "POS Invoice": + self.date_out_of_range_doc.append( + "payments", {"mode_of_payment": "Cash", "amount": 600, "default": 1} + ) + + self.date_out_of_range_doc.save() + self.date_out_of_range_doc.submit() + + def transaction_doc_with_revoked_commission(self): + try: + frappe.db.set_value("Item", "_Test Item", "grant_commission", 0) + args = { + "item_code": "_Test Item", + "rate": 80, + "qty": 10, + self.date_field: "2026-01-26", + "do_not_save": 1, + } + self.revoked_comm_doc = self.make_transaction_func(**args) + self.revoked_comm_doc.sales_partner = "_Test Sales Partner India - 1" + + if self.revoked_comm_doc.doctype == "POS Invoice": + self.revoked_comm_doc.append( + "payments", {"mode_of_payment": "Cash", "amount": 800, "default": 1} + ) + + self.revoked_comm_doc.save() + self.revoked_comm_doc.submit() + finally: + frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + + def transaction_doc_not_submitted(self): + args = { + "item_code": "_Test Item", + "rate": 80, + "qty": 10, + self.date_field: "2026-01-26", + "do_not_save": 1, + } + self.doc_not_submitted = self.make_transaction_func(**args) + self.doc_not_submitted.set(self.date_field, "2026-01-26") + self.doc_not_submitted.sales_partner = "_Test Sales Partner India - 1" + if self.doc_not_submitted.doctype == "POS Invoice": + self.doc_not_submitted.append( + "payments", {"mode_of_payment": "Cash", "amount": 800, "default": 1} + ) + + self.doc_not_submitted.save() + + def transaction_doc_cancelled(self): + args = { + "item_code": "_Test Item", + "rate": 80, + "qty": 10, + self.date_field: "2026-01-26", + "do_not_save": 1, + } + self.cancelled_doc = self.make_transaction_func(**args) + self.cancelled_doc.sales_partner = "_Test Sales Partner India - 1" + if self.cancelled_doc.doctype == "POS Invoice": + self.cancelled_doc.append("payments", {"mode_of_payment": "Cash", "amount": 800, "default": 1}) + + self.cancelled_doc.save() + self.cancelled_doc.submit() + self.cancelled_doc.cancel() + + def transaction_doc_returned(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + args = { + "item_code": "_Test Item", + "rate": 90, + "qty": 10, + self.date_field: "2026-01-18", + "do_not_save": 1, + } + self.to_be_returned_doc = self.make_transaction_func(**args) + self.to_be_returned_doc.sales_partner = "_Test Sales Partner India - 2" + if self.to_be_returned_doc.doctype == "POS Invoice": + self.to_be_returned_doc.append( + "payments", {"mode_of_payment": "Cash", "amount": 900, "default": 1} + ) + + self.to_be_returned_doc.save() + self.to_be_returned_doc.submit() + + self.returned_doc = make_return_doc(self.to_be_returned_doc.doctype, self.to_be_returned_doc.name) + self.returned_doc.posting_date = "2026-01-19" + if self.returned_doc.doctype == "POS Invoice": + self.returned_doc.payments = [] + self.returned_doc.append("payments", {"mode_of_payment": "Cash", "amount": -900, "default": 1}) + + self.returned_doc.save() + self.returned_doc.submit() + + +class TestSalesPartnerCommissionSummary(SalesPartnerSummaryReportTestMixin): + def setUp(self): + self.filters = { + "company": "_Test Company", + "doctype": "Sales Order", + "from_date": "2026-01-01", + "to_date": "2026-01-31", + } + self.report_name = "Sales Partner Commission Summary" + + def test_doctype_filters(self): + self.assert_doctype_filters() + + def test_posting_date_column_label(self): + self.assert_posting_date_label() + + def test_sales_order_sp_commission_summary(self): + self.filters["doctype"] = "Sales Order" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_commission_summary_report() + + def test_sales_invoice_sp_commission_summary(self): + self.filters["doctype"] = "Sales Invoice" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_commission_summary_report() + + def test_delivery_note_sp_commission_summary(self): + self.filters["doctype"] = "Delivery Note" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_commission_summary_report() + + def test_pos_invoice_sp_commission_summary(self): + self.filters["doctype"] = "POS Invoice" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_commission_summary_report() + + def assert_sales_partner_commission_summary_report(self): + report_data = run(self.report_name, self.filters) + + self.report_result = report_data.get("result") + self.report_result_without_total_row = self.report_result[:-1] + + self.assertIsNotNone(self.report_result_without_total_row) + + self.assert_7pc_commission() + self.assert_5pc_commission_with_multiple_items() + self.assert_doc_with_no_sp() + self.assert_doc_with_posting_date_out_of_range() + self.assert_doc_with_revoked_commission() + self.assert_doc_not_submitted() + self.assert_doc_cancelled() + self.assert_total_commission() + + if self.filters["doctype"] != "Sales Order": + self.assert_returned_doc() + + def assert_7pc_commission(self): + doc_name = self.seven_pc_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNotNone(row) + self.assertEqual(row["amount"], 1000) + self.assertEqual(row["commission_rate"], 7) + self.assertEqual(row["total_commission"], 70) + + def assert_5pc_commission_with_multiple_items(self): + doc_name = self.five_pc_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNotNone(row) + self.assertEqual(row["amount"], 240) + self.assertEqual(row["commission_rate"], 5) + self.assertEqual(row["total_commission"], 12) + + def assert_doc_with_no_sp(self): + doc_name = self.no_sp_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_with_posting_date_out_of_range(self): + doc_name = self.date_out_of_range_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_with_revoked_commission(self): + doc_name = self.revoked_comm_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNotNone(row) + self.assertEqual(row["amount"], 800) + self.assertEqual(row["commission_rate"], 7) + self.assertEqual(row["total_commission"], 0) + + def assert_doc_not_submitted(self): + doc_name = self.doc_not_submitted.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_cancelled(self): + doc_name = self.cancelled_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_total_commission(self): + total_row = self.report_result[-1] + + # Total Amount + self.assertEqual(total_row[-4], 2040) + + # Total Commission + self.assertEqual(total_row[-1], 82) + + def assert_returned_doc(self): + doc_name = self.to_be_returned_doc.name + returned_doc_name = self.returned_doc.name + + outward_row = next( + (row for row in self.report_result_without_total_row if row.get("name") == doc_name), None + ) + inward_row = next( + (row for row in self.report_result_without_total_row if row.get("name") == returned_doc_name), + None, + ) + + self.assertIsNotNone(outward_row) + self.assertIsNotNone(inward_row) + + self.assertEqual(outward_row["amount"], 900) + self.assertEqual(outward_row["total_commission"], 45) + + self.assertEqual(inward_row["amount"], -900) + self.assertEqual(inward_row["total_commission"], -45) diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js index f6f7c3f3cf3..e4e2199606a 100644 --- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js +++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js @@ -3,6 +3,14 @@ frappe.query_reports["Sales Partner Transaction Summary"] = { filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, { fieldname: "sales_partner", label: __("Sales Partner"), @@ -28,14 +36,6 @@ frappe.query_reports["Sales Partner Transaction Summary"] = { fieldtype: "Date", default: frappe.datetime.get_today(), }, - { - fieldname: "company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1, - }, { fieldname: "item_group", label: __("Item Group"), diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py index 216adde18fd..f322b89f897 100644 --- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py +++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py @@ -3,144 +3,84 @@ import frappe -from frappe import _, msgprint +from frappe import _ +from frappe.query_builder import Case + +from erpnext.selling.report.sales_partner_commission_summary.sales_partner_commission_summary import ( + SalesPartnerSummaryReport, +) def execute(filters=None): if not filters: filters = {} - columns = get_columns(filters) - data = get_entries(filters) - - return columns, data + return SalesPartnerTransactionSummaryReport(filters=filters).run() -def get_columns(filters): - if not filters.get("doctype"): - msgprint(_("Please select the document type first"), raise_exception=1) +class SalesPartnerTransactionSummaryReport(SalesPartnerSummaryReport): + def prepare_columns(self): + self.make_column(_(self.filters.get("doctype")), "name", "Link", options=self.filters.get("doctype")) - columns = [ - { - "label": _(filters["doctype"]), - "options": filters["doctype"], - "fieldname": "name", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Customer"), - "options": "Customer", - "fieldname": "customer", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Territory"), - "options": "Territory", - "fieldname": "territory", - "fieldtype": "Link", - "width": 100, - }, - {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100}, - { - "label": _("Item Code"), - "fieldname": "item_code", - "fieldtype": "Link", - "options": "Item", - "width": 100, - }, - { - "label": _("Item Group"), - "fieldname": "item_group", - "fieldtype": "Link", - "options": "Item Group", - "width": 100, - }, - { - "label": _("Brand"), - "fieldname": "brand", - "fieldtype": "Link", - "options": "Brand", - "width": 100, - }, - {"label": _("Quantity"), "fieldname": "qty", "fieldtype": "Float", "width": 120}, - {"label": _("Rate"), "fieldname": "rate", "fieldtype": "Currency", "width": 120}, - {"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120}, - { - "label": _("Sales Partner"), - "options": "Sales Partner", - "fieldname": "sales_partner", - "fieldtype": "Link", - "width": 140, - }, - { - "label": _("Commission Rate %"), - "fieldname": "commission_rate", - "fieldtype": "Data", - "width": 100, - }, - {"label": _("Commission"), "fieldname": "commission", "fieldtype": "Currency", "width": 120}, - { - "label": _("Currency"), - "fieldname": "currency", - "fieldtype": "Link", - "options": "Currency", - "width": 120, - }, - ] + self.make_column(_("Customer"), "customer", "Link", options="Customer") - return columns + self.make_column(_("Currency"), "currency", "Data", 80, hidden=1) + self.make_column(_("Territory"), "territory", "Link", 100, "Territory") -def get_entries(filters): - date_field = "transaction_date" if filters.get("doctype") == "Sales Order" else "posting_date" + self.make_column(self.date_label, "posting_date", "Date") - conditions = get_conditions(filters, date_field) - entries = frappe.db.sql( - """ - SELECT - dt.name, dt.customer, dt.territory, dt.{date_field} as posting_date, dt.currency, - dt_item.base_net_rate as rate, dt_item.qty, dt_item.base_net_amount as amount, - ((dt_item.base_net_amount * dt.commission_rate) / 100) as commission, - dt_item.brand, dt.sales_partner, dt.commission_rate, dt_item.item_group, dt_item.item_code - FROM - `tab{doctype}` dt, `tab{doctype} Item` dt_item - WHERE - {cond} and dt.name = dt_item.parent and dt.docstatus = 1 - and dt.sales_partner is not null and dt.sales_partner != '' - order by dt.name desc, dt.sales_partner - """.format(date_field=date_field, doctype=filters.get("doctype"), cond=conditions), - filters, - as_dict=1, - ) + self.make_column(_("Item Code"), "item_code", "Link", 100, "Item") - return entries + self.make_column(_("Item Group"), "item_group", "Link", 100, "Item Group") + self.make_column(_("Brand"), "brand", "Link", 100, "Brand") -def get_conditions(filters, date_field): - conditions = "1=1" + self.make_column(_("Quantity"), "qty", "Float", 120) - for field in ["company", "customer", "territory", "sales_partner"]: - if filters.get(field): - conditions += f" and dt.{field} = %({field})s" + self.make_column(_("Rate"), "rate", "Currency", 120, "currency") - if filters.get("from_date"): - conditions += f" and dt.{date_field} >= %(from_date)s" + self.make_column(_("Amount"), "amount", "Currency", 120, "currency") - if filters.get("to_date"): - conditions += f" and dt.{date_field} <= %(to_date)s" + self.make_column(_("Sales Partner"), "sales_partner", "Link", options="Sales Partner") - if not filters.get("show_return_entries"): - conditions += " and dt_item.qty > 0.0" + self.make_column(_("Commission Rate %"), "commission_rate", "Data", 100) - if filters.get("brand"): - conditions += " and dt_item.brand = %(brand)s" + self.make_column(_("Commission"), "commission", "Currency", 120, "currency") - if filters.get("item_group"): - lft, rgt = frappe.get_cached_value("Item Group", filters.get("item_group"), ["lft", "rgt"]) + def extend_report_query(self): + self.dt_item = frappe.qb.DocType(f"{self.filters['doctype']} Item") - conditions += f""" and dt_item.item_group in (select name from - `tabItem Group` where lft >= {lft} and rgt <= {rgt})""" + self.query = ( + self.query.join(self.dt_item) + .on(self.dt.name == self.dt_item.parent) + .select( + self.dt_item.base_net_rate.as_("rate"), + self.dt_item.qty, + self.dt_item.base_net_amount.as_("amount"), + Case() + .when( + self.dt_item.grant_commission.eq(1), + ((self.dt_item.base_net_amount * self.dt.commission_rate) / 100), + ) + .else_(0) + .as_("commission"), + self.dt_item.brand, + self.dt_item.item_group, + self.dt_item.item_code, + ) + ) - return conditions + def apply_filters(self): + if not self.filters.get("show_return_entries"): + self.query = self.query.where(self.dt_item.qty > 0.0) + + if self.filters.get("brand"): + self.query = self.query.where(self.dt_item.brand == self.filters.get("brand")) + + if self.filters.get("item_group"): + lft, rgt = frappe.get_cached_value("Item Group", self.filters.get("item_group"), ["lft", "rgt"]) + if item_groups := frappe.get_all( + "Item Group", filters=[["lft", ">=", lft], ["rgt", "<=", rgt]], pluck="name" + ): + self.query = self.query.where(self.dt_item.item_group.isin(item_groups)) diff --git a/erpnext/selling/report/sales_partner_transaction_summary/test_sales_partner_transaction_summary.py b/erpnext/selling/report/sales_partner_transaction_summary/test_sales_partner_transaction_summary.py new file mode 100644 index 00000000000..45b4efee867 --- /dev/null +++ b/erpnext/selling/report/sales_partner_transaction_summary/test_sales_partner_transaction_summary.py @@ -0,0 +1,183 @@ +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from frappe.desk.query_report import run + +from erpnext.selling.report.sales_partner_commission_summary.test_sales_partner_commission_summary import ( + SalesPartnerSummaryReportTestMixin, +) + + +class TestSalesPartnerTransactionSummary(SalesPartnerSummaryReportTestMixin): + def setUp(self): + self.filters = { + "company": "_Test Company", + "doctype": "Sales Order", + "from_date": "2026-01-01", + "to_date": "2026-01-31", + "show_return_entries": 1, + } + self.report_name = "Sales Partner Transaction Summary" + + def test_doctype_filters(self): + self.assert_doctype_filters() + + def test_posting_date_column_label(self): + self.assert_posting_date_label() + + def test_sales_order_sp_transaction_summary(self): + self.filters["doctype"] = "Sales Order" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_transaction_summary_report() + + def test_sales_invoice_sp_transaction_summary(self): + self.filters["doctype"] = "Sales Invoice" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_transaction_summary_report() + + def test_delivery_note_sp_transaction_summary(self): + self.filters["doctype"] = "Delivery Note" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_transaction_summary_report() + + def test_pos_invoice_sp_transaction_summary(self): + self.filters["doctype"] = "POS Invoice" + self.create_transactions(self.filters["doctype"]) + + self.assert_sales_partner_transaction_summary_report() + + def assert_sales_partner_transaction_summary_report(self): + report_data = run(self.report_name, self.filters) + + self.report_result = report_data.get("result") + self.report_result_without_total_row = self.report_result[:-1] + + self.assertIsNotNone(self.report_result_without_total_row) + + self.assert_7pc_commission() + self.assert_5pc_commission_with_multiple_items() + self.assert_doc_with_no_sp() + self.assert_doc_with_posting_date_out_of_range() + self.assert_doc_with_revoked_commission() + self.assert_doc_not_submitted() + self.assert_doc_cancelled() + self.assert_commission() + + if self.filters["doctype"] != "Sales Order": + self.assert_returned_doc() + + def assert_7pc_commission(self): + doc_name = self.seven_pc_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNotNone(row) + + self.assertEqual(row["customer"], "_Test Customer") + self.assertEqual(row["item_code"], "_Test Item") + self.assertEqual(row["item_group"], "_Test Item Group") + self.assertEqual(row["amount"], 1000) + self.assertEqual(row["commission_rate"], 7) + self.assertEqual(row["commission"], 70) + + def assert_5pc_commission_with_multiple_items(self): + doc_name = self.five_pc_doc.name + + row1 = next( + ( + row + for row in self.report_result_without_total_row + if row.get("name") == doc_name and row.get("item_code") == "_Test Item" + ), + None, + ) + self.assertIsNotNone(row1) + + row2 = next( + ( + row + for row in self.report_result_without_total_row + if row.get("name") == doc_name and row.get("item_code") == "_Test Item 2" + ), + None, + ) + self.assertIsNotNone(row2) + + self.assertEqual(row1["amount"], 120) + self.assertEqual(row1["commission_rate"], 5) + self.assertEqual(row1["commission"], 6) + + self.assertEqual(row2["amount"], 120) + self.assertEqual(row2["commission_rate"], 5) + self.assertEqual(row2["commission"], 6) + + def assert_doc_with_no_sp(self): + doc_name = self.no_sp_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_with_posting_date_out_of_range(self): + doc_name = self.date_out_of_range_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_with_revoked_commission(self): + doc_name = self.revoked_comm_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNotNone(row) + self.assertEqual(row["amount"], 800) + self.assertEqual(row["commission_rate"], 7) + self.assertEqual(row["commission"], 0) + + def assert_doc_not_submitted(self): + doc_name = self.doc_not_submitted.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_doc_cancelled(self): + doc_name = self.cancelled_doc.name + + row = next((row for row in self.report_result_without_total_row if row.get("name") == doc_name), None) + + self.assertIsNone(row) + + def assert_commission(self): + total_row = self.report_result[-1] + + # Total Amount + self.assertEqual(total_row[-4], 2040) + + # Total Commission + self.assertEqual(total_row[-1], 82) + + def assert_returned_doc(self): + doc_name = self.to_be_returned_doc.name + returned_doc_name = self.returned_doc.name + + outward_row = next( + (row for row in self.report_result_without_total_row if row.get("name") == doc_name), None + ) + inward_row = next( + (row for row in self.report_result_without_total_row if row.get("name") == returned_doc_name), + None, + ) + + self.assertIsNotNone(outward_row) + self.assertIsNotNone(inward_row) + + self.assertEqual(outward_row["amount"], 900) + self.assertEqual(outward_row["commission"], 45) + + self.assertEqual(inward_row["amount"], -900) + self.assertEqual(inward_row["commission"], -45) From 5397b7da25ad7032f29392dbef97f6f7acac3747 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:44:26 +0530 Subject: [PATCH 50/57] fix: sales order is not valid when creating WO from MR from PP (backport #54435) (#54436) fix: sales order is not valid when creating WO from MR from PP (#54435) (cherry picked from commit e65b9fc2ae96fa10c03ff4d064bad956a8cc13d6) Co-authored-by: Mihir Kandoi --- .../doctype/work_order/work_order.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index df8d4ce266f..8bc4485785c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -425,6 +425,18 @@ class WorkOrder(Document): if self.production_plan_sub_assembly_item: return + production_item = self.production_item + + if self.material_request_item and ( + mr_plan_item := frappe.get_value( + "Material Request Item", self.material_request_item, "material_request_plan_item" + ) + ): + if main_item_code := frappe.get_value( + "Material Request Plan Item", mr_plan_item, "main_item_code" + ): + production_item = main_item_code + if self.sales_order: self.check_sales_order_on_hold_or_close() @@ -445,8 +457,8 @@ class WorkOrder(Document): & (SalesOrder.docstatus == 1) & (SalesOrder.name == self.sales_order) & ( - (SalesOrderItem.item_code == self.production_item) - | (ProductBundleItem.item_code == self.production_item) + (SalesOrderItem.item_code == production_item) + | (ProductBundleItem.item_code == production_item) ) ) .run(as_dict=1) @@ -465,7 +477,7 @@ class WorkOrder(Document): & (SalesOrder.skip_delivery_note == 0) & (SalesOrderItem.item_code == PackedItem.parent_item) & (SalesOrder.docstatus == 1) - & (PackedItem.item_code == self.production_item) + & (PackedItem.item_code == production_item) ) .run(as_dict=1) ) From 9e6300bf76b8534d52296f0725a71686e677f541 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:15:21 +0530 Subject: [PATCH 51/57] fix: resolve conflict --- erpnext/accounts/doctype/journal_entry/journal_entry.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index e080c206ab5..0eaefb5f618 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -653,8 +653,6 @@ "fieldname": "tax_withholding_tab", "fieldtype": "Tab Break", "label": "Tax Withholding" -<<<<<<< HEAD -======= }, { "fieldname": "auto_repeat_section", @@ -666,7 +664,6 @@ "fieldname": "custom_remark", "fieldtype": "Check", "label": "Custom Remark" ->>>>>>> 697f521e14 (feat: use single remark field with custom remark toggle) } ], "icon": "fa fa-file-text", From 62a9a761b755f3045d294b149dbc107475b74368 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:54:56 +0530 Subject: [PATCH 52/57] fix(accounts): fetch project name from payment entry to journal entry (backport #54307) (#54453) Co-authored-by: sarathibalamurugan --- .../payment_entry/test_payment_entry.py | 31 +++++++++++++++++++ erpnext/accounts/utils.py | 3 ++ erpnext/controllers/accounts_controller.py | 2 ++ 3 files changed, 36 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 9d2890f5e79..f6a23ddf450 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -2105,6 +2105,37 @@ class TestPaymentEntry(ERPNextTestSuite): self.assertEqual(ref.voucher_no, so.name) self.assertIsNotNone(ref.payment_term) + def test_project_name_in_exchange_gain_loss_entry(self): + si = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=50, + do_not_submit=True, + ) + from erpnext.projects.doctype.project.test_project import make_project + + si.project = make_project({"project_name": "_Test Project for Exchange Gain Loss Entry"}).name + + si.submit() + + pe = get_payment_entry("Sales Invoice", si.name) + + pe.source_exchange_rate = 100 + + pe.insert() + pe.submit() + + rows = frappe.get_all( + "Journal Entry Account", + or_filters=[{"reference_name": pe.name}, {"reference_name": si.name}], + fields=["project"], + ) + self.assertEqual(len(rows), 2) + + self.assertEqual(rows[0].project, si.project) + self.assertEqual(rows[1].project, si.project) + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0c23353d1eb..fe68c4018aa 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -2513,6 +2513,7 @@ def create_gain_loss_journal( ref2_detail_no, cost_center, dimensions, + project=None, ) -> str: journal_entry = frappe.new_doc("Journal Entry") journal_entry.voucher_type = "Exchange Gain Or Loss" @@ -2539,6 +2540,7 @@ def create_gain_loss_journal( "account_currency": party_account_currency, "exchange_rate": 0, "cost_center": cost_center or erpnext.get_default_cost_center(company), + "project": project, "reference_type": ref1_dt, "reference_name": ref1_dn, "reference_detail_no": ref1_detail_no, @@ -2556,6 +2558,7 @@ def create_gain_loss_journal( "account_currency": gain_loss_account_currency, "exchange_rate": 1, "cost_center": cost_center or erpnext.get_default_cost_center(company), + "project": project, "reference_type": ref2_dt, "reference_name": ref2_dn, "reference_detail_no": ref2_detail_no, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bd4233c6350..6a2794dd9ab 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1768,6 +1768,7 @@ class AccountsController(TransactionBase): arg.get("referenced_row"), arg.get("cost_center"), dimensions_dict, + arg.get("project"), ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( @@ -1852,6 +1853,7 @@ class AccountsController(TransactionBase): d.idx, self.cost_center, dimensions_dict, + self.project, ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( From 57cd2a06e843147d040fe6d36e6128515f5217d3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:56:35 +0530 Subject: [PATCH 53/57] fix: add project filter to accounts payable and receivable reports (backport #54344) (#54442) Co-authored-by: ljain112 --- .../accounts_payable/accounts_payable.js | 11 +++++ .../accounts_payable/test_accounts_payable.py | 46 +++++++++++++++++ .../accounts_payable_summary.js | 11 +++++ .../accounts_receivable.js | 11 +++++ .../accounts_receivable.py | 14 +++++- .../test_accounts_receivable.py | 49 +++++++++++++++++++ .../accounts_receivable_summary.js | 11 +++++ 7 files changed, 151 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 4d74a5c2d7c..c94f8c245ec 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -34,6 +34,17 @@ frappe.query_reports["Accounts Payable"] = { }, options: "Cost Center", }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + options: "Project", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), + }); + }, + }, { fieldname: "party_account", label: __("Payable Account"), diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index a8074468f55..ee296447c0e 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -117,3 +117,49 @@ class TestAccountsPayable(ERPNextTestSuite, AccountsTestMixin): self.assertEqual(len(report[1]), 2) self.assertEqual([pi.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term]) + + def test_project_filter(self): + project = frappe.get_doc( + {"doctype": "Project", "project_name": "_Test AP Project", "company": self.company} + ).insert() + + pi = self.create_purchase_invoice(do_not_submit=True) + pi.project = project.name + pi.save().submit() + + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + "project": [project.name], + } + + report = execute(filters)[1] + self.assertEqual(len(report), 1) + row = report[0] + self.assertEqual(row.project, project.name) + self.assertEqual(row.invoiced, 300.0) + + def test_project_on_report_output(self): + """ + Report row must carry the invoice's project. + """ + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + } + + project = frappe.get_doc( + {"doctype": "Project", "project_name": "_Test AP Project Output", "company": self.company} + ).insert() + + pi = self.create_purchase_invoice(do_not_submit=True) + pi.project = project.name + pi.save().submit() + + report = execute(filters) + + self.assertEqual(len(report[1]), 1) + row = report[1][0] + self.assertEqual([pi.name, project.name, 300], [row.voucher_no, row.project, row.outstanding]) diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index a4cb0584bf1..3f603b62833 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -53,6 +53,17 @@ frappe.query_reports["Accounts Payable Summary"] = { }, options: "Cost Center", }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + options: "Project", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), + }); + }, + }, { fieldname: "party_type", label: __("Party Type"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index a05b17bfade..e7aa1f57036 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -36,6 +36,17 @@ frappe.query_reports["Accounts Receivable"] = { }, options: "Cost Center", }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + options: "Project", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), + }); + }, + }, { fieldname: "party_type", label: __("Party Type"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e29ed79cc49..4ca1bdcfffb 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -196,6 +196,7 @@ class ReceivablePayableReport: and ple.against_voucher_type in self.advance_payment_doctypes ): self.voucher_balance[key].cost_center = ple.cost_center + self.voucher_balance[key].project = ple.project self.get_invoices(ple) @@ -362,6 +363,7 @@ class ReceivablePayableReport: posting_date, account_currency, cost_center, + project, sum(invoiced) `invoiced`, sum(paid) `paid`, sum(credit_note) `credit_note`, @@ -390,6 +392,7 @@ class ReceivablePayableReport: "credit_note_in_account_currency", "outstanding_in_account_currency", "cost_center", + "project", ]: _d[field] = x.get(field) @@ -931,6 +934,7 @@ class ReceivablePayableReport: ple.against_voucher_no, ple.party_type, ple.cost_center, + ple.project, ple.party, ple.posting_date, ple.due_date, @@ -998,6 +1002,9 @@ class ReceivablePayableReport: if self.filters.cost_center: self.get_cost_center_conditions() + if self.filters.project: + self.qb_selection_filter.append(self.ple.project.isin(self.filters.project)) + self.add_accounting_dimensions_filters() def get_cost_center_conditions(self): @@ -1237,6 +1244,7 @@ class ReceivablePayableReport: ) self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data") + self.add_column(label=_("Project"), fieldname="project", fieldtype="Link", options="Project") self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data") self.add_column( label=_("Voucher No"), @@ -1413,6 +1421,7 @@ class InitSQLProceduresForAR: posting_date date, account_currency {_varchar_type}, cost_center {_varchar_type}, + project {_varchar_type}, invoiced {_currency_type}, paid {_currency_type}, credit_note {_currency_type}, @@ -1432,6 +1441,7 @@ class InitSQLProceduresForAR: against_voucher_no {_varchar_type}, party_type {_varchar_type}, cost_center {_varchar_type}, + project {_varchar_type}, party {_varchar_type}, posting_date date, due_date date, @@ -1447,7 +1457,7 @@ class InitSQLProceduresForAR: begin if not exists (select name from `{_voucher_balance_name}` where name = sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party))) then - insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0); + insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, ple.project, 0, 0, 0, 0, 0, 0); end if; end; """ @@ -1489,7 +1499,7 @@ class InitSQLProceduresForAR: end if; - insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.voucher_type, ple.voucher_no, ple.party)), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0); + insert into `{_voucher_balance_name}` values (sha1(concat_ws(',', ple.account, ple.voucher_type, ple.voucher_no, ple.party)), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', '', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0); end; """ diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 4b852e0583d..7dc84943d23 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -1204,3 +1204,52 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin): self.assertEqual(len(report[1]), 2) self.assertEqual([si.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term]) + + def test_project_filter(self): + project = frappe.get_doc( + {"doctype": "Project", "project_name": "_Test AR Project", "company": self.company} + ).insert() + + si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) + si.project = project.name + si.save().submit() + + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + "project": [project.name], + } + + report = execute(filters)[1] + self.assertEqual(len(report), 1) + row = report[0] + self.assertEqual(row.project, project.name) + self.assertEqual(row.invoiced, 100.0) + + def test_project_on_report_output(self): + """ + Report row must carry the invoice's project even when the payment entry + has no project set. + """ + filters = { + "company": self.company, + "report_date": today(), + "range": "30, 60, 90, 120", + } + + project = frappe.get_doc( + {"doctype": "Project", "project_name": "_Test AR Project Output", "company": self.company} + ).insert() + + si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) + si.project = project.name + si.save().submit() + + # payment has no project — report row must still show the invoice's project + self.create_payment_entry(si.name) + report = execute(filters) + + self.assertEqual(len(report[1]), 1) + row = report[1][0] + self.assertEqual([si.name, project.name, 60], [row.voucher_no, row.project, row.outstanding]) diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index c8e59d6e054..46585071174 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -53,6 +53,17 @@ frappe.query_reports["Accounts Receivable Summary"] = { }, options: "Cost Center", }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + options: "Project", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), + }); + }, + }, { fieldname: "party_type", label: __("Party Type"), From 01aff6492c16bb9a3fdb45420aa187fd72a472dc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:58:25 +0530 Subject: [PATCH 54/57] fix(purchase_register): filter tax rows by parenttype in invoice tax map query (backport #54272) (#54444) fix(purchase_register): filter tax rows by parenttype in invoice tax map query (cherry picked from commit 3aeb7d6b01ab01e947e4809e6e7425e22b61ac59) Co-authored-by: ljain112 --- .../purchase_register/purchase_register.py | 2 +- .../test_purchase_register.py | 47 +++++++++++++++++++ .../sales_register/test_sales_register.py | 38 +++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 2ec8931b63d..4801bf15b56 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -499,7 +499,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts, inc else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount from `tabPurchase Taxes and Charges` where parent in (%s) and category in ('Total', 'Valuation and Total') - and base_tax_amount_after_discount_amount != 0 + and base_tax_amount_after_discount_amount != 0 and parenttype='Purchase Invoice' group by parent, account_head, add_deduct_tax """ % ", ".join(["%s"] * len(invoice_list)), diff --git a/erpnext/accounts/report/purchase_register/test_purchase_register.py b/erpnext/accounts/report/purchase_register/test_purchase_register.py index 400ee899fa1..e4ce5ffcfe3 100644 --- a/erpnext/accounts/report/purchase_register/test_purchase_register.py +++ b/erpnext/accounts/report/purchase_register/test_purchase_register.py @@ -5,6 +5,7 @@ import frappe from frappe.utils import add_months, today from erpnext.accounts.report.purchase_register.purchase_register import execute +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.tests.utils import ERPNextTestSuite @@ -26,6 +27,52 @@ class TestPurchaseRegister(ERPNextTestSuite): self.assertEqual(first_row.total_tax, 100) self.assertEqual(first_row.grand_total, 1100) + def test_purchase_register_ignores_tax_rows_from_other_doctype(self): + frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'") + frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'") + + filters = frappe._dict(company="_Test Company 6", from_date=add_months(today(), -1), to_date=today()) + + pi = make_purchase_invoice() + + # Real workflow setup: create a Purchase Receipt tax row in the same shared child table. + pr = make_purchase_receipt( + company="_Test Company 6", + supplier="_Test Supplier", + item="_Test Item", + warehouse="_Test Warehouse - _TC6", + cost_center="_Test Cost Center - _TC6", + do_not_save=1, + do_not_submit=1, + qty=1, + rate=1000, + ) + pr.append( + "taxes", + { + "account_head": "GST - _TC6", + "cost_center": "_Test Cost Center - _TC6", + "add_deduct_tax": "Add", + "category": "Valuation and Total", + "charge_type": "Actual", + "description": "PR Tax", + "tax_amount": 100.0, + "rate": 100, + }, + ) + pr.insert() + pr.submit() + + # Mimic custom naming collision across doctypes (same parent value in shared child table). + frappe.rename_doc("Purchase Receipt", pr.name, pi.name, force=True) + + report_results = execute(filters) + first_row = frappe._dict(report_results[1][0]) + + self.assertEqual(first_row.voucher_no, pi.name) + self.assertEqual(first_row.total_tax, 100) + self.assertEqual(first_row.grand_total, 1100) + def test_purchase_register_ledger_view(self): frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'") frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'") diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index c6926d57dea..0875c7c143f 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -4,6 +4,7 @@ from frappe.utils import getdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.sales_register.sales_register import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.tests.utils import ERPNextTestSuite @@ -72,6 +73,43 @@ class TestItemWiseSalesRegister(ERPNextTestSuite, AccountsTestMixin): report_output = {k: v for k, v in report[1][0].items() if k in expected_result} self.assertDictEqual(report_output, expected_result) + def test_sales_register_ignores_tax_rows_from_other_doctype(self): + si = self.create_sales_invoice(rate=98) + + # Real workflow setup: create a Sales Order with taxes in the shared child table. + so = make_sales_order( + item=self.item, + company=self.company, + customer=self.customer, + rate=77, + do_not_save=1, + do_not_submit=1, + ) + so.append( + "taxes", + { + "charge_type": "Actual", + "account_head": self.income_account, + "description": "SO Tax", + "tax_amount": 55.0, + }, + ) + so.insert() + so.submit() + + # Mimic custom naming collision across doctypes (same parent value in shared child table). + frappe.rename_doc("Sales Order", so.name, si.name, force=True) + + filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company}) + report = execute(filters) + + self.assertEqual(len(report[1]), 1) + result = frappe._dict(report[1][0]) + self.assertEqual(result.voucher_no, si.name) + self.assertEqual(result.net_total, 98.0) + self.assertEqual(result.tax_total, 0) + self.assertEqual(result.grand_total, 98.0) + def test_journal_with_cost_center_filter(self): je1 = frappe.get_doc( { From 457adcee95c57bdf29f33637877505871477051f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:58:57 +0530 Subject: [PATCH 55/57] refactor: fix test cases in tax withholding details report (backport #54422) (#54445) Co-authored-by: ljain112 --- .../tax_withholding_details.py | 6 +- .../test_tax_withholding_details.py | 140 +++++++----------- 2 files changed, 59 insertions(+), 87 deletions(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index ed4a223311b..70813aaeb1d 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -68,8 +68,10 @@ def get_tax_withholding_data(filters): } data.append(row) - # Sort by section code and transaction date - data.sort(key=lambda x: (x["section_code"] or "", x["transaction_date"] or "")) + # Sort by section code, transaction date, then withholding_name for deterministic ordering + data.sort( + key=lambda x: (x["section_code"] or "", x["transaction_date"] or "", x["withholding_name"] or "") + ) return data diff --git a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py index 49e50b7ff32..91683867188 100644 --- a/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/test_tax_withholding_details.py @@ -5,10 +5,12 @@ import frappe from frappe.utils import add_to_date, today from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry -from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice -from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.tax_withholding_category.test_tax_withholding_category import ( + create_purchase_invoice, + create_records, + create_sales_invoice, create_tax_withholding_category, + make_journal_entry_with_tax_withholding, ) from erpnext.accounts.report.tax_withholding_details.tax_withholding_details import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin @@ -20,43 +22,45 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): def setUp(self): self.create_company() self.clear_old_entries() - create_tax_accounts() + create_records() def test_tax_withholding_for_customers(self): create_tax_category(cumulative_threshold=300) - frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS") - si = create_sales_invoice(rate=1000) - pe = create_tcs_payment_entry() + frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "TCS") + si = create_sales_invoice(customer="Test TCS Customer", rate=1000) + si.submit() + + create_tcs_payment_entry() jv = create_tcs_journal_entry() filters = frappe._dict( company="_Test Company", party_type="Customer", from_date=today(), to_date=today() ) result = execute(filters)[1] + expected_values = [ - # Check for JV totals using back calculation logic - [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], - [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + [jv.name, "TCS", 0.075, 1000.75, 0.75, 1000.75], + ["", "TCS", 0.075, 0, 0.75, 0], + [si.name, "TCS", 0.075, 1000.0, 0.75, 1000.75], ] self.check_expected_values(result, expected_values) def test_single_account_for_multiple_categories(self): - create_tax_category("TDS - 1", rate=10, account="TDS - _TC") - inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True) - inv_1.tax_withholding_category = "TDS - 1" + create_tax_category("TDS - 1", rate=10, account="TDS - _TC", cumulative_threshold=1) + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 1") + inv_1 = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000) inv_1.submit() - create_tax_category("TDS - 2", rate=20, account="TDS - _TC") - inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True) - inv_2.tax_withholding_category = "TDS - 2" + create_tax_category("TDS - 2", rate=20, account="TDS - _TC", cumulative_threshold=1) + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 2") + inv_2 = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000) inv_2.submit() result = execute( frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today()) )[1] expected_values = [ - [inv_1.name, "TDS - 1", 10, 5000, 500, 5500], - [inv_2.name, "TDS - 2", 20, 5000, 1000, 6000], + [inv_1.name, "TDS - 1", 10, 5000, 500, 4500], + [inv_2.name, "TDS - 2", 20, 5000, 1000, 4000], ] self.check_expected_values(result, expected_values) @@ -81,20 +85,21 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): tds_doc.save() - inv_1 = make_purchase_invoice( - rate=1000, posting_date=add_to_date(fiscal_year[1], days=1), do_not_save=True, do_not_submit=True + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", tds_doc.name) + inv_1 = create_purchase_invoice( + supplier="Test TDS Supplier", + rate=5000, + posting_date=add_to_date(fiscal_year[1], days=1), + set_posting_time=True, ) - inv_1.set_posting_time = 1 - inv_1.apply_tds = 1 - inv_1.tax_withholding_category = tds_doc.name - inv_1.save() inv_1.submit() - inv_2 = make_purchase_invoice(rate=1000, posting_date=from_date, do_not_save=True, do_not_submit=True) - inv_2.set_posting_time = 1 - inv_2.apply_tds = 1 - inv_2.tax_withholding_category = tds_doc.name - inv_2.save() + inv_2 = create_purchase_invoice( + supplier="Test TDS Supplier", + rate=5000, + posting_date=from_date, + set_posting_time=True, + ) inv_2.submit() result = execute( @@ -113,6 +118,7 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): self.check_expected_values(result, expected_values) def check_expected_values(self, result, expected_values): + self.assertEqual(len(result), len(expected_values)) for i in range(len(result)): voucher = frappe._dict(result[i]) voucher_expected_values = expected_values[i] @@ -127,21 +133,6 @@ class TestTaxWithholdingDetails(ERPNextTestSuite, AccountsTestMixin): self.assertSequenceEqual(voucher_actual_values, voucher_expected_values) -def create_tax_accounts(): - account_names = ["TCS", "TDS"] - for account in account_names: - frappe.get_doc( - { - "doctype": "Account", - "company": "_Test Company", - "account_name": account, - "parent_account": "Duties and Taxes - _TC", - "report_type": "Balance Sheet", - "root_type": "Liability", - } - ).insert(ignore_if_duplicate=True) - - def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0): fiscal_year = get_fiscal_year(today(), company="_Test Company") from_date = fiscal_year[1] @@ -157,55 +148,34 @@ def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulat ) -def create_tcs_payment_entry(): +def create_tcs_payment_entry(party="Test TCS Customer", category="TCS", amount=1000): + """Create a TCS Payment Entry that generates a Tax Withholding Entry (Over Withheld).""" payment_entry = create_payment_entry( payment_type="Receive", party_type="Customer", - party="_Test Customer", + party=party, paid_from="Debtors - _TC", paid_to="Cash - _TC", - paid_amount=2550, - ) - - payment_entry.append( - "taxes", - { - "account_head": "TCS - _TC", - "charge_type": "Actual", - "tax_amount": 0.53, - "add_deduct_tax": "Add", - "description": "Test", - "cost_center": "Main - _TC", - }, + paid_amount=amount, ) + payment_entry.apply_tds = 1 + payment_entry.tax_withholding_category = category + payment_entry.save() payment_entry.submit() return payment_entry -def create_tcs_journal_entry(): - jv = frappe.new_doc("Journal Entry") - jv.posting_date = today() - jv.company = "_Test Company" - jv.set( - "accounts", - [ - { - "account": "Debtors - _TC", - "party_type": "Customer", - "party": "_Test Customer", - "credit_in_account_currency": 10000, - }, - { - "account": "Debtors - _TC", - "party_type": "Customer", - "party": "_Test Customer", - "debit_in_account_currency": 9992.5, - }, - { - "account": "TCS - _TC", - "debit_in_account_currency": 7.5, - }, - ], +def create_tcs_journal_entry(party="Test TCS Customer", category="TCS", amount=1000): + """Create a TCS Credit Note Journal Entry that generates a Tax Withholding Entry.""" + jv = make_journal_entry_with_tax_withholding( + party_type="Customer", + party=party, + voucher_type="Credit Note", + amount=amount, + save=False, ) - jv.insert() - return jv.submit() + jv.apply_tds = 1 + jv.tax_withholding_category = category + jv.save() + jv.submit() + return jv From 193a44f2986bd498670c18989820b61d458709fd Mon Sep 17 00:00:00 2001 From: MochaMind Date: Wed, 22 Apr 2026 00:16:52 +0530 Subject: [PATCH 56/57] chore: update POT file (#54401) --- erpnext/locale/main.pot | 1199 ++++++++++++++++++++------------------- 1 file changed, 626 insertions(+), 573 deletions(-) diff --git a/erpnext/locale/main.pot b/erpnext/locale/main.pot index 06178e5e76e..bacf12f631e 100644 --- a/erpnext/locale/main.pot +++ b/erpnext/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: ERPNext VERSION\n" "Report-Msgid-Bugs-To: hello@frappe.io\n" -"POT-Creation-Date: 2026-04-12 09:47+0000\n" -"PO-Revision-Date: 2026-04-12 09:47+0000\n" +"POT-Creation-Date: 2026-04-19 09:47+0000\n" +"PO-Revision-Date: 2026-04-19 09:47+0000\n" "Last-Translator: hello@frappe.io\n" "Language-Team: hello@frappe.io\n" "MIME-Version: 1.0\n" @@ -271,7 +271,7 @@ msgstr "" msgid "'Account' in the Accounting section of Customer {0}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:360 +#: erpnext/selling/doctype/sales_order/sales_order.py:361 msgid "'Allow Multiple Sales Orders Against a Customer's Purchase Order'" msgstr "" @@ -313,9 +313,9 @@ msgstr "" msgid "'Inspection Required before Purchase' has disabled for the item {0}, no need to create the QI" msgstr "" -#: erpnext/stock/report/stock_ledger/stock_ledger.py:665 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:706 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:811 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:668 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:709 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:814 msgid "'Opening'" msgstr "" @@ -826,7 +826,7 @@ msgstr "" msgid "

Posting Date {0} cannot be before Purchase Order date for the following:

    " msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:69 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:75 msgid "

    Price List Rate has not been set as editable in Selling Settings. In this scenario, setting Update Price List Based On to Price List Rate will prevent auto-updation of Item Price.

    Are you sure you want to continue?" msgstr "" @@ -1184,7 +1184,7 @@ msgid "Accepted Qty in Stock UOM" msgstr "" #. Label of the qty (Float) field in DocType 'Purchase Receipt Item' -#: erpnext/public/js/controllers/transaction.js:2923 +#: erpnext/public/js/controllers/transaction.js:2924 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json msgid "Accepted Quantity" msgstr "" @@ -1875,8 +1875,8 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1238 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1474 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:1496 -#: erpnext/controllers/stock_controller.py:727 -#: erpnext/controllers/stock_controller.py:744 +#: erpnext/controllers/stock_controller.py:732 +#: erpnext/controllers/stock_controller.py:749 #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:936 #: erpnext/stock/doctype/stock_entry/stock_entry.py:2001 #: erpnext/stock/doctype/stock_entry/stock_entry.py:2015 @@ -1961,7 +1961,7 @@ msgstr "" #: erpnext/setup/doctype/email_digest/email_digest.json #: erpnext/setup/doctype/incoterm/incoterm.json #: erpnext/setup/doctype/supplier_group/supplier_group.json -#: erpnext/setup/install.py:369 +#: erpnext/setup/install.py:378 msgid "Accounts" msgstr "" @@ -2263,6 +2263,13 @@ msgstr "" msgid "Actions performed" msgstr "" +#. Label of the enable_serial_and_batch_no_for_item (Check) field in DocType +#. 'Stock Settings' +#: erpnext/stock/doctype/item/item.js:413 +#: erpnext/stock/doctype/stock_settings/stock_settings.json +msgid "Activate Serial / Batch No for Item" +msgstr "" + #: erpnext/selling/page/sales_funnel/sales_funnel.py:55 msgid "Active Leads" msgstr "" @@ -2509,11 +2516,11 @@ msgstr "" msgid "Actual type tax cannot be included in Item rate in row {0}" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1021 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1023 msgid "Ad-hoc Qty" msgstr "" -#: erpnext/stock/doctype/item/item.js:601 +#: erpnext/stock/doctype/item/item.js:682 #: erpnext/stock/doctype/price_list/price_list.js:8 msgid "Add / Edit Prices" msgstr "" @@ -2547,7 +2554,7 @@ msgid "Add Employees" msgstr "" #: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:256 -#: erpnext/selling/doctype/sales_order/sales_order.js:284 +#: erpnext/selling/doctype/sales_order/sales_order.js:285 #: erpnext/stock/dashboard/item_dashboard.js:216 msgid "Add Item" msgstr "" @@ -2623,7 +2630,7 @@ msgid "Add Sales Partners" msgstr "" #. Label of the add_schedule (Button) field in DocType 'Sales Order Item' -#: erpnext/selling/doctype/sales_order/sales_order.js:656 +#: erpnext/selling/doctype/sales_order/sales_order.js:657 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json msgid "Add Schedule" msgstr "" @@ -2885,7 +2892,7 @@ msgstr "" msgid "Additional Discount Amount (Company Currency)" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:837 +#: erpnext/controllers/taxes_and_totals.py:836 msgid "Additional Discount Amount ({discount_amount}) cannot exceed the total before such discount ({total_before_discount})" msgstr "" @@ -2980,7 +2987,7 @@ msgstr "" msgid "Additional Information updated successfully." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:818 +#: erpnext/manufacturing/doctype/work_order/work_order.js:829 msgid "Additional Material Transfer" msgstr "" @@ -3284,7 +3291,7 @@ msgstr "" msgid "Advance amount" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:974 +#: erpnext/controllers/taxes_and_totals.py:973 msgid "Advance amount cannot be greater than {0} {1}" msgstr "" @@ -3328,6 +3335,10 @@ msgstr "" msgid "Aerospace" msgstr "" +#: erpnext/stock/doctype/stock_settings/stock_settings.js:20 +msgid "After save, please refresh the page to apply the changes." +msgstr "" + #. Label of the against (Text) field in DocType 'GL Entry' #: erpnext/accounts/doctype/gl_entry/gl_entry.json #: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.html:20 @@ -3769,7 +3780,7 @@ msgstr "" msgid "All items have already been transferred for this Work Order." msgstr "" -#: erpnext/public/js/controllers/transaction.js:3032 +#: erpnext/public/js/controllers/transaction.js:3033 msgid "All items in this document already have a linked Quality Inspection." msgstr "" @@ -3791,7 +3802,7 @@ msgstr "" msgid "All the items have been already returned." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1256 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1267 msgid "All the required items (raw materials) will be fetched from BOM and populated in this table. Here you can also change the Source Warehouse for any item. And during the production, you can track transferred raw materials from this table." msgstr "" @@ -4026,8 +4037,11 @@ msgstr "" msgid "Allow Negative Stock for Batch" msgstr "" +#. Label of the allow_negative_rates_for_items (Check) field in DocType 'Buying +#. Settings' #. Label of the allow_negative_rates_for_items (Check) field in DocType #. 'Selling Settings' +#: erpnext/buying/doctype/buying_settings/buying_settings.json #: erpnext/selling/doctype/selling_settings/selling_settings.json msgid "Allow Negative rates for Items" msgstr "" @@ -4256,9 +4270,9 @@ msgstr "" msgid "Allowed Dimension" msgstr "" -#. Label of the allowed_types (Table) field in DocType 'Repost Accounting -#. Ledger Settings' -#: erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json +#. Label of the repost_allowed_types (Table) field in DocType 'Accounts +#. Settings' +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json msgid "Allowed Doctypes" msgstr "" @@ -4534,7 +4548,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json #: erpnext/manufacturing/doctype/bom_item/bom_item.json #: erpnext/manufacturing/doctype/work_order_item/work_order_item.json -#: erpnext/public/js/controllers/transaction.js:510 +#: erpnext/public/js/controllers/transaction.js:512 #: erpnext/selling/doctype/quotation/quotation.js:316 #: erpnext/selling/doctype/quotation_item/quotation_item.json #: erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -5235,7 +5249,7 @@ msgstr "" msgid "As the field {0} is enabled, the value of the field {1} should be more than 1." msgstr "" -#: erpnext/stock/doctype/item/item.py:1006 +#: erpnext/stock/doctype/item/item.py:1019 msgid "As there are existing submitted transactions against item {0}, you can not change the value of {1}." msgstr "" @@ -5918,7 +5932,7 @@ msgstr "" msgid "At row {0}: Batch No is mandatory for Item {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:118 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:129 msgid "At row {0}: Parent Row No cannot be set for item {1}" msgstr "" @@ -5930,11 +5944,11 @@ msgstr "" msgid "At row {0}: Serial No is mandatory for Item {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:675 +#: erpnext/controllers/stock_controller.py:680 msgid "At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields." msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:112 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:123 msgid "At row {0}: set Parent Row No for item {1}" msgstr "" @@ -5994,7 +6008,7 @@ msgstr "" msgid "Attribute Value" msgstr "" -#: erpnext/stock/doctype/item/item.py:942 +#: erpnext/stock/doctype/item/item.py:955 msgid "Attribute table is mandatory" msgstr "" @@ -6002,11 +6016,11 @@ msgstr "" msgid "Attribute value: {0} must appear only once" msgstr "" -#: erpnext/stock/doctype/item/item.py:946 +#: erpnext/stock/doctype/item/item.py:959 msgid "Attribute {0} selected multiple times in Attributes Table" msgstr "" -#: erpnext/stock/doctype/item/item.py:874 +#: erpnext/stock/doctype/item/item.py:887 msgid "Attributes" msgstr "" @@ -6192,7 +6206,7 @@ msgstr "" msgid "Auto Reserve Stock for Sales Order on Purchase" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:186 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:193 msgid "Auto Tax Settings Error" msgstr "" @@ -6539,10 +6553,10 @@ msgstr "" #: erpnext/manufacturing/report/bom_stock_analysis/bom_stock_analysis.js:8 #: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:109 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1422 +#: erpnext/selling/doctype/sales_order/sales_order.js:1440 #: erpnext/stock/doctype/material_request/material_request.js:351 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json -#: erpnext/stock/doctype/stock_entry/stock_entry.js:796 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:788 #: erpnext/stock/report/bom_search/bom_search.py:38 #: erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:525 @@ -6556,7 +6570,7 @@ msgstr "" msgid "BOM 1" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1806 +#: erpnext/manufacturing/doctype/bom/bom.py:1801 msgid "BOM 1 {0} and BOM 2 {1} should not be same" msgstr "" @@ -6662,7 +6676,7 @@ msgstr "" #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js:8 #: erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py:31 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1082 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1084 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/stock/doctype/material_request_item/material_request_item.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -6802,7 +6816,7 @@ msgid "BOM and Production" msgstr "" #: erpnext/stock/doctype/material_request/material_request.js:386 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:848 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:840 msgid "BOM does not contain any stock item" msgstr "" @@ -6835,15 +6849,15 @@ msgstr "" msgid "BOMs Updated" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:299 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:310 msgid "BOMs created successfully" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:309 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:320 msgid "BOMs creation failed" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:249 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:260 msgid "BOMs creation has been enqueued, kindly check the status after some time" msgstr "" @@ -6859,7 +6873,7 @@ msgstr "" #. Order Operation' #: erpnext/manufacturing/doctype/bom_operation/bom_operation.json #: erpnext/manufacturing/doctype/job_card/job_card.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:371 +#: erpnext/manufacturing/doctype/work_order/work_order.js:381 #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json msgid "Backflush Materials From WIP Warehouse" msgstr "" @@ -6943,7 +6957,7 @@ msgstr "" #: erpnext/accounts/doctype/process_period_closing_voucher_detail/process_period_closing_voucher_detail.json #: erpnext/accounts/report/balance_sheet/balance_sheet.json #: erpnext/accounts/workspace/financial_reports/financial_reports.json -#: erpnext/public/js/financial_statements.js:311 +#: erpnext/public/js/financial_statements.js:314 #: erpnext/setup/doctype/email_digest/email_digest.json #: erpnext/workspace_sidebar/financial_reports.json msgid "Balance Sheet" @@ -7479,7 +7493,7 @@ msgstr "" #: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:34 #: erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py:80 #: erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.py:158 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:394 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:402 #: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:171 #: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py:80 #: erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.js:19 @@ -7554,7 +7568,7 @@ msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js:89 #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:115 -#: erpnext/public/js/controllers/transaction.js:2949 +#: erpnext/public/js/controllers/transaction.js:2950 #: erpnext/public/js/utils/barcode_scanner.js:281 #: erpnext/public/js/utils/serial_no_batch_selector.js:438 #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -7649,7 +7663,7 @@ msgstr "" #. Label of the batch_size (Float) field in DocType 'Work Order Operation' #: erpnext/manufacturing/doctype/bom_operation/bom_operation.json #: erpnext/manufacturing/doctype/operation/operation.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:353 +#: erpnext/manufacturing/doctype/work_order/work_order.js:363 #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json msgid "Batch Size" @@ -7753,7 +7767,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom/bom.py:1372 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/stock/doctype/material_request/material_request.js:139 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:782 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:774 #: erpnext/workspace_sidebar/subcontracting.json msgid "Bill of Materials" msgstr "" @@ -8907,7 +8921,7 @@ msgstr "" msgid "Can be approved by {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2554 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2559 msgid "Can not close Work Order. Since {0} Job Cards are in Work In Progress state." msgstr "" @@ -9070,7 +9084,7 @@ msgstr "" msgid "Cannot cancel transaction for Completed Work Order." msgstr "" -#: erpnext/stock/doctype/item/item.py:894 +#: erpnext/stock/doctype/item/item.py:907 msgid "Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item" msgstr "" @@ -9082,7 +9096,7 @@ msgstr "" msgid "Cannot change Service Stop Date for item in row {0}" msgstr "" -#: erpnext/stock/doctype/item/item.py:885 +#: erpnext/stock/doctype/item/item.py:898 msgid "Cannot change Variant properties after stock transaction. You will have to make a new Item to do this." msgstr "" @@ -9114,7 +9128,7 @@ msgstr "" msgid "Cannot create Stock Reservation Entries for future dated Purchase Receipts." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:1886 +#: erpnext/selling/doctype/sales_order/sales_order.py:1917 #: erpnext/stock/doctype/pick_list/pick_list.py:255 msgid "Cannot create a pick list for Sales Order {0} because it has reserved stock. Please unreserve the stock in order to create a pick list." msgstr "" @@ -9185,8 +9199,8 @@ msgstr "" msgid "Cannot enable Item-wise Inventory Account, as there are existing Stock Ledger Entries for the company {0} with Warehouse-wise Inventory Account. Please cancel the stock transactions first and try again." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:784 -#: erpnext/selling/doctype/sales_order/sales_order.py:807 +#: erpnext/selling/doctype/sales_order/sales_order.py:785 +#: erpnext/selling/doctype/sales_order/sales_order.py:808 msgid "Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No." msgstr "" @@ -9214,11 +9228,11 @@ msgstr "" msgid "Cannot produce more Item {0} than Sales Order quantity {1} {2}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1451 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1456 msgid "Cannot produce more item for {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1455 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1460 msgid "Cannot produce more than {0} items for {1}" msgstr "" @@ -9302,7 +9316,7 @@ msgstr "" #. Label of the capacity_per_day (Int) field in DocType 'Item Lead Time' #. Label of the capacity (Float) field in DocType 'Putaway Rule' -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:963 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:965 #: erpnext/stock/doctype/item_lead_time/item_lead_time.json #: erpnext/stock/doctype/putaway_rule/putaway_rule.json msgid "Capacity" @@ -9445,7 +9459,7 @@ msgstr "" msgid "Cash Flow" msgstr "" -#: erpnext/public/js/financial_statements.js:343 +#: erpnext/public/js/financial_statements.js:346 msgid "Cash Flow Statement" msgstr "" @@ -9683,7 +9697,7 @@ msgstr "" msgid "Changes in {0}" msgstr "" -#: erpnext/stock/doctype/item/item.js:355 +#: erpnext/stock/doctype/item/item.js:379 msgid "Changing Customer Group for the selected Customer is not allowed." msgstr "" @@ -9884,7 +9898,7 @@ msgstr "" #. Label of the reference_date (Date) field in DocType 'Payment Entry' #: erpnext/accounts/doctype/payment_entry/payment_entry.json -#: erpnext/public/js/controllers/transaction.js:2860 +#: erpnext/public/js/controllers/transaction.js:2861 msgid "Cheque/Reference Date" msgstr "" @@ -9938,7 +9952,7 @@ msgstr "" #. Label of the child_row_reference (Data) field in DocType 'Quality #. Inspection' -#: erpnext/public/js/controllers/transaction.js:2955 +#: erpnext/public/js/controllers/transaction.js:2956 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Child Row Reference" msgstr "" @@ -10110,11 +10124,11 @@ msgstr "" msgid "Closed Documents" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2477 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2482 msgid "Closed Work Order can not be stopped or Re-opened" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:538 +#: erpnext/selling/doctype/sales_order/sales_order.py:539 msgid "Closed order cannot be cancelled. Unclose to cancel." msgstr "" @@ -10357,7 +10371,7 @@ msgstr "" msgid "Communication Medium Type" msgstr "" -#: erpnext/setup/install.py:98 +#: erpnext/setup/install.py:106 msgid "Compact Item Print" msgstr "" @@ -10702,7 +10716,7 @@ msgstr "" #: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/report/project_summary/project_summary.js:8 #: erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.py:44 -#: erpnext/public/js/financial_statements.js:365 +#: erpnext/public/js/financial_statements.js:368 #: erpnext/public/js/purchase_trends_filters.js:8 #: erpnext/public/js/sales_trends_filters.js:51 #: erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -10748,8 +10762,8 @@ msgstr "" #: erpnext/setup/doctype/employee/employee_tree.js:8 #: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json #: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json -#: erpnext/setup/doctype/vehicle/vehicle.json erpnext/setup/install.py:157 -#: erpnext/setup/install.py:166 erpnext/setup/workspace/home/home.json +#: erpnext/setup/doctype/vehicle/vehicle.json erpnext/setup/install.py:165 +#: erpnext/setup/install.py:174 erpnext/setup/workspace/home/home.json #: erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.js:8 #: erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js:8 #: erpnext/stock/doctype/bin/bin.json @@ -10802,7 +10816,7 @@ msgstr "" #: erpnext/stock/report/stock_balance/stock_balance.js:8 #: erpnext/stock/report/stock_balance/stock_balance.py:579 #: erpnext/stock/report/stock_ledger/stock_ledger.js:8 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:422 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:425 #: erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js:18 #: erpnext/stock/report/stock_projected_qty/stock_projected_qty.js:8 #: erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js:8 @@ -10887,6 +10901,10 @@ msgstr "" msgid "Company Address Name" msgstr "" +#: erpnext/controllers/accounts_controller.py:4364 +msgid "Company Address is missing. You don't have permission to create an Address. Please contact your System Manager." +msgstr "" + #: erpnext/controllers/accounts_controller.py:4352 msgid "Company Address is missing. You don't have permission to update it. Please contact your System Manager." msgstr "" @@ -10957,7 +10975,7 @@ msgid "Company Field" msgstr "" #. Label of the company_logo (Attach Image) field in DocType 'Company' -#: erpnext/public/js/print.js:77 erpnext/setup/doctype/company/company.json +#: erpnext/public/js/print.js:80 erpnext/setup/doctype/company/company.json msgid "Company Logo" msgstr "" @@ -10991,7 +11009,7 @@ msgid "Company currencies of both the companies should match for Inter Company T msgstr "" #: erpnext/stock/doctype/material_request/material_request.js:380 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:842 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:834 msgid "Company field is required" msgstr "" @@ -11053,7 +11071,7 @@ msgid "Company {0} added multiple times" msgstr "" #: erpnext/accounts/doctype/account/account.py:509 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1307 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1309 msgid "Company {0} does not exist" msgstr "" @@ -11061,6 +11079,10 @@ msgstr "" msgid "Company {0} is added more than once" msgstr "" +#: erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py:33 +msgid "Company {0} is not in South Africa." +msgstr "" + #: erpnext/setup/setup_wizard/operations/taxes_setup.py:14 msgid "Company {} does not exist yet. Taxes setup aborted." msgstr "" @@ -11138,7 +11160,7 @@ msgstr "" msgid "Completed Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1369 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1374 msgid "Completed Qty cannot be greater than 'Qty to Manufacture'" msgstr "" @@ -11289,7 +11311,7 @@ msgstr "" msgid "Consider Minimum Order Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1078 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1089 msgid "Consider Process Loss" msgstr "" @@ -11480,7 +11502,7 @@ msgstr "" msgid "Consumed Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1745 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1750 msgid "Consumed Qty cannot be greater than Reserved Qty for item {0}" msgstr "" @@ -11813,7 +11835,7 @@ msgstr "" #. Label of the conversion_rate (Float) field in DocType 'BOM Creator' #: erpnext/accounts/doctype/dunning/dunning.json #: erpnext/manufacturing/doctype/bom/bom.json -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:86 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:93 #: erpnext/manufacturing/doctype/bom_creator/bom_creator.json msgid "Conversion Rate" msgstr "" @@ -12093,7 +12115,7 @@ msgstr "" #: erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json #: erpnext/buying/report/procurement_tracker/procurement_tracker.js:15 #: erpnext/buying/report/procurement_tracker/procurement_tracker.py:32 -#: erpnext/public/js/financial_statements.js:458 +#: erpnext/public/js/financial_statements.js:461 #: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/stock/doctype/delivery_note/delivery_note.json @@ -12543,7 +12565,7 @@ msgstr "" msgid "Create Missing Party" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:164 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:196 msgid "Create Multi-level BOM" msgstr "" @@ -12588,11 +12610,11 @@ msgstr "" msgid "Create Payment Entry for Consolidated POS Invoices." msgstr "" -#: erpnext/public/js/controllers/transaction.js:517 +#: erpnext/public/js/controllers/transaction.js:519 msgid "Create Payment Request" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:800 +#: erpnext/manufacturing/doctype/work_order/work_order.js:811 msgid "Create Pick List" msgstr "" @@ -12619,7 +12641,7 @@ msgstr "" #. Title of an Onboarding Step #. Label of an action in the Onboarding Step 'Create Purchase Order' #: erpnext/buying/onboarding_step/create_purchase_order/create_purchase_order.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1675 +#: erpnext/selling/doctype/sales_order/sales_order.js:1693 #: erpnext/utilities/activation.py:106 msgid "Create Purchase Order" msgstr "" @@ -12775,12 +12797,12 @@ msgstr "" msgid "Create Users" msgstr "" -#: erpnext/stock/doctype/item/item.js:897 +#: erpnext/stock/doctype/item/item.js:978 msgid "Create Variant" msgstr "" -#: erpnext/stock/doctype/item/item.js:711 -#: erpnext/stock/doctype/item/item.js:755 +#: erpnext/stock/doctype/item/item.js:792 +#: erpnext/stock/doctype/item/item.js:836 msgid "Create Variants" msgstr "" @@ -12799,12 +12821,12 @@ msgstr "" msgid "Create Workstation" msgstr "" -#: erpnext/stock/doctype/item/item.js:738 -#: erpnext/stock/doctype/item/item.js:890 +#: erpnext/stock/doctype/item/item.js:819 +#: erpnext/stock/doctype/item/item.js:971 msgid "Create a variant with the template image." msgstr "" -#: erpnext/stock/stock_ledger.py:2058 +#: erpnext/stock/stock_ledger.py:2068 msgid "Create an incoming stock transaction for the Item." msgstr "" @@ -12852,11 +12874,11 @@ msgstr "" msgid "Creating Accounts..." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1550 +#: erpnext/selling/doctype/sales_order/sales_order.js:1568 msgid "Creating Delivery Note ..." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:684 +#: erpnext/selling/doctype/sales_order/sales_order.js:685 msgid "Creating Delivery Schedule..." msgstr "" @@ -12876,7 +12898,7 @@ msgstr "" msgid "Creating Purchase Invoices ..." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1699 +#: erpnext/selling/doctype/sales_order/sales_order.js:1717 msgid "Creating Purchase Order ..." msgstr "" @@ -12895,7 +12917,7 @@ msgstr "" msgid "Creating Stock Entry" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1820 +#: erpnext/selling/doctype/sales_order/sales_order.js:1838 msgid "Creating Subcontracting Inward Order ..." msgstr "" @@ -13203,7 +13225,7 @@ msgstr "" msgid "Criteria weights must add up to 100%" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:173 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:180 msgid "Cron Interval should be between 1 and 59 Min" msgstr "" @@ -13637,7 +13659,7 @@ msgstr "" #: erpnext/selling/doctype/customer/customer.json #: erpnext/selling/doctype/installation_note/installation_note.json #: erpnext/selling/doctype/party_specific_item/party_specific_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1190 +#: erpnext/selling/doctype/sales_order/sales_order.js:1199 #: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order/sales_order_calendar.js:19 #: erpnext/selling/doctype/sms_center/sms_center.json @@ -14155,7 +14177,7 @@ msgid "Customer required for 'Customerwise Discount'" msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1140 -#: erpnext/selling/doctype/sales_order/sales_order.py:434 +#: erpnext/selling/doctype/sales_order/sales_order.py:435 #: erpnext/stock/doctype/delivery_note/delivery_note.py:436 msgid "Customer {0} does not belong to project {1}" msgstr "" @@ -14776,7 +14798,7 @@ msgstr "" msgid "Default BOM ({0}) must be active for this item or its template" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2245 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2250 msgid "Default BOM for {0} not found" msgstr "" @@ -14784,7 +14806,7 @@ msgstr "" msgid "Default BOM not found for FG Item {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2242 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2247 msgid "Default BOM not found for Item {0} and Project {1}" msgstr "" @@ -15129,15 +15151,15 @@ msgstr "" msgid "Default Unit of Measure" msgstr "" -#: erpnext/stock/doctype/item/item.py:1289 +#: erpnext/stock/doctype/item/item.py:1302 msgid "Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You need to either cancel the linked documents or create a new Item." msgstr "" -#: erpnext/stock/doctype/item/item.py:1272 +#: erpnext/stock/doctype/item/item.py:1285 msgid "Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM." msgstr "" -#: erpnext/stock/doctype/item/item.py:920 +#: erpnext/stock/doctype/item/item.py:933 msgid "Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'" msgstr "" @@ -15508,11 +15530,11 @@ msgstr "" #. Label of the delivery_date (Date) field in DocType 'Sales Order Item' #: erpnext/manufacturing/doctype/master_production_schedule_item/master_production_schedule_item.json #: erpnext/manufacturing/doctype/sales_forecast_item/sales_forecast_item.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1067 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1069 #: erpnext/public/js/utils.js:895 #: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:631 -#: erpnext/selling/doctype/sales_order/sales_order.js:1497 +#: erpnext/selling/doctype/sales_order/sales_order.js:632 +#: erpnext/selling/doctype/sales_order/sales_order.js:1515 #: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/report/sales_order_analysis/sales_order_analysis.py:321 @@ -15559,7 +15581,7 @@ msgstr "" #: erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py:21 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:291 #: erpnext/accounts/report/sales_register/sales_register.py:245 -#: erpnext/selling/doctype/sales_order/sales_order.js:1049 +#: erpnext/selling/doctype/sales_order/sales_order.js:1050 #: erpnext/selling/doctype/sales_order/sales_order_list.js:81 #: erpnext/setup/doctype/authorization_rule/authorization_rule.json #: erpnext/stock/doctype/delivery_note/delivery_note.json @@ -15629,8 +15651,8 @@ msgstr "" msgid "Delivery Notes {0} updated" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:626 -#: erpnext/selling/doctype/sales_order/sales_order.js:653 +#: erpnext/selling/doctype/sales_order/sales_order.js:627 +#: erpnext/selling/doctype/sales_order/sales_order.js:654 msgid "Delivery Schedule" msgstr "" @@ -15702,7 +15724,7 @@ msgstr "" msgid "Delivery to" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:453 +#: erpnext/selling/doctype/sales_order/sales_order.py:454 msgid "Delivery warehouse required for stock item {0}" msgstr "" @@ -15710,19 +15732,19 @@ msgstr "" #. DocType 'Master Production Schedule' #: erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.json #: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py:233 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:311 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:376 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:313 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:378 msgid "Demand" msgstr "" #. Label of the demand_qty (Float) field in DocType 'Sales Forecast Item' #: erpnext/manufacturing/doctype/sales_forecast_item/sales_forecast_item.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1015 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1017 msgid "Demand Qty" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:323 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:388 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:325 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:390 msgid "Demand vs Supply" msgstr "" @@ -16276,7 +16298,7 @@ msgstr "" #. Option for the 'Purpose' (Select) field in DocType 'Stock Entry' #. Option for the 'Purpose' (Select) field in DocType 'Stock Entry Type' -#: erpnext/manufacturing/doctype/work_order/work_order.js:1056 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1067 #: erpnext/stock/doctype/stock_entry/stock_entry.js:370 #: erpnext/stock/doctype/stock_entry/stock_entry.js:413 #: erpnext/stock/doctype/stock_entry/stock_entry.json @@ -16292,7 +16314,7 @@ msgstr "" msgid "Disassemble Qty cannot be less than or equal to 0." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:447 +#: erpnext/manufacturing/doctype/work_order/work_order.js:458 msgid "Disassemble Qty cannot be less than or equal to 0." msgstr "" @@ -16829,11 +16851,11 @@ msgstr "" msgid "Do you really want to restore this scrapped asset?" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:15 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:23 msgid "Do you still want to enable immutable ledger?" msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:44 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:50 msgid "Do you still want to enable negative inventory?" msgstr "" @@ -16911,7 +16933,7 @@ msgstr "" msgid "Documents Processed on each trigger. Queue Size should be between 5 and 100" msgstr "" -#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:262 +#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:261 msgid "Documents: {0} have deferred revenue/expense enabled for them. Cannot repost." msgstr "" @@ -17167,7 +17189,7 @@ msgstr "" msgid "Duplicate Item Group" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:100 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:102 msgid "Duplicate Item Under Same Parent" msgstr "" @@ -17786,7 +17808,7 @@ msgstr "" msgid "Enable Auto Email" msgstr "" -#: erpnext/stock/doctype/item/item.py:1081 +#: erpnext/stock/doctype/item/item.py:1094 msgid "Enable Auto Re-Order" msgstr "" @@ -17904,10 +17926,8 @@ msgstr "" msgid "Enable Separate Reposting for GL" msgstr "" -#. Label of the enable_serial_and_batch_no_for_item (Check) field in DocType -#. 'Stock Settings' -#: erpnext/stock/doctype/stock_settings/stock_settings.json -msgid "Enable Serial / Batch No for Item" +#: erpnext/stock/report/stock_ledger/stock_ledger.js:122 +msgid "Enable Serial / Batch Bundle" msgstr "" #. Label of the enable_stock_reservation (Check) field in DocType 'Stock @@ -18012,7 +18032,7 @@ msgstr "" msgid "Enabling this will allow creation of multi-currency invoices against single party account in company currency" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:11 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:19 msgid "Enabling this will change the way how cancelled transactions are handled." msgstr "" @@ -18046,7 +18066,7 @@ msgstr "" #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:64 #: erpnext/accounts/report/financial_ratios/financial_ratios.js:25 #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:89 -#: erpnext/public/js/financial_statements.js:427 +#: erpnext/public/js/financial_statements.js:430 msgid "End Year" msgstr "" @@ -18099,7 +18119,7 @@ msgstr "" msgid "Enter API key in Google Settings." msgstr "" -#: erpnext/public/js/print.js:64 +#: erpnext/public/js/print.js:67 msgid "Enter Company Details" msgstr "" @@ -18141,7 +18161,7 @@ msgstr "" msgid "Enter amount to be redeemed." msgstr "" -#: erpnext/stock/doctype/item/item.js:1059 +#: erpnext/stock/doctype/item/item.js:1140 msgid "Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field." msgstr "" @@ -18188,7 +18208,7 @@ msgstr "" msgid "Enter the name of the bank or lending institution before submitting." msgstr "" -#: erpnext/stock/doctype/item/item.js:1085 +#: erpnext/stock/doctype/item/item.js:1166 msgid "Enter the opening stock units." msgstr "" @@ -18196,7 +18216,7 @@ msgstr "" msgid "Enter the quantity of the Item that will be manufactured from this Bill of Materials." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1218 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1229 msgid "Enter the quantity to manufacture. Raw material Items will be fetched only when this is set." msgstr "" @@ -18342,7 +18362,7 @@ msgstr "" msgid "Example URL" msgstr "" -#: erpnext/stock/doctype/item/item.py:1012 +#: erpnext/stock/doctype/item/item.py:1025 msgid "Example of a linked document: {0}" msgstr "" @@ -18358,7 +18378,7 @@ msgstr "" msgid "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings." msgstr "" -#: erpnext/stock/stock_ledger.py:2321 +#: erpnext/stock/stock_ledger.py:2331 msgid "Example: Serial No {0} reserved in {1}." msgstr "" @@ -18517,7 +18537,7 @@ msgstr "" msgid "Excise Entry" msgstr "" -#: erpnext/stock/doctype/stock_entry/stock_entry.js:1501 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:1493 msgid "Excise Invoice" msgstr "" @@ -18636,7 +18656,7 @@ msgstr "" msgid "Expected Delivery Date" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:415 +#: erpnext/selling/doctype/sales_order/sales_order.py:416 msgid "Expected Delivery Date should be after Sales Order Date" msgstr "" @@ -18718,7 +18738,7 @@ msgstr "" msgid "Expense" msgstr "" -#: erpnext/controllers/stock_controller.py:941 +#: erpnext/controllers/stock_controller.py:946 msgid "Expense / Difference account ({0}) must be a 'Profit or Loss' account" msgstr "" @@ -18766,7 +18786,7 @@ msgstr "" msgid "Expense Account" msgstr "" -#: erpnext/controllers/stock_controller.py:921 +#: erpnext/controllers/stock_controller.py:926 msgid "Expense Account Missing" msgstr "" @@ -19007,7 +19027,7 @@ msgstr "" msgid "Failed to setup defaults" msgstr "" -#: erpnext/setup/doctype/company/company.py:871 +#: erpnext/setup/doctype/company/company.py:856 msgid "Failed to setup defaults for country {0}. Please contact support." msgstr "" @@ -19113,7 +19133,7 @@ msgid "Fetch Value From" msgstr "" #: erpnext/stock/doctype/material_request/material_request.js:372 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:819 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:811 msgid "Fetch exploded BOM (including sub-assemblies)" msgstr "" @@ -19134,7 +19154,7 @@ msgid "Fetching Sales Orders..." msgstr "" #: erpnext/accounts/doctype/dunning/dunning.js:135 -#: erpnext/public/js/controllers/transaction.js:1597 +#: erpnext/public/js/controllers/transaction.js:1598 msgid "Fetching exchange rates ..." msgstr "" @@ -19183,7 +19203,7 @@ msgstr "" #: erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js:16 #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:16 -#: erpnext/public/js/financial_statements.js:379 +#: erpnext/public/js/financial_statements.js:382 msgid "Filter Based On" msgstr "" @@ -19289,7 +19309,7 @@ msgstr "" #: erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json #: erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:48 -#: erpnext/public/js/financial_statements.js:373 +#: erpnext/public/js/financial_statements.js:376 #: erpnext/workspace_sidebar/accounts_setup.json msgid "Finance Book" msgstr "" @@ -19360,7 +19380,7 @@ msgstr "" #. Label of a Card Break in the Financial Reports Workspace #: erpnext/accounts/workspace/financial_reports/financial_reports.json -#: erpnext/public/js/financial_statements.js:309 +#: erpnext/public/js/financial_statements.js:312 msgid "Financial Statements" msgstr "" @@ -19374,9 +19394,9 @@ msgstr "" msgid "Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing) " msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:884 -#: erpnext/manufacturing/doctype/work_order/work_order.js:899 -#: erpnext/manufacturing/doctype/work_order/work_order.js:908 +#: erpnext/manufacturing/doctype/work_order/work_order.js:895 +#: erpnext/manufacturing/doctype/work_order/work_order.js:910 +#: erpnext/manufacturing/doctype/work_order/work_order.js:919 msgid "Finish" msgstr "" @@ -19392,7 +19412,7 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json #: erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py:43 #: erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py:147 -#: erpnext/selling/doctype/sales_order/sales_order.js:831 +#: erpnext/selling/doctype/sales_order/sales_order.js:832 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.json msgid "Finished Good" @@ -19486,6 +19506,7 @@ msgstr "" msgid "Finished Good {0} must be a sub-contracted item." msgstr "" +#: erpnext/selling/doctype/sales_order/sales_order.js:1419 #: erpnext/setup/doctype/company/company.py:386 msgid "Finished Goods" msgstr "" @@ -19531,7 +19552,7 @@ msgstr "" msgid "Finished Item {0} does not match with Work Order {1}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:584 +#: erpnext/selling/doctype/sales_order/sales_order.js:585 msgid "First Delivery Date" msgstr "" @@ -19832,7 +19853,7 @@ msgstr "" msgid "For Item" msgstr "" -#: erpnext/controllers/stock_controller.py:1600 +#: erpnext/controllers/stock_controller.py:1605 msgid "For Item {0} cannot be received more than {1} qty against the {2} {3}" msgstr "" @@ -19891,7 +19912,7 @@ msgstr "" #: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json #: erpnext/manufacturing/doctype/production_plan/production_plan.js:471 #: erpnext/manufacturing/doctype/production_plan/production_plan.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1414 +#: erpnext/selling/doctype/sales_order/sales_order.js:1432 #: erpnext/stock/doctype/material_request/material_request.js:361 #: erpnext/templates/form_grid/material_request_grid.html:36 msgid "For Warehouse" @@ -19901,11 +19922,11 @@ msgstr "" msgid "For Work Order" msgstr "" -#: erpnext/controllers/status_updater.py:282 +#: erpnext/controllers/status_updater.py:288 msgid "For an item {0}, quantity must be negative number" msgstr "" -#: erpnext/controllers/status_updater.py:279 +#: erpnext/controllers/status_updater.py:285 msgid "For an item {0}, quantity must be positive number" msgstr "" @@ -19935,7 +19956,7 @@ msgstr "" msgid "For item {0}, only {1} asset have been created or linked to {2}. Please create or link {3} more asset with the respective document." msgstr "" -#: erpnext/controllers/status_updater.py:287 +#: erpnext/controllers/status_updater.py:298 msgid "For item {0}, rate must be a positive number. To Allow negative rates, enable {1} in {2}" msgstr "" @@ -19943,7 +19964,7 @@ msgstr "" msgid "For operation {0} at row {1}, please add raw materials or set a BOM against it." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2624 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2629 msgid "For operation {0}: Quantity ({1}) can not be greater than pending quantity({2})" msgstr "" @@ -19997,12 +20018,12 @@ msgstr "" msgid "For the item {0}, the consumed quantity should be {1} according to the BOM {2}." msgstr "" -#: erpnext/public/js/controllers/transaction.js:1407 +#: erpnext/public/js/controllers/transaction.js:1408 msgctxt "Clear payment terms template and/or payment schedule when due date is changed" msgid "For the new {0} to take effect, would you like to clear the current {1}?" msgstr "" -#: erpnext/controllers/stock_controller.py:442 +#: erpnext/controllers/stock_controller.py:447 msgid "For the {0}, no stock is available for the return in the warehouse {1}." msgstr "" @@ -20074,7 +20095,7 @@ msgstr "" msgid "Forum URL" msgstr "" -#: erpnext/setup/install.py:201 +#: erpnext/setup/install.py:209 msgid "Frappe School" msgstr "" @@ -20844,7 +20865,7 @@ msgstr "" msgid "Get Customer Group Details" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:615 +#: erpnext/selling/doctype/sales_order/sales_order.js:616 msgid "Get Delivery Schedule" msgstr "" @@ -20900,8 +20921,8 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.json #: erpnext/public/js/controllers/buying.js:327 #: erpnext/selling/doctype/quotation/quotation.js:183 -#: erpnext/selling/doctype/sales_order/sales_order.js:210 -#: erpnext/selling/doctype/sales_order/sales_order.js:1207 +#: erpnext/selling/doctype/sales_order/sales_order.js:211 +#: erpnext/selling/doctype/sales_order/sales_order.js:1216 #: erpnext/stock/doctype/delivery_note/delivery_note.js:187 #: erpnext/stock/doctype/delivery_note/delivery_note.js:239 #: erpnext/stock/doctype/material_request/material_request.js:141 @@ -20912,7 +20933,7 @@ msgstr "" #: erpnext/stock/doctype/stock_entry/stock_entry.js:486 #: erpnext/stock/doctype/stock_entry/stock_entry.js:519 #: erpnext/stock/doctype/stock_entry/stock_entry.js:610 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:786 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:778 #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js:165 msgid "Get Items From" msgstr "" @@ -20928,8 +20949,8 @@ msgid "Get Items for Purchase Only" msgstr "" #: erpnext/stock/doctype/material_request/material_request.js:346 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:822 -#: erpnext/stock/doctype/stock_entry/stock_entry.js:835 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:814 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:827 msgid "Get Items from BOM" msgstr "" @@ -21497,7 +21518,7 @@ msgstr "" #: erpnext/accounts/report/budget_variance_report/budget_variance_report.js:64 #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:77 #: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js:59 -#: erpnext/public/js/financial_statements.js:440 +#: erpnext/public/js/financial_statements.js:443 #: erpnext/public/js/purchase_trends_filters.js:21 #: erpnext/public/js/sales_trends_filters.js:13 #: erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js:34 @@ -21706,7 +21727,7 @@ msgstr "" msgid "Here are the error logs for the aforementioned failed depreciation entries: {0}" msgstr "" -#: erpnext/stock/stock_ledger.py:2043 +#: erpnext/stock/stock_ledger.py:2053 msgid "Here are the options to proceed:" msgstr "" @@ -21801,7 +21822,7 @@ msgid "History In Company" msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.js:338 -#: erpnext/selling/doctype/sales_order/sales_order.js:996 +#: erpnext/selling/doctype/sales_order/sales_order.js:997 msgid "Hold" msgstr "" @@ -22291,7 +22312,7 @@ msgstr "" msgid "If no taxes are set, and Taxes and Charges Template is selected, the system will automatically apply the taxes from the chosen template." msgstr "" -#: erpnext/stock/stock_ledger.py:2053 +#: erpnext/stock/stock_ledger.py:2063 msgid "If not, you can Cancel / Submit this entry" msgstr "" @@ -22319,7 +22340,7 @@ msgstr "" msgid "If set, the system does not use the user's Email or the standard outgoing Email account for sending request for quotations." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1251 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1262 msgid "If the BOM results in Scrap material, the Scrap Warehouse needs to be selected." msgstr "" @@ -22328,7 +22349,7 @@ msgstr "" msgid "If the account is frozen, entries are allowed to restricted users." msgstr "" -#: erpnext/stock/stock_ledger.py:2046 +#: erpnext/stock/stock_ledger.py:2056 msgid "If the item is transacting as a Zero Valuation Rate item in this entry, please enable 'Allow Zero Valuation Rate' in the {0} Item table." msgstr "" @@ -22338,7 +22359,7 @@ msgstr "" msgid "If the reorder check is set at the Group warehouse level, the available quantity becomes the sum of the projected quantities of all its child warehouses." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1270 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1281 msgid "If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed." msgstr "" @@ -22415,7 +22436,7 @@ msgstr "" msgid "If yes, then this warehouse will be used to store rejected materials" msgstr "" -#: erpnext/stock/doctype/item/item.js:1071 +#: erpnext/stock/doctype/item/item.js:1152 msgid "If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item." msgstr "" @@ -22505,7 +22526,7 @@ msgstr "" msgid "Ignore Exchange Rate Revaluation and Gain / Loss Journals" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1397 +#: erpnext/selling/doctype/sales_order/sales_order.js:1414 msgid "Ignore Existing Ordered Qty" msgstr "" @@ -22848,7 +22869,7 @@ msgstr "" msgid "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent" msgstr "" -#: erpnext/stock/doctype/item/item.js:1104 +#: erpnext/stock/doctype/item/item.js:1185 msgid "In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc." msgstr "" @@ -22960,7 +22981,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/buying/doctype/purchase_order_item/purchase_order_item.json #: erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1393 +#: erpnext/selling/doctype/sales_order/sales_order.js:1410 #: erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json #: erpnext/subcontracting/doctype/subcontracting_inward_order_item/subcontracting_inward_order_item.json #: erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json @@ -23156,7 +23177,7 @@ msgstr "" msgid "Incoming call from {0}" msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:68 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:74 msgid "Incompatible Setting Detected" msgstr "" @@ -23383,14 +23404,14 @@ msgstr "" msgid "Inspected By" msgstr "" -#: erpnext/controllers/stock_controller.py:1494 +#: erpnext/controllers/stock_controller.py:1499 #: erpnext/manufacturing/doctype/job_card/job_card.py:832 msgid "Inspection Rejected" msgstr "" #. Label of the inspection_required (Check) field in DocType 'Stock Entry' -#: erpnext/controllers/stock_controller.py:1464 -#: erpnext/controllers/stock_controller.py:1466 +#: erpnext/controllers/stock_controller.py:1469 +#: erpnext/controllers/stock_controller.py:1471 #: erpnext/stock/doctype/stock_entry/stock_entry.json msgid "Inspection Required" msgstr "" @@ -23407,7 +23428,7 @@ msgstr "" msgid "Inspection Required before Purchase" msgstr "" -#: erpnext/controllers/stock_controller.py:1479 +#: erpnext/controllers/stock_controller.py:1484 #: erpnext/manufacturing/doctype/job_card/job_card.py:813 msgid "Inspection Submission" msgstr "" @@ -23479,6 +23500,9 @@ msgstr "" #: erpnext/controllers/accounts_controller.py:3850 #: erpnext/controllers/accounts_controller.py:3874 +#: erpnext/controllers/accounts_controller.py:4394 +#: erpnext/controllers/accounts_controller.py:4400 +#: erpnext/controllers/accounts_controller.py:4422 msgid "Insufficient Permissions" msgstr "" @@ -23487,12 +23511,12 @@ msgstr "" #: erpnext/stock/doctype/pick_list/pick_list.py:163 #: erpnext/stock/doctype/pick_list/pick_list.py:1088 #: erpnext/stock/doctype/stock_entry/stock_entry.py:1048 -#: erpnext/stock/serial_batch_bundle.py:1205 erpnext/stock/stock_ledger.py:1734 -#: erpnext/stock/stock_ledger.py:2212 +#: erpnext/stock/serial_batch_bundle.py:1205 erpnext/stock/stock_ledger.py:1744 +#: erpnext/stock/stock_ledger.py:2222 msgid "Insufficient Stock" msgstr "" -#: erpnext/stock/stock_ledger.py:2227 +#: erpnext/stock/stock_ledger.py:2237 msgid "Insufficient Stock for Batch" msgstr "" @@ -23585,7 +23609,7 @@ msgstr "" msgid "Inter Company Order Reference" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1144 +#: erpnext/selling/doctype/sales_order/sales_order.js:1153 msgid "Inter Company Purchase Order" msgstr "" @@ -23651,7 +23675,7 @@ msgstr "" msgid "Internal Customer for company {0} already exists" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1143 +#: erpnext/selling/doctype/sales_order/sales_order.js:1152 msgid "Internal Purchase Order" msgstr "" @@ -23707,7 +23731,7 @@ msgstr "" msgid "Internal Work History" msgstr "" -#: erpnext/controllers/stock_controller.py:1561 +#: erpnext/controllers/stock_controller.py:1566 msgid "Internal transfers can only be done in company's default currency" msgstr "" @@ -23757,7 +23781,7 @@ msgstr "" msgid "Invalid Barcode. There is no Item attached to this barcode." msgstr "" -#: erpnext/public/js/controllers/transaction.js:3216 +#: erpnext/public/js/controllers/transaction.js:3217 msgid "Invalid Blanket Order for the selected Customer and Item" msgstr "" @@ -23787,7 +23811,7 @@ msgstr "" msgid "Invalid Customer Group" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:417 +#: erpnext/selling/doctype/sales_order/sales_order.py:418 msgid "Invalid Delivery Date" msgstr "" @@ -23795,7 +23819,7 @@ msgstr "" msgid "Invalid Discount" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:844 +#: erpnext/controllers/taxes_and_totals.py:843 msgid "Invalid Discount Amount" msgstr "" @@ -23821,7 +23845,7 @@ msgstr "" msgid "Invalid Item" msgstr "" -#: erpnext/stock/doctype/item/item.py:1427 +#: erpnext/stock/doctype/item/item.py:1440 msgid "Invalid Item Defaults" msgstr "" @@ -23986,7 +24010,7 @@ msgid "Invalid {0}: {1}" msgstr "" #. Label of the inventory_section (Tab Break) field in DocType 'Item' -#: erpnext/setup/install.py:359 erpnext/stock/doctype/item/item.json +#: erpnext/setup/install.py:368 erpnext/stock/doctype/item/item.json msgid "Inventory" msgstr "" @@ -24596,11 +24620,14 @@ msgstr "" #. Label of the is_phantom_bom (Check) field in DocType 'BOM' #: erpnext/manufacturing/doctype/bom/bom.json +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:68 msgid "Is Phantom BOM" msgstr "" +#. Label of the is_phantom (Check) field in DocType 'BOM Creator' #. Label of the is_phantom_item (Check) field in DocType 'BOM Creator Item' #. Label of the is_phantom_item (Check) field in DocType 'BOM Item' +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.json #: erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json #: erpnext/manufacturing/doctype/bom_item/bom_item.json #: erpnext/manufacturing/report/bom_explorer/bom_explorer.py:68 @@ -24886,7 +24913,7 @@ msgstr "" msgid "It can take upto few hours for accurate stock values to be visible after merging items." msgstr "" -#: erpnext/public/js/controllers/transaction.js:2617 +#: erpnext/public/js/controllers/transaction.js:2618 msgid "It is needed to fetch Item Details." msgstr "" @@ -24945,7 +24972,7 @@ msgstr "" #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js:33 #: erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py:204 #: erpnext/buying/workspace/buying/buying.json -#: erpnext/controllers/taxes_and_totals.py:1253 +#: erpnext/controllers/taxes_and_totals.py:1252 #: erpnext/manufacturing/doctype/blanket_order/blanket_order.json #: erpnext/manufacturing/doctype/bom/bom.js:1085 #: erpnext/manufacturing/doctype/bom/bom.json @@ -24966,7 +24993,7 @@ msgstr "" #: erpnext/public/js/stock_analytics.js:92 #: erpnext/selling/doctype/party_specific_item/party_specific_item.json #: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1638 +#: erpnext/selling/doctype/sales_order/sales_order.js:1656 #: erpnext/selling/page/point_of_sale/pos_item_cart.js:50 #: erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js:14 #: erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js:36 @@ -25241,14 +25268,14 @@ msgstr "" #: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js:75 #: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py:166 #: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.js:30 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:951 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:987 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:953 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:989 #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:364 #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js:27 #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:86 #: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:119 #: erpnext/projects/doctype/timesheet/timesheet.js:214 -#: erpnext/public/js/controllers/transaction.js:2911 +#: erpnext/public/js/controllers/transaction.js:2912 #: erpnext/public/js/stock_reservation.js:112 #: erpnext/public/js/stock_reservation.js:318 erpnext/public/js/utils.js:579 #: erpnext/public/js/utils.js:736 @@ -25257,10 +25284,10 @@ msgstr "" #: erpnext/selling/doctype/installation_note_item/installation_note_item.json #: erpnext/selling/doctype/quotation/quotation.js:298 #: erpnext/selling/doctype/quotation_item/quotation_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:375 -#: erpnext/selling/doctype/sales_order/sales_order.js:483 -#: erpnext/selling/doctype/sales_order/sales_order.js:1252 -#: erpnext/selling/doctype/sales_order/sales_order.js:1407 +#: erpnext/selling/doctype/sales_order/sales_order.js:376 +#: erpnext/selling/doctype/sales_order/sales_order.js:484 +#: erpnext/selling/doctype/sales_order/sales_order.js:1261 +#: erpnext/selling/doctype/sales_order/sales_order.js:1425 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py:29 #: erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py:27 @@ -25588,6 +25615,7 @@ msgstr "" #: erpnext/stock/doctype/item_variant_settings/item_variant_settings.json #: erpnext/stock/doctype/packing_slip/packing_slip.json #: erpnext/stock/doctype/serial_no/serial_no.json +#: erpnext/stock/doctype/uom_category/uom_category.json #: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse_type/warehouse_type.json msgid "Item Manager" @@ -25723,16 +25751,16 @@ msgstr "" #: erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py:109 #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:106 #: erpnext/manufacturing/report/job_card_summary/job_card_summary.py:158 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:958 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:994 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:960 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:996 #: erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py:153 #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:371 #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:92 #: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py:138 -#: erpnext/public/js/controllers/transaction.js:2917 +#: erpnext/public/js/controllers/transaction.js:2918 #: erpnext/public/js/utils.js:831 #: erpnext/selling/doctype/quotation_item/quotation_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1259 +#: erpnext/selling/doctype/sales_order/sales_order.js:1268 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py:35 #: erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py:33 @@ -26023,7 +26051,7 @@ msgstr "" #. Name of a DocType #. Label of a Link in the Stock Workspace #. Label of a Workspace Sidebar Item -#: erpnext/stock/doctype/item/item.js:169 +#: erpnext/stock/doctype/item/item.js:193 #: erpnext/stock/doctype/item_variant_settings/item_variant_settings.json #: erpnext/stock/workspace/stock/stock.json #: erpnext/workspace_sidebar/erpnext_settings.json @@ -26031,11 +26059,11 @@ msgstr "" msgid "Item Variant Settings" msgstr "" -#: erpnext/stock/doctype/item/item.js:920 +#: erpnext/stock/doctype/item/item.js:1001 msgid "Item Variant {0} already exists with same attributes" msgstr "" -#: erpnext/stock/doctype/item/item.py:790 +#: erpnext/stock/doctype/item/item.py:803 msgid "Item Variants updated" msgstr "" @@ -26131,7 +26159,7 @@ msgstr "" msgid "Item for row {0} does not match Material Request" msgstr "" -#: erpnext/stock/doctype/item/item.py:807 +#: erpnext/stock/doctype/item/item.py:820 msgid "Item has variants." msgstr "" @@ -26148,7 +26176,7 @@ msgid "Item must be added using 'Get Items from Purchase Receipts' button" msgstr "" #: erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.py:42 -#: erpnext/selling/doctype/sales_order/sales_order.js:1645 +#: erpnext/selling/doctype/sales_order/sales_order.js:1663 msgid "Item name" msgstr "" @@ -26183,15 +26211,15 @@ msgstr "" msgid "Item valuation reposting in progress. Report might show incorrect item valuation." msgstr "" -#: erpnext/stock/doctype/item/item.py:964 +#: erpnext/stock/doctype/item/item.py:977 msgid "Item variant {0} exists with same attributes" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:97 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:99 msgid "Item {0} added multiple times under the same parent item {1} at rows {2} and {3}" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:108 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:119 msgid "Item {0} cannot be added as a sub-assembly of itself" msgstr "" @@ -26208,7 +26236,7 @@ msgstr "" msgid "Item {0} does not exist in the system or has expired" msgstr "" -#: erpnext/controllers/stock_controller.py:556 +#: erpnext/controllers/stock_controller.py:561 msgid "Item {0} does not exist." msgstr "" @@ -26224,11 +26252,11 @@ msgstr "" msgid "Item {0} has been disabled" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:791 +#: erpnext/selling/doctype/sales_order/sales_order.py:792 msgid "Item {0} has no Serial No. Only serialized items can have delivery based on Serial No" msgstr "" -#: erpnext/stock/doctype/item/item.py:1143 +#: erpnext/stock/doctype/item/item.py:1156 msgid "Item {0} has reached its end of life on {1}" msgstr "" @@ -26240,11 +26268,11 @@ msgstr "" msgid "Item {0} is already reserved/delivered against Sales Order {1}." msgstr "" -#: erpnext/stock/doctype/item/item.py:1163 +#: erpnext/stock/doctype/item/item.py:1176 msgid "Item {0} is cancelled" msgstr "" -#: erpnext/stock/doctype/item/item.py:1147 +#: erpnext/stock/doctype/item/item.py:1160 msgid "Item {0} is disabled" msgstr "" @@ -26252,7 +26280,7 @@ msgstr "" msgid "Item {0} is not a serialized Item" msgstr "" -#: erpnext/stock/doctype/item/item.py:1155 +#: erpnext/stock/doctype/item/item.py:1168 msgid "Item {0} is not a stock Item" msgstr "" @@ -26367,7 +26395,7 @@ msgid "Items Filter" msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.py:1683 -#: erpnext/selling/doctype/sales_order/sales_order.js:1683 +#: erpnext/selling/doctype/sales_order/sales_order.js:1701 msgid "Items Required" msgstr "" @@ -26398,7 +26426,7 @@ msgstr "" msgid "Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1443 +#: erpnext/selling/doctype/sales_order/sales_order.js:1461 msgid "Items for Raw Material Request" msgstr "" @@ -26426,7 +26454,7 @@ msgid "Items to Order and Receive" msgstr "" #: erpnext/public/js/stock_reservation.js:72 -#: erpnext/selling/doctype/sales_order/sales_order.js:334 +#: erpnext/selling/doctype/sales_order/sales_order.js:335 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:226 msgid "Items to Reserve" msgstr "" @@ -26436,7 +26464,7 @@ msgstr "" msgid "Items under this warehouse will be suggested" msgstr "" -#: erpnext/controllers/stock_controller.py:166 +#: erpnext/controllers/stock_controller.py:171 msgid "Items {0} do not exist in the Item master." msgstr "" @@ -26483,7 +26511,7 @@ msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.py:997 #: erpnext/manufacturing/doctype/operation/operation.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:400 +#: erpnext/manufacturing/doctype/work_order/work_order.js:410 #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js:29 #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:86 @@ -26621,7 +26649,7 @@ msgstr "" msgid "Job Worker Warehouse" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2678 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2684 msgid "Job card {0} created" msgstr "" @@ -27139,7 +27167,7 @@ msgstr "" #. Label of the lead_time (Float) field in DocType 'Work Order' #: erpnext/manufacturing/doctype/master_production_schedule_item/master_production_schedule_item.json #: erpnext/manufacturing/doctype/work_order/work_order.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1072 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1074 #: erpnext/stock/doctype/item/item_dashboard.py:35 msgid "Lead Time" msgstr "" @@ -27363,7 +27391,7 @@ msgstr "" msgid "License Plate" msgstr "" -#: erpnext/controllers/status_updater.py:474 +#: erpnext/controllers/status_updater.py:489 msgid "Limit Crossed" msgstr "" @@ -27445,7 +27473,7 @@ msgstr "" msgid "Linked Location" msgstr "" -#: erpnext/stock/doctype/item/item.py:1016 +#: erpnext/stock/doctype/item/item.py:1029 msgid "Linked with submitted documents" msgstr "" @@ -27807,10 +27835,10 @@ msgstr "" msgid "Machine operator errors" msgstr "" +#: erpnext/setup/doctype/company/company.py:720 #: erpnext/setup/doctype/company/company.py:735 -#: erpnext/setup/doctype/company/company.py:750 -#: erpnext/setup/doctype/company/company.py:751 -#: erpnext/setup/doctype/company/company.py:752 +#: erpnext/setup/doctype/company/company.py:736 +#: erpnext/setup/doctype/company/company.py:737 msgid "Main" msgstr "" @@ -27923,7 +27951,7 @@ msgstr "" #: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js:81 #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1121 +#: erpnext/selling/doctype/sales_order/sales_order.js:1130 #: erpnext/support/workspace/support/support.json #: erpnext/workspace_sidebar/crm.json erpnext/workspace_sidebar/support.json msgid "Maintenance Schedule" @@ -28030,7 +28058,7 @@ msgstr "" #: erpnext/crm/workspace/crm/crm.json #: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js:87 #: erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1114 +#: erpnext/selling/doctype/sales_order/sales_order.js:1123 #: erpnext/support/doctype/warranty_claim/warranty_claim.js:47 #: erpnext/support/workspace/support/support.json #: erpnext/workspace_sidebar/crm.json erpnext/workspace_sidebar/support.json @@ -28054,8 +28082,8 @@ msgstr "" #. Label of the make (Data) field in DocType 'Vehicle' #: erpnext/accounts/doctype/journal_entry/journal_entry.js:123 #: erpnext/manufacturing/doctype/job_card/job_card.js:544 -#: erpnext/manufacturing/doctype/work_order/work_order.js:839 -#: erpnext/manufacturing/doctype/work_order/work_order.js:873 +#: erpnext/manufacturing/doctype/work_order/work_order.js:850 +#: erpnext/manufacturing/doctype/work_order/work_order.js:884 #: erpnext/setup/doctype/vehicle/vehicle.json msgid "Make" msgstr "" @@ -28075,7 +28103,7 @@ msgstr "" msgid "Make Difference Entry" msgstr "" -#: erpnext/stock/doctype/item/item.js:609 +#: erpnext/stock/doctype/item/item.js:690 msgid "Make Lead Time" msgstr "" @@ -28134,11 +28162,11 @@ msgstr "" msgid "Make project from a template." msgstr "" -#: erpnext/stock/doctype/item/item.js:717 +#: erpnext/stock/doctype/item/item.js:798 msgid "Make {0} Variant" msgstr "" -#: erpnext/stock/doctype/item/item.js:719 +#: erpnext/stock/doctype/item/item.js:800 msgid "Make {0} Variants" msgstr "" @@ -28386,7 +28414,7 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json #: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:29 -#: erpnext/setup/doctype/company/company.json erpnext/setup/install.py:364 +#: erpnext/setup/doctype/company/company.json erpnext/setup/install.py:373 #: erpnext/setup/setup_wizard/data/industry_type.txt:31 #: erpnext/stock/doctype/batch/batch.json erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/item_lead_time/item_lead_time.json @@ -28614,7 +28642,6 @@ msgstr "" #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:112 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:191 -#: erpnext/setup/doctype/company/company.py:691 msgid "Marketing Expenses" msgstr "" @@ -28654,7 +28681,7 @@ msgstr "" msgid "Material" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:864 +#: erpnext/manufacturing/doctype/work_order/work_order.js:875 msgid "Material Consumption" msgstr "" @@ -28749,7 +28776,7 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan_material_request/production_plan_material_request.json #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json #: erpnext/manufacturing/doctype/work_order/work_order.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1085 +#: erpnext/selling/doctype/sales_order/sales_order.js:1094 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py:36 #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -28839,11 +28866,11 @@ msgstr "" msgid "Material Request Type" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:1122 +#: erpnext/selling/doctype/sales_order/sales_order.py:1123 msgid "Material Request already created for the ordered quantity" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:1832 +#: erpnext/selling/doctype/sales_order/sales_order.py:1863 msgid "Material Request not created, as quantity for Raw Materials already available." msgstr "" @@ -28861,7 +28888,7 @@ msgstr "" msgid "Material Request {0} is cancelled or stopped" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1459 +#: erpnext/selling/doctype/sales_order/sales_order.js:1477 msgid "Material Request {0} submitted." msgstr "" @@ -29050,9 +29077,9 @@ msgstr "" msgid "Max discount allowed for item: {0} is {1}%" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1040 -#: erpnext/manufacturing/doctype/work_order/work_order.js:1047 -#: erpnext/manufacturing/doctype/work_order/work_order.js:1070 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1051 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1058 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1081 #: erpnext/stock/doctype/pick_list/pick_list.js:200 #: erpnext/stock/doctype/stock_entry/stock_entry.js:382 msgid "Max: {0}" @@ -29139,7 +29166,7 @@ msgstr "" msgid "Megawatt" msgstr "" -#: erpnext/stock/stock_ledger.py:2059 +#: erpnext/stock/stock_ledger.py:2069 msgid "Mention Valuation Rate in the Item master." msgstr "" @@ -29227,7 +29254,7 @@ msgstr "" msgid "Messages greater than 160 characters will be split into multiple messages" msgstr "" -#: erpnext/setup/install.py:128 +#: erpnext/setup/install.py:136 msgid "Messaging CRM Campaign" msgstr "" @@ -29399,7 +29426,7 @@ msgid "Min Grade" msgstr "" #. Label of the min_order_qty (Float) field in DocType 'Material Request Item' -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1062 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1064 #: erpnext/stock/doctype/material_request_item/material_request_item.json msgid "Min Order Qty" msgstr "" @@ -29426,7 +29453,7 @@ msgstr "" msgid "Min Qty should be greater than Recurse Over Qty" msgstr "" -#: erpnext/stock/doctype/item/item.js:871 +#: erpnext/stock/doctype/item/item.js:952 msgid "Min Value: {0}, Max Value: {1}, in Increments of: {2}" msgstr "" @@ -29577,7 +29604,7 @@ msgid "Missing required filter: {0}" msgstr "" #: erpnext/manufacturing/doctype/bom/bom.py:1218 -#: erpnext/manufacturing/doctype/work_order/work_order.py:1477 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1482 msgid "Missing value" msgstr "" @@ -29830,7 +29857,7 @@ msgstr "" msgid "Multiple Tier Program" msgstr "" -#: erpnext/stock/doctype/item/item.js:193 +#: erpnext/stock/doctype/item/item.js:217 msgid "Multiple Variants" msgstr "" @@ -29851,7 +29878,7 @@ msgid "Music" msgstr "" #. Label of the must_be_whole_number (Check) field in DocType 'UOM' -#: erpnext/manufacturing/doctype/work_order/work_order.py:1424 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1429 #: erpnext/setup/doctype/uom/uom.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:267 #: erpnext/utilities/transaction_base.py:568 @@ -30328,11 +30355,11 @@ msgstr "" msgid "New Balance In Base Currency" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:167 +#: erpnext/stock/doctype/batch/batch.js:168 msgid "New Batch ID (Optional)" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:161 +#: erpnext/stock/doctype/batch/batch.js:162 msgid "New Batch Qty" msgstr "" @@ -30563,11 +30590,11 @@ msgstr "" msgid "No Items selected for transfer." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1233 +#: erpnext/selling/doctype/sales_order/sales_order.js:1242 msgid "No Items with Bill of Materials to Manufacture or all items already manufactured" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1379 +#: erpnext/selling/doctype/sales_order/sales_order.js:1395 msgid "No Items with Bill of Materials." msgstr "" @@ -30590,7 +30617,7 @@ msgstr "" #: erpnext/accounts/doctype/journal_entry/journal_entry.py:1573 #: erpnext/accounts/doctype/journal_entry/journal_entry.py:1633 #: erpnext/accounts/doctype/journal_entry/journal_entry.py:1647 -#: erpnext/stock/doctype/item/item.py:1388 +#: erpnext/stock/doctype/item/item.py:1401 msgid "No Permission" msgstr "" @@ -30631,7 +30658,7 @@ msgstr "" msgid "No Tax withholding account set for Company {0} in Tax Withholding Category {1}." msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:971 +#: erpnext/accounts/report/gross_profit/gross_profit.py:998 msgid "No Terms" msgstr "" @@ -30653,7 +30680,7 @@ msgstr "" msgid "No accounting entries for the following warehouses" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:797 +#: erpnext/selling/doctype/sales_order/sales_order.py:798 msgid "No active BOM found for item {0}. Delivery by Serial No cannot be ensured" msgstr "" @@ -30734,7 +30761,7 @@ msgstr "" msgid "No more children on Right" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:607 +#: erpnext/selling/doctype/sales_order/sales_order.js:608 msgid "No of Deliveries" msgstr "" @@ -30884,6 +30911,10 @@ msgstr "" msgid "No rows with zero document count found" msgstr "" +#: erpnext/stock/doctype/batch/batch.js:76 +msgid "No stock available for this batch." +msgstr "" + #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:806 msgid "No stock ledger entries were created. Please set the quantity or valuation rate for the items properly and try again." msgstr "" @@ -30940,7 +30971,7 @@ msgstr "" msgid "Non Profit" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1639 +#: erpnext/manufacturing/doctype/bom/bom.py:1634 msgid "Non stock items" msgstr "" @@ -30953,6 +30984,11 @@ msgstr "" msgid "Non-Zeros" msgstr "" +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:117 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:113 +msgid "Non-phantom BOM cannot be created for non-stock item {0}." +msgstr "" + #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py:558 msgid "None of the items have any change in quantity or value." msgstr "" @@ -31051,7 +31087,7 @@ msgstr "" msgid "Not in stock" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1301 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1303 msgid "Not permitted to make Purchase Orders" msgstr "" @@ -31360,7 +31396,7 @@ msgstr "" msgid "Oldest Of Invoice Or Advance" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1036 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1038 msgid "On Hand" msgstr "" @@ -31451,7 +31487,7 @@ msgstr "" msgid "Once set, this invoice will be on hold till the set date" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:751 +#: erpnext/manufacturing/doctype/work_order/work_order.js:762 msgid "Once the Work Order is Closed. It can't be resumed." msgstr "" @@ -31901,7 +31937,7 @@ msgstr "" msgid "Operating Cost Per BOM Quantity" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1726 +#: erpnext/manufacturing/doctype/bom/bom.py:1721 msgid "Operating Cost as per Work Order / BOM" msgstr "" @@ -31949,7 +31985,7 @@ msgstr "" msgid "Operation ID" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:336 +#: erpnext/manufacturing/doctype/work_order/work_order.js:346 msgid "Operation Id" msgstr "" @@ -31977,7 +32013,7 @@ msgstr "" msgid "Operation Time" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1483 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1488 msgid "Operation Time must be greater than 0 for Operation {0}" msgstr "" @@ -32000,7 +32036,7 @@ msgstr "" msgid "Operation {0} does not belong to the work order {1}" msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:430 +#: erpnext/manufacturing/doctype/workstation/workstation.py:432 msgid "Operation {0} longer than any available working hours in workstation {1}, break down the operation into multiple operations" msgstr "" @@ -32012,7 +32048,7 @@ msgstr "" #. Label of the operations (Table) field in DocType 'Work Order' #. Label of the operation (Section Break) field in DocType 'Email Digest' #: erpnext/manufacturing/doctype/bom/bom.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:317 +#: erpnext/manufacturing/doctype/work_order/work_order.js:327 #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/setup/doctype/company/company.py:469 #: erpnext/setup/doctype/email_digest/email_digest.json @@ -32187,7 +32223,7 @@ msgstr "" msgid "Optimize Route" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1017 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1028 msgid "Optional. Select a specific manufacture entry to reverse." msgstr "" @@ -32335,7 +32371,7 @@ msgstr "" #: erpnext/buying/doctype/supplier/supplier_dashboard.py:11 #: erpnext/selling/doctype/customer/customer_dashboard.py:20 -#: erpnext/selling/doctype/sales_order/sales_order.py:969 +#: erpnext/selling/doctype/sales_order/sales_order.py:970 #: erpnext/setup/doctype/company/company_dashboard.py:23 msgid "Orders" msgstr "" @@ -32613,11 +32649,11 @@ msgstr "" msgid "Over Picking Allowance" msgstr "" -#: erpnext/controllers/stock_controller.py:1731 +#: erpnext/controllers/stock_controller.py:1736 msgid "Over Receipt" msgstr "" -#: erpnext/controllers/status_updater.py:479 +#: erpnext/controllers/status_updater.py:494 msgid "Over Receipt/Delivery of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -32637,7 +32673,7 @@ msgstr "" msgid "Over Withheld" msgstr "" -#: erpnext/controllers/status_updater.py:481 +#: erpnext/controllers/status_updater.py:496 msgid "Overbilling of {0} {1} ignored for item {2} because you have {3} role." msgstr "" @@ -33108,7 +33144,7 @@ msgstr "" msgid "Packed Items" msgstr "" -#: erpnext/controllers/stock_controller.py:1565 +#: erpnext/controllers/stock_controller.py:1570 msgid "Packed Items cannot be transferred internally" msgstr "" @@ -33393,7 +33429,7 @@ msgstr "" msgid "Parent Row No" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:535 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:548 msgid "Parent Row No not found for {0}" msgstr "" @@ -34010,7 +34046,7 @@ msgstr "" #: erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js:42 #: erpnext/buying/doctype/purchase_order/purchase_order.js:421 #: erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py:24 -#: erpnext/selling/doctype/sales_order/sales_order.js:1166 +#: erpnext/selling/doctype/sales_order/sales_order.js:1175 #: erpnext/selling/doctype/sales_order/sales_order_dashboard.py:31 msgid "Payment" msgstr "" @@ -34362,7 +34398,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:135 #: erpnext/accounts/doctype/sales_invoice/sales_invoice.js:139 #: erpnext/buying/doctype/purchase_order/purchase_order.js:429 -#: erpnext/selling/doctype/sales_order/sales_order.js:1159 +#: erpnext/selling/doctype/sales_order/sales_order.js:1168 #: erpnext/workspace_sidebar/invoicing.json #: erpnext/workspace_sidebar/payments.json msgid "Payment Request" @@ -34428,7 +34464,7 @@ msgstr "" msgid "Payment Schedule based Payment Requests cannot be created because a Payment Entry already exists for this document." msgstr "" -#: erpnext/public/js/controllers/transaction.js:482 +#: erpnext/public/js/controllers/transaction.js:483 msgid "Payment Schedules" msgstr "" @@ -34454,7 +34490,7 @@ msgstr "" #: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1256 #: erpnext/accounts/report/gross_profit/gross_profit.py:449 #: erpnext/accounts/workspace/invoicing/invoicing.json -#: erpnext/public/js/controllers/transaction.js:496 +#: erpnext/public/js/controllers/transaction.js:498 #: erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py:30 #: erpnext/workspace_sidebar/accounts_setup.json msgid "Payment Term" @@ -34701,9 +34737,9 @@ msgstr "" #. Label of the pending_qty (Float) field in DocType 'Production Plan Item' #: erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py:254 #: erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:341 +#: erpnext/manufacturing/doctype/work_order/work_order.js:351 #: erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py:182 -#: erpnext/selling/doctype/sales_order/sales_order.js:1652 +#: erpnext/selling/doctype/sales_order/sales_order.js:1670 #: erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py:45 msgid "Pending Qty" msgstr "" @@ -34983,7 +35019,7 @@ msgstr "" #: erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json #: erpnext/maintenance/doctype/maintenance_schedule_item/maintenance_schedule_item.json #: erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js:54 -#: erpnext/public/js/financial_statements.js:435 +#: erpnext/public/js/financial_statements.js:438 msgid "Periodicity" msgstr "" @@ -35020,6 +35056,11 @@ msgstr "" msgid "Petrol" msgstr "" +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:113 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:110 +msgid "Phantom BOM cannot be created for stock item {0}." +msgstr "" + #: erpnext/manufacturing/doctype/bom/bom_item_preview.html:16 #: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:321 msgid "Phantom Item" @@ -35046,7 +35087,7 @@ msgstr "" #. Label of the phone_no (Data) field in DocType 'Company' #. Label of the phone_no (Data) field in DocType 'Warehouse' -#: erpnext/public/js/print.js:79 erpnext/setup/doctype/company/company.json +#: erpnext/public/js/print.js:82 erpnext/setup/doctype/company/company.json #: erpnext/stock/doctype/warehouse/warehouse.json msgid "Phone No" msgstr "" @@ -35065,7 +35106,7 @@ msgstr "" #. Reservation Entry' #. Label of a Link in the Stock Workspace #. Label of a Workspace Sidebar Item -#: erpnext/selling/doctype/sales_order/sales_order.js:1029 +#: erpnext/selling/doctype/sales_order/sales_order.js:1030 #: erpnext/stock/doctype/delivery_note/delivery_note.js:199 #: erpnext/stock/doctype/material_request/material_request.js:156 #: erpnext/stock/doctype/pick_list/pick_list.json @@ -35312,7 +35353,7 @@ msgstr "" msgid "Planned Operating Cost" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1042 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1044 msgid "Planned Purchase Order" msgstr "" @@ -35322,7 +35363,7 @@ msgstr "" #. Label of the planned_qty (Float) field in DocType 'Bin' #: erpnext/manufacturing/doctype/master_production_schedule_item/master_production_schedule_item.json #: erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1030 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1032 #: erpnext/stock/doctype/bin/bin.json #: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:148 msgid "Planned Qty" @@ -35353,7 +35394,7 @@ msgstr "" msgid "Planned Start Time" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1047 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1049 msgid "Planned Work Order" msgstr "" @@ -35475,7 +35516,7 @@ msgstr "" msgid "Please add {1} role to user {0}." msgstr "" -#: erpnext/controllers/stock_controller.py:1742 +#: erpnext/controllers/stock_controller.py:1747 msgid "Please adjust the qty or edit {0} to proceed." msgstr "" @@ -35514,7 +35555,7 @@ msgid "Please check either with operations or FG Based Operating Cost." msgstr "" #: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:150 -msgid "Please check the 'Enable Serial and Batch No for Item' checkbox in the {0} to make Serial and Batch Bundle for the item." +msgid "Please check the 'Activate Serial and Batch No for Item' checkbox in the {0} to make Serial and Batch Bundle for the item." msgstr "" #: erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py:562 @@ -35606,7 +35647,7 @@ msgstr "" msgid "Please enable Use Old Serial / Batch Fields to make_bundle" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:13 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:21 msgid "Please enable only if the understand the effects of enabling this." msgstr "" @@ -35655,7 +35696,7 @@ msgstr "" msgid "Please enter Cost Center" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:421 +#: erpnext/selling/doctype/sales_order/sales_order.py:422 msgid "Please enter Delivery Date" msgstr "" @@ -35672,7 +35713,7 @@ msgstr "" msgid "Please enter Item Code to get Batch Number" msgstr "" -#: erpnext/public/js/controllers/transaction.js:3073 +#: erpnext/public/js/controllers/transaction.js:3074 msgid "Please enter Item Code to get batch no" msgstr "" @@ -35729,15 +35770,15 @@ msgstr "" msgid "Please enter Write Off Account" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:724 +#: erpnext/selling/doctype/sales_order/sales_order.js:725 msgid "Please enter a valid number of deliveries" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:665 +#: erpnext/selling/doctype/sales_order/sales_order.js:666 msgid "Please enter a valid quantity" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:659 +#: erpnext/selling/doctype/sales_order/sales_order.js:660 msgid "Please enter at least one delivery date and quantity" msgstr "" @@ -35777,7 +35818,7 @@ msgstr "" msgid "Please enter the company name to confirm" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:720 +#: erpnext/selling/doctype/sales_order/sales_order.js:721 msgid "Please enter the first delivery date" msgstr "" @@ -35845,7 +35886,7 @@ msgstr "" msgid "Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone." msgstr "" -#: erpnext/stock/doctype/item/item.js:622 +#: erpnext/stock/doctype/item/item.js:703 msgid "Please mention 'Weight UOM' along with Weight." msgstr "" @@ -35887,7 +35928,7 @@ msgstr "" msgid "Please save first" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:866 +#: erpnext/selling/doctype/sales_order/sales_order.js:867 msgid "Please save the Sales Order before adding a delivery schedule." msgstr "" @@ -35895,12 +35936,12 @@ msgstr "" msgid "Please select Template Type to download template" msgstr "" -#: erpnext/controllers/taxes_and_totals.py:850 -#: erpnext/public/js/controllers/taxes_and_totals.js:796 +#: erpnext/controllers/taxes_and_totals.py:849 +#: erpnext/public/js/controllers/taxes_and_totals.js:794 msgid "Please select Apply Discount On" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:1780 +#: erpnext/selling/doctype/sales_order/sales_order.py:1781 msgid "Please select BOM against item {0}" msgstr "" @@ -35992,7 +36033,7 @@ msgstr "" msgid "Please select Price List" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:1782 +#: erpnext/selling/doctype/sales_order/sales_order.py:1783 msgid "Please select Qty against item {0}" msgstr "" @@ -36033,7 +36074,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom/bom.js:727 #: erpnext/manufacturing/doctype/bom/bom.py:278 #: erpnext/public/js/controllers/accounts.js:277 -#: erpnext/public/js/controllers/transaction.js:3372 +#: erpnext/public/js/controllers/transaction.js:3373 msgid "Please select a Company first." msgstr "" @@ -36085,7 +36126,7 @@ msgstr "" msgid "Please select a field to edit from numpad" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:716 +#: erpnext/selling/doctype/sales_order/sales_order.js:717 msgid "Please select a frequency for delivery schedule" msgstr "" @@ -36126,15 +36167,15 @@ msgstr "" msgid "Please select at least one row with difference value" msgstr "" -#: erpnext/public/js/controllers/transaction.js:524 +#: erpnext/public/js/controllers/transaction.js:526 msgid "Please select at least one schedule." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1303 +#: erpnext/selling/doctype/sales_order/sales_order.js:1312 msgid "Please select atleast one item to continue" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:384 +#: erpnext/manufacturing/doctype/work_order/work_order.js:394 msgid "Please select atleast one operation to create Job Card" msgstr "" @@ -36156,13 +36197,13 @@ msgid "Please select item code" msgstr "" #: erpnext/public/js/stock_reservation.js:212 -#: erpnext/selling/doctype/sales_order/sales_order.js:426 +#: erpnext/selling/doctype/sales_order/sales_order.js:427 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:301 msgid "Please select items to reserve." msgstr "" #: erpnext/public/js/stock_reservation.js:290 -#: erpnext/selling/doctype/sales_order/sales_order.js:530 +#: erpnext/selling/doctype/sales_order/sales_order.js:531 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:399 msgid "Please select items to unreserve." msgstr "" @@ -36184,7 +36225,7 @@ msgstr "" msgid "Please select the Multiple Tier Program type for more than one collection rules." msgstr "" -#: erpnext/stock/doctype/item/item.js:341 +#: erpnext/stock/doctype/item/item.js:365 msgid "Please select the Warehouse first" msgstr "" @@ -36292,7 +36333,7 @@ msgstr "" msgid "Please set Fixed Asset Account in {} against {}." msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:281 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:292 msgid "Please set Parent Row No for item {0}" msgstr "" @@ -36351,7 +36392,7 @@ msgstr "" msgid "Please set an Address on the Company '%s'" msgstr "" -#: erpnext/controllers/stock_controller.py:916 +#: erpnext/controllers/stock_controller.py:921 msgid "Please set an Expense Account in the Items table" msgstr "" @@ -36395,11 +36436,11 @@ msgstr "" msgid "Please set default UOM in Stock Settings" msgstr "" -#: erpnext/controllers/stock_controller.py:775 +#: erpnext/controllers/stock_controller.py:780 msgid "Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer" msgstr "" -#: erpnext/controllers/stock_controller.py:231 +#: erpnext/controllers/stock_controller.py:236 msgid "Please set default inventory account for item {0}, or their item group or brand." msgstr "" @@ -36420,7 +36461,7 @@ msgstr "" msgid "Please set opening number of booked depreciations" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2760 +#: erpnext/public/js/controllers/transaction.js:2761 msgid "Please set recurring after saving" msgstr "" @@ -36432,7 +36473,7 @@ msgstr "" msgid "Please set the Default Cost Center in {0} company." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:668 +#: erpnext/manufacturing/doctype/work_order/work_order.js:679 msgid "Please set the Item Code first" msgstr "" @@ -36471,7 +36512,7 @@ msgstr "" msgid "Please set {0} for address {1}" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:234 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:245 msgid "Please set {0} in BOM Creator {1}" msgstr "" @@ -36751,7 +36792,7 @@ msgstr "" msgid "Posting Date cannot be future date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:1112 +#: erpnext/public/js/controllers/transaction.js:1113 msgid "Posting Date will change to today's date as Edit Posting Date and Time is unchecked. Are you sure want to proceed?" msgstr "" @@ -37445,7 +37486,7 @@ msgstr "" msgid "Print Receipt on Order Complete" msgstr "" -#: erpnext/setup/install.py:105 +#: erpnext/setup/install.py:113 msgid "Print UOM after Quantity" msgstr "" @@ -37463,7 +37504,7 @@ msgstr "" msgid "Print settings updated in respective print format" msgstr "" -#: erpnext/setup/install.py:112 +#: erpnext/setup/install.py:120 msgid "Print taxes with zero amount" msgstr "" @@ -37568,7 +37609,7 @@ msgstr "" msgid "Procedure" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:39 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.js:47 msgid "Procedures dropped" msgstr "" @@ -37926,6 +37967,7 @@ msgstr "" #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js:8 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json +#: erpnext/selling/doctype/sales_order/sales_order.js:1066 #: erpnext/stock/doctype/material_request_item/material_request_item.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -38029,7 +38071,7 @@ msgstr "" #: erpnext/accounts/doctype/process_period_closing_voucher_detail/process_period_closing_voucher_detail.json #: erpnext/accounts/workspace/financial_reports/financial_reports.json #: erpnext/accounts/workspace/invoicing/invoicing.json -#: erpnext/public/js/financial_statements.js:327 +#: erpnext/public/js/financial_statements.js:330 #: erpnext/workspace_sidebar/financial_reports.json msgid "Profit and Loss" msgstr "" @@ -38484,7 +38526,7 @@ msgstr "" #: erpnext/accounts/doctype/tax_rule/tax_rule.json #: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json #: erpnext/projects/doctype/project/project_dashboard.py:16 -#: erpnext/setup/doctype/company/company.py:463 erpnext/setup/install.py:378 +#: erpnext/setup/doctype/company/company.py:463 erpnext/setup/install.py:387 #: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/item_lead_time/item_lead_time.json #: erpnext/stock/doctype/item_reorder/item_reorder.json @@ -38687,8 +38729,8 @@ msgstr "" #: erpnext/crm/doctype/contract/contract.json #: erpnext/manufacturing/doctype/blanket_order/blanket_order.js:54 #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:188 -#: erpnext/selling/doctype/sales_order/sales_order.js:1104 +#: erpnext/selling/doctype/sales_order/sales_order.js:189 +#: erpnext/selling/doctype/sales_order/sales_order.js:1113 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/setup/doctype/authorization_rule/authorization_rule.json #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -38789,7 +38831,7 @@ msgstr "" msgid "Purchase Order Trends" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1596 +#: erpnext/selling/doctype/sales_order/sales_order.js:1614 msgid "Purchase Order already created for all Sales Order items" msgstr "" @@ -38797,7 +38839,7 @@ msgstr "" msgid "Purchase Order number required for Item {0}" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1361 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1363 msgid "Purchase Order {0} created" msgstr "" @@ -39045,7 +39087,7 @@ msgstr "" msgid "Purchased" msgstr "" -#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:146 +#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:145 msgid "Purchases" msgstr "" @@ -39159,12 +39201,12 @@ msgstr "" #: erpnext/public/js/stock_reservation.js:336 erpnext/public/js/utils.js:869 #: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json #: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:397 -#: erpnext/selling/doctype/sales_order/sales_order.js:501 -#: erpnext/selling/doctype/sales_order/sales_order.js:591 -#: erpnext/selling/doctype/sales_order/sales_order.js:638 -#: erpnext/selling/doctype/sales_order/sales_order.js:1279 -#: erpnext/selling/doctype/sales_order/sales_order.js:1432 +#: erpnext/selling/doctype/sales_order/sales_order.js:398 +#: erpnext/selling/doctype/sales_order/sales_order.js:502 +#: erpnext/selling/doctype/sales_order/sales_order.js:592 +#: erpnext/selling/doctype/sales_order/sales_order.js:639 +#: erpnext/selling/doctype/sales_order/sales_order.js:1288 +#: erpnext/selling/doctype/sales_order/sales_order.js:1450 #: erpnext/selling/report/sales_order_analysis/sales_order_analysis.py:255 #: erpnext/stock/doctype/landed_cost_item/landed_cost_item.json #: erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json @@ -39274,7 +39316,7 @@ msgstr "" msgid "Qty To Manufacture" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1420 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1425 msgid "Qty To Manufacture ({0}) cannot be a fraction for the UOM {2}. To allow this, disable '{1}' in the UOM {2}." msgstr "" @@ -39329,8 +39371,8 @@ msgstr "" msgid "Qty for which recursion isn't applicable." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1045 -#: erpnext/manufacturing/doctype/work_order/work_order.js:1068 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1056 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1079 msgid "Qty for {0}" msgstr "" @@ -39771,7 +39813,7 @@ msgstr "" #: erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json #: erpnext/manufacturing/doctype/bom/bom.js:493 #: erpnext/manufacturing/doctype/bom/bom.json -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:69 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:76 #: erpnext/manufacturing/doctype/bom_creator/bom_creator.json #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:194 #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:218 @@ -39791,7 +39833,7 @@ msgstr "" #: erpnext/stock/doctype/material_request_item/material_request_item.json #: erpnext/stock/doctype/packing_slip_item/packing_slip_item.json #: erpnext/stock/doctype/pick_list_item/pick_list_item.json -#: erpnext/stock/doctype/stock_entry/stock_entry.js:815 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:807 #: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json #: erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json #: erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py:36 @@ -39906,7 +39948,7 @@ msgstr "" msgid "Quantity must be less than or equal to {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1098 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1109 #: erpnext/stock/doctype/pick_list/pick_list.js:206 msgid "Quantity must not be more than {0}" msgstr "" @@ -39927,15 +39969,15 @@ msgstr "" msgid "Quantity should be greater than 0" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:346 +#: erpnext/manufacturing/doctype/work_order/work_order.js:356 msgid "Quantity to Manufacture" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2617 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2622 msgid "Quantity to Manufacture can not be zero for the operation {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1412 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1417 msgid "Quantity to Manufacture must be greater than 0." msgstr "" @@ -39968,7 +40010,7 @@ msgstr "" msgid "Query Route String" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:177 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:184 msgid "Queue Size should be between 5 and 100" msgstr "" @@ -40025,7 +40067,7 @@ msgstr "" #: erpnext/crm/report/lead_details/lead_details.js:37 #: erpnext/manufacturing/doctype/blanket_order/blanket_order.js:38 #: erpnext/selling/doctype/quotation/quotation.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1182 +#: erpnext/selling/doctype/sales_order/sales_order.js:1191 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/workspace/selling/selling.json #: erpnext/setup/doctype/authorization_rule/authorization_rule.json @@ -40076,11 +40118,11 @@ msgstr "" msgid "Quotation Trends" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:485 +#: erpnext/selling/doctype/sales_order/sales_order.py:486 msgid "Quotation {0} is cancelled" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:398 +#: erpnext/selling/doctype/sales_order/sales_order.py:399 msgid "Quotation {0} not of type {1}" msgstr "" @@ -40617,8 +40659,8 @@ msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.js:369 #: erpnext/manufacturing/doctype/production_plan/production_plan.js:124 -#: erpnext/manufacturing/doctype/work_order/work_order.js:767 -#: erpnext/selling/doctype/sales_order/sales_order.js:975 +#: erpnext/manufacturing/doctype/work_order/work_order.js:778 +#: erpnext/selling/doctype/sales_order/sales_order.js:976 #: erpnext/selling/doctype/sales_order/sales_order_list.js:70 #: erpnext/stock/doctype/material_request/material_request.js:243 #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.js:116 @@ -40717,7 +40759,7 @@ msgid "Reason for Failure" msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.js:679 -#: erpnext/selling/doctype/sales_order/sales_order.js:1767 +#: erpnext/selling/doctype/sales_order/sales_order.js:1785 msgid "Reason for Hold" msgstr "" @@ -40726,7 +40768,7 @@ msgstr "" msgid "Reason for Leaving" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1782 +#: erpnext/selling/doctype/sales_order/sales_order.js:1800 msgid "Reason for hold:" msgstr "" @@ -41164,7 +41206,7 @@ msgstr "" msgid "Reference #{0} dated {1}" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2873 +#: erpnext/public/js/controllers/transaction.js:2874 msgid "Reference Date for Early Payment Discount" msgstr "" @@ -41437,7 +41479,7 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js:310 #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/buying/doctype/supplier/supplier.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1077 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1079 msgid "Release Date" msgstr "" @@ -41543,7 +41585,7 @@ msgstr "" msgid "Remarks:" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:119 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:130 msgid "Remove Parent Row No in Items Table" msgstr "" @@ -41716,7 +41758,7 @@ msgstr "" msgid "Report Type is mandatory" msgstr "" -#: erpnext/setup/install.py:207 +#: erpnext/setup/install.py:215 msgid "Report an Issue" msgstr "" @@ -41744,6 +41786,12 @@ msgstr "" msgid "Reports to" msgstr "" +#. Label of the repost_section (Section Break) field in DocType 'Accounts +#. Settings' +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json +msgid "Repost" +msgstr "" + #. Name of a DocType #. Label of a Workspace Sidebar Item #: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json @@ -41757,9 +41805,7 @@ msgstr "" msgid "Repost Accounting Ledger Items" msgstr "" -#. Name of a DocType #. Label of a Workspace Sidebar Item -#: erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json #: erpnext/workspace_sidebar/accounts_setup.json #: erpnext/workspace_sidebar/erpnext_settings.json msgid "Repost Accounting Ledger Settings" @@ -41980,7 +42026,7 @@ msgstr "" msgid "Request for Quotation Supplier" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1091 +#: erpnext/selling/doctype/sales_order/sales_order.js:1100 msgid "Request for Raw Materials" msgstr "" @@ -42105,7 +42151,7 @@ msgstr "" #: erpnext/manufacturing/doctype/work_order_item/work_order_item.json #: erpnext/manufacturing/report/bom_stock_analysis/bom_stock_analysis.py:119 #: erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py:58 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1057 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1059 #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:426 #: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py:139 #: erpnext/subcontracting/doctype/subcontracting_inward_order_received_item/subcontracting_inward_order_received_item.json @@ -42185,8 +42231,8 @@ msgstr "" msgid "Reservation Based On" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:925 -#: erpnext/selling/doctype/sales_order/sales_order.js:106 +#: erpnext/manufacturing/doctype/work_order/work_order.js:936 +#: erpnext/selling/doctype/sales_order/sales_order.js:107 #: erpnext/stock/doctype/pick_list/pick_list.js:150 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:180 msgid "Reserve" @@ -42200,7 +42246,7 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.json #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/public/js/stock_reservation.js:15 -#: erpnext/selling/doctype/sales_order/sales_order.js:404 +#: erpnext/selling/doctype/sales_order/sales_order.js:405 #: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:278 @@ -42230,7 +42276,7 @@ msgstr "" msgid "Reserved" msgstr "" -#: erpnext/controllers/stock_controller.py:1323 +#: erpnext/controllers/stock_controller.py:1328 msgid "Reserved Batch Conflict" msgstr "" @@ -42300,29 +42346,29 @@ msgstr "" msgid "Reserved Quantity for Production" msgstr "" -#: erpnext/stock/stock_ledger.py:2327 +#: erpnext/stock/stock_ledger.py:2337 msgid "Reserved Serial No." msgstr "" #. Label of the reserved_stock (Float) field in DocType 'Bin' #. Name of a report #: erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html:24 -#: erpnext/manufacturing/doctype/work_order/work_order.js:941 +#: erpnext/manufacturing/doctype/work_order/work_order.js:952 #: erpnext/public/js/stock_reservation.js:236 -#: erpnext/selling/doctype/sales_order/sales_order.js:134 -#: erpnext/selling/doctype/sales_order/sales_order.js:464 +#: erpnext/selling/doctype/sales_order/sales_order.js:135 +#: erpnext/selling/doctype/sales_order/sales_order.js:465 #: erpnext/stock/dashboard/item_dashboard_list.html:15 #: erpnext/stock/doctype/bin/bin.json #: erpnext/stock/doctype/pick_list/pick_list.js:170 #: erpnext/stock/report/reserved_stock/reserved_stock.json #: erpnext/stock/report/stock_balance/stock_balance.py:572 -#: erpnext/stock/stock_ledger.py:2311 +#: erpnext/stock/stock_ledger.py:2321 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:205 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:333 msgid "Reserved Stock" msgstr "" -#: erpnext/stock/stock_ledger.py:2356 +#: erpnext/stock/stock_ledger.py:2366 msgid "Reserved Stock for Batch" msgstr "" @@ -42367,7 +42413,7 @@ msgid "Reserved for sub contracting" msgstr "" #: erpnext/public/js/stock_reservation.js:203 -#: erpnext/selling/doctype/sales_order/sales_order.js:417 +#: erpnext/selling/doctype/sales_order/sales_order.js:418 #: erpnext/stock/doctype/pick_list/pick_list.js:295 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:293 msgid "Reserving Stock..." @@ -42584,7 +42630,7 @@ msgstr "" #: erpnext/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.js:43 #: erpnext/buying/doctype/purchase_order/purchase_order.js:344 #: erpnext/manufacturing/doctype/workstation/workstation_job_card.html:63 -#: erpnext/selling/doctype/sales_order/sales_order.js:961 +#: erpnext/selling/doctype/sales_order/sales_order.js:962 msgid "Resume" msgstr "" @@ -42689,7 +42735,7 @@ msgstr "" msgid "Return Against Subcontracting Receipt" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:287 +#: erpnext/manufacturing/doctype/work_order/work_order.js:297 msgid "Return Components" msgstr "" @@ -43206,8 +43252,8 @@ msgstr "" msgid "Rounding Loss Allowance should be between 0 and 1" msgstr "" -#: erpnext/controllers/stock_controller.py:787 -#: erpnext/controllers/stock_controller.py:802 +#: erpnext/controllers/stock_controller.py:792 +#: erpnext/controllers/stock_controller.py:807 msgid "Rounding gain/loss Entry for Stock Transfer" msgstr "" @@ -43217,7 +43263,7 @@ msgstr "" #. Label of a Link in the Manufacturing Workspace #. Label of a Workspace Sidebar Item #: erpnext/manufacturing/doctype/bom/bom.json -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:94 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.js:101 #: erpnext/manufacturing/doctype/bom_creator/bom_creator.json #: erpnext/manufacturing/doctype/routing/routing.json #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -43318,7 +43364,7 @@ msgstr "" msgid "Row #{0}: BOM is not specified for subcontracting item {0}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:300 +#: erpnext/selling/doctype/sales_order/sales_order.py:301 msgid "Row #{0}: BOM not found for FG Item {1}" msgstr "" @@ -43465,11 +43511,11 @@ msgstr "" msgid "Row #{0}: Duplicate entry in References {1} {2}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:330 +#: erpnext/selling/doctype/sales_order/sales_order.py:331 msgid "Row #{0}: Expected Delivery Date cannot be before Purchase Order Date" msgstr "" -#: erpnext/controllers/stock_controller.py:918 +#: erpnext/controllers/stock_controller.py:923 msgid "Row #{0}: Expense Account not set for the Item {1}. {2}" msgstr "" @@ -43478,17 +43524,17 @@ msgid "Row #{0}: Expense account {1} is not valid for Purchase Invoice {2}. Only msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.py:362 -#: erpnext/selling/doctype/sales_order/sales_order.py:303 +#: erpnext/selling/doctype/sales_order/sales_order.py:304 msgid "Row #{0}: Finished Good Item Qty can not be zero" msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.py:344 -#: erpnext/selling/doctype/sales_order/sales_order.py:283 +#: erpnext/selling/doctype/sales_order/sales_order.py:284 msgid "Row #{0}: Finished Good Item is not specified for service item {1}" msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.py:351 -#: erpnext/selling/doctype/sales_order/sales_order.py:290 +#: erpnext/selling/doctype/sales_order/sales_order.py:291 msgid "Row #{0}: Finished Good Item {1} must be a sub-contracted item" msgstr "" @@ -43545,8 +43591,8 @@ msgstr "" msgid "Row #{0}: Item {1} has no stock in warehouse {2}." msgstr "" -#: erpnext/controllers/stock_controller.py:150 -msgid "Row #{0}: Item {1} has zero rate but 'Allow Zero Valuation Rate' is not enabled." +#: erpnext/controllers/stock_controller.py:153 +msgid "Row #{0}: Item {1} has zero rate but '{2}' is not enabled." msgstr "" #: erpnext/accounts/doctype/pos_invoice/pos_invoice.py:455 @@ -43594,7 +43640,7 @@ msgstr "" msgid "Row #{0}: Next Depreciation Date cannot be before Purchase Date" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:670 +#: erpnext/selling/doctype/sales_order/sales_order.py:671 msgid "Row #{0}: Not allowed to change Supplier as Purchase Order already exists" msgstr "" @@ -43657,15 +43703,15 @@ msgstr "" msgid "Row #{0}: Qty should be less than or equal to Available Qty to Reserve (Actual Qty - Reserved Qty) {1} for Iem {2} against Batch {3} in Warehouse {4}." msgstr "" -#: erpnext/controllers/stock_controller.py:1460 +#: erpnext/controllers/stock_controller.py:1465 msgid "Row #{0}: Quality Inspection is required for Item {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1475 +#: erpnext/controllers/stock_controller.py:1480 msgid "Row #{0}: Quality Inspection {1} is not submitted for the item: {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:1490 +#: erpnext/controllers/stock_controller.py:1495 msgid "Row #{0}: Quality Inspection {1} was rejected for item {2}" msgstr "" @@ -43744,7 +43790,7 @@ msgstr "" msgid "Row #{0}: Sequence ID must be {1} or {2} for Operation {3}." msgstr "" -#: erpnext/controllers/stock_controller.py:302 +#: erpnext/controllers/stock_controller.py:307 msgid "Row #{0}: Serial No {1} does not belong to Batch {2}" msgstr "" @@ -43772,7 +43818,7 @@ msgstr "" msgid "Row #{0}: Service Start and End Date is required for deferred accounting" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:493 +#: erpnext/selling/doctype/sales_order/sales_order.py:494 msgid "Row #{0}: Set Supplier for item {1}" msgstr "" @@ -43800,7 +43846,7 @@ msgstr "" msgid "Row #{0}: Source, Target Warehouse and Inventory Dimensions cannot be the exact same for Material Transfer" msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:105 +#: erpnext/manufacturing/doctype/workstation/workstation.py:107 msgid "Row #{0}: Start Time must be before End Time" msgstr "" @@ -43849,7 +43895,7 @@ msgstr "" msgid "Row #{0}: Target Warehouse must be same as Customer Warehouse {1} from the linked Subcontracting Inward Order" msgstr "" -#: erpnext/controllers/stock_controller.py:315 +#: erpnext/controllers/stock_controller.py:320 msgid "Row #{0}: The batch {1} has already expired." msgstr "" @@ -43857,7 +43903,7 @@ msgstr "" msgid "Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2}" msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:178 +#: erpnext/manufacturing/doctype/workstation/workstation.py:180 msgid "Row #{0}: Timings conflicts with row {1}" msgstr "" @@ -44006,7 +44052,7 @@ msgstr "" msgid "Row #{}: {} {} does not exist." msgstr "" -#: erpnext/stock/doctype/item/item.py:1420 +#: erpnext/stock/doctype/item/item.py:1433 msgid "Row #{}: {} {} doesn't belong to Company {}. Please select valid {}." msgstr "" @@ -44154,7 +44200,7 @@ msgstr "" msgid "Row {0}: From Time and To Time of {1} is overlapping with {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:1556 +#: erpnext/controllers/stock_controller.py:1561 msgid "Row {0}: From Warehouse is mandatory for internal transfers" msgstr "" @@ -44294,7 +44340,7 @@ msgstr "" msgid "Row {0}: Subcontracted Item is mandatory for the raw material {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1547 +#: erpnext/controllers/stock_controller.py:1552 msgid "Row {0}: Target Warehouse is mandatory for internal transfers" msgstr "" @@ -44528,7 +44574,7 @@ msgstr "" #. Item' #. Label of the safety_stock (Float) field in DocType 'Item' #: erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1052 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1054 #: erpnext/stock/doctype/item/item.json #: erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py:58 msgid "Safety Stock" @@ -44572,14 +44618,14 @@ msgstr "" #: erpnext/crm/doctype/opportunity/opportunity.js:288 #: erpnext/crm/doctype/opportunity/opportunity.py:158 #: erpnext/projects/doctype/project/project_dashboard.py:15 -#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:146 +#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:145 #: erpnext/selling/doctype/quotation/quotation.json #: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/setup/doctype/company/company.py:457 #: erpnext/setup/doctype/company/company.py:649 #: erpnext/setup/doctype/company/company_dashboard.py:9 #: erpnext/setup/doctype/sales_person/sales_person_dashboard.py:12 -#: erpnext/setup/install.py:373 +#: erpnext/setup/install.py:382 #: erpnext/setup/setup_wizard/operations/install_fixtures.py:297 #: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/pick_list/pick_list_dashboard.py:16 @@ -44687,7 +44733,7 @@ msgstr "" #: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet_detail/timesheet_detail.json #: erpnext/selling/doctype/quotation/quotation_list.js:22 -#: erpnext/selling/doctype/sales_order/sales_order.js:1070 +#: erpnext/selling/doctype/sales_order/sales_order.js:1079 #: erpnext/selling/doctype/sales_order/sales_order_list.js:75 #: erpnext/selling/workspace/selling/selling.json #: erpnext/setup/doctype/authorization_rule/authorization_rule.json @@ -44783,7 +44829,7 @@ msgstr "" msgid "Sales Invoice {0} has already been submitted" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:589 +#: erpnext/selling/doctype/sales_order/sales_order.py:590 msgid "Sales Invoice {0} must be deleted before cancelling this Sales Order" msgstr "" @@ -44933,8 +44979,8 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:343 -#: erpnext/selling/doctype/sales_order/sales_order.js:1286 +#: erpnext/selling/doctype/sales_order/sales_order.js:344 +#: erpnext/selling/doctype/sales_order/sales_order.js:1295 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/stock/doctype/material_request_item/material_request_item.json #: erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -44981,10 +45027,15 @@ msgstr "" msgid "Sales Order required for Item {0}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:354 +#: erpnext/selling/doctype/sales_order/sales_order.py:355 msgid "Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}" msgstr "" +#: erpnext/selling/doctype/sales_order/sales_order.py:1817 +#: erpnext/selling/doctype/sales_order/sales_order.py:1830 +msgid "Sales Order {0} is not available for production" +msgstr "" + #: erpnext/accounts/doctype/sales_invoice/sales_invoice.py:1395 msgid "Sales Order {0} is not submitted" msgstr "" @@ -45250,7 +45301,7 @@ msgstr "" msgid "Sales Representative" msgstr "" -#: erpnext/accounts/report/gross_profit/gross_profit.py:970 +#: erpnext/accounts/report/gross_profit/gross_profit.py:997 #: erpnext/stock/doctype/delivery_note/delivery_note.js:270 msgid "Sales Return" msgstr "" @@ -45427,7 +45478,7 @@ msgstr "" #. Label of the sample_size (Float) field in DocType 'Quality Inspection' #: erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py:93 -#: erpnext/public/js/controllers/transaction.js:2930 +#: erpnext/public/js/controllers/transaction.js:2931 #: erpnext/stock/doctype/quality_inspection/quality_inspection.json msgid "Sample Size" msgstr "" @@ -45533,7 +45584,7 @@ msgstr "" msgid "Schedule Date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:490 +#: erpnext/public/js/controllers/transaction.js:492 msgid "Schedule Name" msgstr "" @@ -45793,10 +45844,6 @@ msgstr "" msgid "See all open tickets" msgstr "" -#: erpnext/stock/report/stock_ledger/stock_ledger.js:122 -msgid "Segregate Serial / Batch Bundle" -msgstr "" - #: erpnext/accounts/report/profitability_analysis/profitability_analysis.py:23 msgid "Select Accounting Dimension." msgstr "" @@ -45809,22 +45856,18 @@ msgstr "" msgid "Select Alternative Items for Sales Order" msgstr "" -#: erpnext/stock/doctype/item/item.js:733 +#: erpnext/stock/doctype/item/item.js:814 msgid "Select Attribute Values" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1269 +#: erpnext/selling/doctype/sales_order/sales_order.js:1278 msgid "Select BOM" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1246 +#: erpnext/selling/doctype/sales_order/sales_order.js:1255 msgid "Select BOM and Qty for Production" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1402 -msgid "Select BOM, Qty and For Warehouse" -msgstr "" - #: erpnext/assets/doctype/asset_repair/asset_repair.js:234 #: erpnext/public/js/utils/sales_common.js:443 #: erpnext/stock/doctype/pick_list/pick_list.js:387 @@ -45851,7 +45894,7 @@ msgstr "" msgid "Select Company" msgstr "" -#: erpnext/public/js/print.js:115 +#: erpnext/public/js/print.js:118 msgid "Select Company Address" msgstr "" @@ -45896,7 +45939,7 @@ msgid "Select Employees" msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.js:198 -#: erpnext/selling/doctype/sales_order/sales_order.js:825 +#: erpnext/selling/doctype/sales_order/sales_order.js:826 msgid "Select Finished Good" msgstr "" @@ -45906,24 +45949,24 @@ msgstr "" #. Forecast' #: erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.json #: erpnext/manufacturing/doctype/sales_forecast/sales_forecast.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1603 -#: erpnext/selling/doctype/sales_order/sales_order.js:1631 +#: erpnext/selling/doctype/sales_order/sales_order.js:1621 +#: erpnext/selling/doctype/sales_order/sales_order.js:1649 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:493 msgid "Select Items" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1489 +#: erpnext/selling/doctype/sales_order/sales_order.js:1507 msgid "Select Items based on Delivery Date" msgstr "" -#: erpnext/public/js/controllers/transaction.js:2969 +#: erpnext/public/js/controllers/transaction.js:2970 msgid "Select Items for Quality Inspection" msgstr "" #. Label of the select_items_to_manufacture_section (Section Break) field in #. DocType 'Production Plan' #: erpnext/manufacturing/doctype/production_plan/production_plan.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1298 +#: erpnext/selling/doctype/sales_order/sales_order.js:1307 msgid "Select Items to Manufacture" msgstr "" @@ -45946,7 +45989,7 @@ msgstr "" msgid "Select Loyalty Program" msgstr "" -#: erpnext/public/js/controllers/transaction.js:477 +#: erpnext/public/js/controllers/transaction.js:478 msgid "Select Payment Schedule" msgstr "" @@ -45954,7 +45997,7 @@ msgstr "" msgid "Select Possible Supplier" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1104 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1115 #: erpnext/stock/doctype/pick_list/pick_list.js:216 msgid "Select Quantity" msgstr "" @@ -45984,7 +46027,7 @@ msgstr "" msgid "Select Supplier Address" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:148 +#: erpnext/stock/doctype/batch/batch.js:149 msgid "Select Target Warehouse" msgstr "" @@ -46037,7 +46080,7 @@ msgstr "" msgid "Select a company" msgstr "" -#: erpnext/stock/doctype/item/item.js:1066 +#: erpnext/stock/doctype/item/item.js:1147 msgid "Select an Item Group." msgstr "" @@ -46053,7 +46096,7 @@ msgstr "" msgid "Select an item from each set to be used in the Sales Order." msgstr "" -#: erpnext/stock/doctype/item/item.js:747 +#: erpnext/stock/doctype/item/item.js:828 msgid "Select at least one value from each of the attributes." msgstr "" @@ -46088,7 +46131,7 @@ msgstr "" msgid "Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1206 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1217 msgid "Select the Item to be manufactured." msgstr "" @@ -46306,7 +46349,7 @@ msgid "Send Emails to Suppliers" msgstr "" #. Label of the send_sms (Button) field in DocType 'SMS Center' -#: erpnext/public/js/controllers/transaction.js:696 +#: erpnext/public/js/controllers/transaction.js:697 #: erpnext/selling/doctype/sms_center/sms_center.json msgid "Send SMS" msgstr "" @@ -46442,7 +46485,7 @@ msgstr "" #: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js:74 #: erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py:114 -#: erpnext/public/js/controllers/transaction.js:2943 +#: erpnext/public/js/controllers/transaction.js:2944 #: erpnext/public/js/utils/serial_no_batch_selector.js:421 #: erpnext/selling/doctype/installation_note_item/installation_note_item.json #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -46462,7 +46505,7 @@ msgstr "" #: erpnext/stock/report/serial_no_and_batch_traceability/serial_no_and_batch_traceability.py:450 #: erpnext/stock/report/serial_no_ledger/serial_no_ledger.js:38 #: erpnext/stock/report/serial_no_ledger/serial_no_ledger.py:60 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:401 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:410 #: erpnext/stock/workspace/stock/stock.json #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json #: erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json @@ -46547,7 +46590,7 @@ msgstr "" msgid "Serial No and Batch" msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:28 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:34 msgid "Serial No and Batch Selector cannot be use when Use Serial / Batch Fields is enabled." msgstr "" @@ -46640,14 +46683,14 @@ msgstr "" #. Label of the serial_nos_and_batches (Section Break) field in DocType 'Item' #: erpnext/stock/doctype/item/item.json -msgid "Serial Nos and Batches" +msgid "Serial Nos / Batches" msgstr "" #: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:1958 msgid "Serial Nos are created successfully" msgstr "" -#: erpnext/stock/stock_ledger.py:2317 +#: erpnext/stock/stock_ledger.py:2327 msgid "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding." msgstr "" @@ -46717,7 +46760,7 @@ msgstr "" #: erpnext/stock/report/available_serial_no/available_serial_no.py:188 #: erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.py:31 #: erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py:80 -#: erpnext/stock/report/stock_ledger/stock_ledger.py:408 +#: erpnext/stock/report/stock_ledger/stock_ledger.py:394 #: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:177 #: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json #: erpnext/workspace_sidebar/stock.json @@ -46732,7 +46775,7 @@ msgstr "" msgid "Serial and Batch Bundle updated" msgstr "" -#: erpnext/controllers/stock_controller.py:196 +#: erpnext/controllers/stock_controller.py:201 msgid "Serial and Batch Bundle {0} is already used in {1} {2}." msgstr "" @@ -47101,12 +47144,12 @@ msgid "Service Stop Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:45 -#: erpnext/public/js/controllers/transaction.js:1779 +#: erpnext/public/js/controllers/transaction.js:1780 msgid "Service Stop Date cannot be after Service End Date" msgstr "" #: erpnext/accounts/deferred_revenue.py:42 -#: erpnext/public/js/controllers/transaction.js:1776 +#: erpnext/public/js/controllers/transaction.js:1777 msgid "Service Stop Date cannot be before Service Start Date" msgstr "" @@ -47203,7 +47246,7 @@ msgstr "" msgid "Set Operating Cost Based On BOM Quantity" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:113 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:124 msgid "Set Parent Row No in Items Table" msgstr "" @@ -47255,7 +47298,7 @@ msgstr "" msgid "Set Source Warehouse" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1609 +#: erpnext/selling/doctype/sales_order/sales_order.js:1627 msgid "Set Supplier" msgstr "" @@ -47285,7 +47328,7 @@ msgstr "" msgid "Set Valuation Rate for Rejected Materials" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:263 +#: erpnext/selling/doctype/sales_order/sales_order.js:264 msgid "Set Warehouse" msgstr "" @@ -47351,7 +47394,7 @@ msgstr "" msgid "Set targets Item Group-wise for this Sales Person." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1263 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1274 msgid "Set the Planned Start Date (an Estimated Date at which you want the Production to begin)" msgstr "" @@ -47447,7 +47490,7 @@ msgid "Setting up company" msgstr "" #: erpnext/manufacturing/doctype/bom/bom.py:1217 -#: erpnext/manufacturing/doctype/work_order/work_order.py:1476 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1481 msgid "Setting {0} is required" msgstr "" @@ -48024,7 +48067,7 @@ msgstr "" msgid "Show Variant Attributes" msgstr "" -#: erpnext/stock/doctype/item/item.js:161 +#: erpnext/stock/doctype/item/item.js:185 msgid "Show Variants" msgstr "" @@ -48189,7 +48232,7 @@ msgstr "" msgid "Single Tier Program" msgstr "" -#: erpnext/stock/doctype/item/item.js:186 +#: erpnext/stock/doctype/item/item.js:210 msgid "Single Variant" msgstr "" @@ -48200,7 +48243,7 @@ msgstr "" #. Label of the skip_material_transfer (Check) field in DocType 'Work Order #. Operation' -#: erpnext/manufacturing/doctype/work_order/work_order.js:365 +#: erpnext/manufacturing/doctype/work_order/work_order.js:375 #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/workstation/workstation.js:454 msgid "Skip Material Transfer" @@ -48331,7 +48374,7 @@ msgstr "" msgid "Source Location" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1014 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1025 msgid "Source Manufacture Entry" msgstr "" @@ -48382,7 +48425,7 @@ msgstr "" #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/stock/dashboard/item_dashboard.js:227 #: erpnext/stock/doctype/material_request_item/material_request_item.json -#: erpnext/stock/doctype/stock_entry/stock_entry.js:806 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:798 #: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json msgid "Source Warehouse" msgstr "" @@ -48464,8 +48507,8 @@ msgid "Spending for Account {0} ({1}) between {2} and {3} has already exceeded t msgstr "" #: erpnext/assets/doctype/asset/asset.js:689 -#: erpnext/stock/doctype/batch/batch.js:102 -#: erpnext/stock/doctype/batch/batch.js:183 +#: erpnext/stock/doctype/batch/batch.js:103 +#: erpnext/stock/doctype/batch/batch.js:184 #: erpnext/support/doctype/issue/issue.js:114 msgid "Split" msgstr "" @@ -48475,7 +48518,7 @@ msgstr "" msgid "Split Asset" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:182 +#: erpnext/stock/doctype/batch/batch.js:183 msgid "Split Batch" msgstr "" @@ -48561,7 +48604,7 @@ msgstr "" msgid "Stale Days" msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:147 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:154 msgid "Stale Days should start from 1." msgstr "" @@ -48663,7 +48706,7 @@ msgstr "" #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js:56 #: erpnext/accounts/report/financial_ratios/financial_ratios.js:17 #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:81 -#: erpnext/public/js/financial_statements.js:419 +#: erpnext/public/js/financial_statements.js:422 msgid "Start Year" msgstr "" @@ -48819,7 +48862,7 @@ msgstr "" #. Label of a Link in the Stock Workspace #. Label of a Workspace Sidebar Item #: erpnext/selling/doctype/quotation_item/quotation_item.json -#: erpnext/stock/doctype/item/item.js:108 +#: erpnext/stock/doctype/item/item.js:132 #: erpnext/stock/doctype/warehouse/warehouse.js:62 #: erpnext/stock/report/stock_balance/stock_balance.json #: erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py:107 @@ -48938,7 +48981,7 @@ msgstr "" msgid "Stock Entry has been already created against this Pick List" msgstr "" -#: erpnext/stock/doctype/batch/batch.js:136 +#: erpnext/stock/doctype/batch/batch.js:137 msgid "Stock Entry {0} created" msgstr "" @@ -48977,7 +49020,7 @@ msgstr "" #. Label of a Workspace Sidebar Item #: erpnext/public/js/controllers/stock_controller.js:67 #: erpnext/public/js/utils/ledger_preview.js:37 -#: erpnext/stock/doctype/item/item.js:118 +#: erpnext/stock/doctype/item/item.js:142 #: erpnext/stock/doctype/item/item_dashboard.py:8 #: erpnext/stock/report/stock_ledger/stock_ledger.json #: erpnext/stock/workspace/stock/stock.json @@ -49019,8 +49062,8 @@ msgstr "" msgid "Stock Ledgers won’t be reposted." msgstr "" -#: erpnext/stock/doctype/batch/batch.js:79 -#: erpnext/stock/doctype/item/item.js:586 +#: erpnext/stock/doctype/batch/batch.js:80 +#: erpnext/stock/doctype/item/item.js:667 msgid "Stock Levels" msgstr "" @@ -49065,6 +49108,7 @@ msgstr "" #: erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json #: erpnext/stock/doctype/stock_settings/stock_settings.json +#: erpnext/stock/doctype/uom_category/uom_category.json #: erpnext/stock/doctype/warehouse_type/warehouse_type.json #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json msgid "Stock Manager" @@ -49088,7 +49132,7 @@ msgstr "" #. Name of a report #. Label of a Link in the Stock Workspace #. Label of a Workspace Sidebar Item -#: erpnext/stock/doctype/item/item.js:128 +#: erpnext/stock/doctype/item/item.js:152 #: erpnext/stock/report/stock_projected_qty/stock_projected_qty.json #: erpnext/stock/workspace/stock/stock.json #: erpnext/workspace_sidebar/stock.json @@ -49177,15 +49221,15 @@ msgstr "" #: erpnext/manufacturing/doctype/production_plan/production_plan.js:289 #: erpnext/manufacturing/doctype/production_plan/production_plan.js:297 #: erpnext/manufacturing/doctype/production_plan/production_plan.js:303 -#: erpnext/manufacturing/doctype/work_order/work_order.js:927 -#: erpnext/manufacturing/doctype/work_order/work_order.js:936 -#: erpnext/manufacturing/doctype/work_order/work_order.js:943 +#: erpnext/manufacturing/doctype/work_order/work_order.js:938 +#: erpnext/manufacturing/doctype/work_order/work_order.js:947 +#: erpnext/manufacturing/doctype/work_order/work_order.js:954 #: erpnext/manufacturing/doctype/work_order/work_order_dashboard.py:14 #: erpnext/public/js/stock_reservation.js:12 -#: erpnext/selling/doctype/sales_order/sales_order.js:108 -#: erpnext/selling/doctype/sales_order/sales_order.js:123 -#: erpnext/selling/doctype/sales_order/sales_order.js:136 -#: erpnext/selling/doctype/sales_order/sales_order.js:257 +#: erpnext/selling/doctype/sales_order/sales_order.js:109 +#: erpnext/selling/doctype/sales_order/sales_order.js:124 +#: erpnext/selling/doctype/sales_order/sales_order.js:137 +#: erpnext/selling/doctype/sales_order/sales_order.js:258 #: erpnext/stock/doctype/pick_list/pick_list.js:152 #: erpnext/stock/doctype/pick_list/pick_list.js:167 #: erpnext/stock/doctype/pick_list/pick_list.js:172 @@ -49217,7 +49261,7 @@ msgstr "" #: erpnext/controllers/subcontracting_inward_controller.py:1018 #: erpnext/manufacturing/doctype/production_plan/production_plan.py:2245 -#: erpnext/manufacturing/doctype/work_order/work_order.py:2125 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2130 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1777 msgid "Stock Reservation Entries Created" msgstr "" @@ -49228,7 +49272,7 @@ msgstr "" #. Name of a DocType #: erpnext/public/js/stock_reservation.js:309 -#: erpnext/selling/doctype/sales_order/sales_order.js:474 +#: erpnext/selling/doctype/sales_order/sales_order.js:475 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:388 #: erpnext/stock/report/reserved_stock/reserved_stock.js:53 @@ -49285,6 +49329,7 @@ msgstr "" #: erpnext/selling/doctype/selling_settings/selling_settings.py:115 #: erpnext/setup/doctype/company/company.json #: erpnext/setup/workspace/erpnext_settings/erpnext_settings.json +#: erpnext/stock/doctype/item/item.js:414 #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:675 #: erpnext/stock/doctype/stock_settings/stock_settings.json #: erpnext/stock/workspace/stock/stock.json @@ -49417,7 +49462,7 @@ msgid "Stock UOM Quantity" msgstr "" #: erpnext/public/js/stock_reservation.js:230 -#: erpnext/selling/doctype/sales_order/sales_order.js:458 +#: erpnext/selling/doctype/sales_order/sales_order.js:459 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:327 msgid "Stock Unreservation" msgstr "" @@ -49480,6 +49525,7 @@ msgstr "" #: erpnext/stock/doctype/stock_entry_type/stock_entry_type.json #: erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json +#: erpnext/stock/doctype/uom_category/uom_category.json #: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse_type/warehouse_type.json #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json @@ -49838,7 +49884,7 @@ msgstr "" #. Label of a Link in the Subcontracting Workspace #. Label of a Workspace Sidebar Item #: erpnext/manufacturing/doctype/work_order/work_order.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1012 +#: erpnext/selling/doctype/sales_order/sales_order.js:1013 #: erpnext/stock/doctype/stock_entry/stock_entry.json #: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json @@ -50381,8 +50427,8 @@ msgstr "" #: erpnext/regional/report/irs_1099/irs_1099.py:77 #: erpnext/selling/doctype/customer/customer.js:253 #: erpnext/selling/doctype/party_specific_item/party_specific_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:196 -#: erpnext/selling/doctype/sales_order/sales_order.js:1667 +#: erpnext/selling/doctype/sales_order/sales_order.js:197 +#: erpnext/selling/doctype/sales_order/sales_order.js:1685 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/doctype/sms_center/sms_center.json #: erpnext/setup/workspace/home/home.json @@ -50701,7 +50747,7 @@ msgstr "" msgid "Supplier Quotation Item" msgstr "" -#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.py:497 +#: erpnext/buying/doctype/request_for_quotation/request_for_quotation.py:502 msgid "Supplier Quotation {0} Created" msgstr "" @@ -50709,7 +50755,7 @@ msgstr "" msgid "Supplier Reference" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1691 +#: erpnext/selling/doctype/sales_order/sales_order.js:1709 msgid "Supplier Required" msgstr "" @@ -50803,7 +50849,7 @@ msgstr "" msgid "Supplier delivers to Customer" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1690 +#: erpnext/selling/doctype/sales_order/sales_order.js:1708 msgid "Supplier is required for all selected Items" msgstr "" @@ -50844,8 +50890,8 @@ msgstr "" msgid "Supplies subject to the reverse charge provision" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:315 -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:380 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:317 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:382 msgid "Supply" msgstr "" @@ -51110,7 +51156,7 @@ msgstr "" #: erpnext/stock/dashboard/item_dashboard.js:234 #: erpnext/stock/doctype/delivery_note_item/delivery_note_item.json #: erpnext/stock/doctype/material_request_item/material_request_item.json -#: erpnext/stock/doctype/stock_entry/stock_entry.js:812 +#: erpnext/stock/doctype/stock_entry/stock_entry.js:804 #: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json msgid "Target Warehouse" msgstr "" @@ -51619,7 +51665,7 @@ msgstr "" #. Detail' #: erpnext/accounts/doctype/item_wise_tax_detail/item_wise_tax_detail.json #: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:157 -#: erpnext/controllers/taxes_and_totals.py:1253 +#: erpnext/controllers/taxes_and_totals.py:1252 msgid "Taxable Amount" msgstr "" @@ -52242,10 +52288,14 @@ msgstr "" msgid "The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document." msgstr "" -#: erpnext/controllers/stock_controller.py:1312 +#: erpnext/controllers/stock_controller.py:1317 msgid "The batch {0} is already reserved in {1} {2}. So, cannot proceed with the {3} {4}, which is created against the {5} {6}." msgstr "" +#: erpnext/regional/report/vat_audit_report/vat_audit_report.py:43 +msgid "The company {0} is not in South Africa. VAT Audit Report is only available for companies in South Africa." +msgstr "" + #: erpnext/manufacturing/doctype/job_card/job_card.py:1319 msgid "The completed quantity {0} of an operation {1} cannot be greater than the completed quantity {2} of a previous operation {3}." msgstr "" @@ -52258,7 +52308,7 @@ msgstr "" msgid "The current POS opening entry is outdated. Please close it and create a new one." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1211 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1222 msgid "The default BOM for that item will be fetched by the system. You can also change the BOM." msgstr "" @@ -52319,7 +52369,7 @@ msgstr "" msgid "The following cancelled repost entries exist for {0}:

    {1}

    Kindly delete these entries before continuing." msgstr "" -#: erpnext/stock/doctype/item/item.py:861 +#: erpnext/stock/doctype/item/item.py:874 msgid "The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template." msgstr "" @@ -52366,11 +52416,11 @@ msgstr "" msgid "The items {items} are not marked as {type_of} item. You can enable them as {type_of} item from their Item masters." msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:546 +#: erpnext/manufacturing/doctype/workstation/workstation.py:548 msgid "The job card {0} is in {1} state and you cannot complete." msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:540 +#: erpnext/manufacturing/doctype/workstation/workstation.py:542 msgid "The job card {0} is in {1} state and you cannot start it again." msgstr "" @@ -52497,7 +52547,7 @@ msgstr "" msgid "The shares don't exist with the {0}" msgstr "" -#: erpnext/stock/stock_ledger.py:814 +#: erpnext/stock/stock_ledger.py:824 msgid "The stock for the item {0} in the {1} warehouse was negative on the {2}. You should create a positive entry {3} before the date {4} and time {5} to post the correct valuation rate. For more details, please read the documentation." msgstr "" @@ -52563,15 +52613,15 @@ msgstr "" msgid "The value {0} is already assigned to an existing Item {1}." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1239 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1250 msgid "The warehouse where you store finished Items before they are shipped." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1232 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1243 msgid "The warehouse where you store your raw materials. Each required item can have a separate source warehouse. Group warehouse also can be selected as source warehouse. On submission of the Work Order, the raw materials will be reserved in these warehouses for production usage." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1244 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1255 msgid "The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse." msgstr "" @@ -52579,7 +52629,7 @@ msgstr "" msgid "The {0} ({1}) must be equal to {2} ({3})" msgstr "" -#: erpnext/public/js/controllers/transaction.js:3412 +#: erpnext/public/js/controllers/transaction.js:3413 msgid "The {0} contains Unit Price Items." msgstr "" @@ -52627,7 +52677,7 @@ msgstr "" msgid "There are no slots available on this date" msgstr "" -#: erpnext/stock/doctype/item/item.js:1090 +#: erpnext/stock/doctype/item/item.js:1171 msgid "There are two options to maintain valuation of stock. FIFO (first in - first out) and Moving Average. To understand this topic in detail please visit Item Valuation, FIFO and Moving Average." msgstr "" @@ -52690,11 +52740,11 @@ msgstr "" msgid "This Account has '0' balance in either Base Currency or Account Currency" msgstr "" -#: erpnext/stock/doctype/item/item.js:154 +#: erpnext/stock/doctype/item/item.js:178 msgid "This Item is a Template and cannot be used in transactions.
    All fields present in the 'Copy Fields to Variant' table in Item Variant Settings will be copied to its variant items." msgstr "" -#: erpnext/stock/doctype/item/item.js:213 +#: erpnext/stock/doctype/item/item.js:237 msgid "This Item is a Variant of {0} (Template)." msgstr "" @@ -52706,7 +52756,7 @@ msgstr "" msgid "This Purchase Order has been fully subcontracted." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:2050 +#: erpnext/selling/doctype/sales_order/sales_order.py:2081 msgid "This Sales Order has been fully subcontracted." msgstr "" @@ -52730,7 +52780,7 @@ msgstr "" msgid "This covers all scorecards tied to this Setup" msgstr "" -#: erpnext/controllers/status_updater.py:463 +#: erpnext/controllers/status_updater.py:478 msgid "This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?" msgstr "" @@ -52817,7 +52867,7 @@ msgstr "" msgid "This is based on transactions against this Sales Person. See timeline below for details" msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:42 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:48 msgid "This is considered dangerous from accounting point of view." msgstr "" @@ -52825,11 +52875,11 @@ msgstr "" msgid "This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1225 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1236 msgid "This is enabled by default. If you want to plan materials for sub-assemblies of the Item you're manufacturing leave this enabled. If you plan and manufacture the sub-assemblies separately, you can disable this checkbox." msgstr "" -#: erpnext/stock/doctype/item/item.js:1078 +#: erpnext/stock/doctype/item/item.js:1159 msgid "This is for raw material Items that'll be used to create finished goods. If the Item is an additional service like 'washing' that'll be used in the BOM, keep this unchecked." msgstr "" @@ -53361,7 +53411,7 @@ msgid "To Value" msgstr "" #: erpnext/manufacturing/doctype/plant_floor/plant_floor.js:224 -#: erpnext/stock/doctype/batch/batch.js:114 +#: erpnext/stock/doctype/batch/batch.js:115 msgid "To Warehouse" msgstr "" @@ -53378,11 +53428,11 @@ msgstr "" msgid "To add subcontracted Item's raw materials if include exploded items is disabled." msgstr "" -#: erpnext/controllers/status_updater.py:456 +#: erpnext/controllers/status_updater.py:471 msgid "To allow over billing, update \"Over Billing Allowance\" in Accounts Settings or the Item." msgstr "" -#: erpnext/controllers/status_updater.py:452 +#: erpnext/controllers/status_updater.py:467 msgid "To allow over receipt / delivery, update \"Over Receipt/Delivery Allowance\" in Stock Settings or the Item." msgstr "" @@ -53414,10 +53464,6 @@ msgstr "" msgid "To enable Capital Work in Progress Accounting," msgstr "" -#: erpnext/stock/doctype/item/item.js:94 -msgid "To enable the Serial No and Batch No feature, please check the 'Enable Serial / Batch No for Item' checkbox in Stock Settings." -msgstr "" - #: erpnext/manufacturing/doctype/production_plan/production_plan.js:733 msgid "To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked." msgstr "" @@ -54001,7 +54047,7 @@ msgstr "" msgid "Total Payments" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:725 +#: erpnext/selling/doctype/sales_order/sales_order.py:726 msgid "Total Picked Quantity {0} is more than ordered qty {1}. You can set the Over Picking Allowance in Stock Settings." msgstr "" @@ -54312,7 +54358,7 @@ msgstr "" msgid "Total percentage against cost centers should be 100" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:672 +#: erpnext/selling/doctype/sales_order/sales_order.js:673 msgid "Total quantity in delivery schedule cannot be greater than the item quantity" msgstr "" @@ -54441,7 +54487,7 @@ msgstr "" msgid "Transaction Date" msgstr "" -#: erpnext/setup/doctype/company/company.py:1106 +#: erpnext/setup/doctype/company/company.py:1091 msgid "Transaction Deletion Document {0} has been triggered for company {1}" msgstr "" @@ -54976,7 +55022,7 @@ msgstr "" #: erpnext/selling/doctype/delivery_schedule_item/delivery_schedule_item.json #: erpnext/selling/doctype/product_bundle_item/product_bundle_item.json #: erpnext/selling/doctype/quotation_item/quotation_item.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1660 +#: erpnext/selling/doctype/sales_order/sales_order.js:1678 #: erpnext/selling/doctype/sales_order_item/sales_order_item.json #: erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py:43 #: erpnext/selling/report/sales_analytics/sales_analytics.py:138 @@ -55380,15 +55426,15 @@ msgstr "" msgid "Unreconciled Entries" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:934 -#: erpnext/selling/doctype/sales_order/sales_order.js:121 +#: erpnext/manufacturing/doctype/work_order/work_order.js:945 +#: erpnext/selling/doctype/sales_order/sales_order.js:122 #: erpnext/stock/doctype/pick_list/pick_list.js:158 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:193 msgid "Unreserve" msgstr "" #: erpnext/public/js/stock_reservation.js:245 -#: erpnext/selling/doctype/sales_order/sales_order.js:509 +#: erpnext/selling/doctype/sales_order/sales_order.js:510 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:378 msgid "Unreserve Stock" msgstr "" @@ -55402,7 +55448,7 @@ msgid "Unreserve for Sub-assembly" msgstr "" #: erpnext/public/js/stock_reservation.js:281 -#: erpnext/selling/doctype/sales_order/sales_order.js:521 +#: erpnext/selling/doctype/sales_order/sales_order.js:522 #: erpnext/stock/doctype/pick_list/pick_list.js:310 #: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:390 msgid "Unreserving Stock..." @@ -55590,8 +55636,8 @@ msgstr "" #: erpnext/buying/doctype/supplier_quotation/supplier_quotation.js:43 #: erpnext/public/js/utils.js:946 #: erpnext/selling/doctype/quotation/quotation.js:135 -#: erpnext/selling/doctype/sales_order/sales_order.js:89 -#: erpnext/selling/doctype/sales_order/sales_order.js:947 +#: erpnext/selling/doctype/sales_order/sales_order.js:90 +#: erpnext/selling/doctype/sales_order/sales_order.js:948 msgid "Update Items" msgstr "" @@ -55686,15 +55732,15 @@ msgstr "" msgid "Updating Costing and Billing fields against this Project..." msgstr "" -#: erpnext/stock/doctype/item/item.py:1404 +#: erpnext/stock/doctype/item/item.py:1417 msgid "Updating Variants..." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:1187 +#: erpnext/manufacturing/doctype/work_order/work_order.js:1198 msgid "Updating Work Order status" msgstr "" -#: erpnext/public/js/print.js:153 +#: erpnext/public/js/print.js:156 msgid "Updating details." msgstr "" @@ -55907,7 +55953,7 @@ msgstr "" msgid "Used with Financial Report Template" msgstr "" -#: erpnext/setup/install.py:195 +#: erpnext/setup/install.py:203 msgid "User Forum" msgstr "" @@ -55987,7 +56033,7 @@ msgstr "" msgid "Users with this role will be notified if the asset depreciation gets failed" msgstr "" -#: erpnext/stock/doctype/stock_settings/stock_settings.js:38 +#: erpnext/stock/doctype/stock_settings/stock_settings.js:44 msgid "Using negative stock disables FIFO/Moving average valuation when inventory is negative." msgstr "" @@ -56242,11 +56288,11 @@ msgstr "" msgid "Valuation Rate (In / Out)" msgstr "" -#: erpnext/stock/stock_ledger.py:2062 +#: erpnext/stock/stock_ledger.py:2072 msgid "Valuation Rate Missing" msgstr "" -#: erpnext/stock/stock_ledger.py:2040 +#: erpnext/stock/stock_ledger.py:2050 msgid "Valuation Rate for the Item {0}, is required to do accounting entries for {1} {2}." msgstr "" @@ -56402,13 +56448,13 @@ msgstr "" msgid "Variance ({})" msgstr "" -#: erpnext/stock/doctype/item/item.js:201 +#: erpnext/stock/doctype/item/item.js:225 #: erpnext/stock/doctype/item/item_list.js:22 #: erpnext/stock/report/item_variant_details/item_variant_details.py:74 msgid "Variant" msgstr "" -#: erpnext/stock/doctype/item/item.py:876 +#: erpnext/stock/doctype/item/item.py:889 msgid "Variant Attribute Error" msgstr "" @@ -56427,11 +56473,11 @@ msgstr "" msgid "Variant Based On" msgstr "" -#: erpnext/stock/doctype/item/item.py:904 +#: erpnext/stock/doctype/item/item.py:917 msgid "Variant Based On cannot be changed" msgstr "" -#: erpnext/stock/doctype/item/item.js:177 +#: erpnext/stock/doctype/item/item.js:201 msgid "Variant Details Report" msgstr "" @@ -56445,7 +56491,7 @@ msgstr "" msgid "Variant Item" msgstr "" -#: erpnext/stock/doctype/item/item.py:874 +#: erpnext/stock/doctype/item/item.py:887 msgid "Variant Items" msgstr "" @@ -56456,7 +56502,7 @@ msgstr "" msgid "Variant Of" msgstr "" -#: erpnext/stock/doctype/item/item.js:770 +#: erpnext/stock/doctype/item/item.js:851 msgid "Variant creation has been queued." msgstr "" @@ -57090,7 +57136,7 @@ msgstr "" msgid "Warehouse {0} is not allowed for Sales Order {1}, it should be {2}" msgstr "" -#: erpnext/controllers/stock_controller.py:815 +#: erpnext/controllers/stock_controller.py:820 msgid "Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}." msgstr "" @@ -57191,7 +57237,7 @@ msgstr "" msgid "Warning - Row {0}: Billing Hours are more than Actual Hours" msgstr "" -#: erpnext/stock/stock_ledger.py:824 +#: erpnext/stock/stock_ledger.py:834 msgid "Warning on Negative Stock" msgstr "" @@ -57211,11 +57257,11 @@ msgstr "" msgid "Warning: Material Requested Qty is less than Minimum Order Qty" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1461 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1466 msgid "Warning: Quantity exceeds maximum producible quantity based on quantity of raw materials received through the Subcontracting Inward Order {0}." msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.py:347 +#: erpnext/selling/doctype/sales_order/sales_order.py:348 msgid "Warning: Sales Order {0} already exists against Customer's Purchase Order {1}" msgstr "" @@ -57478,7 +57524,7 @@ msgstr "" msgid "When checked, the system will use the posting datetime of the document for naming the document instead of the creation datetime of the document." msgstr "" -#: erpnext/stock/doctype/item/item.js:1097 +#: erpnext/stock/doctype/item/item.js:1178 msgid "When creating an Item, entering a value for this field will automatically create an Item Price at the backend." msgstr "" @@ -57634,7 +57680,7 @@ msgstr "" #: erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js:29 #: erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py:104 #: erpnext/manufacturing/workspace/manufacturing/manufacturing.json -#: erpnext/selling/doctype/sales_order/sales_order.js:1057 +#: erpnext/selling/doctype/sales_order/sales_order.js:1058 #: erpnext/stock/doctype/material_request/material_request.js:216 #: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request.py:871 @@ -57716,20 +57762,20 @@ msgstr "" msgid "Work Order cannot be created for following reason:
    {0}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:1405 +#: erpnext/manufacturing/doctype/work_order/work_order.py:1410 msgid "Work Order cannot be raised against a Item Template" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.py:2481 -#: erpnext/manufacturing/doctype/work_order/work_order.py:2561 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2486 +#: erpnext/manufacturing/doctype/work_order/work_order.py:2566 msgid "Work Order has been {0}" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1232 +#: erpnext/selling/doctype/sales_order/sales_order.js:1241 msgid "Work Order not created" msgstr "" -#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1390 +#: erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py:1392 msgid "Work Order {0} created" msgstr "" @@ -57746,7 +57792,7 @@ msgstr "" msgid "Work Orders" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:1325 +#: erpnext/selling/doctype/sales_order/sales_order.js:1334 msgid "Work Orders Created: {0}" msgstr "" @@ -57815,7 +57861,7 @@ msgstr "" #: erpnext/manufacturing/doctype/bom_operation/bom_operation.json #: erpnext/manufacturing/doctype/bom_website_operation/bom_website_operation.json #: erpnext/manufacturing/doctype/job_card/job_card.json -#: erpnext/manufacturing/doctype/work_order/work_order.js:329 +#: erpnext/manufacturing/doctype/work_order/work_order.js:339 #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/workstation/workstation.json #: erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js:35 @@ -57890,7 +57936,7 @@ msgstr "" msgid "Workstation Working Hour" msgstr "" -#: erpnext/manufacturing/doctype/workstation/workstation.py:450 +#: erpnext/manufacturing/doctype/workstation/workstation.py:452 msgid "Workstation is closed on the following dates as per Holiday List: {0}" msgstr "" @@ -58168,7 +58214,7 @@ msgstr "" msgid "You cannot edit root node." msgstr "" -#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:182 +#: erpnext/accounts/doctype/accounts_settings/accounts_settings.py:189 msgid "You cannot enable both the settings '{0}' and '{1}'." msgstr "" @@ -58200,7 +58246,7 @@ msgstr "" msgid "You cannot {0} this document because another Period Closing Entry {1} exists after {2}" msgstr "" -#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:567 +#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:580 msgid "You do not have permission to edit this document" msgstr "" @@ -58216,6 +58262,18 @@ msgstr "" msgid "You don't have enough points to redeem." msgstr "" +#: erpnext/controllers/accounts_controller.py:4419 +msgid "You don't have permission to create a Company Address. Please contact your System Manager." +msgstr "" + +#: erpnext/controllers/accounts_controller.py:4399 +msgid "You don't have permission to update Company details. Please contact your System Manager." +msgstr "" + +#: erpnext/controllers/accounts_controller.py:4393 +msgid "You don't have permission to update this document. Please contact your System Manager." +msgstr "" + #: erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py:291 msgid "You had {} errors while creating opening invoices. Check {} for more details" msgstr "" @@ -58240,7 +58298,7 @@ msgstr "" msgid "You have entered a duplicate Delivery Note on Row" msgstr "" -#: erpnext/stock/doctype/item/item.py:1080 +#: erpnext/stock/doctype/item/item.py:1093 msgid "You have to enable auto re-order in Stock Settings to maintain re-order levels." msgstr "" @@ -58320,11 +58378,11 @@ msgstr "" msgid "[Important] [ERPNext] Auto Reorder Errors" msgstr "" -#: erpnext/controllers/status_updater.py:291 +#: erpnext/controllers/status_updater.py:302 msgid "`Allow Negative rates for Items`" msgstr "" -#: erpnext/stock/stock_ledger.py:2054 +#: erpnext/stock/stock_ledger.py:2064 msgid "after" msgstr "" @@ -58432,11 +58490,6 @@ msgstr "" msgid "hours" msgstr "" -#. Label of the image (Attach Image) field in DocType 'Batch' -#: erpnext/stock/doctype/batch/batch.json -msgid "image" -msgstr "" - #. Label of the lft (Int) field in DocType 'Cost Center' #. Label of the lft (Int) field in DocType 'Location' #. Label of the lft (Int) field in DocType 'Task' @@ -58472,7 +58525,7 @@ msgstr "" msgid "must be between 0 and 100" msgstr "" -#: erpnext/selling/doctype/sales_order/sales_order.js:645 +#: erpnext/selling/doctype/sales_order/sales_order.js:646 msgid "name" msgstr "" @@ -58514,7 +58567,7 @@ msgstr "" msgid "per hour" msgstr "" -#: erpnext/stock/stock_ledger.py:2055 +#: erpnext/stock/stock_ledger.py:2065 msgid "performing either one below:" msgstr "" @@ -58586,8 +58639,8 @@ msgstr "" msgid "subscription is already cancelled." msgstr "" -#: erpnext/controllers/status_updater.py:466 -#: erpnext/controllers/status_updater.py:485 +#: erpnext/controllers/status_updater.py:481 +#: erpnext/controllers/status_updater.py:500 msgid "target_ref_field" msgstr "" @@ -58676,11 +58729,11 @@ msgstr "" msgid "{0} Number {1} is already used in {2} {3}" msgstr "" -#: erpnext/manufacturing/doctype/bom/bom.py:1684 +#: erpnext/manufacturing/doctype/bom/bom.py:1679 msgid "{0} Operating Cost for operation {1}" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:562 +#: erpnext/manufacturing/doctype/work_order/work_order.js:573 msgid "{0} Operations: {1}" msgstr "" @@ -58913,27 +58966,27 @@ msgstr "" msgid "{0} is open. Close the POS or cancel the existing POS Opening Entry to create a new POS Opening Entry." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:527 +#: erpnext/manufacturing/doctype/work_order/work_order.js:538 msgid "{0} items disassembled" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:491 +#: erpnext/manufacturing/doctype/work_order/work_order.js:502 msgid "{0} items in progress" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:515 +#: erpnext/manufacturing/doctype/work_order/work_order.js:526 msgid "{0} items lost during process." msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:472 +#: erpnext/manufacturing/doctype/work_order/work_order.js:483 msgid "{0} items produced" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:495 +#: erpnext/manufacturing/doctype/work_order/work_order.js:506 msgid "{0} items returned" msgstr "" -#: erpnext/manufacturing/doctype/work_order/work_order.js:498 +#: erpnext/manufacturing/doctype/work_order/work_order.js:509 msgid "{0} items to return" msgstr "" @@ -58957,7 +59010,7 @@ msgstr "" msgid "{0} payment entries can not be filtered by {1}" msgstr "" -#: erpnext/controllers/stock_controller.py:1734 +#: erpnext/controllers/stock_controller.py:1739 msgid "{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}." msgstr "" @@ -58977,16 +59030,16 @@ msgstr "" msgid "{0} units of {1} are required in {2} with the inventory dimension: {3} on {4} {5} for {6} to complete the transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:1707 erpnext/stock/stock_ledger.py:2203 -#: erpnext/stock/stock_ledger.py:2217 +#: erpnext/stock/stock_ledger.py:1717 erpnext/stock/stock_ledger.py:2213 +#: erpnext/stock/stock_ledger.py:2227 msgid "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:2304 erpnext/stock/stock_ledger.py:2349 +#: erpnext/stock/stock_ledger.py:2314 erpnext/stock/stock_ledger.py:2359 msgid "{0} units of {1} needed in {2} on {3} {4} to complete this transaction." msgstr "" -#: erpnext/stock/stock_ledger.py:1701 +#: erpnext/stock/stock_ledger.py:1711 msgid "{0} units of {1} needed in {2} to complete this transaction." msgstr "" @@ -58998,7 +59051,7 @@ msgstr "" msgid "{0} valid serial nos for Item {1}" msgstr "" -#: erpnext/stock/doctype/item/item.js:775 +#: erpnext/stock/doctype/item/item.js:856 msgid "{0} variants created." msgstr "" @@ -59053,7 +59106,7 @@ msgid "{0} {1} has already been partly paid. Please use the 'Get Outstanding Inv msgstr "" #: erpnext/buying/doctype/purchase_order/purchase_order.py:434 -#: erpnext/selling/doctype/sales_order/sales_order.py:598 +#: erpnext/selling/doctype/sales_order/sales_order.py:599 #: erpnext/stock/doctype/material_request/material_request.py:255 msgid "{0} {1} has been modified. Please refresh." msgstr "" @@ -59128,8 +59181,8 @@ msgstr "" msgid "{0} {1} must be submitted" msgstr "" -#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:277 -msgid "{0} {1} not allowed to be reposted. Modify {2} to enable reposting." +#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:276 +msgid "{0} {1} not allowed to be reposted. You can enable it by adding it '{2}' table in {3}." msgstr "" #: erpnext/buying/utils.py:116 @@ -59163,7 +59216,7 @@ msgstr "" msgid "{0} {1}: Accounting Entry for {2} can only be made in currency: {3}" msgstr "" -#: erpnext/controllers/stock_controller.py:947 +#: erpnext/controllers/stock_controller.py:952 msgid "{0} {1}: Cost Center is mandatory for Item {2}" msgstr "" @@ -59265,7 +59318,7 @@ msgstr "" msgid "{field_label} is mandatory for sub-contracted {doctype}." msgstr "" -#: erpnext/controllers/stock_controller.py:2141 +#: erpnext/controllers/stock_controller.py:2146 msgid "{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})" msgstr "" From 5923618df337d446e43652279e8f54d37c0459d1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 00:24:36 +0530 Subject: [PATCH 57/57] refactor(test): move contact and address creation to bootstrap (backport #54406) (#54410) Co-authored-by: ruthra kumar --- .../accounts/doctype/account/test_account.py | 66 ----------------- .../test_accounts_receivable.py | 44 +++++------ .../selling/doctype/customer/test_customer.py | 4 +- .../delivery_trip/test_delivery_trip.py | 3 +- erpnext/tests/utils.py | 73 ++++++++----------- 5 files changed, 50 insertions(+), 140 deletions(-) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 95f17967fa7..f840ac86207 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -321,72 +321,6 @@ class TestAccount(ERPNextTestSuite): self.assertEqual(balance, 0) -def _make_test_records(verbose=None): - from frappe.tests.utils import make_test_objects - - accounts = [ - # [account_name, parent_account, is_group] - ["_Test Bank", "Bank Accounts", 0, "Bank", None], - ["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"], - ["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"], - ["_Test Cash", "Cash In Hand", 0, "Cash", None], - ["_Test Account Stock Expenses", "Direct Expenses", 1, None, None], - ["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None], - ["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None], - ["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None], - ["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None], - ["_Test Employee Advance", "Current Liabilities", 0, None, None], - ["_Test Account Tax Assets", "Current Assets", 1, None, None], - ["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None], - ["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None], - ["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None], - ["_Test Account Cost for Goods Sold", "Expenses", 0, None, None], - ["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None], - ["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None], - ["_Test Account S&H Education Cess", "_Test Account Tax Assets", 0, "Tax", None], - ["_Test Account CST", "Direct Expenses", 0, "Tax", None], - ["_Test Account Discount", "Direct Expenses", 0, None, None], - ["_Test Write Off", "Indirect Expenses", 0, None, None], - ["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None], - ["_Test Account Sales", "Direct Income", 0, None, None], - # related to Account Inventory Integration - ["_Test Account Stock In Hand", "Current Assets", 0, None, None], - # fixed asset depreciation - ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], - ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None], - ["_Test Depreciations", "Expenses", 0, "Depreciation", None], - ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], - # Receivable / Payable Account - ["_Test Receivable", "Current Assets", 0, "Receivable", None], - ["_Test Payable", "Current Liabilities", 0, "Payable", None], - ["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"], - ["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"], - ] - - for company, abbr in [ - ["_Test Company", "_TC"], - ["_Test Company 1", "_TC1"], - ["_Test Company with perpetual inventory", "TCP1"], - ]: - test_objects = make_test_objects( - "Account", - [ - { - "doctype": "Account", - "account_name": account_name, - "parent_account": parent_account + " - " + abbr, - "company": company, - "is_group": is_group, - "account_type": account_type, - "account_currency": currency, - } - for account_name, parent_account, is_group, account_type, currency in accounts - ], - ) - - return test_objects - - def get_inventory_account(company, warehouse=None): account = None if warehouse: diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 7dc84943d23..9b8b8b709db 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -774,22 +774,18 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin): def test_party_account_filter(self): si1 = self.create_sales_invoice() - self.customer2 = ( - frappe.get_doc( - { - "doctype": "Customer", - "customer_name": "Jane Doe", - "type": "Individual", - "default_currency": "USD", - } - ) - .insert() - .submit() - ) + jane = frappe.get_doc( + { + "doctype": "Customer", + "customer_name": "Jane Doe", + "type": "Individual", + "default_currency": "USD", + } + ).insert() + self.customer = jane.name si2 = self.create_sales_invoice(do_not_submit=True) si2.posting_date = add_days(today(), -1) - si2.customer = self.customer2.name si2.currency = "USD" si2.conversion_rate = 80 si2.debit_to = self.debtors_usd @@ -997,22 +993,18 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin): self.assertEqual(expected_data, report_output) def test_future_payments_on_foreign_currency(self): - self.customer2 = ( - frappe.get_doc( - { - "doctype": "Customer", - "customer_name": "Jane Doe", - "type": "Individual", - "default_currency": "USD", - } - ) - .insert() - .submit() - ) + jane = frappe.get_doc( + { + "doctype": "Customer", + "customer_name": "Jane Doe", + "type": "Individual", + "default_currency": "USD", + } + ).insert() + self.customer = jane.name si = self.create_sales_invoice(do_not_submit=True) si.posting_date = add_days(today(), -1) - si.customer = self.customer2.name si.currency = "USD" si.conversion_rate = 80 si.debit_to = self.debtors_usd diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 211343966f9..5eef7f95651 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -14,7 +14,7 @@ from erpnext.selling.doctype.customer.customer import ( get_customer_outstanding, parse_full_name, ) -from erpnext.tests.utils import ERPNextTestSuite, create_test_contact_and_address +from erpnext.tests.utils import ERPNextTestSuite class TestCustomer(ERPNextTestSuite): @@ -71,8 +71,6 @@ class TestCustomer(ERPNextTestSuite): "customer_name": "_Test Customer", } - create_test_contact_and_address() - frappe.db.set_value( "Contact", "_Test Contact for _Test Customer-_Test Customer", "is_primary_contact", 1 ) diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py index 783a28a47d8..83b7395f342 100644 --- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py @@ -10,7 +10,7 @@ from erpnext.stock.doctype.delivery_trip.delivery_trip import ( get_contact_and_address, notify_customers, ) -from erpnext.tests.utils import ERPNextTestSuite, create_test_contact_and_address +from erpnext.tests.utils import ERPNextTestSuite class TestDeliveryTrip(ERPNextTestSuite): @@ -19,7 +19,6 @@ class TestDeliveryTrip(ERPNextTestSuite): driver = create_driver() create_vehicle() create_delivery_notification() - create_test_contact_and_address() address = create_address(driver) self.delivery_trip = create_delivery_trip(driver, address, company="_Test Company") diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index 7ea4f10a423..15c89a24b4f 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -16,49 +16,6 @@ ReportFilters = dict[str, Any] ReportName = NewType("ReportName", str) -def create_test_contact_and_address(): - frappe.db.sql("delete from tabContact") - frappe.db.sql("delete from `tabContact Email`") - frappe.db.sql("delete from `tabContact Phone`") - frappe.db.sql("delete from tabAddress") - frappe.db.sql("delete from `tabDynamic Link`") - - frappe.get_doc( - { - "doctype": "Address", - "address_title": "_Test Address for Customer", - "address_type": "Office", - "address_line1": "Station Road", - "city": "_Test City", - "state": "Test State", - "country": "India", - "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], - } - ).insert() - - contact = frappe.get_doc( - { - "doctype": "Contact", - "first_name": "_Test Contact for _Test Customer", - "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], - } - ) - contact.add_email("test_contact_customer@example.com", is_primary=True) - contact.add_phone("+91 0000000000", is_primary_phone=True) - contact.insert() - - contact_two = frappe.get_doc( - { - "doctype": "Contact", - "first_name": "_Test Contact 2 for _Test Customer", - "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], - } - ) - contact_two.add_email("test_contact_two_customer@example.com", is_primary=True) - contact_two.add_phone("+92 0000000000", is_primary_phone=True) - contact_two.insert() - - def execute_script_report( report_name: ReportName, module: str, @@ -242,6 +199,7 @@ class BootStrapTestData: self.make_sales_partner() self.make_activity_type() self.make_address() + self.make_contact() self.update_support_settings() self.update_selling_settings() self.update_stock_settings() @@ -2883,9 +2841,38 @@ class BootStrapTestData: {"link_doctype": "Customer", "link_name": "_Test Customer 1", "doctype": "Dynamic Link"} ], }, + { + "doctype": "Address", + "address_title": "_Test Address for Customer", + "address_type": "Office", + "address_line1": "Station Road", + "city": "_Test City", + "state": "Test State", + "country": "India", + "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], + }, ] self.make_records(["address_title", "address_type"], records) + def make_contact(self): + records = [ + { + "doctype": "Contact", + "first_name": "_Test Contact for _Test Customer", + "email_ids": [{"email_id": "test_contact_customer@example.com", "is_primary": True}], + "phone_nos": [{"phone": "+91 0000000000", "is_primary_phone": True}], + "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], + }, + { + "doctype": "Contact", + "first_name": "_Test Contact 2 for _Test Customer", + "email_ids": [{"email_id": "test_contact_two_customer@example.com", "is_primary": True}], + "phone_nos": [{"phone": "+92 0000000000", "is_primary_phone": True}], + "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], + }, + ] + self.make_records(["first_name"], records) + def make_dimensions(self): records = [ {