From 38a46424799bb4dc2481d8aa884c0e051874a93f Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:10:38 +0530 Subject: [PATCH 001/106] feat: modify field properties --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 8 +++----- .../sales_invoice_timesheet/sales_invoice_timesheet.json | 6 +++--- erpnext/projects/doctype/timesheet/timesheet.json | 7 ++++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index a889ca3c2ed..80bc226388c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -772,8 +772,7 @@ }, { "collapsible": 1, - "collapsible_depends_on": "eval:doc.total_billing_amount > 0", - "depends_on": "eval:!doc.is_return", + "depends_on": "eval:doc.total_billing_amount > 0", "fieldname": "time_sheet_list", "fieldtype": "Section Break", "hide_border": 1, @@ -787,7 +786,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Time Sheets", - "no_copy": 1, "options": "Sales Invoice Timesheet", "print_hide": 1 }, @@ -2086,7 +2084,7 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)", + "depends_on": "eval:doc.total_billing_amount > 0", "fieldname": "section_break_104", "fieldtype": "Section Break" }, @@ -2260,7 +2258,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-10-09 14:48:59.472826", + "modified": "2025-12-23 20:09:14.424656", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json index 1302fd38b26..cf9a90062b2 100644 --- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json +++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json @@ -52,7 +52,6 @@ "fieldtype": "Data", "hidden": 1, "label": "Timesheet Detail", - "no_copy": 1, "print_hide": 1, "read_only": 1 }, @@ -117,15 +116,16 @@ ], "istable": 1, "links": [], - "modified": "2024-03-27 13:10:36.562795", + "modified": "2025-12-23 13:54:17.677187", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Timesheet", "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/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 255f7e8ed97..0022ef9193b 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -91,7 +91,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "Draft\nSubmitted\nBilled\nPayslip\nCompleted\nCancelled", + "options": "Draft\nSubmitted\nPartially Billed\nBilled\nPayslip\nCompleted\nCancelled", "print_hide": 1, "read_only": 1 }, @@ -310,7 +310,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2024-03-27 13:10:53.551907", + "modified": "2025-12-19 13:48:23.453636", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", @@ -386,8 +386,9 @@ "write": 1 } ], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "ASC", "states": [], "title_field": "title" -} \ No newline at end of file +} From c87b5d31323ab7018f8080b8e1dafac3e470c7c6 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:13:54 +0530 Subject: [PATCH 002/106] feat(timesheet): handle partial billing in sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 39 +++++++++++++++---- .../projects/doctype/timesheet/timesheet.py | 9 ++++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 46797291d92..fbfb5a4c09a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -340,10 +340,17 @@ class SalesInvoice(SellingController): self.is_opening = "No" self.set_against_income_account() - self.validate_time_sheets_are_submitted() + + if not self.is_return: + self.validate_time_sheets_are_submitted() + self.validate_multiple_billing("Delivery Note", "dn_detail", "amount") - if self.is_return: - self.timesheets = [] + + if self.is_return and self.return_against: + for row in self.timesheets: + row.billing_hours *= -1 + row.billing_amount *= -1 + self.update_packing_list() self.set_billing_hours_and_amount() self.update_timesheet_billing_for_project() @@ -502,7 +509,7 @@ class SalesInvoice(SellingController): if cint(self.is_pos) != 1 and not self.is_return: self.update_against_document_in_jv() - self.update_time_sheet(self.name) + self.update_time_sheet(None if (self.is_return and self.return_against) else self.name) if frappe.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction": update_company_current_month_sales(self.company) @@ -820,8 +827,19 @@ class SalesInvoice(SellingController): for data in timesheet.time_logs: if ( (self.project and args.timesheet_detail == data.name) - or (not self.project and not data.sales_invoice) - or (not sales_invoice and data.sales_invoice == self.name) + or (not self.project and not data.sales_invoice and args.timesheet_detail == data.name) + or ( + not sales_invoice + and data.sales_invoice == self.name + and args.timesheet_detail == data.name + ) + or ( + self.is_return + and self.return_against + and data.sales_invoice + and not sales_invoice + and args.timesheet_detail == data.name + ) ): data.sales_invoice = sales_invoice @@ -864,7 +882,7 @@ class SalesInvoice(SellingController): for data in self.timesheets: if data.time_sheet: status = frappe.db.get_value("Timesheet", data.time_sheet, "status") - if status not in ["Submitted", "Payslip"]: + if status not in ["Submitted", "Payslip", "Partially Billed"]: frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) def set_pos_fields(self, for_validate=False): @@ -1299,7 +1317,12 @@ class SalesInvoice(SellingController): timesheet.billing_amount = ts_doc.total_billable_amount def update_timesheet_billing_for_project(self): - if not self.timesheets and self.project and self.is_auto_fetch_timesheet_enabled(): + if ( + not self.is_return + and not self.timesheets + and self.project + and self.is_auto_fetch_timesheet_enabled() + ): self.add_timesheet_data() else: self.calculate_billing_amount_for_timesheet() diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 5ef50218a3f..347b09b4656 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -51,7 +51,9 @@ class Timesheet(Document): per_billed: DF.Percent sales_invoice: DF.Link | None start_date: DF.Date | None - status: DF.Literal["Draft", "Submitted", "Billed", "Payslip", "Completed", "Cancelled"] + status: DF.Literal[ + "Draft", "Submitted", "Partially Billed", "Billed", "Payslip", "Completed", "Cancelled" + ] time_logs: DF.Table[TimesheetDetail] title: DF.Data | None total_billable_amount: DF.Currency @@ -120,6 +122,9 @@ class Timesheet(Document): if flt(self.per_billed, self.precision("per_billed")) >= 100.0: self.status = "Billed" + if 0.0 < flt(self.per_billed, self.precision("per_billed")) < 100.0: + self.status = "Partially Billed" + if self.sales_invoice: self.status = "Completed" @@ -425,7 +430,7 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None target.append("items", {"item_code": item_code, "qty": hours, "rate": billing_rate}) for time_log in timesheet.time_logs: - if time_log.is_billable: + if time_log.is_billable and not time_log.sales_invoice: target.append( "timesheets", { From ff0b37055b9890ab368fc60553103dd2eca46a35 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:14:45 +0530 Subject: [PATCH 003/106] feat: add list_view status for partial billing --- erpnext/projects/doctype/timesheet/timesheet_list.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet_list.js b/erpnext/projects/doctype/timesheet/timesheet_list.js index 0de568ce589..b733cccc787 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_list.js +++ b/erpnext/projects/doctype/timesheet/timesheet_list.js @@ -1,6 +1,10 @@ frappe.listview_settings["Timesheet"] = { add_fields: ["status", "total_hours", "start_date", "end_date"], get_indicator: function (doc) { + if (doc.status == "Partially Billed") { + return [__("Partially Billed"), "orange", "status,=," + "Partially Billed"]; + } + if (doc.status == "Billed") { return [__("Billed"), "green", "status,=," + "Billed"]; } From 57d34ab146868f50884a52dfdcbf76ec0d267925 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 18:35:48 +0530 Subject: [PATCH 004/106] fix: include total hours validation in depends on --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 80bc226388c..afc19b02adc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -772,7 +772,7 @@ }, { "collapsible": 1, - "depends_on": "eval:doc.total_billing_amount > 0", + "collapsible_depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0", "fieldname": "time_sheet_list", "fieldtype": "Section Break", "hide_border": 1, @@ -2084,7 +2084,7 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.total_billing_amount > 0", + "depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0", "fieldname": "section_break_104", "fieldtype": "Section Break" }, @@ -2258,7 +2258,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-12-23 20:09:14.424656", + "modified": "2025-12-24 18:29:50.242618", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From ae594e81f98a6edd904755d70404427c7bb1e942 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 22:08:28 +0530 Subject: [PATCH 005/106] test: add test for partial billing and return --- .../doctype/timesheet/test_timesheet.py | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 046db983dc0..4735b3df870 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -7,6 +7,7 @@ import frappe from frappe.tests import IntegrationTestCase from frappe.utils import add_to_date, now_datetime, nowdate +from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.projects.doctype.timesheet.timesheet import OverlapError, make_sales_invoice from erpnext.setup.doctype.employee.test_employee import make_employee @@ -207,6 +208,60 @@ class TestTimesheet(ERPNextTestSuite): ts.calculate_percentage_billed() self.assertEqual(ts.per_billed, 100) + def test_partial_billing_and_return(self): + """ + Test Timesheet status transitions during partial billing, full billing, + sales return, and return cancellation. + + Scenario: + 1. Create a Timesheet with two billable time logs. + 2. Create a Sales Invoice billing only one time log → Timesheet becomes Partially Billed. + 3. Create another Sales Invoice billing the remaining time log → Timesheet becomes Billed. + 4. Create a Sales Return against the second invoice → Timesheet reverts to Partially Billed. + 5. Cancel the Sales Return → Timesheet returns to Billed status. + + This test ensures Timesheet status is recalculated correctly + across billing and return lifecycle events. + """ + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, is_billable=1, do_not_submit=True) + timesheet_detail = timesheet.append("time_logs", {}) + timesheet_detail.is_billable = 1 + timesheet_detail.activity_type = "_Test Activity Type" + timesheet_detail.from_time = timesheet.time_logs[0].to_time + datetime.timedelta(minutes=1) + timesheet_detail.hours = 2 + timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta( + hours=timesheet_detail.hours + ) + timesheet.save().submit() + + sales_invoice = make_sales_invoice(timesheet.name, "_Test Item", "_Test Customer", currency="INR") + sales_invoice.due_date = nowdate() + sales_invoice.timesheets.pop() + sales_invoice.submit() + + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Partially Billed") + + sales_invoice2 = make_sales_invoice(timesheet.name, "_Test Item", "_Test Customer", currency="INR") + sales_invoice2.due_date = nowdate() + sales_invoice2.submit() + + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Billed") + + sales_return = make_sales_return(sales_invoice2.name).submit() + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Partially Billed") + + sales_return.load_from_db() + sales_return.cancel() + + timesheet.load_from_db() + self.assertEqual(timesheet.time_logs[1].sales_invoice, sales_invoice2.name) + self.assertEqual(timesheet.status, "Billed") + def make_timesheet( employee, @@ -218,6 +273,7 @@ def make_timesheet( company=None, currency=None, exchange_rate=None, + do_not_submit=False, ): update_activity_type(activity_type) timesheet = frappe.new_doc("Timesheet") @@ -246,7 +302,8 @@ def make_timesheet( else: timesheet.save(ignore_permissions=True) - timesheet.submit() + if not do_not_submit: + timesheet.submit() return timesheet From 50f73a5072b471e347decf2819879a5a957dbe0a Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 22:09:03 +0530 Subject: [PATCH 006/106] fix: handle return cancellation --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fbfb5a4c09a..000d1f602c5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -589,7 +589,7 @@ class SalesInvoice(SellingController): self.check_if_consolidated_invoice() super().before_cancel() - self.update_time_sheet(None) + self.update_time_sheet(self.return_against if (self.is_return and self.return_against) else None) def on_cancel(self): check_if_return_invoice_linked_with_payment_entry(self) From 9a2710b9d7f82e17f83303b2b382f11912f8fae3 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 29 Dec 2025 15:40:46 +0530 Subject: [PATCH 007/106] fix(asset): handle partial asset sales by splitting remaining quantity --- .../doctype/sales_invoice/sales_invoice.py | 38 ++++++++++ erpnext/assets/doctype/asset/asset.js | 69 ++++++++++++++----- erpnext/assets/doctype/asset/asset.py | 4 +- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 18a86434ef9..13413ad2ab7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -33,6 +33,7 @@ from erpnext.accounts.utils import ( get_account_currency, update_voucher_outstanding, ) +from erpnext.assets.doctype.asset.asset import split_asset from erpnext.assets.doctype.asset.depreciation import ( depreciate_asset, get_gl_entries_on_asset_disposal, @@ -468,6 +469,8 @@ class SalesInvoice(SellingController): self.update_stock_reservation_entries() self.update_stock_ledger() + self.split_asset_based_on_sale_qty() + self.process_asset_depreciation() # this sequence because outstanding may get -ve @@ -1358,6 +1361,41 @@ class SalesInvoice(SellingController): ): throw(_("Delivery Note {0} is not submitted").format(d.delivery_note)) + def split_asset_based_on_sale_qty(self): + asset_qty_map = self.get_asset_qty() + for asset, qty in asset_qty_map.items(): + remaining_qty = qty["actual_qty"] - qty["sale_qty"] + if remaining_qty > 0: + split_asset(asset, remaining_qty) + + def get_asset_qty(self): + asset_qty_map = {} + + assets = {row.asset for row in self.items if row.is_fixed_asset and row.asset} + if not assets or self.is_return: + return asset_qty_map + + asset_actual_qty = dict( + frappe.db.get_all( + "Asset", + {"name": ["in", list(assets)]}, + ["name", "asset_quantity"], + as_list=True, + ) + ) + for row in self.items: + if row.is_fixed_asset and row.asset: + actual_qty = asset_actual_qty.get(row.asset) + asset_qty_map.setdefault( + row.asset, + { + "sale_qty": flt(row.qty), + "actual_qty": flt(actual_qty), + }, + ) + + return asset_qty_map + def process_asset_depreciation(self): if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1): self.depreciate_asset_on_sale() diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index e0b4593416a..ec167fafb3b 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -111,7 +111,7 @@ frappe.ui.form.on("Asset", { frm.add_custom_button( __("Sell Asset"), function () { - frm.trigger("make_sales_invoice"); + frm.trigger("sell_asset"); }, __("Manage") ); @@ -484,22 +484,6 @@ frappe.ui.form.on("Asset", { frm.trigger("toggle_reference_doc"); }, - make_sales_invoice: function (frm) { - frappe.call({ - args: { - asset: frm.doc.name, - item_code: frm.doc.item_code, - company: frm.doc.company, - serial_no: frm.doc.serial_no, - }, - method: "erpnext.assets.doctype.asset.asset.make_sales_invoice", - callback: function (r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - }, - }); - }, - create_asset_maintenance: function (frm) { frappe.call({ args: { @@ -548,6 +532,57 @@ frappe.ui.form.on("Asset", { }); }, + sell_asset: function (frm) { + const make_sales_invoice = (sell_qty) => { + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.make_sales_invoice", + args: { + asset: frm.doc.name, + item_code: frm.doc.item_code, + company: frm.doc.company, + serial_no: frm.doc.serial_no, + sell_qty: sell_qty, + }, + callback: function (r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + }, + }); + }; + + const dialog = new frappe.ui.Dialog({ + title: __("Sell Asset"), + fields: [ + { + fieldname: "sell_qty", + fieldtype: "Int", + label: __("Sell Qty"), + reqd: 1, + }, + ], + }); + + dialog.set_primary_action(__("Sell"), function () { + const dialog_data = dialog.get_values(); + const sell_qty = cint(dialog_data.sell_qty); + + if (sell_qty < cint(frm.doc.asset_quantity)) { + frappe.confirm( + __( + "The sell quantity is less than the total asset quantity. The remaining quantity will be split into a new asset. This action cannot be undone.
Do you want to continue?" + ), + () => make_sales_invoice(sell_qty) + ); + } else { + make_sales_invoice(sell_qty); + } + + dialog.hide(); + }); + + dialog.show(); + }, + split_asset: function (frm) { const title = __("Split Asset"); diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 701be98a0d6..1e64f74b372 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -1083,7 +1083,7 @@ def get_asset_naming_series(): @frappe.whitelist() -def make_sales_invoice(asset, item_code, company, serial_no=None, posting_date=None): +def make_sales_invoice(asset, item_code, company, sell_qty, serial_no=None): asset_doc = frappe.get_doc("Asset", asset) si = frappe.new_doc("Sales Invoice") si.company = company @@ -1098,7 +1098,7 @@ def make_sales_invoice(asset, item_code, company, serial_no=None, posting_date=N "income_account": disposal_account, "serial_no": serial_no, "cost_center": depreciation_cost_center, - "qty": 1, + "qty": sell_qty, }, ) From a88fe2ecab9d35ab4a483c63f4df3f6aa8eefc2c Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 29 Dec 2025 15:53:31 +0530 Subject: [PATCH 008/106] fix: refactor older testcases --- erpnext/assets/doctype/asset/test_asset.py | 8 ++++++-- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c54c39ab7c8..d86e1836108 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -330,7 +330,9 @@ class TestAsset(AssetSetup): post_depreciation_entries(date=add_months(purchase_date, 2)) - si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company") + si = make_sales_invoice( + asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity + ) si.customer = "_Test Customer" si.due_date = date si.get("items")[0].rate = 25000 @@ -458,7 +460,9 @@ class TestAsset(AssetSetup): post_depreciation_entries(date="2021-01-01") - si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company") + si = make_sales_invoice( + asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity + ) si.customer = "_Test Customer" si.due_date = nowdate() si.get("items")[0].rate = 25000 diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 15ceb51648b..d085a4c6e4b 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -51,7 +51,9 @@ class TestAssetRepair(IntegrationTestCase): submit=1, ) - si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company") + si = make_sales_invoice( + asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=asset.asset_quantity + ) si.customer = "_Test Customer" si.due_date = date si.get("items")[0].rate = 25000 From 9eeccb765d715d4858dbb75c53d0a01bffcd1f56 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 29 Dec 2025 22:14:26 +0530 Subject: [PATCH 009/106] test: validate asset partial sales --- erpnext/assets/doctype/asset/test_asset.py | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index d86e1836108..4e46a809b7f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1695,6 +1695,59 @@ class TestDepreciationBasics(AssetSetup): pr.submit() self.assertTrue(get_gl_entries("Purchase Receipt", pr.name)) + def test_partial_asset_sale_for_existing_asset(self): + date = nowdate() + purchase_date = add_months(get_first_day(date), -2) + depreciation_start_date = add_months(get_last_day(date), -2) + + # create an asset + asset = create_asset( + item_code="Macbook Pro", + is_existing_asset=1, + calculate_depreciation=1, + available_for_use_date=purchase_date, + purchase_date=purchase_date, + depreciation_start_date=depreciation_start_date, + net_purchase_amount=1000000, + purchase_amount=1000000, + asset_quantity=10, + total_number_of_depreciations=12, + frequency_of_depreciation=1, + submit=1, + ) + asset_depr_schedule_before_sale = get_asset_depr_schedule_doc(asset.name, "Active") + post_depreciation_entries(date) + asset.reload() + + # check asset values before sale + self.assertEqual(asset.asset_quantity, 10) + self.assertEqual(asset.net_purchase_amount, 1000000) + self.assertEqual(asset.status, "Partially Depreciated") + self.assertEqual( + asset_depr_schedule_before_sale.depreciation_schedule[0].get("depreciation_amount"), 83333.33 + ) + + # make a partial sales againt the asset + si = make_sales_invoice( + asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=5 + ) + si.customer = "_Test Customer" + si.due_date = date + si.get("items")[0].rate = 25000 + si.insert() + si.submit() + + asset.reload() + asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset.name, "Active") + + # check asset values after sales + self.assertEqual(asset.asset_quantity, 5) + self.assertEqual(asset.net_purchase_amount, 500000) + self.assertEqual(asset.status, "Sold") + self.assertEqual( + asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66 + ) + def get_gl_entries(doctype, docname): gl_entry = frappe.qb.DocType("GL Entry") From e7e656779299f9e339ffdd440d3e964762c1a03d Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 30 Dec 2025 12:09:37 +0530 Subject: [PATCH 010/106] fix(asset): skip purchase document validation while splitting existing asset --- erpnext/assets/doctype/asset/asset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 1e64f74b372..b929eda7d6a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -482,6 +482,9 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_linked_purchase_documents(self): + if self.flags.is_split_asset: + return + for fieldname, doctype in [ ("purchase_receipt", "Purchase Receipt"), ("purchase_invoice", "Purchase Invoice"), @@ -1378,6 +1381,7 @@ def process_asset_split(existing_asset, split_qty, splitted_asset=None, is_new_a scaling_factor = flt(split_qty) / flt(existing_asset.asset_quantity) new_asset = frappe.copy_doc(existing_asset) if is_new_asset else splitted_asset asset_doc = new_asset if is_new_asset else existing_asset + asset_doc.flags.is_split_asset = True set_split_asset_values(asset_doc, scaling_factor, split_qty, existing_asset, is_new_asset) log_asset_activity(existing_asset, asset_doc, splitted_asset, is_new_asset) From 23b094f151ab606f52142ff2789ad80fb97b574c Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 30 Dec 2025 14:47:28 +0530 Subject: [PATCH 011/106] fix(asset): handle same asset being sold in multiple line items in sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 24 ++++++++++---- erpnext/assets/doctype/asset/asset.js | 32 +++++++++++++------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 13413ad2ab7..9ff85096bd6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1364,6 +1364,13 @@ class SalesInvoice(SellingController): def split_asset_based_on_sale_qty(self): asset_qty_map = self.get_asset_qty() for asset, qty in asset_qty_map.items(): + if qty["actual_qty"] < qty["sale_qty"]: + frappe.throw( + _( + "Sell quantity cannot exceed the asset quantity. Asset {0} has only {1} item(s)." + ).format(asset, qty["actual_qty"]) + ) + remaining_qty = qty["actual_qty"] - qty["sale_qty"] if remaining_qty > 0: split_asset(asset, remaining_qty) @@ -1386,13 +1393,16 @@ class SalesInvoice(SellingController): for row in self.items: if row.is_fixed_asset and row.asset: actual_qty = asset_actual_qty.get(row.asset) - asset_qty_map.setdefault( - row.asset, - { - "sale_qty": flt(row.qty), - "actual_qty": flt(actual_qty), - }, - ) + if row.asset in asset_qty_map.keys(): + asset_qty_map[row.asset]["sale_qty"] += flt(row.qty) + else: + asset_qty_map.setdefault( + row.asset, + { + "sale_qty": flt(row.qty), + "actual_qty": flt(actual_qty), + }, + ) return asset_qty_map diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index ec167fafb3b..264690872f2 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -550,7 +550,7 @@ frappe.ui.form.on("Asset", { }); }; - const dialog = new frappe.ui.Dialog({ + let dialog = new frappe.ui.Dialog({ title: __("Sell Asset"), fields: [ { @@ -565,18 +565,30 @@ frappe.ui.form.on("Asset", { dialog.set_primary_action(__("Sell"), function () { const dialog_data = dialog.get_values(); const sell_qty = cint(dialog_data.sell_qty); + const asset_qty = cint(frm.doc.asset_quantity); - if (sell_qty < cint(frm.doc.asset_quantity)) { - frappe.confirm( - __( - "The sell quantity is less than the total asset quantity. The remaining quantity will be split into a new asset. This action cannot be undone.
Do you want to continue?" - ), - () => make_sales_invoice(sell_qty) - ); - } else { - make_sales_invoice(sell_qty); + if (sell_qty <= 0) { + frappe.throw(__("Sell quantity must be greater than zero")); } + if (sell_qty > asset_qty) { + frappe.throw(__("Sell quantity cannot exceed the asset quantity")); + } + + if (sell_qty < asset_qty) { + frappe.confirm( + __( + "The sell quantity is less than the total asset quantity. The remaining quantity will be split into a new asset. This action cannot be undone.

Do you want to continue?" + ), + () => { + make_sales_invoice(sell_qty); + dialog.hide(); + } + ); + return; + } + + make_sales_invoice(sell_qty); dialog.hide(); }); From 4adeaedfde19abe617e63c22a2bfd4dcc34b621b Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 30 Dec 2025 16:29:46 +0530 Subject: [PATCH 012/106] test: validate asset split for auto created asset from purchase voucher --- erpnext/assets/doctype/asset/test_asset.py | 176 ++++++++++++++------- 1 file changed, 123 insertions(+), 53 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 4e46a809b7f..f81b06129cb 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -702,6 +702,129 @@ class TestAsset(AssetSetup): frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc) frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc) + def test_partial_asset_sale(self): + date = nowdate() + purchase_date = add_months(get_first_day(date), -2) + depreciation_start_date = add_months(get_last_day(date), -2) + + # create an asset + asset = create_asset( + item_code="Macbook Pro", + is_existing_asset=1, + calculate_depreciation=1, + available_for_use_date=purchase_date, + purchase_date=purchase_date, + depreciation_start_date=depreciation_start_date, + net_purchase_amount=1000000.0, + purchase_amount=1000000.0, + asset_quantity=10, + total_number_of_depreciations=12, + frequency_of_depreciation=1, + submit=1, + ) + asset_depr_schedule_before_sale = get_asset_depr_schedule_doc(asset.name, "Active") + post_depreciation_entries(date) + asset.reload() + + # check asset values before sale + self.assertEqual(asset.asset_quantity, 10) + self.assertEqual(asset.net_purchase_amount, 1000000) + self.assertEqual(asset.status, "Partially Depreciated") + self.assertEqual( + asset_depr_schedule_before_sale.depreciation_schedule[0].get("depreciation_amount"), 83333.33 + ) + + # make a partial sales againt the asset + si = make_sales_invoice( + asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=5 + ) + si.customer = "_Test Customer" + si.due_date = date + si.get("items")[0].rate = 25000 + si.insert() + si.submit() + + asset.reload() + asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset.name, "Active") + + # check asset values after sales + self.assertEqual(asset.asset_quantity, 5) + self.assertEqual(asset.net_purchase_amount, 500000) + self.assertEqual(asset.status, "Sold") + self.assertEqual( + asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66 + ) + + def test_asset_splitting_for_non_existing_asset(self): + date = nowdate() + purchase_date = add_months(get_first_day(date), -2) + depreciation_start_date = add_months(get_last_day(date), -2) + + asset_qty = 10 + asset_rate = 100000.0 + asset_item = "Macbook Pro" + asset_location = "Test Location" + + frappe.db.set_value("Item", asset_item, "is_grouped_asset", 1) + + # Inward asset via Purchase Receipt + pr = make_purchase_receipt( + item_code="Macbook Pro", + posting_date=purchase_date, + qty=asset_qty, + rate=asset_rate, + location=asset_location, + supplier="_Test Supplier", + ) + pr.submit() + + asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name") + asset_doc = frappe.get_doc("Asset", asset) + asset_doc.calculate_depreciation = 1 + asset_doc.available_for_use_date = purchase_date + asset_doc.location = asset_location + asset_doc.append( + "finance_books", + { + "expected_value_after_useful_life": 0, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 12, + "frequency_of_depreciation": 1, + "depreciation_start_date": depreciation_start_date, + }, + ) + asset_doc.submit() + + # check asset values before splitting + asset_depr_schedule_before_splitting = get_asset_depr_schedule_doc(asset_doc.name, "Active") + self.assertEqual(asset_doc.asset_quantity, 10) + self.assertEqual(asset_doc.net_purchase_amount, 1000000) + self.assertEqual( + asset_depr_schedule_before_splitting.depreciation_schedule[0].get("depreciation_amount"), 83333.33 + ) + + # initate asset split + new_asset = split_asset(asset_doc.name, 5) + asset_doc.reload() + asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset_doc.name, "Active") + new_asset_depr_schedule = get_asset_depr_schedule_doc(new_asset.name, "Active") + + # check asset values after splitting + self.assertEqual(asset_doc.asset_quantity, 5) + self.assertEqual(asset_doc.net_purchase_amount, 500000) + self.assertEqual( + asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66 + ) + + # check new asset values after splitting + self.assertEqual(asset_doc.asset_quantity, 5) + self.assertEqual(asset_doc.net_purchase_amount, 500000) + self.assertEqual( + new_asset_depr_schedule.depreciation_schedule[0].get("depreciation_amount"), 41666.66 + ) + + frappe.db.set_value("Item", asset_item, "is_grouped_asset", 0) + class TestDepreciationMethods(AssetSetup): def test_schedule_for_straight_line_method(self): @@ -1695,59 +1818,6 @@ class TestDepreciationBasics(AssetSetup): pr.submit() self.assertTrue(get_gl_entries("Purchase Receipt", pr.name)) - def test_partial_asset_sale_for_existing_asset(self): - date = nowdate() - purchase_date = add_months(get_first_day(date), -2) - depreciation_start_date = add_months(get_last_day(date), -2) - - # create an asset - asset = create_asset( - item_code="Macbook Pro", - is_existing_asset=1, - calculate_depreciation=1, - available_for_use_date=purchase_date, - purchase_date=purchase_date, - depreciation_start_date=depreciation_start_date, - net_purchase_amount=1000000, - purchase_amount=1000000, - asset_quantity=10, - total_number_of_depreciations=12, - frequency_of_depreciation=1, - submit=1, - ) - asset_depr_schedule_before_sale = get_asset_depr_schedule_doc(asset.name, "Active") - post_depreciation_entries(date) - asset.reload() - - # check asset values before sale - self.assertEqual(asset.asset_quantity, 10) - self.assertEqual(asset.net_purchase_amount, 1000000) - self.assertEqual(asset.status, "Partially Depreciated") - self.assertEqual( - asset_depr_schedule_before_sale.depreciation_schedule[0].get("depreciation_amount"), 83333.33 - ) - - # make a partial sales againt the asset - si = make_sales_invoice( - asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=5 - ) - si.customer = "_Test Customer" - si.due_date = date - si.get("items")[0].rate = 25000 - si.insert() - si.submit() - - asset.reload() - asset_depr_schedule_after_sale = get_asset_depr_schedule_doc(asset.name, "Active") - - # check asset values after sales - self.assertEqual(asset.asset_quantity, 5) - self.assertEqual(asset.net_purchase_amount, 500000) - self.assertEqual(asset.status, "Sold") - self.assertEqual( - asset_depr_schedule_after_sale.depreciation_schedule[0].get("depreciation_amount"), 41666.66 - ) - def get_gl_entries(doctype, docname): gl_entry = frappe.qb.DocType("GL Entry") From 86b0f67dbc9adc1352379d2660ffe67366a13517 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 31 Dec 2025 14:26:22 +0530 Subject: [PATCH 013/106] fix(tds): correct tax logic for customer --- .../test_tax_withholding_category.py | 7 +------ .../tax_withholding_entry/tax_withholding_entry.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 38561815d65..3b0124372b3 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -415,7 +415,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 500, "description": "Test", - "add_deduct_tax": "Add", }, ) pi.save() @@ -506,7 +505,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 200, "description": "Test Gross Tax", - "add_deduct_tax": "Add", }, ) si.save() @@ -541,10 +539,10 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 400, "description": "Test Gross Tax", - "add_deduct_tax": "Add", }, ) si.save() + si.reload() si.submit() invoices.append(si) # For amount before threshold (first 8000 + VAT): TCS entry with amount zero @@ -594,7 +592,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 500, "description": "VAT added to test TDS calculation on gross amount", - "add_deduct_tax": "Add", }, ) si.save() @@ -1024,7 +1021,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 1000, "description": "VAT added to test TDS calculation on gross amount", - "add_deduct_tax": "Add", }, ) pi.save() @@ -1162,7 +1158,6 @@ class TestTaxWithholdingCategory(IntegrationTestCase): "cost_center": "Main - _TC", "tax_amount": 8000, "description": "Test", - "add_deduct_tax": "Add", }, ) diff --git a/erpnext/accounts/doctype/tax_withholding_entry/tax_withholding_entry.py b/erpnext/accounts/doctype/tax_withholding_entry/tax_withholding_entry.py index 96a2768e68c..b2438c23527 100644 --- a/erpnext/accounts/doctype/tax_withholding_entry/tax_withholding_entry.py +++ b/erpnext/accounts/doctype/tax_withholding_entry/tax_withholding_entry.py @@ -715,6 +715,10 @@ class TaxWithholdingController: existing_taxes = {row.account_head: row for row in self.doc.taxes if row.is_tax_withholding_account} precision = self.doc.precision("tax_amount", "taxes") conversion_rate = self.get_conversion_rate() + add_deduct_tax = "Deduct" + + if self.party_type == "Customer": + add_deduct_tax = "Add" for account_head, base_amount in account_amount_map.items(): tax_amount = flt(base_amount / conversion_rate, precision) @@ -731,6 +735,7 @@ class TaxWithholdingController: tax_row = self._create_tax_row(account_head, tax_amount) for_update = False + tax_row.add_deduct_tax = add_deduct_tax # Set item-wise tax breakup for this tax row self._set_item_wise_tax_for_tds( tax_row, account_head, category_withholding_map, for_update=for_update @@ -750,7 +755,6 @@ class TaxWithholdingController: "account_head": account_head, "description": account_head, "cost_center": cost_center, - "add_deduct_tax": "Deduct", "tax_amount": tax_amount, "dont_recompute_tax": 1, }, @@ -814,12 +818,14 @@ class TaxWithholdingController: else: item_tax_amount = 0 + multiplier = -1 if tax_row.add_deduct_tax == "Deduct" else 1 + self.doc._item_wise_tax_details.append( frappe._dict( item=item, tax=tax_row, rate=category.tax_rate, - amount=item_tax_amount * -1, # Negative because it's a deduction + amount=item_tax_amount * multiplier, taxable_amount=item_base_taxable, ) ) From 59f5ee7b63f604bd8998ba5397a85edbe0bbe42e Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Fri, 2 Jan 2026 15:09:30 +0530 Subject: [PATCH 014/106] fix(payment_entry): merge GL entries with similar account heads based on setting --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index eb1673a8798..350e8b700a9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1285,8 +1285,11 @@ class PaymentEntry(AccountsController): def make_gl_entries(self, cancel=0, adv_adj=0): gl_entries = self.build_gl_map() - gl_entries = process_gl_map(gl_entries) - make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj) + + merge_entries = frappe.get_single_value("Accounts Settings", "merge_similar_account_heads") + + gl_entries = process_gl_map(gl_entries, merge_entries=merge_entries) + make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj, merge_entries=merge_entries) if cancel: cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name)) else: From 6147f9c6a333391e27e96581e0257b27d489d24a Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Fri, 2 Jan 2026 16:11:32 +0530 Subject: [PATCH 015/106] test: add tests for merging GL entries based on Accounts Settings --- .../payment_entry/test_payment_entry.py | 91 +++++++++++++++++++ .../doctype/item_price/test_records.json | 56 ------------ 2 files changed, 91 insertions(+), 56 deletions(-) delete mode 100644 erpnext/stock/doctype/item_price/test_records.json diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 355aa46ea05..f6c240c0714 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1045,6 +1045,7 @@ class TestPaymentEntry(IntegrationTestCase): ) def test_gl_of_multi_currency_payment_with_taxes(self): + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 1) payment_entry = create_payment_entry( party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True ) @@ -1606,6 +1607,96 @@ class TestPaymentEntry(IntegrationTestCase): self.voucher_no = pe.name self.check_gl_entries() + def test_payment_entry_merges_gl_entries_with_same_account_head(self): + """ + Test that Payment Entry merges GL entries with same account head + when 'Merge Similar Account Heads' setting is enabled. + """ + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 1) + + pe = create_payment_entry( + party_type="Supplier", + party="_Test Supplier", + paid_from="_Test Bank - _TC", + paid_to="Creditors - _TC", + ) + + pe.append( + "deductions", + { + "account": "Write Off - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 50, + }, + ) + + pe.append( + "deductions", + { + "account": "Write Off - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 30, + }, + ) + + pe.save() + pe.submit() + + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "account": "Write Off - _TC", "is_cancelled": 0}, + fields=["debit", "credit"], + ) + + self.assertEqual(len(gl_entries), 1) + self.assertEqual(gl_entries[0].debit, 80) + + def test_payment_entry_does_not_merge_gl_entries_when_setting_disabled(self): + """ + Test that Payment Entry does NOT merge GL entries + when 'Merge Similar Account Heads' is disabled. + """ + + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) + + pe = create_payment_entry( + party_type="Supplier", + party="_Test Supplier", + paid_from="_Test Bank - _TC", + paid_to="Creditors - _TC", + ) + + pe.append( + "deductions", + { + "account": "Write Off - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 50, + }, + ) + + pe.append( + "deductions", + { + "account": "Write Off - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 30, + }, + ) + + pe.save() + pe.submit() + + gl_entries = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pe.name, "account": "Write Off - _TC", "is_cancelled": 0}, + fields=["debit", "credit"], + ) + + self.assertEqual(len(gl_entries), 2) + + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 1) + def check_pl_entries(self): ple = frappe.qb.DocType("Payment Ledger Entry") pl_entries = ( diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json deleted file mode 100644 index afe5ad65b75..00000000000 --- a/erpnext/stock/doctype/item_price/test_records.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "doctype": "Item Price", - "item_code": "_Test Item", - "price_list": "_Test Price List", - "price_list_rate": 100, - "valid_from": "2017-04-18", - "valid_upto": "2017-04-26" - }, - { - "doctype": "Item Price", - "item_code": "_Test Item", - "price_list": "_Test Price List Rest of the World", - "price_list_rate": 10 - }, - { - "doctype": "Item Price", - "item_code": "_Test Item 2", - "price_list": "_Test Price List Rest of the World", - "price_list_rate": 20, - "valid_from": "2017-04-18", - "valid_upto": "2017-04-26", - "customer": "_Test Customer", - "uom": "_Test UOM" - }, - { - "doctype": "Item Price", - "item_code": "_Test Item Home Desktop 100", - "price_list": "_Test Price List", - "price_list_rate": 1000, - "valid_from": "2017-04-10", - "valid_upto": "2017-04-17" - }, - { - "doctype": "Item Price", - "item_code": "_Test Item Home Desktop Manufactured", - "price_list": "_Test Price List", - "price_list_rate": 1000, - "valid_from": "2017-04-10", - "valid_upto": "2017-04-17" - }, - { - "doctype": "Item Price", - "item_code": "_Test Item", - "price_list": "_Test Buying Price List", - "price_list_rate": 100, - "supplier": "_Test Supplier" - }, - { - "doctype": "Item Price", - "item_code": "_Test Item", - "price_list": "_Test Selling Price List", - "price_list_rate": 200, - "customer": "_Test Customer" - } -] From 7baa75faa52db0a86f4e2c5d7b3b0b0d0d6c69bb Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Fri, 2 Jan 2026 16:13:54 +0530 Subject: [PATCH 016/106] fix: add back test record - removed for debugging --- .../doctype/item_price/test_records.json | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 erpnext/stock/doctype/item_price/test_records.json diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json new file mode 100644 index 00000000000..afe5ad65b75 --- /dev/null +++ b/erpnext/stock/doctype/item_price/test_records.json @@ -0,0 +1,56 @@ +[ + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Price List", + "price_list_rate": 100, + "valid_from": "2017-04-18", + "valid_upto": "2017-04-26" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Price List Rest of the World", + "price_list_rate": 10 + }, + { + "doctype": "Item Price", + "item_code": "_Test Item 2", + "price_list": "_Test Price List Rest of the World", + "price_list_rate": 20, + "valid_from": "2017-04-18", + "valid_upto": "2017-04-26", + "customer": "_Test Customer", + "uom": "_Test UOM" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item Home Desktop 100", + "price_list": "_Test Price List", + "price_list_rate": 1000, + "valid_from": "2017-04-10", + "valid_upto": "2017-04-17" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item Home Desktop Manufactured", + "price_list": "_Test Price List", + "price_list_rate": 1000, + "valid_from": "2017-04-10", + "valid_upto": "2017-04-17" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Buying Price List", + "price_list_rate": 100, + "supplier": "_Test Supplier" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Selling Price List", + "price_list_rate": 200, + "customer": "_Test Customer" + } +] From b8b55754c8f8abbd6f186412ff1cc2999fb59838 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Fri, 2 Jan 2026 16:39:23 +0530 Subject: [PATCH 017/106] fix: breaking test --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 9392a0e3220..38437fb5b37 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1500,6 +1500,8 @@ class TestPurchaseInvoice(IntegrationTestCase, StockTestMixin): def test_purchase_invoice_advance_taxes(self): from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 1) + company = "_Test Company" tds_account_args = { From 0950e67eea5d0a1880407e4a1095a197d75ea3cc Mon Sep 17 00:00:00 2001 From: elshafei-developer Date: Mon, 5 Jan 2026 10:49:35 +0000 Subject: [PATCH 018/106] fix: add missing translation function for company default error message --- erpnext/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 557eb21441b..7dfe9041506 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1146,7 +1146,7 @@ def get_company_default(company, fieldname, ignore_validation=False): if not ignore_validation and not value: throw( _("Please set default {0} in Company {1}").format( - frappe.get_meta("Company").get_label(fieldname), company + _(frappe.get_meta("Company").get_label(fieldname)), company ) ) From 1296829b9cb37a9d45d69d7ff5b5a9531938653f Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 5 Jan 2026 16:44:06 +0530 Subject: [PATCH 019/106] fix(test): Use the system-configured float precision --- erpnext/assets/doctype/asset/asset.py | 6 ++---- erpnext/assets/doctype/asset/test_asset.py | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 701be98a0d6..2fb36816932 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -589,9 +589,7 @@ class Asset(AccountsController): def set_depreciation_rate(self): for d in self.get("finance_books"): self.validate_asset_finance_books(d) - d.rate_of_depreciation = flt( - self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation") - ) + d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True) def validate_asset_finance_books(self, row): row.expected_value_after_useful_life = flt( @@ -981,7 +979,7 @@ class Asset(AccountsController): if isinstance(args, str): args = json.loads(args) - rate_field_precision = frappe.get_precision(args.doctype, "rate_of_depreciation") or 2 + rate_field_precision = frappe.get_single_value("System Settings", "float_precision") or 2 if args.get("depreciation_method") == "Double Declining Balance": return self.get_double_declining_balance_rate(args, rate_field_precision) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c54c39ab7c8..8613ec7e183 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -37,12 +37,14 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( make_purchase_invoice as make_invoice, ) from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt +from erpnext.tests.utils import ERPNextTestSuite -class AssetSetup(IntegrationTestCase): +class AssetSetup(ERPNextTestSuite): @classmethod def setUpClass(cls): super().setUpClass() + frappe.db.set_single_value("System Settings", "float_precision", 3) set_depreciation_settings_in_company() create_asset_data() enable_cwip_accounting("Computers") From 8d186d6b3ff38c99eba310f45297994447b08bf7 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 5 Jan 2026 16:47:32 +0530 Subject: [PATCH 020/106] fix: use correct test class --- erpnext/assets/doctype/asset/test_asset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 8613ec7e183..b683aa4f8de 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -37,10 +37,9 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( make_purchase_invoice as make_invoice, ) from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt -from erpnext.tests.utils import ERPNextTestSuite -class AssetSetup(ERPNextTestSuite): +class AssetSetup(IntegrationTestCase): @classmethod def setUpClass(cls): super().setUpClass() From 5a47503611e9fd63c7e6b5a317f7b1a23aa06dad Mon Sep 17 00:00:00 2001 From: Pandiyan5273 Date: Tue, 6 Jan 2026 12:58:54 +0530 Subject: [PATCH 021/106] fix(accounts): correct sales order item deletion message for MR and PO linkage --- erpnext/controllers/accounts_controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 59810544c0c..73cc888fc4a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3748,9 +3748,9 @@ def validate_child_on_delete(row, parent, ordered_item=None): ) if flt(row.ordered_qty): frappe.throw( - _("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format( - row.idx, row.item_code - ) + _( + "Row #{0}: Cannot delete item {1} which is already ordered against this Sales Order." + ).format(row.idx, row.item_code) ) if parent.doctype == "Purchase Order" and flt(row.received_qty): From 007258d657ef0b03c9bef37b9b69de3dc55f1a44 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Tue, 6 Jan 2026 14:39:31 +0530 Subject: [PATCH 022/106] refactor: modify test cases to handle float precision rounded to 2 decimals --- erpnext/assets/doctype/asset/test_asset.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index b683aa4f8de..14ba699c8a6 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -798,9 +798,9 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(asset.status, "Draft") expected_schedules = [ - ["2030-12-31", 66667.00, 66667.00], - ["2031-12-31", 22222.11, 88889.11], - ["2032-12-31", 1110.89, 90000.0], + ["2030-12-31", 66670.0, 66670.0], + ["2031-12-31", 22221.11, 88891.11], + ["2032-12-31", 1108.89, 90000.0], ] schedules = [ @@ -826,7 +826,7 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(asset.status, "Draft") - expected_schedules = [["2031-12-31", 33333.50, 83333.50], ["2032-12-31", 6666.50, 90000.0]] + expected_schedules = [["2031-12-31", 33335.0, 83335.0], ["2032-12-31", 6665.0, 90000.0]] schedules = [ [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] @@ -944,12 +944,12 @@ class TestDepreciationMethods(AssetSetup): ) expected_schedules = [ - ["2022-02-28", 337.72, 337.72], - ["2022-03-31", 675.45, 1013.17], - ["2022-04-30", 675.45, 1688.62], - ["2022-05-31", 675.45, 2364.07], - ["2022-06-30", 675.45, 3039.52], - ["2022-07-15", 1960.48, 5000.0], + ["2022-02-28", 337.71, 337.71], + ["2022-03-31", 675.42, 1013.13], + ["2022-04-30", 675.42, 1688.55], + ["2022-05-31", 675.42, 2363.97], + ["2022-06-30", 675.42, 3039.39], + ["2022-07-15", 1960.61, 5000.0], ] schedules = [ From 825e3717ca2760cde11fd5510718f8c0b2500947 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Tue, 6 Jan 2026 14:41:36 +0530 Subject: [PATCH 023/106] fix: do not update float precision on setup --- erpnext/assets/doctype/asset/test_asset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 14ba699c8a6..a382237db6a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -43,7 +43,6 @@ class AssetSetup(IntegrationTestCase): @classmethod def setUpClass(cls): super().setUpClass() - frappe.db.set_single_value("System Settings", "float_precision", 3) set_depreciation_settings_in_company() create_asset_data() enable_cwip_accounting("Computers") From 20320c4a6c161d9d2c63fa71199dad4d074a1515 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Dec 2025 10:36:43 +0530 Subject: [PATCH 024/106] perf: SABB taking time to save the record --- .../doctype/pos_invoice/test_pos_invoice.py | 3 + .../tests/test_subcontracting_controller.py | 3 +- .../serial_and_batch_bundle.py | 44 ++++++- .../test_serial_and_batch_bundle.py | 1 + erpnext/stock/serial_batch_bundle.py | 110 ++++++++++++++---- 5 files changed, 129 insertions(+), 32 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 58985195867..59d47dce726 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -539,6 +539,7 @@ class TestPOSInvoice(IntegrationTestCase): rate=1000, serial_no=[serial_nos[0]], do_not_save=1, + ignore_sabb_validation=True, ) pos2.append("payments", {"mode_of_payment": "Bank Draft", "amount": 1000}) @@ -1016,6 +1017,7 @@ class TestPOSInvoice(IntegrationTestCase): qty=1, rate=100, do_not_submit=True, + ignore_sabb_validation=True, ) self.assertRaises(frappe.ValidationError, pos_inv.submit) @@ -1157,6 +1159,7 @@ def create_pos_invoice(**args): "posting_time": pos_inv.posting_time, "type_of_transaction": type_of_transaction, "do_not_submit": True, + "ignore_sabb_validation": args.ignore_sabb_validation, } ) ).name diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index bd6afdf56a7..b6c21fa0b45 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -778,9 +778,8 @@ class TestSubcontractingController(IntegrationTestCase): row.serial_no = "ABC" break - bundle.save() + self.assertRaises(frappe.ValidationError, bundle.save) - self.assertRaises(frappe.ValidationError, scr1.save) bundle.load_from_db() for row in bundle.entries: if row.idx == 1: diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 31d63165103..40a542cc88a 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -118,10 +118,19 @@ class SerialandBatchBundle(Document): return self.allow_existing_serial_nos() - if not self.flags.ignore_validate_serial_batch or frappe.in_test: - self.validate_serial_nos_duplicate() + if self.docstatus == 1: + if not self.flags.ignore_validate_serial_batch or frappe.in_test: + self.validate_serial_nos_duplicate() + + self.check_future_entries_exists() + elif ( + self.has_serial_no + and self.type_of_transaction == "Outward" + and self.voucher_type != "Stock Reconciliation" + and self.voucher_no + ): + self.validate_serial_no_status() - self.check_future_entries_exists() self.set_is_outward() self.calculate_total_qty() self.set_warehouse() @@ -131,6 +140,25 @@ class SerialandBatchBundle(Document): self.calculate_qty_and_amount() + def validate_serial_no_status(self): + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + invalid_serial_nos = frappe.get_all( + "Serial No", + filters={ + "name": ("in", serial_nos), + "warehouse": ("!=", self.warehouse), + }, + pluck="name", + ) + + if invalid_serial_nos: + msg = _( + "You cannot outward following {0} as either they are Delivered, Inactive or located in a different warehouse." + ).format(_("Serial Nos") if len(invalid_serial_nos) > 1 else _("Serial No")) + msg += "
" + msg += ", ".join(sn for sn in invalid_serial_nos) + frappe.throw(msg) + def validate_voucher_detail_no(self): if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [ "Installation Note", @@ -698,10 +726,16 @@ class SerialandBatchBundle(Document): "Buying Settings", "set_valuation_rate_for_rejected_materials" ) + precision = frappe.get_precision("Serial and Batch Entry", "incoming_rate") for d in self.entries: if self.is_rejected and not set_valuation_rate_for_rejected_materials: rate = 0.0 - elif (d.incoming_rate == rate) and not stock_queue and d.qty and d.stock_value_difference: + elif ( + (flt(d.incoming_rate, precision) == flt(rate, precision)) + and not stock_queue + and d.qty + and d.stock_value_difference + ): continue if is_packed_item and d.incoming_rate: @@ -761,7 +795,7 @@ class SerialandBatchBundle(Document): self.calculate_total_qty(save=True) # If user has changed the rate in the child table - if self.docstatus == 0: + if self.docstatus == 0 and self.type_of_transaction == "Inward": self.set_incoming_rate(parent=parent, row=row, save=True) if self.docstatus == 0 and parent.get("is_return") and parent.is_new(): diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py index 4d188eb2e93..8cbb59599b7 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py @@ -988,6 +988,7 @@ def make_serial_batch_bundle(kwargs): "type_of_transaction": type_of_transaction, "company": kwargs.company or "_Test Company", "do_not_submit": kwargs.do_not_submit, + "ignore_sabb_validation": kwargs.ignore_sabb_validation or False, } ) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 298cec2fa89..77941b11fc1 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -4,7 +4,7 @@ import frappe from frappe import _, bold from frappe.model.naming import NamingSeries, make_autoname, parse_naming_series from frappe.query_builder import Case -from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp +from frappe.query_builder.functions import Max, Sum from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, now, nowtime, today from pypika import Order from pypika.terms import ExistsCriterion @@ -629,8 +629,9 @@ class SerialNoValuation(DeprecatedSerialNoValuation): self.old_serial_nos = [] serial_nos = self.get_serial_nos() + result = self.get_serial_no_wise_incoming_rate(serial_nos) for serial_no in serial_nos: - incoming_rate = self.get_incoming_rate_from_bundle(serial_no) + incoming_rate = result.get(serial_no) if incoming_rate is None: self.old_serial_nos.append(serial_no) continue @@ -640,39 +641,95 @@ class SerialNoValuation(DeprecatedSerialNoValuation): self.calculate_stock_value_from_deprecarated_ledgers() - def get_incoming_rate_from_bundle(self, serial_no) -> float: + def get_serial_no_wise_incoming_rate(self, serial_nos): bundle = frappe.qb.DocType("Serial and Batch Bundle") bundle_child = frappe.qb.DocType("Serial and Batch Entry") + def get_latest_based_on_posting_datetime(): + # Get latest inward record based on posting datetime for each serial no + + latest_posting = ( + frappe.qb.from_(bundle) + .inner_join(bundle_child) + .on(bundle.name == bundle_child.parent) + .select( + bundle_child.serial_no, + Max(bundle.posting_datetime).as_("max_posting_dt"), + ) + .where( + (bundle.is_cancelled == 0) + & (bundle.docstatus == 1) + & (bundle.type_of_transaction == "Inward") + & (bundle_child.qty > 0) + & (bundle.item_code == self.sle.item_code) + & (bundle_child.warehouse == self.sle.warehouse) + & (bundle_child.serial_no.isin(serial_nos)) + ) + .groupby(bundle_child.serial_no) + ) + + # Important to exclude the current voucher to calculate correct the stock value difference + if self.sle.voucher_no: + latest_posting = latest_posting.where(bundle.voucher_no != self.sle.voucher_no) + + if self.sle.posting_datetime: + timestamp_condition = bundle.posting_datetime <= self.sle.posting_datetime + + latest_posting = latest_posting.where(timestamp_condition) + + latest_posting = latest_posting.as_("latest_posting") + + return latest_posting + + def get_latest_based_on_creation(latest_posting): + # Get latest inward record based on creation for each serial no + latest_creation = ( + frappe.qb.from_(bundle) + .join(bundle_child) + .on(bundle.name == bundle_child.parent) + .join(latest_posting) + .on( + (latest_posting.serial_no == bundle_child.serial_no) + & (latest_posting.max_posting_dt == bundle.posting_datetime) + ) + .select( + bundle_child.serial_no, + Max(bundle.creation).as_("max_creation"), + ) + .where( + (bundle.is_cancelled == 0) + & (bundle.docstatus == 1) + & (bundle.type_of_transaction == "Inward") + & (bundle_child.qty > 0) + & (bundle.item_code == self.sle.item_code) + & (bundle_child.warehouse == self.sle.warehouse) + ) + .groupby(bundle_child.serial_no) + ).as_("latest_creation") + + return latest_creation + + latest_posting = get_latest_based_on_posting_datetime() + latest_creation = get_latest_based_on_creation(latest_posting) + query = ( frappe.qb.from_(bundle) - .inner_join(bundle_child) + .join(bundle_child) .on(bundle.name == bundle_child.parent) - .select((bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate")) - .where( - (bundle.is_cancelled == 0) - & (bundle.docstatus == 1) - & (bundle_child.serial_no == serial_no) - & (bundle.type_of_transaction == "Inward") - & (bundle_child.qty > 0) - & (bundle.item_code == self.sle.item_code) - & (bundle_child.warehouse == self.sle.warehouse) + .join(latest_creation) + .on( + (latest_creation.serial_no == bundle_child.serial_no) + & (latest_creation.max_creation == bundle.creation) + ) + .select( + bundle_child.serial_no, + (bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"), ) - .orderby(bundle.posting_datetime, order=Order.desc) - .limit(1) ) - # Important to exclude the current voucher to calculate correct the stock value difference - if self.sle.voucher_no: - query = query.where(bundle.voucher_no != self.sle.voucher_no) + result = query.run(as_list=1) - if self.sle.posting_datetime: - timestamp_condition = bundle.posting_datetime <= self.sle.posting_datetime - - query = query.where(timestamp_condition) - - incoming_rate = query.run() - return flt(incoming_rate[0][0]) if incoming_rate else None + return frappe._dict(result) if result else frappe._dict({}) def get_serial_nos(self): if self.sle.get("serial_nos"): @@ -1135,6 +1192,9 @@ class SerialBatchCreation: doc.submit() else: + if self.get("ignore_sabb_validation"): + doc.flags.ignore_validate = True + doc.save() self.validate_qty(doc) From c0a85faa68777ece2fed91026de0f4d6c7a568c1 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Tue, 6 Jan 2026 23:49:21 +0530 Subject: [PATCH 025/106] test: set up float precision --- erpnext/assets/doctype/asset/test_asset.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a382237db6a..9e701083764 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -700,6 +700,18 @@ class TestAsset(AssetSetup): class TestDepreciationMethods(AssetSetup): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls._old_float_precision = frappe.db.get_single_value("System Settings", "float_precision") + frappe.db.set_single_value("System Settings", "float_precision", 2) + + @classmethod + def tearDownClass(cls): + frappe.db.set_single_value("System Settings", "float_precision", cls._old_float_precision) + super().tearDownClass() + def test_schedule_for_straight_line_method(self): asset = create_asset( calculate_depreciation=1, From 64f391adf7c5c60414af291260bbba3425463964 Mon Sep 17 00:00:00 2001 From: Matt Howard Date: Mon, 5 Jan 2026 15:28:45 -0500 Subject: [PATCH 026/106] fix(postgres): compute current month sales without DATE_FORMAT --- erpnext/setup/doctype/company/company.py | 65 +++++++++++++++--------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index def469e4fa3..5a2b6db83f2 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -11,7 +11,16 @@ from frappe.cache_manager import clear_defaults_cache from frappe.contacts.address_and_contact import load_address_and_contact from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.desk.page.setup_wizard.setup_wizard import make_records -from frappe.utils import add_months, cint, formatdate, get_first_day, get_link_to_form, get_timestamp, today +from frappe.utils import ( + add_months, + cint, + formatdate, + get_first_day, + get_last_day, + get_link_to_form, + get_timestamp, + today, +) from frappe.utils.nestedset import NestedSet, rebuild_tree from erpnext.accounts.doctype.account.account import get_account_currency @@ -866,31 +875,41 @@ def install_country_fixtures(company, country): def update_company_current_month_sales(company): - from_date = get_first_day(today()) - to_date = get_first_day(add_months(from_date, 1)) + """Update Company's Total Monthly Sales. - results = frappe.db.sql( - """ - SELECT - SUM(base_grand_total) AS total, - DATE_FORMAT(posting_date, '%%m-%%Y') AS month_year - FROM - `tabSales Invoice` - WHERE - posting_date >= %s - AND posting_date < %s - AND docstatus = 1 - AND company = %s - GROUP BY - month_year - """, - (from_date, to_date, company), - as_dict=True, + Postgres compatibility: + - Avoid MariaDB-only DATE_FORMAT(). + - Use a date range for the current month instead (portable + index-friendly). + """ + + # Local imports so you don't have to touch file-level imports + from frappe.query_builder.functions import Sum + + start_date = get_first_day(today()) + end_date = get_last_day(today()) + + si = frappe.qb.DocType("Sales Invoice") + + total_monthly_sales = ( + frappe.qb.from_(si) + .select(Sum(si.base_grand_total)) + .where( + (si.docstatus == 1) + & (si.company == company) + & (si.posting_date >= start_date) + & (si.posting_date <= end_date) + ) + ).run(pluck=True)[0] or 0 + + # Fieldname in standard ERPNext is `total_monthly_sales` + frappe.db.set_value( + "Company", + company, + "total_monthly_sales", + total_monthly_sales, + update_modified=False, ) - monthly_total = results[0]["total"] if len(results) > 0 else 0 - frappe.db.set_value("Company", company, "total_monthly_sales", monthly_total) - def update_company_monthly_sales(company): """Cache past year monthly sales of every company based on sales invoices""" From 1bfb62465f847afcbe902ca334756502a3018050 Mon Sep 17 00:00:00 2001 From: Pandiyan5273 Date: Wed, 7 Jan 2026 19:27:16 +0530 Subject: [PATCH 027/106] fix(stock): enable allow on submit for tracking status field --- erpnext/stock/doctype/shipment/shipment.json | 3 ++- erpnext/stock/doctype/shipment/shipment.py | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index 9d7882f7094..d7f0877a298 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -382,6 +382,7 @@ "print_hide": 1 }, { + "allow_on_submit": 1, "fieldname": "tracking_status", "fieldtype": "Select", "label": "Tracking Status", @@ -440,7 +441,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2025-02-20 16:55:20.076418", + "modified": "2026-01-07 19:24:23.566312", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index f638d6125fe..a2d6dee50dc 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -20,9 +20,7 @@ class Shipment(Document): if TYPE_CHECKING: from frappe.types import DF - from erpnext.stock.doctype.shipment_delivery_note.shipment_delivery_note import ( - ShipmentDeliveryNote, - ) + from erpnext.stock.doctype.shipment_delivery_note.shipment_delivery_note import ShipmentDeliveryNote from erpnext.stock.doctype.shipment_parcel.shipment_parcel import ShipmentParcel amended_from: DF.Link | None From 8acf373e68780476c172cf4943f329def36f4ef4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 8 Jan 2026 15:23:40 +0530 Subject: [PATCH 028/106] fix: Workspace sidebar links for Debit/Credit Notes --- erpnext/workspace_sidebar/accounting.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/workspace_sidebar/accounting.json b/erpnext/workspace_sidebar/accounting.json index e40f17b546f..a7286ecbfd0 100644 --- a/erpnext/workspace_sidebar/accounting.json +++ b/erpnext/workspace_sidebar/accounting.json @@ -67,7 +67,7 @@ { "child": 1, "collapsible": 1, - "filters": "", + "filters": "[[\"Sales Invoice\",\"is_return\",\"=\",1]]", "icon": "", "indent": 0, "keep_closed": 0, @@ -126,6 +126,7 @@ { "child": 1, "collapsible": 1, + "filters": "[[\"Purchase Invoice\",\"is_return\",\"=\",1]]", "indent": 0, "keep_closed": 0, "label": "Debit Note", @@ -570,7 +571,7 @@ "type": "Link" } ], - "modified": "2026-01-02 18:07:04.450536", + "modified": "2026-01-08 15:04:31.767795", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", From 721b29c8dffb1f62933cbf6cfa89845e00434fe6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 8 Jan 2026 17:56:36 +0530 Subject: [PATCH 029/106] fix: Added AR, AP, Sales and Purchase Register reports --- .../workspace_sidebar/financial_reports.json | 111 +++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/erpnext/workspace_sidebar/financial_reports.json b/erpnext/workspace_sidebar/financial_reports.json index 85363d9e6d3..3af8b051f8c 100644 --- a/erpnext/workspace_sidebar/financial_reports.json +++ b/erpnext/workspace_sidebar/financial_reports.json @@ -72,6 +72,17 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Custom Financial Statement", + "link_to": "Custom Financial Statement", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -128,6 +139,104 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "indent": 1, + "keep_closed": 1, + "label": "Registers", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Accounts Receivable", + "link_to": "Accounts Receivable", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Accounts Payable", + "link_to": "Accounts Payable", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "AR Summary", + "link_to": "Accounts Receivable Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "AP Summary", + "link_to": "Accounts Payable Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Register", + "link_to": "Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Purchase Register", + "link_to": "Purchase Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise sales Register", + "link_to": "Item-wise Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -272,7 +381,7 @@ "type": "Link" } ], - "modified": "2026-01-02 14:44:46.627363", + "modified": "2026-01-08 17:50:55.681403", "modified_by": "Administrator", "module": "Accounts", "name": "Financial Reports", From b0a04e202a0831061ccaa5d1e142223dac2a6098 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 8 Jan 2026 18:02:32 +0530 Subject: [PATCH 030/106] fix: Removed opening and closing workspace --- erpnext/desktop_icon/opening_&_closing.json | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 erpnext/desktop_icon/opening_&_closing.json diff --git a/erpnext/desktop_icon/opening_&_closing.json b/erpnext/desktop_icon/opening_&_closing.json deleted file mode 100644 index 9c1cc0dbeb1..00000000000 --- a/erpnext/desktop_icon/opening_&_closing.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "app": "erpnext", - "creation": "2025-11-12 15:15:15.824801", - "docstatus": 0, - "doctype": "Desktop Icon", - "hidden": 0, - "icon": "panel-top-open", - "icon_type": "Link", - "idx": 2, - "label": "Opening & Closing", - "link_to": "Opening & Closing", - "link_type": "Workspace Sidebar", - "modified": "2026-01-01 20:07:01.344481", - "modified_by": "Administrator", - "name": "Opening & Closing", - "owner": "Administrator", - "parent_icon": "Accounts", - "restrict_removal": 0, - "roles": [], - "standard": 1 -} From c5ce14dc144c0cfc1673c1f007c4010322a9cc6e Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 8 Jan 2026 18:51:01 +0530 Subject: [PATCH 031/106] chore: export sidebars for new schema --- erpnext/workspace_sidebar/accounting.json | 74 +-- erpnext/workspace_sidebar/assets.json | 14 +- erpnext/workspace_sidebar/banking.json | 80 +-- erpnext/workspace_sidebar/budget.json | 18 +- erpnext/workspace_sidebar/buying.json | 203 ++---- erpnext/workspace_sidebar/crm.json | 19 +- .../workspace_sidebar/financial_reports.json | 124 +--- erpnext/workspace_sidebar/home.json | 3 +- erpnext/workspace_sidebar/manufacturing.json | 39 +- erpnext/workspace_sidebar/projects.json | 151 +---- erpnext/workspace_sidebar/quality.json | 147 +++-- erpnext/workspace_sidebar/selling.json | 585 ++++++------------ erpnext/workspace_sidebar/settings.json | 201 ++---- .../workspace_sidebar/share_management.json | 7 +- erpnext/workspace_sidebar/stock.json | 518 +++------------- erpnext/workspace_sidebar/subcontracting.json | 66 +- erpnext/workspace_sidebar/subscription.json | 3 +- erpnext/workspace_sidebar/support.json | 76 ++- erpnext/workspace_sidebar/taxes.json | 68 +- 19 files changed, 678 insertions(+), 1718 deletions(-) diff --git a/erpnext/workspace_sidebar/accounting.json b/erpnext/workspace_sidebar/accounting.json index a7286ecbfd0..adcc139f882 100644 --- a/erpnext/workspace_sidebar/accounting.json +++ b/erpnext/workspace_sidebar/accounting.json @@ -126,7 +126,6 @@ { "child": 1, "collapsible": 1, - "filters": "[[\"Purchase Invoice\",\"is_return\",\"=\",1]]", "indent": 0, "keep_closed": 0, "label": "Debit Note", @@ -234,28 +233,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Repost Accounting Ledger", - "link_to": "Repost Accounting Ledger", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Repost Payment Ledger", - "link_to": "Repost Payment Ledger", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -404,17 +381,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Account Category", - "link_to": "Account Category", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -529,52 +495,20 @@ "child": 0, "collapsible": 1, "icon": "settings", - "indent": 1, - "keep_closed": 1, + "indent": 0, + "keep_closed": 0, "label": "Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Accounts Settings", "link_to": "Accounts Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Currency Exchange Settings", - "link_to": "Currency Exchange Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Repost Accounting Ledger Settings", - "link_to": "Repost Accounting Ledger Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" } ], - "modified": "2026-01-08 15:04:31.767795", + "modified": "2026-01-08 18:49:12.169163", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", "owner": "Administrator", + "standard": 1, "title": "Accounting" } diff --git a/erpnext/workspace_sidebar/assets.json b/erpnext/workspace_sidebar/assets.json index 6f6e709c8f6..06cd4ea78e5 100644 --- a/erpnext/workspace_sidebar/assets.json +++ b/erpnext/workspace_sidebar/assets.json @@ -243,6 +243,17 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Depreciation Schedule", + "link_to": "Asset Depreciation Schedule", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -258,10 +269,11 @@ "url": "" } ], - "modified": "2026-01-02 14:54:20.640887", + "modified": "2026-01-08 18:49:12.152039", "modified_by": "Administrator", "module": "Assets", "name": "Assets", "owner": "Administrator", + "standard": 1, "title": "Assets" } diff --git a/erpnext/workspace_sidebar/banking.json b/erpnext/workspace_sidebar/banking.json index 95834113772..3e37e9bdb76 100644 --- a/erpnext/workspace_sidebar/banking.json +++ b/erpnext/workspace_sidebar/banking.json @@ -6,18 +6,6 @@ "header_icon": "circle-dollar-sign", "idx": 0, "items": [ - { - "child": 0, - "collapsible": 1, - "icon": "home", - "indent": 0, - "keep_closed": 0, - "label": "Home", - "link_to": "Banking", - "link_type": "Workspace", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -48,36 +36,12 @@ "icon": "clipboard-check", "indent": 0, "keep_closed": 0, - "label": "Reconciliation Statement", + "label": "Reconciliation Report", "link_to": "Bank Reconciliation Statement", "link_type": "Report", "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "split", - "indent": 0, - "keep_closed": 0, - "label": "Unreconcile Payment", - "link_to": "Unreconcile Payment", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "link", - "indent": 0, - "keep_closed": 0, - "label": "Process Payment Reconciliation", - "link_to": "Process Payment Reconciliation", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -92,7 +56,7 @@ { "child": 1, "collapsible": 1, - "icon": "", + "icon": "organization", "indent": 0, "keep_closed": 0, "label": "Bank", @@ -104,7 +68,7 @@ { "child": 1, "collapsible": 1, - "icon": "", + "icon": "accounting", "indent": 0, "keep_closed": 0, "label": "Bank Account", @@ -116,40 +80,7 @@ { "child": 1, "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Bank Account Type", - "link_to": "Bank Account Type", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Bank Account Subtype", - "link_to": "Bank Account Subtype", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Bank Guarantee", - "link_to": "Bank Guarantee", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", + "icon": "settings", "indent": 0, "keep_closed": 0, "label": "Plaid Settings", @@ -192,10 +123,11 @@ "type": "Link" } ], - "modified": "2026-01-02 13:53:50.930215", + "modified": "2026-01-08 18:49:11.808066", "modified_by": "Administrator", "module": "Accounts", "name": "Banking", "owner": "Administrator", + "standard": 1, "title": "Banking" } diff --git a/erpnext/workspace_sidebar/budget.json b/erpnext/workspace_sidebar/budget.json index 94ebfc9c994..3c10f2f895f 100644 --- a/erpnext/workspace_sidebar/budget.json +++ b/erpnext/workspace_sidebar/budget.json @@ -21,7 +21,7 @@ { "child": 0, "collapsible": 1, - "icon": "badge-cent", + "icon": "circle-dollar-sign", "indent": 0, "keep_closed": 0, "label": "Cost Center", @@ -57,7 +57,18 @@ { "child": 0, "collapsible": 1, - "icon": "sheet", + "icon": "notepad-text", + "indent": 1, + "keep_closed": 1, + "label": "Reports", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "file-text", "indent": 0, "keep_closed": 0, "label": "Budget Variance", @@ -67,10 +78,11 @@ "type": "Link" } ], - "modified": "2026-01-02 11:46:10.598472", + "modified": "2026-01-08 18:49:11.821823", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", "owner": "Administrator", + "standard": 1, "title": "Budget" } diff --git a/erpnext/workspace_sidebar/buying.json b/erpnext/workspace_sidebar/buying.json index ecf87ecc68f..a595aa964ce 100644 --- a/erpnext/workspace_sidebar/buying.json +++ b/erpnext/workspace_sidebar/buying.json @@ -33,11 +33,11 @@ { "child": 0, "collapsible": 1, - "icon": "notepad-text", + "icon": "customer", "indent": 0, "keep_closed": 0, - "label": "Material Request", - "link_to": "Material Request", + "label": "Supplier", + "link_to": "Supplier", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -45,11 +45,23 @@ { "child": 0, "collapsible": 1, - "icon": "git-pull-request-arrow", + "icon": "notebook-tabs", "indent": 0, "keep_closed": 0, - "label": "Request for Quotation", - "link_to": "Request for Quotation", + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "notepad-text", + "indent": 0, + "keep_closed": 0, + "label": "Material Request", + "link_to": "Material Request", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -93,130 +105,7 @@ { "child": 0, "collapsible": 1, - "icon": "database", - "indent": 1, - "keep_closed": 1, - "label": "Setup", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Supplier", - "link_to": "Supplier", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Group", - "link_to": "Supplier Group", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Price List", - "link_to": "Price List", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Address", - "link_to": "Address", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Contacts", - "link_to": "Contact", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Scorecard", - "link_to": "Supplier Scorecard", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Scorecard Criteria", - "link_to": "Supplier Scorecard Criteria", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Scorecard Variable", - "link_to": "Supplier Scorecard Variable", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Scorecard Standing", - "link_to": "Supplier Scorecard Standing", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "sheet", + "icon": "notepad-text", "indent": 1, "keep_closed": 1, "label": "Reports", @@ -301,14 +190,25 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, { "child": 1, "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Purchase Order Trends", - "link_to": "Purchase Order Trends", - "link_type": "Report", + "label": "Supplier Group", + "link_to": "Supplier Group", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -317,9 +217,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Procurement Tracker", - "link_to": "Procurement Tracker", - "link_type": "Report", + "label": "Address", + "link_to": "Address", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -328,31 +228,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Supplier-Wise Sales Analytics", - "link_to": "Supplier-Wise Sales Analytics", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Quotation Comparison", - "link_to": "Supplier Quotation Comparison", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Supplier Addresses And Contacts", - "link_to": "Address And Contacts", - "link_type": "Report", + "label": "Contacts", + "link_to": "Contact", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -369,10 +247,11 @@ "type": "Link" } ], - "modified": "2026-01-02 12:10:07.304778", + "modified": "2026-01-08 18:49:11.694985", "modified_by": "Administrator", "module": "Buying", "name": "Buying", "owner": "Administrator", + "standard": 1, "title": "Buying" } diff --git a/erpnext/workspace_sidebar/crm.json b/erpnext/workspace_sidebar/crm.json index 264318c1c8d..df59f6a641f 100644 --- a/erpnext/workspace_sidebar/crm.json +++ b/erpnext/workspace_sidebar/crm.json @@ -9,11 +9,23 @@ { "child": 0, "collapsible": 1, - "icon": "chart", + "icon": "home", "indent": 0, "keep_closed": 0, "label": "Home", "link_to": "CRM", + "link_type": "Workspace", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "chart", + "indent": 0, + "keep_closed": 0, + "label": "Dashboard", + "link_to": "CRM", "link_type": "Dashboard", "show_arrow": 0, "type": "Link" @@ -57,7 +69,7 @@ { "child": 0, "collapsible": 1, - "icon": "sheet", + "icon": "notepad-text", "indent": 1, "keep_closed": 1, "label": "Reports", @@ -485,10 +497,11 @@ "type": "Link" } ], - "modified": "2026-01-02 15:06:29.836236", + "modified": "2026-01-08 18:49:12.112990", "modified_by": "Administrator", "module": "CRM", "name": "CRM", "owner": "Administrator", + "standard": 1, "title": "CRM" } diff --git a/erpnext/workspace_sidebar/financial_reports.json b/erpnext/workspace_sidebar/financial_reports.json index 3af8b051f8c..faa17089d83 100644 --- a/erpnext/workspace_sidebar/financial_reports.json +++ b/erpnext/workspace_sidebar/financial_reports.json @@ -72,29 +72,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Custom Financial Statement", - "link_to": "Custom Financial Statement", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Financial Report Template", - "link_to": "Financial Report Template", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -139,104 +116,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "indent": 1, - "keep_closed": 1, - "label": "Registers", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Accounts Receivable", - "link_to": "Accounts Receivable", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Accounts Payable", - "link_to": "Accounts Payable", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "AR Summary", - "link_to": "Accounts Receivable Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "AP Summary", - "link_to": "Accounts Payable Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Register", - "link_to": "Sales Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Purchase Register", - "link_to": "Purchase Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item-wise sales Register", - "link_to": "Item-wise Sales Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item-wise Purchase Register", - "link_to": "Item-wise Purchase Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -381,10 +260,11 @@ "type": "Link" } ], - "modified": "2026-01-08 17:50:55.681403", + "modified": "2026-01-08 18:49:12.047975", "modified_by": "Administrator", "module": "Accounts", "name": "Financial Reports", "owner": "Administrator", + "standard": 1, "title": "Financial Reports" } diff --git a/erpnext/workspace_sidebar/home.json b/erpnext/workspace_sidebar/home.json index 61b3e600421..83d0cd4226c 100644 --- a/erpnext/workspace_sidebar/home.json +++ b/erpnext/workspace_sidebar/home.json @@ -62,10 +62,11 @@ "type": "Link" } ], - "modified": "2025-11-25 10:46:09.198568", + "modified": "2026-01-08 18:49:11.686293", "modified_by": "Administrator", "module": "Setup", "name": "Home", "owner": "Administrator", + "standard": 1, "title": "Home" } diff --git a/erpnext/workspace_sidebar/manufacturing.json b/erpnext/workspace_sidebar/manufacturing.json index 08b6a054660..72bfad77483 100644 --- a/erpnext/workspace_sidebar/manufacturing.json +++ b/erpnext/workspace_sidebar/manufacturing.json @@ -30,6 +30,30 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "notebook-tabs", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "warehouse", + "indent": 0, + "keep_closed": 0, + "label": "Warehouse", + "link_to": "Warehouse", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -332,18 +356,6 @@ "show_arrow": 0, "type": "Section Break" }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -425,10 +437,11 @@ "type": "Link" } ], - "modified": "2026-01-02 15:08:30.661411", + "modified": "2026-01-08 18:49:11.863897", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", "owner": "Administrator", + "standard": 1, "title": "Manufacturing" } diff --git a/erpnext/workspace_sidebar/projects.json b/erpnext/workspace_sidebar/projects.json index 58fec69ff3d..984bff3e794 100644 --- a/erpnext/workspace_sidebar/projects.json +++ b/erpnext/workspace_sidebar/projects.json @@ -18,18 +18,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "chart", - "indent": 0, - "keep_closed": 0, - "label": "Dashboard", - "link_to": "Project", - "link_type": "Dashboard", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -69,75 +57,8 @@ { "child": 0, "collapsible": 1, - "icon": "database", - "indent": 1, - "keep_closed": 1, - "label": "Setup", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Activity Type", - "link_to": "Activity Type", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Activity Cost", - "link_to": "Activity Cost", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Project Template", - "link_to": "Project Template", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Project Type", - "link_to": "Project Type", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Project Update", - "link_to": "Project Update", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "sheet", - "indent": 1, - "keep_closed": 1, "label": "Reports", "link_type": "DocType", "show_arrow": 0, @@ -146,7 +67,7 @@ { "child": 1, "collapsible": 1, - "icon": "", + "icon": "table", "indent": 0, "keep_closed": 0, "label": "Project Summary", @@ -154,79 +75,13 @@ "link_type": "Report", "show_arrow": 0, "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Daily Timesheet Summary", - "link_to": "Daily Timesheet Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Timesheet Billing Summary", - "link_to": "Timesheet Billing Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Project wise Stock Tracking", - "link_to": "Project wise Stock Tracking", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Project Profitability", - "link_to": "Project Profitability", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delayed Tasks Summary", - "link_to": "Delayed Tasks Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "settings", - "indent": 0, - "keep_closed": 0, - "label": "Settings", - "link_to": "Projects Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" } ], - "modified": "2026-01-02 11:57:04.237376", + "modified": "2026-01-08 18:49:12.014031", "modified_by": "Administrator", "module": "Projects", "name": "Projects", "owner": "Administrator", + "standard": 1, "title": "Projects" } diff --git a/erpnext/workspace_sidebar/quality.json b/erpnext/workspace_sidebar/quality.json index 5dc41335404..de2625cd4ca 100644 --- a/erpnext/workspace_sidebar/quality.json +++ b/erpnext/workspace_sidebar/quality.json @@ -21,11 +21,11 @@ { "child": 0, "collapsible": 1, - "icon": "inspection-panel", + "icon": "quality", "indent": 0, "keep_closed": 0, - "label": "Quality Inspection", - "link_to": "Quality Inspection", + "label": "Quality Goal", + "link_to": "Quality Goal", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -33,11 +33,23 @@ { "child": 0, "collapsible": 1, - "icon": "goal", + "icon": "book-text", "indent": 0, "keep_closed": 0, - "label": "Quality Goal", - "link_to": "Quality Goal", + "label": "Quality Procedure", + "link_to": "Quality Procedure", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "inspection-panel", + "indent": 0, + "keep_closed": 0, + "label": "Quality Inspection", + "link_to": "Quality Inspection", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -81,38 +93,25 @@ { "child": 0, "collapsible": 1, - "icon": "thumbs-up", - "indent": 0, - "keep_closed": 0, - "label": "Quality Feedback", - "link_to": "Quality Feedback", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "users", - "indent": 0, - "keep_closed": 0, - "label": "Quality Meeting", - "link_to": "Quality Meeting", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "database", + "icon": "calendar-check-2", "indent": 1, "keep_closed": 1, - "label": "Setup", + "label": "Goal and Procedure", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quality Goal", + "link_to": "Quality Goal", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -135,6 +134,28 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "thumbs-up", + "indent": 1, + "keep_closed": 1, + "label": "Feedback", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quality Feedback", + "link_to": "Quality Feedback", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -146,22 +167,78 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "calendar-check", + "indent": 1, + "keep_closed": 1, + "label": "Meeting", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, { "child": 1, "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Quality Inspection Template", - "link_to": "Quality Inspection Template", + "label": "Quality Meeting", + "link_to": "Quality Meeting", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "review", + "indent": 1, + "keep_closed": 1, + "label": "Review and Action", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Non Conformance", + "link_to": "Non Conformance", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quality Review", + "link_to": "Quality Review", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quality Action", + "link_to": "Quality Action", "link_type": "DocType", "show_arrow": 0, "type": "Link" } ], - "modified": "2026-01-02 17:39:50.641254", + "modified": "2026-01-08 18:49:12.000467", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", "owner": "Administrator", + "standard": 1, "title": "Quality" } diff --git a/erpnext/workspace_sidebar/selling.json b/erpnext/workspace_sidebar/selling.json index 766b8f87fa9..679eb7b2803 100644 --- a/erpnext/workspace_sidebar/selling.json +++ b/erpnext/workspace_sidebar/selling.json @@ -30,6 +30,30 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "customer", + "indent": 0, + "keep_closed": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "notebook-tabs", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -70,17 +94,6 @@ "child": 0, "collapsible": 1, "icon": "computer", - "indent": 1, - "keep_closed": 1, - "label": "POS", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", "indent": 0, "keep_closed": 0, "label": "POS", @@ -89,101 +102,13 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Invoice", - "link_to": "POS Invoice", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Opening Entry", - "link_to": "POS Opening Entry", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Closing Entry", - "link_to": "POS Closing Entry", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Profile", - "link_to": "POS Profile", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Invoice Merge Log", - "link_to": "POS Invoice Merge Log", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "POS Settings", - "link_to": "POS Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Loyalty Program", - "link_to": "Loyalty Program", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Loyalty Point Entry", - "link_to": "Loyalty Point Entry", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, - "icon": "stock", + "icon": "package", "indent": 1, "keep_closed": 1, - "label": "Items & Pricing", + "label": "Item and Pricing", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -280,10 +205,10 @@ { "child": 0, "collapsible": 1, - "icon": "database", + "icon": "notepad-text", "indent": 1, "keep_closed": 1, - "label": "Setup", + "label": "Reports", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -291,15 +216,146 @@ { "child": 1, "collapsible": 1, - "icon": "", "indent": 0, "keep_closed": 0, - "label": "Customer", - "link_to": "Customer", - "link_type": "DocType", + "label": "Sales Analytics", + "link_to": "Sales Analytics", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Pending SO Items For Purchase Request", + "link_to": "Pending SO Items For Purchase Request", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Available Stock for Packing Items", + "link_to": "Available Stock for Packing Items", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customer Addresses And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Note Trends", + "link_to": "Delivery Note Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Invoice Trends", + "link_to": "Sales Invoice Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customer Credit Balance", + "link_to": "Customer Credit Balance", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customers Without Any Sales Transactions", + "link_to": "Customers Without Any Sales Transactions", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Partners Commission", + "link_to": "Sales Partners Commission", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Territory Target Variance Based On Item Group", + "link_to": "Territory Target Variance Based On Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Person Target Variance Based On Item Group", + "link_to": "Sales Person Target Variance Based On Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Partner Target Variance Based On Item Group", + "link_to": "Sales Partner Target Variance based on Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, { "child": 1, "collapsible": 1, @@ -355,17 +411,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Person", - "link_to": "Sales Person", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -382,8 +427,41 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Monthly Distribution", - "link_to": "Monthly Distribution", + "label": "Sales Person", + "link_to": "Sales Person", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Product Bundle", + "link_to": "Product Bundle", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Shipping Rule", + "link_to": "Shipping Rule", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "UTM Source", + "link_to": "UTM Source", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -410,270 +488,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Product Bundle", - "link_to": "Product Bundle", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "UTM Source", - "link_to": "UTM Source", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Shipping Rule", - "link_to": "Shipping Rule", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "sheet", - "indent": 1, - "keep_closed": 1, - "label": "Reports", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Register", - "link_to": "Sales Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item-wise Sales Register", - "link_to": "Item-wise Sales Register", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Analytics", - "link_to": "Sales Analytics", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customer Addresses And Contacts", - "link_to": "Address And Contacts", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Invoice Trends", - "link_to": "Sales Invoice Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customer Credit Balance", - "link_to": "Customer Credit Balance", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customers Without Any Sales Transactions", - "link_to": "Customers Without Any Sales Transactions", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Partners Commission", - "link_to": "Sales Partners Commission", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Available Stock for Packing Items", - "link_to": "Available Stock for Packing Items", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Territory Target Variance Based On Item Group", - "link_to": "Territory Target Variance Based On Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Person Target Variance Based On Item Group", - "link_to": "Sales Person Target Variance Based On Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Partner Target Variance Based On Item Group", - "link_to": "Sales Partner Target Variance based on Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Pending SO Items For Purchase Request", - "link_to": "Pending SO Items For Purchase Request", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Funnel", - "link_to": "sales-funnel", - "link_type": "Page", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Order Analysis", - "link_to": "Sales Order Analysis", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customer Acquisition and Loyalty", - "link_to": "Customer Acquisition and Loyalty", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quotation Trends", - "link_to": "Quotation Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Order Trends", - "link_to": "Sales Order Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item-wise Sales History", - "link_to": "Item-wise Sales History", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Person-wise Transaction Summary", - "link_to": "Sales Person-wise Transaction Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -687,10 +501,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:44:08.721891", + "modified": "2026-01-08 18:49:11.973725", "modified_by": "Administrator", "module": "Selling", "name": "Selling", "owner": "Administrator", + "standard": 1, "title": "Selling" } diff --git a/erpnext/workspace_sidebar/settings.json b/erpnext/workspace_sidebar/settings.json index 6414b0f21f8..5f8737a1374 100644 --- a/erpnext/workspace_sidebar/settings.json +++ b/erpnext/workspace_sidebar/settings.json @@ -9,47 +9,23 @@ { "child": 0, "collapsible": 1, - "icon": "earth", + "icon": "home", "indent": 0, "keep_closed": 0, - "label": "Global Defaults", - "link_to": "Global Defaults", - "link_type": "DocType", + "label": "Home", + "link_to": "Settings", + "link_type": "Workspace", "show_arrow": 0, "type": "Link" }, { "child": 0, "collapsible": 1, - "icon": "washing-machine", + "icon": "crm", "indent": 0, "keep_closed": 0, - "label": "System Settings", - "link_to": "System Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "accounting", - "indent": 0, - "keep_closed": 0, - "label": "Accounts Settings", - "link_to": "Accounts Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "computer", - "indent": 0, - "keep_closed": 0, - "label": "POS Settings", - "link_to": "POS Settings", + "label": "CRM Settings", + "link_to": "CRM Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -78,6 +54,18 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "accounting", + "indent": 0, + "keep_closed": 0, + "label": "Accounts Settings", + "link_to": "Accounts Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -102,6 +90,42 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "printer", + "indent": 0, + "keep_closed": 0, + "label": "Print Settings", + "link_to": "Print Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "computer", + "indent": 0, + "keep_closed": 0, + "label": "System Settings", + "link_to": "System Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "earth", + "indent": 0, + "keep_closed": 0, + "label": "Global Defaults", + "link_to": "Global Defaults", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -113,124 +137,13 @@ "link_type": "DocType", "show_arrow": 0, "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "crm", - "indent": 0, - "keep_closed": 0, - "label": "CRM Settings", - "link_to": "CRM Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "support", - "indent": 0, - "keep_closed": 0, - "label": "Support Settings", - "link_to": "Support Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "getting-started", - "indent": 1, - "keep_closed": 1, - "label": "Other Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Subscription Settings", - "link_to": "Subscription Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Variant Settings", - "link_to": "Item Variant Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Settings", - "link_to": "Delivery Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Currency Exchange Settings", - "link_to": "Currency Exchange Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Appointment Booking Settings", - "link_to": "Appointment Booking Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Reposting Settings", - "link_to": "Stock Reposting Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Repost Accounting Ledger Settings", - "link_to": "Repost Accounting Ledger Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" } ], - "modified": "2026-01-02 18:04:13.860117", + "modified": "2026-01-08 18:49:11.674859", "modified_by": "Administrator", "module": "Setup", "name": "Settings", "owner": "Administrator", + "standard": 1, "title": "Settings" } diff --git a/erpnext/workspace_sidebar/share_management.json b/erpnext/workspace_sidebar/share_management.json index 1b49348e477..6700343cfef 100644 --- a/erpnext/workspace_sidebar/share_management.json +++ b/erpnext/workspace_sidebar/share_management.json @@ -9,7 +9,6 @@ { "child": 1, "collapsible": 1, - "icon": "customer", "indent": 0, "keep_closed": 0, "label": "Shareholder", @@ -21,7 +20,6 @@ { "child": 1, "collapsible": 1, - "icon": "move-horizontal", "indent": 0, "keep_closed": 0, "label": "Share Transfer", @@ -33,7 +31,6 @@ { "child": 1, "collapsible": 1, - "icon": "list", "indent": 0, "keep_closed": 0, "label": "Share Ledger", @@ -45,7 +42,6 @@ { "child": 1, "collapsible": 1, - "icon": "notepad-text", "indent": 0, "keep_closed": 0, "label": "Share Balance", @@ -55,10 +51,11 @@ "type": "Link" } ], - "modified": "2026-01-02 14:53:29.842384", + "modified": "2026-01-08 18:49:11.834013", "modified_by": "Administrator", "module": "Accounts", "name": "Share Management", "owner": "Administrator", + "standard": 1, "title": "Share Management" } diff --git a/erpnext/workspace_sidebar/stock.json b/erpnext/workspace_sidebar/stock.json index 87c53dad060..9102b02b0d6 100644 --- a/erpnext/workspace_sidebar/stock.json +++ b/erpnext/workspace_sidebar/stock.json @@ -30,6 +30,30 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "notebook-tabs", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "warehouse", + "indent": 0, + "keep_closed": 0, + "label": "Warehouse", + "link_to": "Warehouse", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -69,7 +93,19 @@ { "child": 0, "collapsible": 1, - "icon": "arrow-left-to-line", + "icon": "recycle", + "indent": 0, + "keep_closed": 0, + "label": "Stock Reconciliation", + "link_to": "Stock Reconciliation", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "arrow-right", "indent": 0, "keep_closed": 0, "label": "Material Request", @@ -81,22 +117,10 @@ { "child": 0, "collapsible": 1, - "icon": "caravan", - "indent": 0, - "keep_closed": 0, - "label": "Pick List", - "link_to": "Pick List", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "tool", + "icon": "notepad-text", "indent": 1, "keep_closed": 1, - "label": "Tools", + "label": "Reports", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -104,24 +128,11 @@ { "child": 1, "collapsible": 1, - "icon": "", "indent": 0, "keep_closed": 0, - "label": "Stock Reconciliation", - "link_to": "Stock Reconciliation", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Landed Cost Voucher", - "link_to": "Landed Cost Voucher", - "link_type": "DocType", + "label": "Stock Ledger", + "link_to": "Stock Ledger", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -130,9 +141,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Repost Item Valuation", - "link_to": "Repost Item Valuation", - "link_type": "DocType", + "label": "Stock Balance", + "link_to": "Stock Balance", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -141,9 +152,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Packing Slip", - "link_to": "Packing Slip", - "link_type": "DocType", + "label": "Stock Analytics", + "link_to": "Stock Analytics", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -152,9 +163,31 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Quality Inspection", - "link_to": "Quality Inspection", - "link_type": "DocType", + "label": "Serial No and Batch Traceability", + "link_to": "Serial No and Batch Traceability", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Purchase Receipt Trends", + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Note Trends", + "link_to": "Delivery Note Trends", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -170,85 +203,6 @@ "show_arrow": 0, "type": "Section Break" }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Group", - "link_to": "Item Group", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Attribute", - "link_to": "Item Attribute", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Brand", - "link_to": "Brand", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Warehouse", - "link_to": "Warehouse", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Unit of Measure (UOM)", - "link_to": "UOM", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "UOM Conversion Factor", - "link_to": "UOM Conversion Factor", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -293,342 +247,24 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Shipping Rule", - "link_to": "Shipping Rule", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Alternative", - "link_to": "Item Alternative", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quality Inspection Template", - "link_to": "Quality Inspection Template", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Trip", - "link_to": "Delivery Trip", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "sheet", - "indent": 1, - "keep_closed": 1, - "label": "Reports", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Ledger", - "link_to": "Stock Ledger", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Balance", - "link_to": "Stock Balance", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quick Stock Balance", - "link_to": "Quick Stock Balance", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Projected Qty", - "link_to": "Stock Projected Qty", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Analytics", - "link_to": "Stock Analytics", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Ageing", - "link_to": "Stock Ageing", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Purchase Receipt Trends", - "link_to": "Purchase Receipt Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Note Trends", - "link_to": "Delivery Note Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Price Stock", - "link_to": "Item Price Stock", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Warehouse Wise Stock Balance", - "link_to": "Warehouse Wise Stock Balance", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Shortage Report", - "link_to": "Item Shortage Report", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Serial No and Batch Traceability", - "link_to": "Serial No and Batch Traceability", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Serial No Status", - "link_to": "Serial No Status", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Serial No Ledger", - "link_to": "Serial No Ledger", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Serial No Warranty Expiry", - "link_to": "Serial No Warranty Expiry", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Batch-Wise Balance History", - "link_to": "Batch-Wise Balance History", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Batch Item Expiry Status", - "link_to": "Batch Item Expiry Status", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Requested Items To Be Transferred", - "link_to": "Requested Items To Be Transferred", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Itemwise Recommended Reorder Level", - "link_to": "Itemwise Recommended Reorder Level", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Variant Details", - "link_to": "Item Variant Details", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, "icon": "settings", - "indent": 1, - "keep_closed": 1, + "indent": 0, + "keep_closed": 0, "label": "Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Stock Settings", "link_to": "Stock Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Item Variant Settings", - "link_to": "Item Variant Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Stock Reposting Settings", - "link_to": "Stock Reposting Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Settings", - "link_to": "Delivery Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" } ], - "modified": "2026-01-02 18:10:31.153427", + "modified": "2026-01-08 18:49:11.656807", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "owner": "Administrator", + "standard": 1, "title": "Stock" } diff --git a/erpnext/workspace_sidebar/subcontracting.json b/erpnext/workspace_sidebar/subcontracting.json index f555875c292..ca0e87de16e 100644 --- a/erpnext/workspace_sidebar/subcontracting.json +++ b/erpnext/workspace_sidebar/subcontracting.json @@ -18,34 +18,10 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "icon": "folder-tree", - "indent": 0, - "keep_closed": 0, - "label": "Subcontracting BOM", - "link_to": "Subcontracting BOM", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "move-horizontal", - "indent": 0, - "keep_closed": 0, - "label": "Stock Entry", - "link_to": "Stock Entry", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, - "icon": "arrow-left-to-line", + "icon": "arrow-right", "indent": 1, "keep_closed": 0, "label": "Inward Order", @@ -92,7 +68,7 @@ { "child": 0, "collapsible": 1, - "icon": "arrow-right-from-line", + "icon": "arrow-left", "indent": 1, "keep_closed": 0, "label": "Outward Order", @@ -136,6 +112,41 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "tool", + "indent": 1, + "keep_closed": 1, + "label": "Tools", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Stock Entry", + "link_to": "Stock Entry", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Subcontracting BOM", + "link_to": "Subcontracting BOM", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -196,10 +207,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:51:15.843931", + "modified": "2026-01-08 18:49:11.605180", "modified_by": "Administrator", "module": "Buying", "name": "Subcontracting", "owner": "Administrator", + "standard": 1, "title": "Subcontracting" } diff --git a/erpnext/workspace_sidebar/subscription.json b/erpnext/workspace_sidebar/subscription.json index ed91cc0c04c..5976d23fc98 100644 --- a/erpnext/workspace_sidebar/subscription.json +++ b/erpnext/workspace_sidebar/subscription.json @@ -87,10 +87,11 @@ "type": "Link" } ], - "modified": "2025-12-22 17:06:54.262451", + "modified": "2026-01-08 18:49:11.840656", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", "owner": "Administrator", + "standard": 1, "title": "Subscription" } diff --git a/erpnext/workspace_sidebar/support.json b/erpnext/workspace_sidebar/support.json index b62a52554f3..227df774387 100644 --- a/erpnext/workspace_sidebar/support.json +++ b/erpnext/workspace_sidebar/support.json @@ -24,24 +24,12 @@ "icon": "file-question-mark", "indent": 0, "keep_closed": 0, - "label": "Issue", + "label": "Issues", "link_to": "Issue", "link_type": "DocType", "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "calendar-days", - "indent": 0, - "keep_closed": 0, - "label": "Maintenance Schedule", - "link_to": "Maintenance Schedule", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -57,6 +45,18 @@ { "child": 0, "collapsible": 1, + "icon": "receipt-text", + "indent": 0, + "keep_closed": 0, + "label": "SLA", + "link_to": "Service Level Agreement", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, "icon": "grid-2x2-check", "indent": 0, "keep_closed": 0, @@ -69,10 +69,10 @@ { "child": 0, "collapsible": 1, - "icon": "database", + "icon": "tool", "indent": 1, "keep_closed": 1, - "label": "Setup", + "label": "Maintenance", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -82,8 +82,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Issue Type", - "link_to": "Issue Type", + "label": "Maintenance Schedule", + "link_to": "Maintenance Schedule", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -93,8 +93,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Issue Priority", - "link_to": "Issue Priority", + "label": "Maintenance Visit", + "link_to": "Maintenance Visit", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -132,23 +132,57 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Issue Type", + "link_to": "Issue Type", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Issue Priority", + "link_to": "Issue Priority", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, "icon": "settings", "indent": 0, "keep_closed": 0, - "label": "Settings", + "label": "Support Settings", "link_to": "Support Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" } ], - "modified": "2026-01-02 17:47:19.864824", + "modified": "2026-01-08 18:49:11.958639", "modified_by": "Administrator", "module": "Support", "name": "Support", "owner": "Administrator", + "standard": 1, "title": "Support" } diff --git a/erpnext/workspace_sidebar/taxes.json b/erpnext/workspace_sidebar/taxes.json index 9d9033f29ac..a9cd5166787 100644 --- a/erpnext/workspace_sidebar/taxes.json +++ b/erpnext/workspace_sidebar/taxes.json @@ -13,9 +13,8 @@ "indent": 0, "keep_closed": 0, "label": "Sales Tax Template", - "link_to": "Item Tax Template", + "link_to": "Sales Taxes and Charges Template", "link_type": "DocType", - "navigate_to_tab": "", "show_arrow": 0, "type": "Link" }, @@ -46,17 +45,6 @@ { "child": 0, "collapsible": 1, - "icon": "database", - "indent": 1, - "keep_closed": 1, - "label": "Setup", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, "icon": "triangle", "indent": 0, "keep_closed": 0, @@ -67,7 +55,7 @@ "type": "Link" }, { - "child": 1, + "child": 0, "collapsible": 1, "icon": "book-open-text", "indent": 0, @@ -79,28 +67,17 @@ "type": "Link" }, { - "child": 1, + "child": 0, "collapsible": 1, "icon": "book-text", "indent": 0, "keep_closed": 0, - "label": "Tax Withholding Category", + "label": "Tax Withholding", "link_to": "Tax Withholding Category", "link_type": "DocType", "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Tax Withholding Group", - "link_to": "Tax Withholding Group", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -112,46 +89,13 @@ "link_type": "DocType", "show_arrow": 0, "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "sheet", - "indent": 1, - "keep_closed": 1, - "label": "Reports", - "link_to": "", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "TDS Computation Summary", - "link_to": "TDS Computation Summary", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Tax Withholding Details", - "link_to": "Tax Withholding Details", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" } ], - "modified": "2026-01-02 14:40:04.075046", + "modified": "2026-01-08 18:49:11.798691", "modified_by": "Administrator", "module": "Accounts", "name": "Taxes", "owner": "Administrator", + "standard": 1, "title": "Taxes" } From 5eb062c065de0effdaee7c33ecbdc7c70914c133 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 8 Jan 2026 19:40:18 +0530 Subject: [PATCH 032/106] fix: item not found --- erpnext/utilities/transaction_base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 0f29de81ebd..375c2a4409a 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -295,6 +295,9 @@ class TransactionBase(StatusUpdater): # Server side 'item' doc. Update this to reflect in UI item_obj = self.get("items", {"idx": item_idx})[0] + if not item_obj.item_code: + return + # 'item_details' has latest item related values item_details = self.fetch_item_details(item_obj) From 069f28feeb3df3c3278ed813968f2831b706b27f Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Tue, 6 Jan 2026 00:29:07 +0530 Subject: [PATCH 033/106] fix(pos): item selector section ui/ux --- erpnext/public/scss/point-of-sale.scss | 7 +++ .../page/point_of_sale/point_of_sale.py | 12 ++-- .../page/point_of_sale/pos_item_selector.js | 59 +++++++++++++------ 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index d86d2038adf..6eb6b56c570 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -117,6 +117,13 @@ overflow-y: scroll; overflow-x: hidden; + &.items-not-found { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + &.show-item-image { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--margin-lg); diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 50ab2404078..35d22e40fb7 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -122,11 +122,13 @@ def filter_result_items(result, pos_profile): @frappe.whitelist() -def get_parent_item_group(): - # Using get_all to ignore user permission - item_group = frappe.get_all("Item Group", {"lft": 1, "is_group": 1}, pluck="name") - if item_group: - return item_group[0] +def get_parent_item_group(pos_profile): + item_groups = get_item_groups(pos_profile) + + if not item_groups: + item_groups = frappe.get_all("Item Group", {"lft": 1, "is_group": 1}, pluck="name") + + return item_groups[0] if item_groups else None @frappe.whitelist() diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 835fd65f846..c40dfcfde5e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -7,12 +7,14 @@ erpnext.PointOfSale.ItemSelector = class { this.events = events; this.pos_profile = pos_profile; this.hide_images = settings.hide_images; + this.item_display_class = this.hide_images ? "hide-item-image" : "show-item-image"; this.auto_add_item = settings.auto_add_item_to_cart; + this.get_parent_item_group(); this.inti_component(); } - inti_component() { + async inti_component() { this.prepare_dom(); this.make_search_bar(); this.load_items_data(); @@ -35,20 +37,20 @@ erpnext.PointOfSale.ItemSelector = class { this.$component = this.wrapper.find(".items-selector"); this.$items_container = this.$component.find(".items-container"); - const show_hide_images = this.hide_images ? "hide-item-image" : "show-item-image"; - this.$items_container.addClass(show_hide_images); + this.$items_container.addClass(this.item_display_class); + } + + async get_parent_item_group() { + const r = await frappe.call({ + method: "erpnext.selling.page.point_of_sale.point_of_sale.get_parent_item_group", + args: { + pos_profile: this.pos_profile, + }, + }); + if (r.message) this.item_group = this.parent_item_group = r.message; } async load_items_data() { - if (!this.item_group) { - frappe.call({ - method: "erpnext.selling.page.point_of_sale.point_of_sale.get_parent_item_group", - async: false, - callback: (r) => { - if (r.message) this.parent_item_group = r.message; - }, - }); - } if (!this.price_list) { const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list"); this.price_list = res.message.selling_price_list; @@ -64,8 +66,6 @@ erpnext.PointOfSale.ItemSelector = class { const price_list = (doc && doc.selling_price_list) || this.price_list; let { item_group, pos_profile } = this; - !item_group && (item_group = this.parent_item_group); - return frappe.call({ method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items", freeze: true, @@ -76,16 +76,32 @@ erpnext.PointOfSale.ItemSelector = class { render_item_list(items) { this.$items_container.html(""); + if (!items?.length) { + this.set_items_not_found_banner(); + return; + } + + if (this.$items_container.hasClass("items-not-found")) { + this.$items_container.removeClass("items-not-found"); + this.$items_container.addClass(this.item_display_class); + } + if (this.hide_images) { this.$items_container.append(this.render_item_list_column_header()); } - items.forEach((item) => { + items?.forEach((item) => { const item_html = this.get_item_html(item); this.$items_container.append(item_html); }); } + set_items_not_found_banner() { + this.$items_container.removeClass(this.item_display_class); + this.$items_container.addClass("items-not-found"); + this.$items_container.html(__("Items not found.")); + } + render_item_list_column_header() { return `
Name
@@ -189,17 +205,18 @@ erpnext.PointOfSale.ItemSelector = class { fieldtype: "Link", options: "Item Group", placeholder: __("Select item group"), + only_select: true, onchange: function () { me.item_group = this.value; !me.item_group && (me.item_group = me.parent_item_group); me.filter_items(); + me.set_item_selector_filter_label(this.value); }, get_query: function () { - const doc = me.events.get_frm().doc; return { query: "erpnext.selling.page.point_of_sale.point_of_sale.item_group_query", filters: { - pos_profile: doc ? doc.pos_profile : "", + pos_profile: me.pos_profile, }, }; }, @@ -210,9 +227,17 @@ erpnext.PointOfSale.ItemSelector = class { this.search_field.toggle_label(false); this.item_group_field.toggle_label(false); + $(this.item_group_field.awesomplete.ul).css("min-width", "unset"); + this.attach_clear_btn(); } + set_item_selector_filter_label(value) { + const $filter_label = this.$component.find(".label"); + + $filter_label.html(value ? __(value) : __("All Items")); + } + attach_clear_btn() { this.search_field.$wrapper.find(".control-input").append( ` From aef2e2794ba62c93680c7fb127af505e50888455 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 8 Jan 2026 18:15:03 +0530 Subject: [PATCH 034/106] fix: race condition --- erpnext/selling/page/point_of_sale/pos_item_selector.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index c40dfcfde5e..b6b92c2204e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -10,11 +10,11 @@ erpnext.PointOfSale.ItemSelector = class { this.item_display_class = this.hide_images ? "hide-item-image" : "show-item-image"; this.auto_add_item = settings.auto_add_item_to_cart; - this.get_parent_item_group(); + this.item_ready_group = this.get_parent_item_group(); this.inti_component(); } - async inti_component() { + inti_component() { this.prepare_dom(); this.make_search_bar(); this.load_items_data(); @@ -51,6 +51,8 @@ erpnext.PointOfSale.ItemSelector = class { } async load_items_data() { + await this.item_ready_group; + if (!this.price_list) { const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list"); this.price_list = res.message.selling_price_list; From 02cefa8bdbdab40430f7bf81498a6756d73ed7e4 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 8 Jan 2026 18:54:14 +0530 Subject: [PATCH 035/106] fix: item group field clear button --- .../page/point_of_sale/pos_item_selector.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index b6b92c2204e..d03b2a0b8fa 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -231,6 +231,7 @@ erpnext.PointOfSale.ItemSelector = class { $(this.item_group_field.awesomplete.ul).css("min-width", "unset"); + this.hide_open_link_btn(); this.attach_clear_btn(); } @@ -240,6 +241,10 @@ erpnext.PointOfSale.ItemSelector = class { $filter_label.html(value ? __(value) : __("All Items")); } + hide_open_link_btn() { + $(this.item_group_field.$wrapper.find(".btn-open")).css("display", "none"); + } + attach_clear_btn() { this.search_field.$wrapper.find(".control-input").append( ` @@ -249,12 +254,24 @@ erpnext.PointOfSale.ItemSelector = class { ` ); + this.item_group_field.$wrapper.find(".link-btn").append( + ` + ${frappe.utils.icon("close", "xs", "es-icon")} + ` + ); + this.$clear_search_btn = this.search_field.$wrapper.find(".link-btn"); + this.$clear_item_group_btn = this.item_group_field.$wrapper.find(".btn-clear"); this.$clear_search_btn.on("click", "a", () => { this.set_search_value(""); this.search_field.set_focus(); }); + + this.$clear_item_group_btn.on("click", () => { + $(this.item_group_field.$input[0]).val("").trigger("input"); + this.item_group_field.set_focus(); + }); } set_search_value(value) { From 4d8d29b0df14cd2abeed1cdbf1cc0d5b7f2350f5 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 8 Jan 2026 21:35:55 +0530 Subject: [PATCH 036/106] fix: animate on item load --- erpnext/public/scss/point-of-sale.scss | 29 +++++++++++ .../page/point_of_sale/pos_item_selector.js | 52 +++++++++++++------ 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 6eb6b56c570..5ef6e8fc5db 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -117,6 +117,35 @@ overflow-y: scroll; overflow-x: hidden; + &.item-loading { + position: relative; + pointer-events: none; + } + + &.item-loading::after { + content: ""; + position: absolute; + inset: 0; + background: repeating-linear-gradient( + 90deg, + #f3f3f3 0px, + #f3f3f3 160px, + #e9ecef 160px, + #e9ecef 320px + ); + animation: skeletonMove 1.1s linear infinite; + z-index: 1; + } + + @keyframes skeletonMove { + from { + background-position: 0 0; + } + to { + background-position: 320px 0; + } + } + &.items-not-found { display: flex; align-items: center; diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index d03b2a0b8fa..69ec1e56934 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -53,14 +53,20 @@ erpnext.PointOfSale.ItemSelector = class { async load_items_data() { await this.item_ready_group; + this.start_item_loading_animation(); + if (!this.price_list) { const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list"); this.price_list = res.message.selling_price_list; } - this.get_items({}).then(({ message }) => { - this.render_item_list(message.items); - }); + this.get_items({}) + .then(({ message }) => { + this.render_item_list(message.items); + }) + .always(() => { + this.stop_item_loading_animation(); + }); } get_items({ start = 0, page_length = 40, search_term = "" }) { @@ -403,6 +409,8 @@ erpnext.PointOfSale.ItemSelector = class { } filter_items({ search_term = "" } = {}) { + this.start_item_loading_animation(); + const selling_price_list = this.events.get_frm().doc.selling_price_list; if (search_term) { @@ -423,19 +431,31 @@ erpnext.PointOfSale.ItemSelector = class { } } - this.get_items({ search_term }).then(({ message }) => { - // eslint-disable-next-line no-unused-vars - const { items, serial_no, batch_no, barcode } = message; - if (search_term && !barcode) { - this.search_index[selling_price_list][search_term] = items; - } - this.items = items; - this.render_item_list(items); - this.auto_add_item && - this.search_field.$input[0].value && - this.items.length == 1 && - this.add_filtered_item_to_cart(); - }); + this.get_items({ search_term }) + .then(({ message }) => { + // eslint-disable-next-line no-unused-vars + const { items, serial_no, batch_no, barcode } = message; + if (search_term && !barcode) { + this.search_index[selling_price_list][search_term] = items; + } + this.items = items; + this.render_item_list(items); + this.auto_add_item && + this.search_field.$input[0].value && + this.items.length == 1 && + this.add_filtered_item_to_cart(); + }) + .always(() => { + this.stop_item_loading_animation(); + }); + } + + start_item_loading_animation() { + this.$items_container.addClass("is-loading"); + } + + stop_item_loading_animation() { + this.$items_container.removeClass("is-loading"); } add_filtered_item_to_cart() { From 67def7dc1346e9dfea83acfc1ec060446b1f6950 Mon Sep 17 00:00:00 2001 From: jacob-salvi Date: Fri, 9 Jan 2026 14:50:37 +0530 Subject: [PATCH 037/106] chore: update new icons --- erpnext/public/icons/desktop_icons/subtle/banking.svg | 4 ++++ erpnext/public/icons/desktop_icons/subtle/budget.svg | 4 ++++ erpnext/public/icons/desktop_icons/subtle/subscription.svg | 4 ++++ erpnext/public/icons/desktop_icons/subtle/taxes.svg | 6 ++++++ 4 files changed, 18 insertions(+) create mode 100644 erpnext/public/icons/desktop_icons/subtle/banking.svg create mode 100644 erpnext/public/icons/desktop_icons/subtle/budget.svg create mode 100644 erpnext/public/icons/desktop_icons/subtle/subscription.svg create mode 100644 erpnext/public/icons/desktop_icons/subtle/taxes.svg diff --git a/erpnext/public/icons/desktop_icons/subtle/banking.svg b/erpnext/public/icons/desktop_icons/subtle/banking.svg new file mode 100644 index 00000000000..d27566101aa --- /dev/null +++ b/erpnext/public/icons/desktop_icons/subtle/banking.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/subtle/budget.svg b/erpnext/public/icons/desktop_icons/subtle/budget.svg new file mode 100644 index 00000000000..1a84e5fe7af --- /dev/null +++ b/erpnext/public/icons/desktop_icons/subtle/budget.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/subtle/subscription.svg b/erpnext/public/icons/desktop_icons/subtle/subscription.svg new file mode 100644 index 00000000000..aa23528ec5e --- /dev/null +++ b/erpnext/public/icons/desktop_icons/subtle/subscription.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/subtle/taxes.svg b/erpnext/public/icons/desktop_icons/subtle/taxes.svg new file mode 100644 index 00000000000..b89d6014fa3 --- /dev/null +++ b/erpnext/public/icons/desktop_icons/subtle/taxes.svg @@ -0,0 +1,6 @@ + + + + + + From fc9bc36110a6c845d25e2f124a6e75eba7d09982 Mon Sep 17 00:00:00 2001 From: jacob-salvi Date: Fri, 9 Jan 2026 14:53:09 +0530 Subject: [PATCH 038/106] chore: update new solid icons --- erpnext/public/icons/desktop_icons/solid/banking.svg | 4 ++++ erpnext/public/icons/desktop_icons/solid/budget.svg | 4 ++++ erpnext/public/icons/desktop_icons/solid/subscription.svg | 4 ++++ erpnext/public/icons/desktop_icons/solid/taxes.svg | 6 ++++++ 4 files changed, 18 insertions(+) create mode 100644 erpnext/public/icons/desktop_icons/solid/banking.svg create mode 100644 erpnext/public/icons/desktop_icons/solid/budget.svg create mode 100644 erpnext/public/icons/desktop_icons/solid/subscription.svg create mode 100644 erpnext/public/icons/desktop_icons/solid/taxes.svg diff --git a/erpnext/public/icons/desktop_icons/solid/banking.svg b/erpnext/public/icons/desktop_icons/solid/banking.svg new file mode 100644 index 00000000000..57d7d11a8e1 --- /dev/null +++ b/erpnext/public/icons/desktop_icons/solid/banking.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/solid/budget.svg b/erpnext/public/icons/desktop_icons/solid/budget.svg new file mode 100644 index 00000000000..ddee38fb472 --- /dev/null +++ b/erpnext/public/icons/desktop_icons/solid/budget.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/solid/subscription.svg b/erpnext/public/icons/desktop_icons/solid/subscription.svg new file mode 100644 index 00000000000..bf371a29eca --- /dev/null +++ b/erpnext/public/icons/desktop_icons/solid/subscription.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/solid/taxes.svg b/erpnext/public/icons/desktop_icons/solid/taxes.svg new file mode 100644 index 00000000000..6c9ae689c8b --- /dev/null +++ b/erpnext/public/icons/desktop_icons/solid/taxes.svg @@ -0,0 +1,6 @@ + + + + + + From b11f20596e3cb26a001da36b7f1581a9961f432f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 9 Jan 2026 16:03:14 +0530 Subject: [PATCH 039/106] chore: removed unused code --- erpnext/stock/deprecated_serial_batch.py | 2 +- erpnext/stock/doctype/bin/bin.py | 2 +- .../serial_and_batch_bundle.py | 2 +- .../stock_reconciliation.py | 2 +- .../report/stock_analytics/stock_analytics.py | 1 - .../stock/report/stock_ledger/stock_ledger.py | 2 +- erpnext/stock/serial_batch_bundle.py | 2 +- erpnext/stock/stock_ledger.py | 25 ---------------- erpnext/stock/utils.py | 30 +------------------ 9 files changed, 7 insertions(+), 61 deletions(-) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index dd7a3830c9b..0fb3f048983 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -3,7 +3,7 @@ import json from collections import defaultdict import frappe -from frappe.query_builder.functions import CombineDatetime, Sum +from frappe.query_builder.functions import Sum from frappe.utils import flt, nowtime from pypika import Order from pypika.functions import Coalesce diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 79e9776b91c..62c4528f432 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -5,7 +5,7 @@ import frappe from frappe.model.document import Document from frappe.query_builder import Case, Order -from frappe.query_builder.functions import Coalesce, CombineDatetime, Sum +from frappe.query_builder.functions import Coalesce, Sum from frappe.utils import flt diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 5a749b31f62..d4307dbc7f7 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -12,7 +12,7 @@ import frappe.query_builder.functions from frappe import _, _dict, bold from frappe.model.document import Document from frappe.model.naming import make_autoname -from frappe.query_builder.functions import CombineDatetime, Sum +from frappe.query_builder.functions import Sum from frappe.utils import ( cint, cstr, diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index fa3cada5731..59683e87e3d 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -4,7 +4,7 @@ import frappe from frappe import _, bold, json, msgprint -from frappe.query_builder.functions import CombineDatetime, Sum +from frappe.query_builder.functions import Sum from frappe.utils import add_to_date, cint, cstr, flt, get_datetime, now import erpnext diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 16dea3c2942..36f0f2e5be6 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -4,7 +4,6 @@ import datetime import frappe from frappe import _, scrub -from frappe.query_builder.functions import CombineDatetime from frappe.utils import get_datetime, get_first_day_of_week, get_quarter_start, getdate from frappe.utils import get_first_day as get_first_day_of_month from frappe.utils.nestedset import get_descendants_of diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index b55a8e43f67..8e37799e8ce 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -7,7 +7,7 @@ from collections import defaultdict import frappe from frappe import _ -from frappe.query_builder.functions import CombineDatetime, Sum +from frappe.query_builder.functions import Sum from frappe.utils import cint, flt, get_datetime from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 298cec2fa89..aee27b8a8d4 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -4,7 +4,7 @@ import frappe from frappe import _, bold from frappe.model.naming import NamingSeries, make_autoname, parse_naming_series from frappe.query_builder import Case -from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp +from frappe.query_builder.functions import Sum, Timestamp from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, now, nowtime, today from pypika import Order from pypika.terms import ExistsCriterion diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index dfd516cc293..1f641ab25b0 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1899,31 +1899,6 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): ) -def get_batch_incoming_rate(item_code, warehouse, batch_no, posting_date, posting_time, creation=None): - sle = frappe.qb.DocType("Stock Ledger Entry") - - timestamp_condition = sle.posting_datetime < get_combine_datetime(posting_date, posting_time) - if creation: - timestamp_condition |= (sle.posting_datetime == get_combine_datetime(posting_date, posting_time)) & ( - sle.creation < creation - ) - - batch_details = ( - frappe.qb.from_(sle) - .select(Sum(sle.stock_value_difference).as_("batch_value"), Sum(sle.actual_qty).as_("batch_qty")) - .where( - (sle.item_code == item_code) - & (sle.warehouse == warehouse) - & (sle.batch_no == batch_no) - & (sle.is_cancelled == 0) - ) - .where(timestamp_condition) - ).run(as_dict=True) - - if batch_details and batch_details[0].batch_qty: - return batch_details[0].batch_value / batch_details[0].batch_qty - - def get_valuation_rate( item_code, warehouse, diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 5367d67004c..7a60dcb64fc 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -6,7 +6,7 @@ import json import frappe from frappe import _ -from frappe.query_builder.functions import CombineDatetime, IfNull, Sum +from frappe.query_builder.functions import IfNull, Sum from frappe.utils import cstr, flt, get_link_to_form, get_time, getdate, nowdate, nowtime import erpnext @@ -331,34 +331,6 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): return flt(in_rate) -def get_batch_incoming_rate(item_code, warehouse, batch_no, posting_date, posting_time, creation=None): - sle = frappe.qb.DocType("Stock Ledger Entry") - - timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( - posting_date, posting_time - ) - if creation: - timestamp_condition |= ( - CombineDatetime(sle.posting_date, sle.posting_time) == CombineDatetime(posting_date, posting_time) - ) & (sle.creation < creation) - - batch_details = ( - frappe.qb.from_(sle) - .select(Sum(sle.stock_value_difference).as_("batch_value"), Sum(sle.actual_qty).as_("batch_qty")) - .where( - (sle.item_code == item_code) - & (sle.warehouse == warehouse) - & (sle.batch_no == batch_no) - & (sle.serial_and_batch_bundle.isnull()) - & (sle.is_cancelled == 0) - ) - .where(timestamp_condition) - ).run(as_dict=True) - - if batch_details and batch_details[0].batch_qty: - return batch_details[0].batch_value / batch_details[0].batch_qty - - def get_avg_purchase_rate(serial_nos): """get average value of serial numbers""" From c1d50c492b4b24d1dca28db60b85619cf197e0e4 Mon Sep 17 00:00:00 2001 From: nivithamerlin Date: Fri, 9 Jan 2026 16:36:12 +0530 Subject: [PATCH 040/106] fix(asset): remove references for composite and existing asset --- erpnext/assets/doctype/asset/asset.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index e0b4593416a..b8a8dd6db10 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -237,6 +237,8 @@ frappe.ui.form.on("Asset", { } else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { frm.toggle_reqd("purchase_receipt", 0); frm.toggle_reqd("purchase_invoice", 0); + frm.set_value("purchase_receipt", ""); + frm.set_value("purchase_invoice", ""); } else if (frm.doc.purchase_receipt) { // if purchase receipt link is set then set PI disabled frm.toggle_reqd("purchase_invoice", 0); @@ -480,7 +482,6 @@ frappe.ui.form.on("Asset", { } else { frm.set_df_property("net_purchase_amount", "read_only", 0); } - frm.trigger("toggle_reference_doc"); }, From 79a14f49e144b05cbb0ba2733fd9e7ae217055bd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 9 Jan 2026 17:24:17 +0530 Subject: [PATCH 041/106] Revert "chore: export sidebars for new schema" (#51631) --- erpnext/workspace_sidebar/accounting.json | 72 ++- erpnext/workspace_sidebar/assets.json | 14 +- erpnext/workspace_sidebar/banking.json | 80 ++- erpnext/workspace_sidebar/budget.json | 18 +- erpnext/workspace_sidebar/buying.json | 215 +++++-- erpnext/workspace_sidebar/crm.json | 19 +- .../workspace_sidebar/financial_reports.json | 124 +++- erpnext/workspace_sidebar/home.json | 3 +- erpnext/workspace_sidebar/manufacturing.json | 39 +- erpnext/workspace_sidebar/projects.json | 151 ++++- erpnext/workspace_sidebar/quality.json | 159 ++--- erpnext/workspace_sidebar/selling.json | 579 ++++++++++++------ erpnext/workspace_sidebar/settings.json | 201 ++++-- .../workspace_sidebar/share_management.json | 7 +- erpnext/workspace_sidebar/stock.json | 516 +++++++++++++--- erpnext/workspace_sidebar/subcontracting.json | 66 +- erpnext/workspace_sidebar/subscription.json | 3 +- erpnext/workspace_sidebar/support.json | 76 +-- erpnext/workspace_sidebar/taxes.json | 68 +- 19 files changed, 1725 insertions(+), 685 deletions(-) diff --git a/erpnext/workspace_sidebar/accounting.json b/erpnext/workspace_sidebar/accounting.json index adcc139f882..a7286ecbfd0 100644 --- a/erpnext/workspace_sidebar/accounting.json +++ b/erpnext/workspace_sidebar/accounting.json @@ -126,6 +126,7 @@ { "child": 1, "collapsible": 1, + "filters": "[[\"Purchase Invoice\",\"is_return\",\"=\",1]]", "indent": 0, "keep_closed": 0, "label": "Debit Note", @@ -233,6 +234,28 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Repost Accounting Ledger", + "link_to": "Repost Accounting Ledger", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Repost Payment Ledger", + "link_to": "Repost Payment Ledger", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -381,6 +404,17 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Account Category", + "link_to": "Account Category", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -495,20 +529,52 @@ "child": 0, "collapsible": 1, "icon": "settings", + "indent": 1, + "keep_closed": 1, + "label": "Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", "indent": 0, "keep_closed": 0, - "label": "Settings", + "label": "Accounts Settings", "link_to": "Accounts Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Currency Exchange Settings", + "link_to": "Currency Exchange Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Repost Accounting Ledger Settings", + "link_to": "Repost Accounting Ledger Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" } ], - "modified": "2026-01-08 18:49:12.169163", + "modified": "2026-01-08 15:04:31.767795", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", "owner": "Administrator", - "standard": 1, "title": "Accounting" } diff --git a/erpnext/workspace_sidebar/assets.json b/erpnext/workspace_sidebar/assets.json index 06cd4ea78e5..6f6e709c8f6 100644 --- a/erpnext/workspace_sidebar/assets.json +++ b/erpnext/workspace_sidebar/assets.json @@ -243,17 +243,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Depreciation Schedule", - "link_to": "Asset Depreciation Schedule", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -269,11 +258,10 @@ "url": "" } ], - "modified": "2026-01-08 18:49:12.152039", + "modified": "2026-01-02 14:54:20.640887", "modified_by": "Administrator", "module": "Assets", "name": "Assets", "owner": "Administrator", - "standard": 1, "title": "Assets" } diff --git a/erpnext/workspace_sidebar/banking.json b/erpnext/workspace_sidebar/banking.json index 3e37e9bdb76..95834113772 100644 --- a/erpnext/workspace_sidebar/banking.json +++ b/erpnext/workspace_sidebar/banking.json @@ -6,6 +6,18 @@ "header_icon": "circle-dollar-sign", "idx": 0, "items": [ + { + "child": 0, + "collapsible": 1, + "icon": "home", + "indent": 0, + "keep_closed": 0, + "label": "Home", + "link_to": "Banking", + "link_type": "Workspace", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -36,12 +48,36 @@ "icon": "clipboard-check", "indent": 0, "keep_closed": 0, - "label": "Reconciliation Report", + "label": "Reconciliation Statement", "link_to": "Bank Reconciliation Statement", "link_type": "Report", "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "split", + "indent": 0, + "keep_closed": 0, + "label": "Unreconcile Payment", + "link_to": "Unreconcile Payment", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "link", + "indent": 0, + "keep_closed": 0, + "label": "Process Payment Reconciliation", + "link_to": "Process Payment Reconciliation", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -56,7 +92,7 @@ { "child": 1, "collapsible": 1, - "icon": "organization", + "icon": "", "indent": 0, "keep_closed": 0, "label": "Bank", @@ -68,7 +104,7 @@ { "child": 1, "collapsible": 1, - "icon": "accounting", + "icon": "", "indent": 0, "keep_closed": 0, "label": "Bank Account", @@ -80,7 +116,40 @@ { "child": 1, "collapsible": 1, - "icon": "settings", + "indent": 0, + "keep_closed": 0, + "label": "Bank Account Type", + "link_to": "Bank Account Type", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Bank Account Subtype", + "link_to": "Bank Account Subtype", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Bank Guarantee", + "link_to": "Bank Guarantee", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", "indent": 0, "keep_closed": 0, "label": "Plaid Settings", @@ -123,11 +192,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.808066", + "modified": "2026-01-02 13:53:50.930215", "modified_by": "Administrator", "module": "Accounts", "name": "Banking", "owner": "Administrator", - "standard": 1, "title": "Banking" } diff --git a/erpnext/workspace_sidebar/budget.json b/erpnext/workspace_sidebar/budget.json index 3c10f2f895f..94ebfc9c994 100644 --- a/erpnext/workspace_sidebar/budget.json +++ b/erpnext/workspace_sidebar/budget.json @@ -21,7 +21,7 @@ { "child": 0, "collapsible": 1, - "icon": "circle-dollar-sign", + "icon": "badge-cent", "indent": 0, "keep_closed": 0, "label": "Cost Center", @@ -57,18 +57,7 @@ { "child": 0, "collapsible": 1, - "icon": "notepad-text", - "indent": 1, - "keep_closed": 1, - "label": "Reports", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "file-text", + "icon": "sheet", "indent": 0, "keep_closed": 0, "label": "Budget Variance", @@ -78,11 +67,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.821823", + "modified": "2026-01-02 11:46:10.598472", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", "owner": "Administrator", - "standard": 1, "title": "Budget" } diff --git a/erpnext/workspace_sidebar/buying.json b/erpnext/workspace_sidebar/buying.json index a595aa964ce..ecf87ecc68f 100644 --- a/erpnext/workspace_sidebar/buying.json +++ b/erpnext/workspace_sidebar/buying.json @@ -30,30 +30,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "customer", - "indent": 0, - "keep_closed": 0, - "label": "Supplier", - "link_to": "Supplier", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "notebook-tabs", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -66,6 +42,18 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "git-pull-request-arrow", + "indent": 0, + "keep_closed": 0, + "label": "Request for Quotation", + "link_to": "Request for Quotation", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -105,7 +93,130 @@ { "child": 0, "collapsible": 1, - "icon": "notepad-text", + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Supplier", + "link_to": "Supplier", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Group", + "link_to": "Supplier Group", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Price List", + "link_to": "Price List", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Address", + "link_to": "Address", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Contacts", + "link_to": "Contact", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Scorecard", + "link_to": "Supplier Scorecard", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Scorecard Criteria", + "link_to": "Supplier Scorecard Criteria", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Scorecard Variable", + "link_to": "Supplier Scorecard Variable", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Scorecard Standing", + "link_to": "Supplier Scorecard Standing", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "sheet", "indent": 1, "keep_closed": 1, "label": "Reports", @@ -190,25 +301,14 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "database", - "indent": 1, - "keep_closed": 1, - "label": "Setup", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, { "child": 1, "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Supplier Group", - "link_to": "Supplier Group", - "link_type": "DocType", + "label": "Purchase Order Trends", + "link_to": "Purchase Order Trends", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -217,9 +317,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Address", - "link_to": "Address", - "link_type": "DocType", + "label": "Procurement Tracker", + "link_to": "Procurement Tracker", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -228,9 +328,31 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Contacts", - "link_to": "Contact", - "link_type": "DocType", + "label": "Supplier-Wise Sales Analytics", + "link_to": "Supplier-Wise Sales Analytics", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Quotation Comparison", + "link_to": "Supplier Quotation Comparison", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Supplier Addresses And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", "show_arrow": 0, "type": "Link" }, @@ -247,11 +369,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.694985", + "modified": "2026-01-02 12:10:07.304778", "modified_by": "Administrator", "module": "Buying", "name": "Buying", "owner": "Administrator", - "standard": 1, "title": "Buying" } diff --git a/erpnext/workspace_sidebar/crm.json b/erpnext/workspace_sidebar/crm.json index df59f6a641f..264318c1c8d 100644 --- a/erpnext/workspace_sidebar/crm.json +++ b/erpnext/workspace_sidebar/crm.json @@ -6,25 +6,13 @@ "header_icon": "crm", "idx": 0, "items": [ - { - "child": 0, - "collapsible": 1, - "icon": "home", - "indent": 0, - "keep_closed": 0, - "label": "Home", - "link_to": "CRM", - "link_type": "Workspace", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, "icon": "chart", "indent": 0, "keep_closed": 0, - "label": "Dashboard", + "label": "Home", "link_to": "CRM", "link_type": "Dashboard", "show_arrow": 0, @@ -69,7 +57,7 @@ { "child": 0, "collapsible": 1, - "icon": "notepad-text", + "icon": "sheet", "indent": 1, "keep_closed": 1, "label": "Reports", @@ -497,11 +485,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:12.112990", + "modified": "2026-01-02 15:06:29.836236", "modified_by": "Administrator", "module": "CRM", "name": "CRM", "owner": "Administrator", - "standard": 1, "title": "CRM" } diff --git a/erpnext/workspace_sidebar/financial_reports.json b/erpnext/workspace_sidebar/financial_reports.json index faa17089d83..3af8b051f8c 100644 --- a/erpnext/workspace_sidebar/financial_reports.json +++ b/erpnext/workspace_sidebar/financial_reports.json @@ -72,6 +72,29 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Custom Financial Statement", + "link_to": "Custom Financial Statement", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Financial Report Template", + "link_to": "Financial Report Template", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -116,6 +139,104 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "indent": 1, + "keep_closed": 1, + "label": "Registers", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Accounts Receivable", + "link_to": "Accounts Receivable", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Accounts Payable", + "link_to": "Accounts Payable", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "AR Summary", + "link_to": "Accounts Receivable Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "AP Summary", + "link_to": "Accounts Payable Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Register", + "link_to": "Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Purchase Register", + "link_to": "Purchase Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise sales Register", + "link_to": "Item-wise Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -260,11 +381,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:12.047975", + "modified": "2026-01-08 17:50:55.681403", "modified_by": "Administrator", "module": "Accounts", "name": "Financial Reports", "owner": "Administrator", - "standard": 1, "title": "Financial Reports" } diff --git a/erpnext/workspace_sidebar/home.json b/erpnext/workspace_sidebar/home.json index 83d0cd4226c..61b3e600421 100644 --- a/erpnext/workspace_sidebar/home.json +++ b/erpnext/workspace_sidebar/home.json @@ -62,11 +62,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.686293", + "modified": "2025-11-25 10:46:09.198568", "modified_by": "Administrator", "module": "Setup", "name": "Home", "owner": "Administrator", - "standard": 1, "title": "Home" } diff --git a/erpnext/workspace_sidebar/manufacturing.json b/erpnext/workspace_sidebar/manufacturing.json index 72bfad77483..08b6a054660 100644 --- a/erpnext/workspace_sidebar/manufacturing.json +++ b/erpnext/workspace_sidebar/manufacturing.json @@ -30,30 +30,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "notebook-tabs", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "warehouse", - "indent": 0, - "keep_closed": 0, - "label": "Warehouse", - "link_to": "Warehouse", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -356,6 +332,18 @@ "show_arrow": 0, "type": "Section Break" }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -437,11 +425,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.863897", + "modified": "2026-01-02 15:08:30.661411", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", "owner": "Administrator", - "standard": 1, "title": "Manufacturing" } diff --git a/erpnext/workspace_sidebar/projects.json b/erpnext/workspace_sidebar/projects.json index 984bff3e794..58fec69ff3d 100644 --- a/erpnext/workspace_sidebar/projects.json +++ b/erpnext/workspace_sidebar/projects.json @@ -18,6 +18,18 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "chart", + "indent": 0, + "keep_closed": 0, + "label": "Dashboard", + "link_to": "Project", + "link_type": "Dashboard", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -57,8 +69,75 @@ { "child": 0, "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, "indent": 0, "keep_closed": 0, + "label": "Activity Type", + "link_to": "Activity Type", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Activity Cost", + "link_to": "Activity Cost", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Project Template", + "link_to": "Project Template", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Project Type", + "link_to": "Project Type", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Project Update", + "link_to": "Project Update", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "sheet", + "indent": 1, + "keep_closed": 1, "label": "Reports", "link_type": "DocType", "show_arrow": 0, @@ -67,7 +146,7 @@ { "child": 1, "collapsible": 1, - "icon": "table", + "icon": "", "indent": 0, "keep_closed": 0, "label": "Project Summary", @@ -75,13 +154,79 @@ "link_type": "Report", "show_arrow": 0, "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Daily Timesheet Summary", + "link_to": "Daily Timesheet Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Timesheet Billing Summary", + "link_to": "Timesheet Billing Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Project wise Stock Tracking", + "link_to": "Project wise Stock Tracking", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Project Profitability", + "link_to": "Project Profitability", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delayed Tasks Summary", + "link_to": "Delayed Tasks Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "settings", + "indent": 0, + "keep_closed": 0, + "label": "Settings", + "link_to": "Projects Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" } ], - "modified": "2026-01-08 18:49:12.014031", + "modified": "2026-01-02 11:57:04.237376", "modified_by": "Administrator", "module": "Projects", "name": "Projects", "owner": "Administrator", - "standard": 1, "title": "Projects" } diff --git a/erpnext/workspace_sidebar/quality.json b/erpnext/workspace_sidebar/quality.json index de2625cd4ca..5dc41335404 100644 --- a/erpnext/workspace_sidebar/quality.json +++ b/erpnext/workspace_sidebar/quality.json @@ -18,30 +18,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "quality", - "indent": 0, - "keep_closed": 0, - "label": "Quality Goal", - "link_to": "Quality Goal", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "book-text", - "indent": 0, - "keep_closed": 0, - "label": "Quality Procedure", - "link_to": "Quality Procedure", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -54,6 +30,18 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "goal", + "indent": 0, + "keep_closed": 0, + "label": "Quality Goal", + "link_to": "Quality Goal", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -93,25 +81,38 @@ { "child": 0, "collapsible": 1, - "icon": "calendar-check-2", - "indent": 1, - "keep_closed": 1, - "label": "Goal and Procedure", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, + "icon": "thumbs-up", "indent": 0, "keep_closed": 0, - "label": "Quality Goal", - "link_to": "Quality Goal", + "label": "Quality Feedback", + "link_to": "Quality Feedback", "link_type": "DocType", "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "users", + "indent": 0, + "keep_closed": 0, + "label": "Quality Meeting", + "link_to": "Quality Meeting", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, { "child": 1, "collapsible": 1, @@ -134,28 +135,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "thumbs-up", - "indent": 1, - "keep_closed": 1, - "label": "Feedback", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quality Feedback", - "link_to": "Quality Feedback", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -167,78 +146,22 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "calendar-check", - "indent": 1, - "keep_closed": 1, - "label": "Meeting", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, { "child": 1, "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Quality Meeting", - "link_to": "Quality Meeting", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "review", - "indent": 1, - "keep_closed": 1, - "label": "Review and Action", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Non Conformance", - "link_to": "Non Conformance", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quality Review", - "link_to": "Quality Review", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Quality Action", - "link_to": "Quality Action", + "label": "Quality Inspection Template", + "link_to": "Quality Inspection Template", "link_type": "DocType", "show_arrow": 0, "type": "Link" } ], - "modified": "2026-01-08 18:49:12.000467", + "modified": "2026-01-02 17:39:50.641254", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", "owner": "Administrator", - "standard": 1, "title": "Quality" } diff --git a/erpnext/workspace_sidebar/selling.json b/erpnext/workspace_sidebar/selling.json index 679eb7b2803..766b8f87fa9 100644 --- a/erpnext/workspace_sidebar/selling.json +++ b/erpnext/workspace_sidebar/selling.json @@ -30,30 +30,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "customer", - "indent": 0, - "keep_closed": 0, - "label": "Customer", - "link_to": "Customer", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "notebook-tabs", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -94,6 +70,17 @@ "child": 0, "collapsible": 1, "icon": "computer", + "indent": 1, + "keep_closed": 1, + "label": "POS", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", "indent": 0, "keep_closed": 0, "label": "POS", @@ -102,13 +89,101 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Invoice", + "link_to": "POS Invoice", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Opening Entry", + "link_to": "POS Opening Entry", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Closing Entry", + "link_to": "POS Closing Entry", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Profile", + "link_to": "POS Profile", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Invoice Merge Log", + "link_to": "POS Invoice Merge Log", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "POS Settings", + "link_to": "POS Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Loyalty Program", + "link_to": "Loyalty Program", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Loyalty Point Entry", + "link_to": "Loyalty Point Entry", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, - "icon": "package", + "icon": "stock", "indent": 1, "keep_closed": 1, - "label": "Item and Pricing", + "label": "Items & Pricing", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -202,149 +277,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "notepad-text", - "indent": 1, - "keep_closed": 1, - "label": "Reports", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Analytics", - "link_to": "Sales Analytics", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Pending SO Items For Purchase Request", - "link_to": "Pending SO Items For Purchase Request", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Available Stock for Packing Items", - "link_to": "Available Stock for Packing Items", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customer Addresses And Contacts", - "link_to": "Address And Contacts", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Note Trends", - "link_to": "Delivery Note Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Invoice Trends", - "link_to": "Sales Invoice Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customer Credit Balance", - "link_to": "Customer Credit Balance", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Customers Without Any Sales Transactions", - "link_to": "Customers Without Any Sales Transactions", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Partners Commission", - "link_to": "Sales Partners Commission", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Territory Target Variance Based On Item Group", - "link_to": "Territory Target Variance Based On Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Person Target Variance Based On Item Group", - "link_to": "Sales Person Target Variance Based On Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Partner Target Variance Based On Item Group", - "link_to": "Sales Partner Target Variance based on Item Group", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -356,6 +288,18 @@ "show_arrow": 0, "type": "Section Break" }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Customer", + "link_to": "Customer", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -411,17 +355,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Sales Partner", - "link_to": "Sales Partner", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -438,8 +371,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Product Bundle", - "link_to": "Product Bundle", + "label": "Sales Partner", + "link_to": "Sales Partner", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -449,19 +382,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Shipping Rule", - "link_to": "Shipping Rule", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "UTM Source", - "link_to": "UTM Source", + "label": "Monthly Distribution", + "link_to": "Monthly Distribution", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -488,6 +410,270 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Product Bundle", + "link_to": "Product Bundle", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "UTM Source", + "link_to": "UTM Source", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Shipping Rule", + "link_to": "Shipping Rule", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "sheet", + "indent": 1, + "keep_closed": 1, + "label": "Reports", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Register", + "link_to": "Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise Sales Register", + "link_to": "Item-wise Sales Register", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Analytics", + "link_to": "Sales Analytics", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customer Addresses And Contacts", + "link_to": "Address And Contacts", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Invoice Trends", + "link_to": "Sales Invoice Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customer Credit Balance", + "link_to": "Customer Credit Balance", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customers Without Any Sales Transactions", + "link_to": "Customers Without Any Sales Transactions", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Partners Commission", + "link_to": "Sales Partners Commission", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Available Stock for Packing Items", + "link_to": "Available Stock for Packing Items", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Territory Target Variance Based On Item Group", + "link_to": "Territory Target Variance Based On Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Person Target Variance Based On Item Group", + "link_to": "Sales Person Target Variance Based On Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Partner Target Variance Based On Item Group", + "link_to": "Sales Partner Target Variance based on Item Group", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Pending SO Items For Purchase Request", + "link_to": "Pending SO Items For Purchase Request", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Funnel", + "link_to": "sales-funnel", + "link_type": "Page", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Customer Acquisition and Loyalty", + "link_to": "Customer Acquisition and Loyalty", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quotation Trends", + "link_to": "Quotation Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Order Trends", + "link_to": "Sales Order Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item-wise Sales History", + "link_to": "Item-wise Sales History", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Sales Person-wise Transaction Summary", + "link_to": "Sales Person-wise Transaction Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -501,11 +687,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.973725", + "modified": "2026-01-02 17:44:08.721891", "modified_by": "Administrator", "module": "Selling", "name": "Selling", "owner": "Administrator", - "standard": 1, "title": "Selling" } diff --git a/erpnext/workspace_sidebar/settings.json b/erpnext/workspace_sidebar/settings.json index 5f8737a1374..6414b0f21f8 100644 --- a/erpnext/workspace_sidebar/settings.json +++ b/erpnext/workspace_sidebar/settings.json @@ -9,23 +9,47 @@ { "child": 0, "collapsible": 1, - "icon": "home", + "icon": "earth", "indent": 0, "keep_closed": 0, - "label": "Home", - "link_to": "Settings", - "link_type": "Workspace", + "label": "Global Defaults", + "link_to": "Global Defaults", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, { "child": 0, "collapsible": 1, - "icon": "crm", + "icon": "washing-machine", "indent": 0, "keep_closed": 0, - "label": "CRM Settings", - "link_to": "CRM Settings", + "label": "System Settings", + "link_to": "System Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "accounting", + "indent": 0, + "keep_closed": 0, + "label": "Accounts Settings", + "link_to": "Accounts Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "computer", + "indent": 0, + "keep_closed": 0, + "label": "POS Settings", + "link_to": "POS Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -54,18 +78,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "accounting", - "indent": 0, - "keep_closed": 0, - "label": "Accounts Settings", - "link_to": "Accounts Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -90,42 +102,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "printer", - "indent": 0, - "keep_closed": 0, - "label": "Print Settings", - "link_to": "Print Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "computer", - "indent": 0, - "keep_closed": 0, - "label": "System Settings", - "link_to": "System Settings", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "earth", - "indent": 0, - "keep_closed": 0, - "label": "Global Defaults", - "link_to": "Global Defaults", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -137,13 +113,124 @@ "link_type": "DocType", "show_arrow": 0, "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "crm", + "indent": 0, + "keep_closed": 0, + "label": "CRM Settings", + "link_to": "CRM Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "support", + "indent": 0, + "keep_closed": 0, + "label": "Support Settings", + "link_to": "Support Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "getting-started", + "indent": 1, + "keep_closed": 1, + "label": "Other Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Subscription Settings", + "link_to": "Subscription Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Variant Settings", + "link_to": "Item Variant Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Settings", + "link_to": "Delivery Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Currency Exchange Settings", + "link_to": "Currency Exchange Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Appointment Booking Settings", + "link_to": "Appointment Booking Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Reposting Settings", + "link_to": "Stock Reposting Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Repost Accounting Ledger Settings", + "link_to": "Repost Accounting Ledger Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" } ], - "modified": "2026-01-08 18:49:11.674859", + "modified": "2026-01-02 18:04:13.860117", "modified_by": "Administrator", "module": "Setup", "name": "Settings", "owner": "Administrator", - "standard": 1, "title": "Settings" } diff --git a/erpnext/workspace_sidebar/share_management.json b/erpnext/workspace_sidebar/share_management.json index 6700343cfef..1b49348e477 100644 --- a/erpnext/workspace_sidebar/share_management.json +++ b/erpnext/workspace_sidebar/share_management.json @@ -9,6 +9,7 @@ { "child": 1, "collapsible": 1, + "icon": "customer", "indent": 0, "keep_closed": 0, "label": "Shareholder", @@ -20,6 +21,7 @@ { "child": 1, "collapsible": 1, + "icon": "move-horizontal", "indent": 0, "keep_closed": 0, "label": "Share Transfer", @@ -31,6 +33,7 @@ { "child": 1, "collapsible": 1, + "icon": "list", "indent": 0, "keep_closed": 0, "label": "Share Ledger", @@ -42,6 +45,7 @@ { "child": 1, "collapsible": 1, + "icon": "notepad-text", "indent": 0, "keep_closed": 0, "label": "Share Balance", @@ -51,11 +55,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.834013", + "modified": "2026-01-02 14:53:29.842384", "modified_by": "Administrator", "module": "Accounts", "name": "Share Management", "owner": "Administrator", - "standard": 1, "title": "Share Management" } diff --git a/erpnext/workspace_sidebar/stock.json b/erpnext/workspace_sidebar/stock.json index 9102b02b0d6..87c53dad060 100644 --- a/erpnext/workspace_sidebar/stock.json +++ b/erpnext/workspace_sidebar/stock.json @@ -30,30 +30,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "notebook-tabs", - "indent": 0, - "keep_closed": 0, - "label": "Item", - "link_to": "Item", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "warehouse", - "indent": 0, - "keep_closed": 0, - "label": "Warehouse", - "link_to": "Warehouse", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -93,19 +69,7 @@ { "child": 0, "collapsible": 1, - "icon": "recycle", - "indent": 0, - "keep_closed": 0, - "label": "Stock Reconciliation", - "link_to": "Stock Reconciliation", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 0, - "collapsible": 1, - "icon": "arrow-right", + "icon": "arrow-left-to-line", "indent": 0, "keep_closed": 0, "label": "Material Request", @@ -117,10 +81,22 @@ { "child": 0, "collapsible": 1, - "icon": "notepad-text", + "icon": "caravan", + "indent": 0, + "keep_closed": 0, + "label": "Pick List", + "link_to": "Pick List", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "tool", "indent": 1, "keep_closed": 1, - "label": "Reports", + "label": "Tools", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -128,11 +104,24 @@ { "child": 1, "collapsible": 1, + "icon": "", "indent": 0, "keep_closed": 0, - "label": "Stock Ledger", - "link_to": "Stock Ledger", - "link_type": "Report", + "label": "Stock Reconciliation", + "link_to": "Stock Reconciliation", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Landed Cost Voucher", + "link_to": "Landed Cost Voucher", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -141,9 +130,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Stock Balance", - "link_to": "Stock Balance", - "link_type": "Report", + "label": "Repost Item Valuation", + "link_to": "Repost Item Valuation", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -152,9 +141,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Stock Analytics", - "link_to": "Stock Analytics", - "link_type": "Report", + "label": "Packing Slip", + "link_to": "Packing Slip", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -163,31 +152,9 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Serial No and Batch Traceability", - "link_to": "Serial No and Batch Traceability", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Purchase Receipt Trends", - "link_to": "Purchase Receipt Trends", - "link_type": "Report", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Delivery Note Trends", - "link_to": "Delivery Note Trends", - "link_type": "Report", + "label": "Quality Inspection", + "link_to": "Quality Inspection", + "link_type": "DocType", "show_arrow": 0, "type": "Link" }, @@ -203,6 +170,85 @@ "show_arrow": 0, "type": "Section Break" }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Item", + "link_to": "Item", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Group", + "link_to": "Item Group", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Attribute", + "link_to": "Item Attribute", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Brand", + "link_to": "Brand", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", + "indent": 0, + "keep_closed": 0, + "label": "Warehouse", + "link_to": "Warehouse", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Unit of Measure (UOM)", + "link_to": "UOM", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "UOM Conversion Factor", + "link_to": "UOM Conversion Factor", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 1, "collapsible": 1, @@ -247,24 +293,342 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Shipping Rule", + "link_to": "Shipping Rule", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Alternative", + "link_to": "Item Alternative", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quality Inspection Template", + "link_to": "Quality Inspection Template", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Trip", + "link_to": "Delivery Trip", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "sheet", + "indent": 1, + "keep_closed": 1, + "label": "Reports", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Ledger", + "link_to": "Stock Ledger", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Balance", + "link_to": "Stock Balance", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Quick Stock Balance", + "link_to": "Quick Stock Balance", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Projected Qty", + "link_to": "Stock Projected Qty", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Analytics", + "link_to": "Stock Analytics", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Ageing", + "link_to": "Stock Ageing", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Purchase Receipt Trends", + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Note Trends", + "link_to": "Delivery Note Trends", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Price Stock", + "link_to": "Item Price Stock", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Warehouse Wise Stock Balance", + "link_to": "Warehouse Wise Stock Balance", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Shortage Report", + "link_to": "Item Shortage Report", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Serial No and Batch Traceability", + "link_to": "Serial No and Batch Traceability", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Serial No Status", + "link_to": "Serial No Status", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Serial No Ledger", + "link_to": "Serial No Ledger", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Serial No Warranty Expiry", + "link_to": "Serial No Warranty Expiry", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Batch-Wise Balance History", + "link_to": "Batch-Wise Balance History", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Batch Item Expiry Status", + "link_to": "Batch Item Expiry Status", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Requested Items To Be Transferred", + "link_to": "Requested Items To Be Transferred", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Itemwise Recommended Reorder Level", + "link_to": "Itemwise Recommended Reorder Level", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Variant Details", + "link_to": "Item Variant Details", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, "icon": "settings", + "indent": 1, + "keep_closed": 1, + "label": "Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "icon": "", "indent": 0, "keep_closed": 0, - "label": "Settings", + "label": "Stock Settings", "link_to": "Stock Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Item Variant Settings", + "link_to": "Item Variant Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Stock Reposting Settings", + "link_to": "Stock Reposting Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Delivery Settings", + "link_to": "Delivery Settings", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" } ], - "modified": "2026-01-08 18:49:11.656807", + "modified": "2026-01-02 18:10:31.153427", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "owner": "Administrator", - "standard": 1, "title": "Stock" } diff --git a/erpnext/workspace_sidebar/subcontracting.json b/erpnext/workspace_sidebar/subcontracting.json index ca0e87de16e..f555875c292 100644 --- a/erpnext/workspace_sidebar/subcontracting.json +++ b/erpnext/workspace_sidebar/subcontracting.json @@ -18,10 +18,34 @@ "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "icon": "folder-tree", + "indent": 0, + "keep_closed": 0, + "label": "Subcontracting BOM", + "link_to": "Subcontracting BOM", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "icon": "move-horizontal", + "indent": 0, + "keep_closed": 0, + "label": "Stock Entry", + "link_to": "Stock Entry", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, - "icon": "arrow-right", + "icon": "arrow-left-to-line", "indent": 1, "keep_closed": 0, "label": "Inward Order", @@ -68,7 +92,7 @@ { "child": 0, "collapsible": 1, - "icon": "arrow-left", + "icon": "arrow-right-from-line", "indent": 1, "keep_closed": 0, "label": "Outward Order", @@ -112,41 +136,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "tool", - "indent": 1, - "keep_closed": 1, - "label": "Tools", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Stock Entry", - "link_to": "Stock Entry", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "icon": "", - "indent": 0, - "keep_closed": 0, - "label": "Subcontracting BOM", - "link_to": "Subcontracting BOM", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -207,11 +196,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.605180", + "modified": "2026-01-02 17:51:15.843931", "modified_by": "Administrator", "module": "Buying", "name": "Subcontracting", "owner": "Administrator", - "standard": 1, "title": "Subcontracting" } diff --git a/erpnext/workspace_sidebar/subscription.json b/erpnext/workspace_sidebar/subscription.json index 5976d23fc98..ed91cc0c04c 100644 --- a/erpnext/workspace_sidebar/subscription.json +++ b/erpnext/workspace_sidebar/subscription.json @@ -87,11 +87,10 @@ "type": "Link" } ], - "modified": "2026-01-08 18:49:11.840656", + "modified": "2025-12-22 17:06:54.262451", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", "owner": "Administrator", - "standard": 1, "title": "Subscription" } diff --git a/erpnext/workspace_sidebar/support.json b/erpnext/workspace_sidebar/support.json index 227df774387..b62a52554f3 100644 --- a/erpnext/workspace_sidebar/support.json +++ b/erpnext/workspace_sidebar/support.json @@ -24,12 +24,24 @@ "icon": "file-question-mark", "indent": 0, "keep_closed": 0, - "label": "Issues", + "label": "Issue", "link_to": "Issue", "link_type": "DocType", "show_arrow": 0, "type": "Link" }, + { + "child": 0, + "collapsible": 1, + "icon": "calendar-days", + "indent": 0, + "keep_closed": 0, + "label": "Maintenance Schedule", + "link_to": "Maintenance Schedule", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -45,18 +57,6 @@ { "child": 0, "collapsible": 1, - "icon": "receipt-text", - "indent": 0, - "keep_closed": 0, - "label": "SLA", - "link_to": "Service Level Agreement", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, "icon": "grid-2x2-check", "indent": 0, "keep_closed": 0, @@ -69,10 +69,10 @@ { "child": 0, "collapsible": 1, - "icon": "tool", + "icon": "database", "indent": 1, "keep_closed": 1, - "label": "Maintenance", + "label": "Setup", "link_type": "DocType", "show_arrow": 0, "type": "Section Break" @@ -82,8 +82,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Maintenance Schedule", - "link_to": "Maintenance Schedule", + "label": "Issue Type", + "link_to": "Issue Type", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -93,8 +93,8 @@ "collapsible": 1, "indent": 0, "keep_closed": 0, - "label": "Maintenance Visit", - "link_to": "Maintenance Visit", + "label": "Issue Priority", + "link_to": "Issue Priority", "link_type": "DocType", "show_arrow": 0, "type": "Link" @@ -132,57 +132,23 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 0, - "collapsible": 1, - "icon": "database", - "indent": 1, - "keep_closed": 1, - "label": "Setup", - "link_type": "DocType", - "show_arrow": 0, - "type": "Section Break" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Issue Type", - "link_to": "Issue Type", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Issue Priority", - "link_to": "Issue Priority", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, "icon": "settings", "indent": 0, "keep_closed": 0, - "label": "Support Settings", + "label": "Settings", "link_to": "Support Settings", "link_type": "DocType", "show_arrow": 0, "type": "Link" } ], - "modified": "2026-01-08 18:49:11.958639", + "modified": "2026-01-02 17:47:19.864824", "modified_by": "Administrator", "module": "Support", "name": "Support", "owner": "Administrator", - "standard": 1, "title": "Support" } diff --git a/erpnext/workspace_sidebar/taxes.json b/erpnext/workspace_sidebar/taxes.json index a9cd5166787..9d9033f29ac 100644 --- a/erpnext/workspace_sidebar/taxes.json +++ b/erpnext/workspace_sidebar/taxes.json @@ -13,8 +13,9 @@ "indent": 0, "keep_closed": 0, "label": "Sales Tax Template", - "link_to": "Sales Taxes and Charges Template", + "link_to": "Item Tax Template", "link_type": "DocType", + "navigate_to_tab": "", "show_arrow": 0, "type": "Link" }, @@ -45,6 +46,17 @@ { "child": 0, "collapsible": 1, + "icon": "database", + "indent": 1, + "keep_closed": 1, + "label": "Setup", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, "icon": "triangle", "indent": 0, "keep_closed": 0, @@ -55,7 +67,7 @@ "type": "Link" }, { - "child": 0, + "child": 1, "collapsible": 1, "icon": "book-open-text", "indent": 0, @@ -67,17 +79,28 @@ "type": "Link" }, { - "child": 0, + "child": 1, "collapsible": 1, "icon": "book-text", "indent": 0, "keep_closed": 0, - "label": "Tax Withholding", + "label": "Tax Withholding Category", "link_to": "Tax Withholding Category", "link_type": "DocType", "show_arrow": 0, "type": "Link" }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Tax Withholding Group", + "link_to": "Tax Withholding Group", + "link_type": "DocType", + "show_arrow": 0, + "type": "Link" + }, { "child": 0, "collapsible": 1, @@ -89,13 +112,46 @@ "link_type": "DocType", "show_arrow": 0, "type": "Link" + }, + { + "child": 0, + "collapsible": 1, + "icon": "sheet", + "indent": 1, + "keep_closed": 1, + "label": "Reports", + "link_to": "", + "link_type": "DocType", + "show_arrow": 0, + "type": "Section Break" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "TDS Computation Summary", + "link_to": "TDS Computation Summary", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" + }, + { + "child": 1, + "collapsible": 1, + "indent": 0, + "keep_closed": 0, + "label": "Tax Withholding Details", + "link_to": "Tax Withholding Details", + "link_type": "Report", + "show_arrow": 0, + "type": "Link" } ], - "modified": "2026-01-08 18:49:11.798691", + "modified": "2026-01-02 14:40:04.075046", "modified_by": "Administrator", "module": "Accounts", "name": "Taxes", "owner": "Administrator", - "standard": 1, "title": "Taxes" } From 8e143d68b4bc769836305256902511c7b89a3fd0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 9 Jan 2026 17:29:09 +0530 Subject: [PATCH 042/106] fix: incoming rate calculation --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 4f227115f9d..e52ce3567cb 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -723,7 +723,7 @@ class SerialNoValuation(DeprecatedSerialNoValuation): ) .select( bundle_child.serial_no, - (bundle_child.incoming_rate * bundle_child.qty).as_("incoming_rate"), + bundle_child.incoming_rate, ) ) From cda8a97f4a0344f7e8de04dc52305709fffc14fc Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Fri, 9 Jan 2026 17:43:55 +0530 Subject: [PATCH 043/106] fix: add validation for duplication --- .../accounts/doctype/sales_invoice/sales_invoice.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 000d1f602c5..2e292fc79d7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -885,6 +885,16 @@ class SalesInvoice(SellingController): if status not in ["Submitted", "Payslip", "Partially Billed"]: frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + if data.time_sheet and data.timesheet_detail: + if sales_invoice := frappe.db.get_value( + "Timesheet Detail", data.timesheet_detail, "sales_invoice" + ): + frappe.throw( + _("Row {0}: Sales Invoice {1} is already created for {2}").format( + data.idx, frappe.bold(sales_invoice), frappe.bold(data.time_sheet) + ) + ) + def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" if cint(self.is_pos) != 1: From 43d1d685c63ec8129fe02133cc2f19821f4cd093 Mon Sep 17 00:00:00 2001 From: Logesh Periyasamy Date: Fri, 9 Jan 2026 18:11:42 +0530 Subject: [PATCH 044/106] fix: add validation for amount and hours Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2e292fc79d7..a61717040a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -348,8 +348,10 @@ class SalesInvoice(SellingController): if self.is_return and self.return_against: for row in self.timesheets: - row.billing_hours *= -1 - row.billing_amount *= -1 + if row.billing_hours > 0: + row.billing_hours *= -1 + if row.billing_amount > 0: + row.billing_amount *= -1 self.update_packing_list() self.set_billing_hours_and_amount() From e4741072a6718bbe866bef5e80df27e65f4ee8b3 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Fri, 9 Jan 2026 18:15:14 +0530 Subject: [PATCH 045/106] fix: moved pos related accounts settings configuration to pos settings --- .../accounts_settings/accounts_settings.json | 27 +------------------ .../accounts_settings/accounts_settings.py | 1 - .../doctype/pos_settings/pos_settings.json | 16 ++++++++++- .../doctype/pos_settings/pos_settings.py | 1 + .../doctype/sales_invoice/sales_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 4 +-- 6 files changed, 20 insertions(+), 31 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 55db06f8ca7..e68b1e58007 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -64,10 +64,6 @@ "role_allowed_to_over_bill", "credit_controller", "make_payment_via_journal_entry", - "pos_tab", - "pos_setting_section", - "post_change_gl_entries", - "column_break_xrnd", "assets_tab", "asset_settings_section", "calculate_depr_using_total_days", @@ -282,13 +278,6 @@ "fieldname": "column_break_19", "fieldtype": "Column Break" }, - { - "default": "1", - "description": "If enabled, ledger entries will be posted for change amount in POS transactions", - "fieldname": "post_change_gl_entries", - "fieldtype": "Check", - "label": "Create Ledger Entries for Change Amount" - }, { "default": "0", "description": "Learn about Common Party", @@ -329,11 +318,6 @@ "fieldtype": "Tab Break", "label": "Accounts Closing" }, - { - "fieldname": "pos_setting_section", - "fieldtype": "Section Break", - "label": "POS Setting" - }, { "fieldname": "invoice_and_billing_tab", "fieldtype": "Tab Break", @@ -348,11 +332,6 @@ "fieldname": "column_break_17", "fieldtype": "Column Break" }, - { - "fieldname": "pos_tab", - "fieldtype": "Tab Break", - "label": "POS" - }, { "default": "0", "description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency", @@ -536,10 +515,6 @@ "label": "Posting Date Inheritance for Exchange Gain / Loss", "options": "Invoice\nPayment\nReconciliation Date" }, - { - "fieldname": "column_break_xrnd", - "fieldtype": "Column Break" - }, { "default": "Buffered Cursor", "fieldname": "receivable_payable_fetch_method", @@ -665,7 +640,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-01-02 18:17:18.994348", + "modified": "2026-01-09 17:38:04.250917", "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 73d51000a5b..dbe86f6d7b2 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -57,7 +57,6 @@ class AccountsSettings(Document): make_payment_via_journal_entry: DF.Check merge_similar_account_heads: DF.Check over_billing_allowance: DF.Currency - post_change_gl_entries: DF.Check receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor", "Raw SQL"] receivable_payable_remarks_length: DF.Int reconciliation_queue_size: DF.Int diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.json b/erpnext/accounts/doctype/pos_settings/pos_settings.json index ac0b884b3d7..7afc19423d8 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.json +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.json @@ -6,6 +6,8 @@ "engine": "InnoDB", "field_order": [ "invoice_type", + "column_break_vwwt", + "post_change_gl_entries", "section_break_gyos", "invoice_fields", "pos_search_fields" @@ -34,12 +36,24 @@ { "fieldname": "section_break_gyos", "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_vwwt", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If enabled, ledger entries will be posted for change amount in POS transactions", + "fieldname": "post_change_gl_entries", + "fieldtype": "Check", + "label": "Create Ledger Entries for Change Amount", + "options": "1" } ], "hide_toolbar": 1, "issingle": 1, "links": [], - "modified": "2026-01-02 18:18:17.586225", + "modified": "2026-01-09 17:30:41.476806", "modified_by": "Administrator", "module": "Accounts", "name": "POS Settings", diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.py b/erpnext/accounts/doctype/pos_settings/pos_settings.py index 4865262b83a..4f57326a528 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.py +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py @@ -23,6 +23,7 @@ class POSSettings(Document): invoice_fields: DF.Table[POSField] invoice_type: DF.Literal["Sales Invoice", "POS Invoice"] pos_search_fields: DF.Table[POSSearchFields] + post_change_gl_entries: DF.Check # end: auto-generated types def validate(self): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 18a86434ef9..cad1a597386 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1726,7 +1726,7 @@ class SalesInvoice(SellingController): def make_pos_gl_entries(self, gl_entries): if cint(self.is_pos): skip_change_gl_entries = not cint( - frappe.get_single_value("Accounts Settings", "post_change_gl_entries") + frappe.get_single_value("POS Settings", "post_change_gl_entries") ) for payment_mode in self.payments: diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 67dae79d083..13eb0f6ae18 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1173,7 +1173,7 @@ class TestSalesInvoice(ERPNextTestSuite): self.assertEqual(expected, res) def test_pos_with_no_gl_entry_for_change_amount(self): - frappe.db.set_single_value("Accounts Settings", "post_change_gl_entries", 0) + frappe.db.set_single_value("POS Settings", "post_change_gl_entries", 0) make_pos_profile( company="_Test Company with perpetual inventory", @@ -1221,7 +1221,7 @@ class TestSalesInvoice(ERPNextTestSuite): self.validate_pos_gl_entry(pos, pos, 60, validate_without_change_gle=True) - frappe.db.set_single_value("Accounts Settings", "post_change_gl_entries", 1) + frappe.db.set_single_value("POS Settings", "post_change_gl_entries", 1) def validate_pos_gl_entry(self, si, pos, cash_amount, validate_without_change_gle=False): if validate_without_change_gle: From bf199cc2e0b90e8725fec390ba7aea1c654a3ba6 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Fri, 9 Jan 2026 13:16:29 +0530 Subject: [PATCH 046/106] fix: renaming 'Settings' desktop icon and workspace to 'ERPNext Settings' --- .../{settings.json => erpnext_settings.json} | 12 ++++++------ .../{settings.svg => erpnext_settings.svg} | 0 .../erpnext_settings.json} | 8 ++++---- .../{settings.json => erpnext_settings.json} | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) rename erpnext/desktop_icon/{settings.json => erpnext_settings.json} (58%) rename erpnext/public/desktop_icons/{settings.svg => erpnext_settings.svg} (100%) rename erpnext/setup/workspace/{settings/settings.json => erpnext_settings/erpnext_settings.json} (96%) rename erpnext/workspace_sidebar/{settings.json => erpnext_settings.json} (97%) diff --git a/erpnext/desktop_icon/settings.json b/erpnext/desktop_icon/erpnext_settings.json similarity index 58% rename from erpnext/desktop_icon/settings.json rename to erpnext/desktop_icon/erpnext_settings.json index 484fd57cd38..247238ee502 100644 --- a/erpnext/desktop_icon/settings.json +++ b/erpnext/desktop_icon/erpnext_settings.json @@ -1,19 +1,19 @@ { "app": "erpnext", - "creation": "2025-11-17 13:19:04.260916", + "creation": "2026-01-09 12:48:25.524807", "docstatus": 0, "doctype": "Desktop Icon", "hidden": 0, "icon": "setting", "icon_type": "Link", "idx": 10, - "label": "Settings", - "link_to": "Settings", + "label": "ERPNext Settings", + "link_to": "ERPNext Settings", "link_type": "Workspace Sidebar", - "logo_url": "/assets/erpnext/desktop_icons/settings.svg", - "modified": "2026-01-01 20:07:01.330786", + "logo_url": "", + "modified": "2026-01-09 14:59:56.044037", "modified_by": "Administrator", - "name": "Settings", + "name": "ERPNext Settings", "owner": "Administrator", "parent_icon": "", "restrict_removal": 0, diff --git a/erpnext/public/desktop_icons/settings.svg b/erpnext/public/desktop_icons/erpnext_settings.svg similarity index 100% rename from erpnext/public/desktop_icons/settings.svg rename to erpnext/public/desktop_icons/erpnext_settings.svg diff --git a/erpnext/setup/workspace/settings/settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json similarity index 96% rename from erpnext/setup/workspace/settings/settings.json rename to erpnext/setup/workspace/erpnext_settings/erpnext_settings.json index 2ebb66e6518..745d0ad4d24 100644 --- a/erpnext/setup/workspace/settings/settings.json +++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json @@ -11,7 +11,7 @@ "icon": "setting", "idx": 0, "is_hidden": 0, - "label": "Settings", + "label": "ERPNext Settings", "links": [ { "dependencies": "", @@ -69,10 +69,10 @@ "type": "Link" } ], - "modified": "2025-11-18 13:20:51.473774", + "modified": "2026-01-09 13:05:08.007297", "modified_by": "Administrator", "module": "Setup", - "name": "Settings", + "name": "ERPNext Settings", "number_cards": [], "owner": "Administrator", "parent_page": "", @@ -128,6 +128,6 @@ "type": "DocType" } ], - "title": "Settings", + "title": "ERPNext Settings", "type": "Workspace" } diff --git a/erpnext/workspace_sidebar/settings.json b/erpnext/workspace_sidebar/erpnext_settings.json similarity index 97% rename from erpnext/workspace_sidebar/settings.json rename to erpnext/workspace_sidebar/erpnext_settings.json index 6414b0f21f8..1a50a3ca735 100644 --- a/erpnext/workspace_sidebar/settings.json +++ b/erpnext/workspace_sidebar/erpnext_settings.json @@ -227,10 +227,10 @@ "type": "Link" } ], - "modified": "2026-01-02 18:04:13.860117", + "modified": "2026-01-09 13:10:53.641182", "modified_by": "Administrator", "module": "Setup", - "name": "Settings", + "name": "ERPNext Settings", "owner": "Administrator", - "title": "Settings" + "title": "ERPNext Settings" } From 34fd4e704366fc74cf21c78fde73dd60053cf636 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Fri, 9 Jan 2026 15:06:13 +0530 Subject: [PATCH 047/106] chore: renaming settings icon to erpnext_setting --- .../desktop_icons/solid/{settings.svg => erpnext_settings.svg} | 0 .../desktop_icons/subtle/{settings.svg => erpnext_settings.svg} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename erpnext/public/icons/desktop_icons/solid/{settings.svg => erpnext_settings.svg} (100%) rename erpnext/public/icons/desktop_icons/subtle/{settings.svg => erpnext_settings.svg} (100%) diff --git a/erpnext/public/icons/desktop_icons/solid/settings.svg b/erpnext/public/icons/desktop_icons/solid/erpnext_settings.svg similarity index 100% rename from erpnext/public/icons/desktop_icons/solid/settings.svg rename to erpnext/public/icons/desktop_icons/solid/erpnext_settings.svg diff --git a/erpnext/public/icons/desktop_icons/subtle/settings.svg b/erpnext/public/icons/desktop_icons/subtle/erpnext_settings.svg similarity index 100% rename from erpnext/public/icons/desktop_icons/subtle/settings.svg rename to erpnext/public/icons/desktop_icons/subtle/erpnext_settings.svg From e9c009b564ea4173942d76e338deed339e0dd21d Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Fri, 9 Jan 2026 18:16:23 +0530 Subject: [PATCH 048/106] fix(patch): copy the value of post_change_gl_entries from accounts settings to pos settings --- erpnext/patches.txt | 1 + .../set_post_change_gl_entries_on_pos_settings.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 erpnext/patches/v16_0/set_post_change_gl_entries_on_pos_settings.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ce466bc94cd..00448f197b0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -456,3 +456,4 @@ erpnext.patches.v16_0.update_tax_withholding_field_in_payment_entry erpnext.patches.v16_0.migrate_tax_withholding_data erpnext.patches.v16_0.update_corrected_cancelled_status erpnext.patches.v16_0.fix_barcode_typo +erpnext.patches.v16_0.set_post_change_gl_entries_on_pos_settings \ No newline at end of file diff --git a/erpnext/patches/v16_0/set_post_change_gl_entries_on_pos_settings.py b/erpnext/patches/v16_0/set_post_change_gl_entries_on_pos_settings.py new file mode 100644 index 00000000000..9e051859157 --- /dev/null +++ b/erpnext/patches/v16_0/set_post_change_gl_entries_on_pos_settings.py @@ -0,0 +1,14 @@ +import frappe + + +def execute(): + Singles = frappe.qb.DocType("Singles") + query = ( + frappe.qb.from_(Singles) + .select("value") + .where((Singles.doctype == "Accounts Settings") & (Singles.field == "post_change_gl_entries")) + ) + result = query.run(as_dict=1) + if result: + post_change_gl_entries = int(result[0].get("value", 1)) + frappe.db.set_single_value("POS Settings", "post_change_gl_entries", post_change_gl_entries) From ff9b936634fbd3d17677dbe124cab3521c0458f7 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Fri, 9 Jan 2026 19:08:23 +0530 Subject: [PATCH 049/106] fix: add validation for return against --- .../doctype/sales_invoice/sales_invoice.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a61717040a7..a7df2d38420 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -839,6 +839,7 @@ class SalesInvoice(SellingController): self.is_return and self.return_against and data.sales_invoice + and data.sales_invoice == self.return_against and not sales_invoice and args.timesheet_detail == data.name ) @@ -881,12 +882,10 @@ class SalesInvoice(SellingController): payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account") def validate_time_sheets_are_submitted(self): + # Note: This validation is skipped for return invoices + # to allow returns to reference already-billed timesheet details for data in self.timesheets: - if data.time_sheet: - status = frappe.db.get_value("Timesheet", data.time_sheet, "status") - if status not in ["Submitted", "Payslip", "Partially Billed"]: - frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) - + # Handle invoice duplication if data.time_sheet and data.timesheet_detail: if sales_invoice := frappe.db.get_value( "Timesheet Detail", data.timesheet_detail, "sales_invoice" @@ -897,6 +896,11 @@ class SalesInvoice(SellingController): ) ) + if data.time_sheet: + status = frappe.db.get_value("Timesheet", data.time_sheet, "status") + if status not in ["Submitted", "Payslip", "Partially Billed"]: + frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" if cint(self.is_pos) != 1: From b6e5b67676024d2669b6f6ce5a35069d6d1bd5d4 Mon Sep 17 00:00:00 2001 From: sokumon Date: Sat, 10 Jan 2026 00:07:12 +0530 Subject: [PATCH 050/106] chore: export sidebars for new schema --- erpnext/workspace_sidebar/accounting.json | 3 ++- erpnext/workspace_sidebar/assets.json | 3 ++- erpnext/workspace_sidebar/banking.json | 15 ++------------- erpnext/workspace_sidebar/budget.json | 3 ++- erpnext/workspace_sidebar/buying.json | 3 ++- erpnext/workspace_sidebar/crm.json | 3 ++- erpnext/workspace_sidebar/erpnext_settings.json | 3 ++- erpnext/workspace_sidebar/financial_reports.json | 3 ++- erpnext/workspace_sidebar/home.json | 3 ++- erpnext/workspace_sidebar/manufacturing.json | 3 ++- erpnext/workspace_sidebar/projects.json | 3 ++- erpnext/workspace_sidebar/quality.json | 3 ++- erpnext/workspace_sidebar/selling.json | 3 ++- erpnext/workspace_sidebar/share_management.json | 3 ++- erpnext/workspace_sidebar/stock.json | 3 ++- erpnext/workspace_sidebar/subcontracting.json | 3 ++- erpnext/workspace_sidebar/subscription.json | 3 ++- erpnext/workspace_sidebar/support.json | 3 ++- erpnext/workspace_sidebar/taxes.json | 3 ++- 19 files changed, 38 insertions(+), 31 deletions(-) diff --git a/erpnext/workspace_sidebar/accounting.json b/erpnext/workspace_sidebar/accounting.json index a7286ecbfd0..0246a1f31b2 100644 --- a/erpnext/workspace_sidebar/accounting.json +++ b/erpnext/workspace_sidebar/accounting.json @@ -571,10 +571,11 @@ "type": "Link" } ], - "modified": "2026-01-08 15:04:31.767795", + "modified": "2026-01-10 00:06:13.234927", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", "owner": "Administrator", + "standard": 1, "title": "Accounting" } diff --git a/erpnext/workspace_sidebar/assets.json b/erpnext/workspace_sidebar/assets.json index 6f6e709c8f6..5f54ce2bd35 100644 --- a/erpnext/workspace_sidebar/assets.json +++ b/erpnext/workspace_sidebar/assets.json @@ -258,10 +258,11 @@ "url": "" } ], - "modified": "2026-01-02 14:54:20.640887", + "modified": "2026-01-10 00:06:13.218453", "modified_by": "Administrator", "module": "Assets", "name": "Assets", "owner": "Administrator", + "standard": 1, "title": "Assets" } diff --git a/erpnext/workspace_sidebar/banking.json b/erpnext/workspace_sidebar/banking.json index 95834113772..b73120cc4a7 100644 --- a/erpnext/workspace_sidebar/banking.json +++ b/erpnext/workspace_sidebar/banking.json @@ -6,18 +6,6 @@ "header_icon": "circle-dollar-sign", "idx": 0, "items": [ - { - "child": 0, - "collapsible": 1, - "icon": "home", - "indent": 0, - "keep_closed": 0, - "label": "Home", - "link_to": "Banking", - "link_type": "Workspace", - "show_arrow": 0, - "type": "Link" - }, { "child": 0, "collapsible": 1, @@ -192,10 +180,11 @@ "type": "Link" } ], - "modified": "2026-01-02 13:53:50.930215", + "modified": "2026-01-10 00:06:13.017457", "modified_by": "Administrator", "module": "Accounts", "name": "Banking", "owner": "Administrator", + "standard": 1, "title": "Banking" } diff --git a/erpnext/workspace_sidebar/budget.json b/erpnext/workspace_sidebar/budget.json index 94ebfc9c994..dd9b6f87311 100644 --- a/erpnext/workspace_sidebar/budget.json +++ b/erpnext/workspace_sidebar/budget.json @@ -67,10 +67,11 @@ "type": "Link" } ], - "modified": "2026-01-02 11:46:10.598472", + "modified": "2026-01-10 00:06:13.032297", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", "owner": "Administrator", + "standard": 1, "title": "Budget" } diff --git a/erpnext/workspace_sidebar/buying.json b/erpnext/workspace_sidebar/buying.json index ecf87ecc68f..ec7f6a3ccd4 100644 --- a/erpnext/workspace_sidebar/buying.json +++ b/erpnext/workspace_sidebar/buying.json @@ -369,10 +369,11 @@ "type": "Link" } ], - "modified": "2026-01-02 12:10:07.304778", + "modified": "2026-01-10 00:06:12.979668", "modified_by": "Administrator", "module": "Buying", "name": "Buying", "owner": "Administrator", + "standard": 1, "title": "Buying" } diff --git a/erpnext/workspace_sidebar/crm.json b/erpnext/workspace_sidebar/crm.json index 264318c1c8d..6e8bde422f2 100644 --- a/erpnext/workspace_sidebar/crm.json +++ b/erpnext/workspace_sidebar/crm.json @@ -485,10 +485,11 @@ "type": "Link" } ], - "modified": "2026-01-02 15:06:29.836236", + "modified": "2026-01-10 00:06:13.192366", "modified_by": "Administrator", "module": "CRM", "name": "CRM", "owner": "Administrator", + "standard": 1, "title": "CRM" } diff --git a/erpnext/workspace_sidebar/erpnext_settings.json b/erpnext/workspace_sidebar/erpnext_settings.json index 1a50a3ca735..117a36b666e 100644 --- a/erpnext/workspace_sidebar/erpnext_settings.json +++ b/erpnext/workspace_sidebar/erpnext_settings.json @@ -227,10 +227,11 @@ "type": "Link" } ], - "modified": "2026-01-09 13:10:53.641182", + "modified": "2026-01-10 00:06:12.956275", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", "owner": "Administrator", + "standard": 1, "title": "ERPNext Settings" } diff --git a/erpnext/workspace_sidebar/financial_reports.json b/erpnext/workspace_sidebar/financial_reports.json index 3af8b051f8c..52cfb4fd9ec 100644 --- a/erpnext/workspace_sidebar/financial_reports.json +++ b/erpnext/workspace_sidebar/financial_reports.json @@ -381,10 +381,11 @@ "type": "Link" } ], - "modified": "2026-01-08 17:50:55.681403", + "modified": "2026-01-10 00:06:13.168391", "modified_by": "Administrator", "module": "Accounts", "name": "Financial Reports", "owner": "Administrator", + "standard": 1, "title": "Financial Reports" } diff --git a/erpnext/workspace_sidebar/home.json b/erpnext/workspace_sidebar/home.json index 61b3e600421..82e1b8d7605 100644 --- a/erpnext/workspace_sidebar/home.json +++ b/erpnext/workspace_sidebar/home.json @@ -62,10 +62,11 @@ "type": "Link" } ], - "modified": "2025-11-25 10:46:09.198568", + "modified": "2026-01-10 00:06:12.971358", "modified_by": "Administrator", "module": "Setup", "name": "Home", "owner": "Administrator", + "standard": 1, "title": "Home" } diff --git a/erpnext/workspace_sidebar/manufacturing.json b/erpnext/workspace_sidebar/manufacturing.json index 08b6a054660..3c2a4a3dd9d 100644 --- a/erpnext/workspace_sidebar/manufacturing.json +++ b/erpnext/workspace_sidebar/manufacturing.json @@ -425,10 +425,11 @@ "type": "Link" } ], - "modified": "2026-01-02 15:08:30.661411", + "modified": "2026-01-10 00:06:13.058137", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", "owner": "Administrator", + "standard": 1, "title": "Manufacturing" } diff --git a/erpnext/workspace_sidebar/projects.json b/erpnext/workspace_sidebar/projects.json index 58fec69ff3d..ec0960aa32a 100644 --- a/erpnext/workspace_sidebar/projects.json +++ b/erpnext/workspace_sidebar/projects.json @@ -223,10 +223,11 @@ "type": "Link" } ], - "modified": "2026-01-02 11:57:04.237376", + "modified": "2026-01-10 00:06:13.151947", "modified_by": "Administrator", "module": "Projects", "name": "Projects", "owner": "Administrator", + "standard": 1, "title": "Projects" } diff --git a/erpnext/workspace_sidebar/quality.json b/erpnext/workspace_sidebar/quality.json index 5dc41335404..346fa15b3e2 100644 --- a/erpnext/workspace_sidebar/quality.json +++ b/erpnext/workspace_sidebar/quality.json @@ -158,10 +158,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:39:50.641254", + "modified": "2026-01-10 00:06:13.140271", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", "owner": "Administrator", + "standard": 1, "title": "Quality" } diff --git a/erpnext/workspace_sidebar/selling.json b/erpnext/workspace_sidebar/selling.json index 766b8f87fa9..3ac2dc8665c 100644 --- a/erpnext/workspace_sidebar/selling.json +++ b/erpnext/workspace_sidebar/selling.json @@ -687,10 +687,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:44:08.721891", + "modified": "2026-01-10 00:06:13.103140", "modified_by": "Administrator", "module": "Selling", "name": "Selling", "owner": "Administrator", + "standard": 1, "title": "Selling" } diff --git a/erpnext/workspace_sidebar/share_management.json b/erpnext/workspace_sidebar/share_management.json index 1b49348e477..20eb476bfb7 100644 --- a/erpnext/workspace_sidebar/share_management.json +++ b/erpnext/workspace_sidebar/share_management.json @@ -55,10 +55,11 @@ "type": "Link" } ], - "modified": "2026-01-02 14:53:29.842384", + "modified": "2026-01-10 00:06:13.040767", "modified_by": "Administrator", "module": "Accounts", "name": "Share Management", "owner": "Administrator", + "standard": 1, "title": "Share Management" } diff --git a/erpnext/workspace_sidebar/stock.json b/erpnext/workspace_sidebar/stock.json index 87c53dad060..14c5faa24e8 100644 --- a/erpnext/workspace_sidebar/stock.json +++ b/erpnext/workspace_sidebar/stock.json @@ -625,10 +625,11 @@ "type": "Link" } ], - "modified": "2026-01-02 18:10:31.153427", + "modified": "2026-01-10 00:06:12.912403", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "owner": "Administrator", + "standard": 1, "title": "Stock" } diff --git a/erpnext/workspace_sidebar/subcontracting.json b/erpnext/workspace_sidebar/subcontracting.json index f555875c292..4cd0bc9003c 100644 --- a/erpnext/workspace_sidebar/subcontracting.json +++ b/erpnext/workspace_sidebar/subcontracting.json @@ -196,10 +196,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:51:15.843931", + "modified": "2026-01-10 00:06:12.744529", "modified_by": "Administrator", "module": "Buying", "name": "Subcontracting", "owner": "Administrator", + "standard": 1, "title": "Subcontracting" } diff --git a/erpnext/workspace_sidebar/subscription.json b/erpnext/workspace_sidebar/subscription.json index ed91cc0c04c..ca42736b27c 100644 --- a/erpnext/workspace_sidebar/subscription.json +++ b/erpnext/workspace_sidebar/subscription.json @@ -87,10 +87,11 @@ "type": "Link" } ], - "modified": "2025-12-22 17:06:54.262451", + "modified": "2026-01-10 00:06:13.048591", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", "owner": "Administrator", + "standard": 1, "title": "Subscription" } diff --git a/erpnext/workspace_sidebar/support.json b/erpnext/workspace_sidebar/support.json index b62a52554f3..364c0071ebd 100644 --- a/erpnext/workspace_sidebar/support.json +++ b/erpnext/workspace_sidebar/support.json @@ -145,10 +145,11 @@ "type": "Link" } ], - "modified": "2026-01-02 17:47:19.864824", + "modified": "2026-01-10 00:06:13.089991", "modified_by": "Administrator", "module": "Support", "name": "Support", "owner": "Administrator", + "standard": 1, "title": "Support" } diff --git a/erpnext/workspace_sidebar/taxes.json b/erpnext/workspace_sidebar/taxes.json index 9d9033f29ac..5cf65ff3c67 100644 --- a/erpnext/workspace_sidebar/taxes.json +++ b/erpnext/workspace_sidebar/taxes.json @@ -148,10 +148,11 @@ "type": "Link" } ], - "modified": "2026-01-02 14:40:04.075046", + "modified": "2026-01-10 00:06:13.005238", "modified_by": "Administrator", "module": "Accounts", "name": "Taxes", "owner": "Administrator", + "standard": 1, "title": "Taxes" } From 7c96a08054ec40860229b4b4be5b1d703d858d9c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 10 Jan 2026 15:35:37 +0530 Subject: [PATCH 051/106] fix: reorder checkbox in MR should be readonly --- .../stock/doctype/material_request/material_request.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 02183bcfad3..b34bc7ded7d 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -366,16 +366,19 @@ }, { "default": "0", + "depends_on": "auto_created_via_reorder", "fieldname": "auto_created_via_reorder", "fieldtype": "Check", - "label": "Auto Created (Reorder)" + "label": "Auto Created (Reorder)", + "no_copy": 1, + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 70, "is_submittable": 1, "links": [], - "modified": "2025-12-02 13:56:33.001436", + "modified": "2026-01-10 15:34:59.000603", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", From 9f4bf65768e50a1ca0878a9956882bbe7cfd00bd Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 10 Jan 2026 15:40:03 +0530 Subject: [PATCH 052/106] fix: error message args in sle.py --- erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 87e070b0b05..4f6919e145b 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -232,7 +232,7 @@ class StockLedgerEntry(Document): ) if item_detail.is_stock_item != 1: - self.throw_error_message("Item {0} must be a stock Item").format(self.item_code) + self.throw_error_message(f"Item {self.item_code} must be a stock Item") if item_detail.has_serial_no or item_detail.has_batch_no: if not self.serial_and_batch_bundle: From 7e7e83440f1f983966c44dac953e0c82f03be654 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 10 Jan 2026 16:43:39 +0530 Subject: [PATCH 053/106] ci: version 16 related changes --- .github/workflows/initiate_release.yml | 2 +- .mergify.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml index 5b6536844fc..78b3cabe8a5 100644 --- a/.github/workflows/initiate_release.yml +++ b/.github/workflows/initiate_release.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - version: ["14", "15"] + version: ["15", "16"] steps: - uses: octokit/request-action@v2.x diff --git a/.mergify.yml b/.mergify.yml index 54e1bce46f2..5e558062048 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -50,13 +50,13 @@ pull_request_rules: - version-15-hotfix assignees: - "{{ author }}" - - name: backport to version-16-beta + - name: backport to version-16-hotfix conditions: - - label="backport version-16-beta" + - label="backport version-16-hotfix" actions: backport: branches: - - version-16-beta + - version-16-hotfix assignees: - "{{ author }}" - name: Automatic merge on CI success and review From 4987b2fe26defb9534560efce1339c85ff0d2585 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 10 Jan 2026 17:01:04 +0530 Subject: [PATCH 054/106] ci: ignore ci folder for tests --- .github/workflows/patch.yml | 1 + .github/workflows/server-tests-mariadb.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index fac11c071db..d275899ae39 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -11,6 +11,7 @@ on: - 'crowdin.yml' - '.coderabbit.yml' - '.mergify.yml' + - '.github/**' workflow_dispatch: permissions: diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index 036f587dbf9..94d7dec7780 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -12,6 +12,7 @@ on: - 'crowdin.yml' - '.coderabbit.yml' - '.mergify.yml' + - '.github/**' schedule: # Run everday at midnight UTC / 5:30 IST - cron: "0 0 * * *" From 1d6d9c204005878a1013b45fb371b69234d40544 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sat, 10 Jan 2026 17:27:39 +0530 Subject: [PATCH 055/106] fix: pick list qty does not reset when pick list is cancelled --- erpnext/stock/doctype/pick_list/pick_list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index d1a273f12a8..d9b1d29e5aa 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -400,7 +400,7 @@ class PickList(TransactionBase): picked_items = get_picked_items_qty(packed_items, contains_packed_items=True) self.validate_picked_qty(picked_items) - doc_updates = {} + doc_updates = {item: {"picked_qty": 0} for item in set(packed_items)} for d in picked_items: doc_updates[d.product_bundle_item] = {"picked_qty": flt(d.picked_qty)} @@ -411,7 +411,7 @@ class PickList(TransactionBase): picked_items = get_picked_items_qty(so_items) self.validate_picked_qty(picked_items) - doc_updates = {} + doc_updates = {item: {"picked_qty": 0} for item in set(so_items)} for d in picked_items: doc_updates[d.sales_order_item] = {"picked_qty": flt(d.picked_qty)} From 4e6d86d6f0bafc19c11e224e68c211a0db3371fa Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 10 Jan 2026 19:29:25 +0530 Subject: [PATCH 056/106] fix(stock entry): calculate transferred quantity using transfer_qty --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 215ad6d2ee9..8c200715397 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3238,7 +3238,7 @@ class StockEntry(StockController, SubcontractingInwardController): stock_entries_child_list.append(d.ste_detail) transferred_qty = frappe.get_all( "Stock Entry Detail", - fields=[{"SUM": "qty", "as": "qty"}], + fields=[{"SUM": "transfer_qty", "as": "qty"}], filters={ "against_stock_entry": d.against_stock_entry, "ste_detail": d.ste_detail, From 7e991483575c804b7cee1ae80a7e72c6679c1edb Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 10 Jan 2026 19:42:09 +0530 Subject: [PATCH 057/106] test: allow from_warehouse while creating material request --- .../stock/doctype/material_request/test_material_request.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 93f27d1c382..24a30581188 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -1138,7 +1138,8 @@ def make_material_request(**args): mr = frappe.new_doc("Material Request") mr.material_request_type = args.material_request_type or "Purchase" mr.company = args.company or "_Test Company" - mr.customer = args.customer or "_Test Customer" + if mr.material_request_type == "Customer Provided": + mr.customer = args.customer or "_Test Customer" mr.append( "items", { @@ -1147,6 +1148,7 @@ def make_material_request(**args): "uom": args.uom or "_Test UOM", "conversion_factor": args.conversion_factor or 1, "schedule_date": args.schedule_date or today(), + "from_warehouse": args.from_warehouse, "warehouse": args.warehouse or "_Test Warehouse - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC", }, From bf2ab32abf991b07ca6a45e1c9be840445181f7d Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sat, 10 Jan 2026 20:13:17 +0530 Subject: [PATCH 058/106] test: validate transferred quantity for material transfer entry --- .../doctype/stock_entry/test_stock_entry.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index a3e0aea07be..935e86dcdc6 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -14,6 +14,13 @@ from erpnext.stock.doctype.item.test_item import ( make_item_variant, set_item_variant_settings, ) +from erpnext.stock.doctype.material_request.material_request import ( + make_in_transit_stock_entry, +) +from erpnext.stock.doctype.material_request.test_material_request import ( + get_in_transit_warehouse, + make_material_request, +) from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import ( get_batch_from_bundle, get_serial_nos_from_bundle, @@ -2190,6 +2197,59 @@ class TestStockEntry(IntegrationTestCase): self.assertEqual(se.purpose, "Repack") self.assertRaises(frappe.ValidationError, se.submit) + def test_transferred_qty_in_material_transfer(self): + item_code = "_Test Item" + source_warehouse = "_Test Warehouse - _TC" + target_warehouse = "_Test Warehouse 1 - _TC" + + if not frappe.db.get_value("UOM Conversion Detail", {"parent": item_code, "uom": "Box"}): + item_doc = frappe.get_doc("Item", item_code) + item_doc.append("uoms", {"uom": "Box", "conversion_factor": 12}) + item_doc.save(ignore_permissions=True) + + make_stock_entry(item_code=item_code, target=source_warehouse, qty=12, rate=100) + + # Create a Material Request for Material Transfer + material_request = make_material_request( + material_request_type="Material Transfer", + qty=1, + item_code=item_code, + uom="Box", + conversion_factor=12, + from_warehouse=source_warehouse, + warehouse=target_warehouse, + ) + in_transit_wh = get_in_transit_warehouse(material_request.company) + + # Create first Stock Entry (Source -> In-Transit) + stock_entry_1 = make_in_transit_stock_entry(material_request.name, in_transit_wh) + stock_entry_1.items[0].update( + { + "qty": 1, + "s_warehouse": source_warehouse, + } + ) + stock_entry_1.save().submit() + + # Validate transfer status after first transfer + material_request.reload() + self.assertEqual(material_request.transfer_status, "In Transit") + + # Create final Stock Entry (In-Transit -> Target) + end_transit_1 = make_stock_in_entry(stock_entry_1.name) + end_transit_1.save().submit() + end_transit_1.reload() + + # Validate quantities + stock_entry_1.reload() + self.assertEqual(stock_entry_1.items[0].qty, 1) + self.assertEqual(stock_entry_1.items[0].transfer_qty, 12) + self.assertEqual(stock_entry_1.items[0].transferred_qty, 12) + + # Validate transfer status after final transfer + material_request.reload() + self.assertEqual(material_request.transfer_status, "Completed") + def make_serialized_item(self, **args): args = frappe._dict(args) From 058500e011674feb1e96926732849fd0389c57bc Mon Sep 17 00:00:00 2001 From: MochaMind Date: Sun, 11 Jan 2026 12:04:33 +0530 Subject: [PATCH 059/106] fix: Indonesian translations --- erpnext/locale/id.po | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/erpnext/locale/id.po b/erpnext/locale/id.po index 53d7951bdf7..78eb9b9a015 100644 --- a/erpnext/locale/id.po +++ b/erpnext/locale/id.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: hello@frappe.io\n" "POT-Creation-Date: 2025-12-21 09:37+0000\n" -"PO-Revision-Date: 2025-12-22 03:08\n" +"PO-Revision-Date: 2026-01-11 06:34\n" "Last-Translator: hello@frappe.io\n" "Language-Team: Indonesian\n" "MIME-Version: 1.0\n" @@ -207,13 +207,13 @@ msgstr "% Progres" #. 'Subcontracting Inward Order' #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json msgid "% Raw Material Received" -msgstr "" +msgstr "% Bahan Baku yang Diterima" #. Label of the per_raw_material_returned (Percent) field in DocType #. 'Subcontracting Inward Order' #: erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.json msgid "% Raw Material Returned" -msgstr "" +msgstr "% Bahan Baku yang Dikembalikan" #. Label of the per_received (Percent) field in DocType 'Purchase Order' #. Label of the per_received (Percent) field in DocType 'Material Request' @@ -242,13 +242,13 @@ msgstr "% Dikembalikan" #: erpnext/selling/doctype/sales_order/sales_order.json #, python-format msgid "% of materials billed against this Sales Order" -msgstr "" +msgstr "Persentase bahan yang ditagihkan terhadap Pesanan Penjualan ini" #. Description of the '% Delivered' (Percent) field in DocType 'Pick List' #: erpnext/stock/doctype/pick_list/pick_list.json #, python-format msgid "% of materials delivered against this Pick List" -msgstr "" +msgstr "% Material yang Dikirim pada Pick List ini" #. Description of the '% Delivered' (Percent) field in DocType 'Sales Order' #: erpnext/selling/doctype/sales_order/sales_order.json @@ -2321,7 +2321,7 @@ msgstr "" #: erpnext/crm/doctype/opportunity/opportunity.json #: erpnext/crm/doctype/prospect/prospect.json msgid "Activities" -msgstr "" +msgstr "Aktivitas" #. Name of a DocType #. Label of a Link in the Projects Workspace @@ -2421,15 +2421,15 @@ msgstr "" #. Operation' #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json msgid "Actual End Time" -msgstr "" +msgstr "Waktu Akhir Aktual" #: erpnext/accounts/report/budget_variance_report/budget_variance_report.py:381 msgid "Actual Expense" -msgstr "" +msgstr "Beban Aktual" #: erpnext/accounts/doctype/budget/budget.py:601 msgid "Actual Expenses" -msgstr "" +msgstr "Beban Aktual" #. Label of the actual_operating_cost (Currency) field in DocType 'Work Order' #. Label of the actual_operating_cost (Currency) field in DocType 'Work Order @@ -2437,13 +2437,13 @@ msgstr "" #: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json msgid "Actual Operating Cost" -msgstr "" +msgstr "Biaya Operasional Aktual" #. Label of the actual_operation_time (Float) field in DocType 'Work Order #. Operation' #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json msgid "Actual Operation Time" -msgstr "" +msgstr "Waktu Operasi Aktual" #: erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py:430 msgid "Actual Posting" @@ -3211,18 +3211,18 @@ msgstr "Jumlah Uang Muka" #: erpnext/buying/doctype/purchase_order/purchase_order.json #: erpnext/selling/doctype/sales_order/sales_order.json msgid "Advance Paid" -msgstr "" +msgstr "Uang Muka Dibayar" #: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75 #: erpnext/selling/doctype/sales_order/sales_order_list.js:122 msgid "Advance Payment" -msgstr "" +msgstr "Pembayaran Uang Muka" #. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType #. 'Company' #: erpnext/setup/doctype/company/company.json msgid "Advance Payment Date" -msgstr "" +msgstr "Tanggal Pembayaran Uang Muka" #. Name of a DocType #: erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -3288,7 +3288,7 @@ msgstr "" #. Advance' #: erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json msgid "Advance amount" -msgstr "" +msgstr "Jumlah uang muka" #: erpnext/controllers/taxes_and_totals.py:942 msgid "Advance amount cannot be greater than {0} {1}" @@ -3320,15 +3320,15 @@ msgstr "" #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json msgid "Advances" -msgstr "" +msgstr "Uang Muka" #: erpnext/setup/setup_wizard/data/marketing_source.txt:3 msgid "Advertisement" -msgstr "" +msgstr "Iklan" #: erpnext/setup/setup_wizard/data/industry_type.txt:2 msgid "Advertising" -msgstr "" +msgstr "Periklanan" #: erpnext/setup/setup_wizard/data/industry_type.txt:3 msgid "Aerospace" @@ -3338,7 +3338,7 @@ msgstr "" #. Valuation' #: erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json msgid "Affected Transactions" -msgstr "" +msgstr "Transaksi Terdampak" #. Label of the against (Text) field in DocType 'GL Entry' #: erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -3766,7 +3766,7 @@ msgstr "Semua komunikasi termasuk dan di atas ini akan dipindahkan ke Isu baru" #: erpnext/manufacturing/doctype/production_plan/production_plan.py:946 msgid "All items are already requested" -msgstr "" +msgstr "Semua barang sudah diminta" #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.py:1369 msgid "All items have already been Invoiced/Returned" @@ -3774,7 +3774,7 @@ msgstr "Semua item sudah Ditagih/Dikembalikan" #: erpnext/stock/doctype/delivery_note/delivery_note.py:1208 msgid "All items have already been received" -msgstr "" +msgstr "Semua barang sudah diterima" #: erpnext/stock/doctype/stock_entry/stock_entry.py:2993 msgid "All items have already been transferred for this Work Order." @@ -3800,7 +3800,7 @@ msgstr "" #: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:200 msgid "All the items have been already returned." -msgstr "" +msgstr "Semua barang sudah dikembalikan." #: erpnext/manufacturing/doctype/work_order/work_order.js:1171 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." @@ -3915,7 +3915,7 @@ msgstr "Alokasi" #: erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json #: erpnext/public/js/utils/unreconcile.js:104 msgid "Allocations" -msgstr "" +msgstr "Alokasi" #: erpnext/manufacturing/report/production_planning_report/production_planning_report.py:427 msgid "Allotted Qty" @@ -3946,7 +3946,7 @@ msgstr "Izinkan Pembuatan Akun Terhadap Perusahaan Anak" #: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json msgid "Allow Alternative Item" -msgstr "" +msgstr "Izinkan Barang Alternatif" #: erpnext/stock/doctype/item_alternative/item_alternative.py:65 msgid "Allow Alternative Item must be checked on Item {}" From 8d4a179a8f992eb0e45123cdd5bfd66a932a7330 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 11 Jan 2026 11:44:48 +0530 Subject: [PATCH 060/106] refactor: single table for better performance --- erpnext/controllers/stock_controller.py | 11 ++++ erpnext/patches.txt | 2 +- .../v16_0/update_serial_batch_entries.py | 4 +- .../delivery_note/test_delivery_note.py | 58 +++++++++++++++++++ .../serial_and_batch_bundle.py | 12 ++++ .../serial_and_batch_entry.json | 25 +++++++- .../serial_and_batch_entry.py | 2 + erpnext/stock/serial_batch_bundle.py | 53 ++++++++--------- 8 files changed, 138 insertions(+), 29 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index fce149e0e84..916d9865662 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -921,6 +921,10 @@ class StockController(AccountsController): "Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1} ) + frappe.db.set_value( + "Serial and Batch Entry", {"parent": row.serial_and_batch_bundle}, {"is_cancelled": 1} + ) + if update_values: row.db_set(update_values) @@ -929,6 +933,12 @@ class StockController(AccountsController): "Serial and Batch Bundle", row.rejected_serial_and_batch_bundle, {"is_cancelled": 1} ) + frappe.db.set_value( + "Serial and Batch Entry", + {"parent": row.rejected_serial_and_batch_bundle}, + {"is_cancelled": 1}, + ) + row.db_set("rejected_serial_and_batch_bundle", None) if row.get("current_serial_and_batch_bundle"): @@ -2310,6 +2320,7 @@ def make_bundle_for_material_transfer(**kwargs): row.voucher_no = bundle_doc.voucher_no row.voucher_detail_no = bundle_doc.voucher_detail_no row.type_of_transaction = bundle_doc.type_of_transaction + row.item_code = bundle_doc.item_code bundle_doc.set_incoming_rate() bundle_doc.calculate_qty_and_amount() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ce466bc94cd..8b7653f5967 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -443,7 +443,7 @@ erpnext.patches.v16_0.rename_subcontracted_quantity erpnext.patches.v16_0.add_new_stock_entry_types erpnext.patches.v15_0.set_asset_status_if_not_already_set erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing -erpnext.patches.v16_0.update_serial_batch_entries +erpnext.patches.v16_0.update_serial_batch_entries #11-01-2026 10:00:00 erpnext.patches.v16_0.set_company_wise_warehouses erpnext.patches.v16_0.set_valuation_method_on_companies erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table diff --git a/erpnext/patches/v16_0/update_serial_batch_entries.py b/erpnext/patches/v16_0/update_serial_batch_entries.py index 26a817dc7bf..a2391edd57f 100644 --- a/erpnext/patches/v16_0/update_serial_batch_entries.py +++ b/erpnext/patches/v16_0/update_serial_batch_entries.py @@ -11,7 +11,9 @@ def execute(): SABE.voucher_type = SABB.voucher_type, SABE.voucher_no = SABB.voucher_no, SABE.voucher_detail_no = SABB.voucher_detail_no, - SABE.type_of_transaction = SABB.type_of_transaction + SABE.type_of_transaction = SABB.type_of_transaction, + SABE.is_cancelled = SABB.is_cancelled, + SABE.item_code = SABB.item_code WHERE SABE.parent = SABB.name """ ) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 9b02ebe2f7a..d85a76f9f75 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -2806,6 +2806,64 @@ class TestDeliveryNote(IntegrationTestCase): frappe.db.set_single_value("System Settings", "float_precision", original_flt_precision) + def test_different_rate_for_same_serial_nos(self): + item_code = make_item( + "Test Different Rate Serial No Item", + properties={"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "DRSN-.#####"}, + ).name + + se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100) + serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle) + + dn = create_delivery_note( + item_code=item_code, + qty=1, + rate=300, + use_serial_batch_fields=1, + serial_no="\n".join(serial_nos), + ) + + dn.reload() + + sabb = frappe.get_doc("Serial and Batch Bundle", dn.items[0].serial_and_batch_bundle) + for entry in sabb.entries: + self.assertEqual(entry.incoming_rate, 100) + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=1, + basic_rate=200, + use_serial_batch_fields=1, + serial_no="\n".join(serial_nos), + ) + dn1 = create_delivery_note( + item_code=item_code, + qty=1, + rate=300, + use_serial_batch_fields=1, + serial_no="\n".join(serial_nos), + ) + + dn1.reload() + + sabb = frappe.get_doc("Serial and Batch Bundle", dn1.items[0].serial_and_batch_bundle) + for entry in sabb.entries: + self.assertEqual(entry.incoming_rate, 200) + + doc = frappe.new_doc("Repost Item Valuation") + doc.voucher_type = "Stock Entry" + doc.voucher_no = se.name + doc.submit() + + sabb = frappe.get_doc("Serial and Batch Bundle", dn.items[0].serial_and_batch_bundle) + for entry in sabb.entries: + self.assertEqual(entry.incoming_rate, 100) + + sabb = frappe.get_doc("Serial and Batch Bundle", dn1.items[0].serial_and_batch_bundle) + for entry in sabb.entries: + self.assertEqual(entry.incoming_rate, 200) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index c42a1e57c32..8b50e1666ef 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -139,6 +139,7 @@ class SerialandBatchBundle(Document): self.set_incoming_rate() self.calculate_qty_and_amount() + self.set_child_details() def validate_serial_no_status(self): serial_nos = [d.serial_no for d in self.entries if d.serial_no] @@ -1342,8 +1343,18 @@ class SerialandBatchBundle(Document): self.set_source_document_no() def on_submit(self): + self.validate_docstatus() self.validate_serial_nos_inventory() + def validate_docstatus(self): + for row in self.entries: + if row.docstatus != 1: + frappe.throw( + _("At Row {0}: In Serial and Batch Bundle {1} must have docstatus as 1 and not 0").format( + bold(row.idx), bold(self.name) + ) + ) + def set_child_details(self): for row in self.entries: for field in [ @@ -1353,6 +1364,7 @@ class SerialandBatchBundle(Document): "voucher_no", "voucher_detail_no", "type_of_transaction", + "item_code", ]: if not row.get(field) or row.get(field) != self.get(field): row.set(field, self.get(field)) diff --git a/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json index 69aaf261945..b5d0200c1c2 100644 --- a/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json +++ b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.json @@ -7,6 +7,7 @@ "field_order": [ "serial_no", "batch_no", + "item_code", "column_break_2", "qty", "warehouse", @@ -22,6 +23,7 @@ "reference_for_reservation", "voucher_type", "voucher_no", + "is_cancelled", "column_break_eykr", "posting_datetime", "type_of_transaction", @@ -146,24 +148,28 @@ "fieldname": "posting_datetime", "fieldtype": "Datetime", "label": "Posting Datetime", + "no_copy": 1, "read_only": 1 }, { "fieldname": "voucher_type", "fieldtype": "Data", "label": "Voucher Type", + "no_copy": 1, "read_only": 1 }, { "fieldname": "voucher_no", "fieldtype": "Data", "label": "Voucher No", + "no_copy": 1, "read_only": 1 }, { "fieldname": "voucher_detail_no", "fieldtype": "Data", "label": "Voucher Detail No", + "no_copy": 1, "read_only": 1, "search_index": 1 }, @@ -171,18 +177,35 @@ "fieldname": "type_of_transaction", "fieldtype": "Data", "label": "Type of Transaction", + "no_copy": 1, "read_only": 1, "search_index": 1 }, { "fieldname": "column_break_eykr", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_cancelled", + "fieldtype": "Check", + "label": "Is Cancelled", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "label": "Item Code", + "no_copy": 1, + "options": "Item", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-11-09 23:28:35.191959", + "modified": "2026-01-11 11:05:10.789054", "modified_by": "Administrator", "module": "Stock", "name": "Serial and Batch Entry", diff --git a/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py index 1f084e60c9c..adeb6a388da 100644 --- a/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py +++ b/erpnext/stock/doctype/serial_and_batch_entry/serial_and_batch_entry.py @@ -17,7 +17,9 @@ class SerialandBatchEntry(Document): batch_no: DF.Link | None delivered_qty: DF.Float incoming_rate: DF.Float + is_cancelled: DF.Check is_outward: DF.Check + item_code: DF.Link | None outgoing_rate: DF.Float parent: DF.Data parentfield: DF.Data diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index e52ce3567cb..13d7b100855 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -324,6 +324,12 @@ class SerialBatchBundle: {"is_cancelled": 1}, ) + frappe.db.set_value( + "Serial and Batch Entry", + {"voucher_no": self.sle.voucher_no, "voucher_type": self.sle.voucher_type}, + {"is_cancelled": 1}, + ) + if self.sle.serial_and_batch_bundle: frappe.get_cached_doc( "Serial and Batch Bundle", self.sle.serial_and_batch_bundle @@ -642,26 +648,23 @@ class SerialNoValuation(DeprecatedSerialNoValuation): self.calculate_stock_value_from_deprecarated_ledgers() def get_serial_no_wise_incoming_rate(self, serial_nos): - bundle = frappe.qb.DocType("Serial and Batch Bundle") bundle_child = frappe.qb.DocType("Serial and Batch Entry") def get_latest_based_on_posting_datetime(): # Get latest inward record based on posting datetime for each serial no latest_posting = ( - frappe.qb.from_(bundle) - .inner_join(bundle_child) - .on(bundle.name == bundle_child.parent) + frappe.qb.from_(bundle_child) .select( bundle_child.serial_no, - Max(bundle.posting_datetime).as_("max_posting_dt"), + Max(bundle_child.posting_datetime).as_("max_posting_dt"), ) .where( - (bundle.is_cancelled == 0) - & (bundle.docstatus == 1) - & (bundle.type_of_transaction == "Inward") + (bundle_child.is_cancelled == 0) + & (bundle_child.docstatus == 1) + & (bundle_child.type_of_transaction == "Inward") & (bundle_child.qty > 0) - & (bundle.item_code == self.sle.item_code) + & (bundle_child.item_code == self.sle.item_code) & (bundle_child.warehouse == self.sle.warehouse) & (bundle_child.serial_no.isin(serial_nos)) ) @@ -670,10 +673,10 @@ class SerialNoValuation(DeprecatedSerialNoValuation): # Important to exclude the current voucher to calculate correct the stock value difference if self.sle.voucher_no: - latest_posting = latest_posting.where(bundle.voucher_no != self.sle.voucher_no) + latest_posting = latest_posting.where(bundle_child.voucher_no != self.sle.voucher_no) if self.sle.posting_datetime: - timestamp_condition = bundle.posting_datetime <= self.sle.posting_datetime + timestamp_condition = bundle_child.posting_datetime <= self.sle.posting_datetime latest_posting = latest_posting.where(timestamp_condition) @@ -684,24 +687,22 @@ class SerialNoValuation(DeprecatedSerialNoValuation): def get_latest_based_on_creation(latest_posting): # Get latest inward record based on creation for each serial no latest_creation = ( - frappe.qb.from_(bundle) - .join(bundle_child) - .on(bundle.name == bundle_child.parent) + frappe.qb.from_(bundle_child) .join(latest_posting) .on( (latest_posting.serial_no == bundle_child.serial_no) - & (latest_posting.max_posting_dt == bundle.posting_datetime) + & (latest_posting.max_posting_dt == bundle_child.posting_datetime) ) .select( bundle_child.serial_no, - Max(bundle.creation).as_("max_creation"), + Max(bundle_child.creation).as_("max_creation"), ) .where( - (bundle.is_cancelled == 0) - & (bundle.docstatus == 1) - & (bundle.type_of_transaction == "Inward") + (bundle_child.is_cancelled == 0) + & (bundle_child.docstatus == 1) + & (bundle_child.type_of_transaction == "Inward") & (bundle_child.qty > 0) - & (bundle.item_code == self.sle.item_code) + & (bundle_child.item_code == self.sle.item_code) & (bundle_child.warehouse == self.sle.warehouse) ) .groupby(bundle_child.serial_no) @@ -713,13 +714,11 @@ class SerialNoValuation(DeprecatedSerialNoValuation): latest_creation = get_latest_based_on_creation(latest_posting) query = ( - frappe.qb.from_(bundle) - .join(bundle_child) - .on(bundle.name == bundle_child.parent) + frappe.qb.from_(bundle_child) .join(latest_creation) .on( (latest_creation.serial_no == bundle_child.serial_no) - & (latest_creation.max_creation == bundle.creation) + & (latest_creation.max_creation == bundle_child.creation) ) .select( bundle_child.serial_no, @@ -841,7 +840,8 @@ class BatchNoValuation(DeprecatedBatchNoValuation): Sum(child.qty).as_("total_qty"), ) .where( - (child.warehouse == self.sle.warehouse) + (child.item_code == self.sle.item_code) + & (child.warehouse == self.sle.warehouse) & (child.batch_no.isin(self.batchwise_valuation_batches)) & (child.docstatus == 1) & (child.type_of_transaction.isin(["Inward", "Outward"])) @@ -887,7 +887,8 @@ class BatchNoValuation(DeprecatedBatchNoValuation): Sum(child.qty).as_("qty"), ) .where( - (child.warehouse == self.sle.warehouse) + (child.item_code == self.sle.item_code) + & (child.warehouse == self.sle.warehouse) & (child.batch_no.isin(self.batchwise_valuation_batches)) & (child.docstatus == 1) & (child.type_of_transaction.isin(["Inward", "Outward"])) From 8379b39aaf7a40ec416f64ee74f618d44cdadb05 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Sun, 11 Jan 2026 13:52:46 +0530 Subject: [PATCH 061/106] fix: add validation for direct return --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a7df2d38420..aec346f6783 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -341,6 +341,9 @@ class SalesInvoice(SellingController): self.set_against_income_account() + if self.is_return and not self.return_against and self.timesheets: + frappe.throw(_("Direct return is not allowed for Timesheet.")) + if not self.is_return: self.validate_time_sheets_are_submitted() @@ -348,10 +351,10 @@ class SalesInvoice(SellingController): if self.is_return and self.return_against: for row in self.timesheets: - if row.billing_hours > 0: - row.billing_hours *= -1 - if row.billing_amount > 0: - row.billing_amount *= -1 + if row.billing_hours: + row.billing_hours = -abs(row.billing_hours) + if row.billing_amount: + row.billing_amount = -abs(row.billing_amount) self.update_packing_list() self.set_billing_hours_and_amount() From f7004aa8c31fc845aa1a8f774c490d50de601b21 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Sun, 11 Jan 2026 14:32:15 +0530 Subject: [PATCH 062/106] chore: modify error msg --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index aec346f6783..b9f3bb8e2c5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,7 +902,9 @@ class SalesInvoice(SellingController): if data.time_sheet: status = frappe.db.get_value("Timesheet", data.time_sheet, "status") if status not in ["Submitted", "Payslip", "Partially Billed"]: - frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + frappe.throw( + _("Timesheet {0} cannot be invoiced in its current state").format(data.time_sheet) + ) def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" From c15e96c4607ec80a6ada1760c4b9a87d65e14735 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 11 Jan 2026 18:30:58 +0530 Subject: [PATCH 063/106] refactor: cleanup accounts settings --- .../accounts_settings/accounts_settings.json | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index e68b1e58007..2b741136f96 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -75,11 +75,6 @@ "ignore_account_closing_balance", "use_legacy_controller_for_pcv", "column_break_25", - "tab_break_dpet", - "show_balance_in_coa", - "banking_tab", - "enable_party_matching", - "enable_fuzzy_matching", "reports_tab", "remarks_section", "general_ledger_remarks_length", @@ -92,9 +87,15 @@ "drop_ar_procedures", "legacy_section", "ignore_is_opening_check_for_reporting", - "payment_request_settings", + "tab_break_dpet", + "chart_of_accounts_section", + "show_balance_in_coa", + "banking_section", + "enable_party_matching", + "enable_fuzzy_matching", + "payment_request_section", "create_pr_in_draft_status", - "budget_settings", + "budget_section", "use_legacy_budget_controller" ], "fields": [ @@ -342,7 +343,7 @@ { "fieldname": "tab_break_dpet", "fieldtype": "Tab Break", - "label": "Chart Of Accounts" + "label": "Others" }, { "default": "1", @@ -386,11 +387,6 @@ "fieldtype": "Check", "label": "Show Taxes as Table in Print" }, - { - "fieldname": "banking_tab", - "fieldtype": "Tab Break", - "label": "Banking" - }, { "default": "0", "description": "Auto match and set the Party in Bank Transactions", @@ -466,14 +462,9 @@ "fieldtype": "Check", "label": "Calculate daily depreciation using total days in depreciation period" }, - { - "description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.", - "fieldname": "payment_request_settings", - "fieldtype": "Tab Break", - "label": "Payment Request" - }, { "default": "1", + "description": "Payment Requests made from Sales / Purchase Invoice will be put in Draft explicitly", "fieldname": "create_pr_in_draft_status", "fieldtype": "Check", "label": "Create in Draft Status" @@ -554,11 +545,6 @@ "label": "Role Allowed to Override Stop Action", "options": "Role" }, - { - "fieldname": "budget_settings", - "fieldtype": "Tab Break", - "label": "Budget" - }, { "default": "1", "description": "If enabled, user will be alerted before resetting posting date to current date in relevant transactions", @@ -631,6 +617,26 @@ "fieldname": "default_ageing_range", "fieldtype": "Data", "label": "Default Ageing Range" + }, + { + "fieldname": "chart_of_accounts_section", + "fieldtype": "Section Break", + "label": "Chart Of Accounts" + }, + { + "fieldname": "banking_section", + "fieldtype": "Section Break", + "label": "Banking" + }, + { + "fieldname": "payment_request_section", + "fieldtype": "Section Break", + "label": "Payment Request" + }, + { + "fieldname": "budget_section", + "fieldtype": "Section Break", + "label": "Budget" } ], "grid_page_length": 50, @@ -640,7 +646,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-01-09 17:38:04.250917", + "modified": "2026-01-11 18:30:45.968531", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From e66b1a06f4d2c9e8ac03c4b1396e0281a3f71e1f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 11 Jan 2026 18:38:23 +0530 Subject: [PATCH 064/106] refactor: remove redundant separators in P&L --- .../profit_and_loss_statement/profit_and_loss_statement.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index b4280ca3067..c7585d9efd8 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -123,9 +123,7 @@ def get_report_summary( return [ {"value": net_income, "label": income_label, "datatype": "Currency", "currency": currency}, - {"type": "separator", "value": "-"}, {"value": net_expense, "label": expense_label, "datatype": "Currency", "currency": currency}, - {"type": "separator", "value": "=", "color": "blue"}, { "value": net_profit, "indicator": "Green" if net_profit > 0 else "Red", From cecd07bbf43451415fb971ad13ab3b305d39aa87 Mon Sep 17 00:00:00 2001 From: NaviN <118178330+Navin-S-R@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:57:39 +0530 Subject: [PATCH 065/106] fix(payment reconciliation): handle adhoc payment returns (#51311) * fix(payment reconciliation): handle reverse payments * test: validate payment return gain or loss * chore: typo --- .../payment_reconciliation.py | 42 +++- .../test_payment_reconciliation.py | 204 ++++++++++++++++++ 2 files changed, 241 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e03db121473..b574941721f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -6,7 +6,7 @@ import frappe from frappe import _, msgprint, qb from frappe.model.document import Document from frappe.model.meta import get_field_precision -from frappe.query_builder import Criterion +from frappe.query_builder import Case, Criterion from frappe.query_builder.custom import ConstantColumn from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today @@ -393,6 +393,9 @@ class PaymentReconciliation(Document): inv.outstanding_amount = flt(entry.get("outstanding_amount")) def get_difference_amount(self, payment_entry, invoice, allocated_amount): + party_account_defaults = frappe.get_cached_value( + "Account", self.receivable_payable_account, ["account_type", "account_currency"], as_dict=True + ) allocated_amount_precision = get_field_precision( frappe.get_meta("Payment Reconciliation Allocation").get_field("allocated_amount") ) @@ -400,9 +403,9 @@ class PaymentReconciliation(Document): frappe.get_meta("Payment Reconciliation Allocation").get_field("difference_amount") ) difference_amount = 0 - if frappe.get_cached_value( - "Account", self.receivable_payable_account, "account_currency" - ) != frappe.get_cached_value("Company", self.company, "default_currency"): + if party_account_defaults.get("account_currency") != frappe.get_cached_value( + "Company", self.company, "default_currency" + ): if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get( "exchange_rate", 1 ): @@ -414,7 +417,14 @@ class PaymentReconciliation(Document): invoice.get("exchange_rate", 1) * flt(allocated_amount, allocated_amount_precision), difference_amount_precision, ) - difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate + + # Added If clause to handle return Adhoc payments for account type holders ("Payable") + if party_account_defaults.get("account_type") in ("Payable") and invoice.get( + "invoice_type" + ) in ["Payment Entry", "Journal Entry"]: + difference_amount = allocated_amount_in_inv_rate - allocated_amount_in_ref_rate + else: + difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate return difference_amount @@ -677,6 +687,28 @@ class PaymentReconciliation(Document): ) invoice_exchange_map.update(journals_map) + payment_entries = [ + d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Payment Entry" + ] + payment_entries.extend( + [d.get("reference_name") for d in payments if d.get("reference_type") == "Payment Entry"] + ) + if payment_entries: + pe = frappe.qb.DocType("Payment Entry") + query = ( + frappe.qb.from_(pe) + .select( + pe.name, + Case() + .when(pe.payment_type == "Receive", pe.source_exchange_rate) + .else_(pe.target_exchange_rate) + .as_("exchange_rate"), + ) + .where(pe.name.isin(payment_entries)) + ) + payment_entries = query.run(as_list=1) + invoice_exchange_map.update(payment_entries) + return invoice_exchange_map def validate_allocation(self): diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index b11f20ec90b..3682e7c63a9 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -2340,6 +2340,210 @@ class TestPaymentReconciliation(IntegrationTestCase): frappe.db.set_value("Company", self.company, default_settings) + def test_foreign_currency_reverse_payment_entry_against_payment_entry_for_customer(self): + transaction_date = nowdate() + customer = self.customer3 + amount = 1000 + exchange_rate_at_payment = 100 + exchange_rate_at_reverse_payment = 95 + + # Receive amount from customer - 1,00,000 + pe = self.create_payment_entry(amount=amount, posting_date=transaction_date, customer=customer) + pe.payment_type = "Receive" + pe.paid_from = self.debtors_eur + pe.paid_from_account_currency = "EUR" + pe.source_exchange_rate = exchange_rate_at_payment + pe.paid_amount = amount + pe.received_amount = exchange_rate_at_payment * amount + pe.paid_to = self.cash + pe.paid_to_account_currency = "INR" + pe = pe.save().submit() + + # Pay amount to customer - 95,000 + reverse_pe = self.create_payment_entry( + amount=amount, posting_date=transaction_date, customer=customer + ) + reverse_pe.payment_type = "Pay" + reverse_pe.paid_from = self.cash + reverse_pe.paid_from_account_currency = "INR" + reverse_pe.target_exchange_rate = exchange_rate_at_reverse_payment + reverse_pe.paid_amount = exchange_rate_at_reverse_payment * amount + reverse_pe.received_amount = amount + reverse_pe.paid_to = self.debtors_eur + reverse_pe.paid_to_account_currency = "EUR" + reverse_pe.save().submit() + + # Reconcile payments + pr = self.create_payment_reconciliation() + pr.party = customer + pr.receivable_payable_account = self.debtors_eur + pr.get_unreconciled_entries() + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + self.assertEqual(len(pr.get("invoices")), 1) + self.assertEqual(len(pr.get("payments")), 1) + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Check the difference_amount is a gain of 5000 + self.assertEqual(flt(pr.allocation[0].get("difference_amount")), 5000.0) + pr.reconcile() + + def test_foreign_currency_reverse_payment_entry_against_payment_entry_for_supplier(self): + transaction_date = nowdate() + self.supplier = "_Test Supplier USD" + amount = 1000 + exchange_rate_at_payment = 100 + exchange_rate_at_reverse_payment = 95 + + # Pay amount to supplier - 1,00,000 + pe = self.create_payment_entry(amount=amount, posting_date=transaction_date) + pe.payment_type = "Pay" + pe.party_type = "Supplier" + pe.party = self.supplier + pe.paid_from = self.cash + pe.paid_from_account_currency = "INR" + pe.target_exchange_rate = exchange_rate_at_payment + pe.paid_amount = exchange_rate_at_payment * amount + pe.received_amount = amount + pe.paid_to = self.creditors_usd + pe.paid_to_account_currency = "USD" + pe.save().submit() + + # Receive amount from supplier - 95,000 + reverse_pe = self.create_payment_entry(amount=amount, posting_date=transaction_date) + reverse_pe.payment_type = "Receive" + reverse_pe.party_type = "Supplier" + reverse_pe.party = self.supplier + reverse_pe.paid_from = self.creditors_usd + reverse_pe.paid_from_account_currency = "USD" + reverse_pe.source_exchange_rate = exchange_rate_at_reverse_payment + reverse_pe.paid_amount = amount + reverse_pe.received_amount = exchange_rate_at_reverse_payment * amount + reverse_pe.paid_to = self.cash + reverse_pe.paid_to_account_currency = "INR" + reverse_pe = reverse_pe.save().submit() + + # Reconcile payments + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.get_unreconciled_entries() + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + + self.assertEqual(len(pr.get("invoices")), 1) + self.assertEqual(len(pr.get("payments")), 1) + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Check the difference_amount is a loss of 5000 + self.assertEqual(flt(pr.allocation[0].get("difference_amount")), -5000.0) + pr.reconcile() + + def test_foreign_currency_reverse_journal_entry_against_journal_entry_for_customer(self): + transaction_date = nowdate() + customer = self.customer3 + amount = 1000 + exchange_rate_at_payment = 95 + exchange_rate_at_reverse_payment = 100 + + # Receive amount from customer - 95,000 + je1 = self.create_journal_entry(self.cash, self.debtors_eur, amount, transaction_date) + je1.multi_currency = 1 + je1.accounts[0].exchange_rate = 1 + je1.accounts[0].debit_in_account_currency = exchange_rate_at_payment * amount + je1.accounts[0].debit = exchange_rate_at_payment * amount + je1.accounts[1].party_type = "Customer" + je1.accounts[1].party = customer + je1.accounts[1].exchange_rate = exchange_rate_at_payment + je1.accounts[1].credit_in_account_currency = amount + je1.accounts[1].credit = exchange_rate_at_payment * amount + je1.save() + je1.submit() + + # Pay amount to customer - 1,00,000 + je2 = self.create_journal_entry(self.debtors_eur, self.cash, amount, transaction_date) + je2.multi_currency = 1 + je2.accounts[0].party_type = "Customer" + je2.accounts[0].party = customer + je2.accounts[0].exchange_rate = exchange_rate_at_reverse_payment + je2.accounts[0].debit_in_account_currency = amount + je2.accounts[0].debit = exchange_rate_at_reverse_payment * amount + je2.accounts[1].exchange_rate = 1 + je2.accounts[1].credit_in_account_currency = exchange_rate_at_reverse_payment * amount + je2.accounts[1].credit = exchange_rate_at_reverse_payment * amount + je2.save() + je2.submit() + + # Reconcile payments + pr = self.create_payment_reconciliation() + pr.party = customer + pr.receivable_payable_account = self.debtors_eur + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Check the difference_amount is a loss of 5000 + self.assertEqual(flt(pr.allocation[0].difference_amount), -5000.0) + pr.reconcile() + + def test_foreign_currency_reverse_journal_entry_against_journal_entry_for_supplier(self): + transaction_date = nowdate() + self.supplier = "_Test Supplier USD" + amount = 1000 + exchange_rate_at_payment = 95 + exchange_rate_at_reverse_payment = 100 + + # Pay amount to supplier - 95,000 + je1 = self.create_journal_entry(self.creditors_usd, self.cash, amount, transaction_date) + je1.multi_currency = 1 + je1.accounts[0].party_type = "Supplier" + je1.accounts[0].party = self.supplier + je1.accounts[0].exchange_rate = exchange_rate_at_payment + je1.accounts[0].debit_in_account_currency = amount + je1.accounts[0].debit = exchange_rate_at_payment * amount + je1.accounts[1].exchange_rate = 1 + je1.accounts[1].credit = exchange_rate_at_payment * amount + je1.accounts[1].credit_in_account_currency = exchange_rate_at_payment * amount + je1.save() + je1.submit() + + # Receive amount from supplier - 1,00,000 + je2 = self.create_journal_entry(self.cash, self.creditors_usd, amount, transaction_date) + je2.multi_currency = 1 + je2.accounts[0].exchange_rate = 1 + je2.accounts[0].debit = exchange_rate_at_reverse_payment * amount + je2.accounts[0].debit_in_account_currency = exchange_rate_at_reverse_payment * amount + je2.accounts[1].party_type = "Supplier" + je2.accounts[1].party = self.supplier + je2.accounts[1].exchange_rate = exchange_rate_at_reverse_payment + je2.accounts[1].credit_in_account_currency = amount + je2.accounts[1].credit = exchange_rate_at_reverse_payment * amount + je2.save() + je2.submit() + + # Reconcile payments + pr = self.create_payment_reconciliation() + pr.party_type = "Supplier" + pr.party = self.supplier + pr.receivable_payable_account = self.creditors_usd + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + + invoices = [invoice.as_dict() for invoice in pr.invoices] + payments = [payment.as_dict() for payment in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + # Check the difference_amount is a gain of 5000 + self.assertEqual(flt(pr.allocation[0].difference_amount), 5000.0) + pr.reconcile() + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): From b1704ccef15c4f36cf41197f781f571cec8f0f11 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sun, 11 Jan 2026 19:20:01 +0530 Subject: [PATCH 066/106] fix(asset value adjustment): skip cancelling revaluation journal entry if already cancelled --- .../asset_value_adjustment/asset_value_adjustment.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 982c6f4fc7d..0cf7b63a3f3 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -74,7 +74,7 @@ class AssetValueAdjustment(Document): ) def on_cancel(self): - frappe.get_doc("Journal Entry", self.journal_entry).cancel() + self.cancel_asset_revaluation_entry() self.update_asset() add_asset_activity( self.asset, @@ -167,6 +167,14 @@ class AssetValueAdjustment(Document): if dimension.get("mandatory_for_pl"): debit_entry.update({dimension["fieldname"]: dimension_value}) + def cancel_asset_revaluation_entry(self): + if not self.journal_entry: + return + + revaluation_entry = frappe.get_doc("Journal Entry", self.journal_entry) + if revaluation_entry.docstatus == 1: + revaluation_entry.cancel() + def update_asset(self): asset = self.update_asset_value_after_depreciation() note = self.get_adjustment_note() From 5f00239bbae8e6251244e97c54459f9a5f4400db Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sun, 11 Jan 2026 19:25:22 +0530 Subject: [PATCH 067/106] refactor(journal entry): replace raw SQL with query builder to unlink asset value adjustment --- .../accounts/doctype/journal_entry/journal_entry.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 93b95d7c02e..5b877e84283 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -555,11 +555,12 @@ class JournalEntry(AccountsController): frappe.db.set_value("Journal Entry", self.name, "inter_company_journal_entry_reference", "") def unlink_asset_adjustment_entry(self): - frappe.db.sql( - """ update `tabAsset Value Adjustment` - set journal_entry = null where journal_entry = %s""", - self.name, - ) + AssetValueAdjustment = frappe.qb.DocType("Asset Value Adjustment") + ( + frappe.qb.update(AssetValueAdjustment) + .set(AssetValueAdjustment.journal_entry, None) + .where(AssetValueAdjustment.journal_entry == self.name) + ).run() def validate_party(self): for d in self.get("accounts"): From 500c44e3f51fcfa6b8463de5c73110e3a5b73094 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Sun, 11 Jan 2026 21:30:09 +0530 Subject: [PATCH 068/106] fix: ignore permissions when cancelling revaluation journal entry --- .../doctype/asset_value_adjustment/asset_value_adjustment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 0cf7b63a3f3..3b0a8a6459b 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -173,6 +173,8 @@ class AssetValueAdjustment(Document): revaluation_entry = frappe.get_doc("Journal Entry", self.journal_entry) if revaluation_entry.docstatus == 1: + # Ignore permissions to match Journal Entry submission behavior + revaluation_entry.flags.ignore_permissions = True revaluation_entry.cancel() def update_asset(self): From 1c6bfc9af71dbd6540b6031f6687209adc7aafa4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Jan 2026 13:06:00 +0530 Subject: [PATCH 069/106] fix: desktop icon for share management and banking (#51676) --- erpnext/desktop_icon/banking.json | 4 ++-- erpnext/desktop_icon/share_management.json | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 erpnext/desktop_icon/share_management.json diff --git a/erpnext/desktop_icon/banking.json b/erpnext/desktop_icon/banking.json index 71a36f21da4..ad2c366716c 100644 --- a/erpnext/desktop_icon/banking.json +++ b/erpnext/desktop_icon/banking.json @@ -9,8 +9,8 @@ "idx": 5, "label": "Banking", "link_to": "Banking", - "link_type": "Workspace", - "modified": "2026-01-02 13:03:29.270503", + "link_type": "Workspace Sidebar", + "modified": "2026-01-12 12:29:48.687545", "modified_by": "Administrator", "name": "Banking", "owner": "Administrator", diff --git a/erpnext/desktop_icon/share_management.json b/erpnext/desktop_icon/share_management.json new file mode 100644 index 00000000000..5f251ada959 --- /dev/null +++ b/erpnext/desktop_icon/share_management.json @@ -0,0 +1,20 @@ +{ + "app": "erpnext", + "creation": "2026-01-12 12:31:53.444807", + "docstatus": 0, + "doctype": "Desktop Icon", + "hidden": 0, + "icon_type": "Link", + "idx": 8, + "label": "Share Management", + "link_to": "Share Management", + "link_type": "Workspace Sidebar", + "modified": "2026-01-12 12:31:53.444807", + "modified_by": "Administrator", + "name": "Share Management", + "owner": "Administrator", + "parent_icon": "Accounts", + "restrict_removal": 0, + "roles": [], + "standard": 1 +} From 671610db1e3307b53d73ea4e1e9c4dfd8417e048 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 12 Jan 2026 13:01:54 +0530 Subject: [PATCH 070/106] fix(asset): properly reset purchase reference and item fields --- erpnext/assets/doctype/asset/asset.js | 78 +++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index b8a8dd6db10..dc65883eb15 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -231,28 +231,64 @@ frappe.ui.form.on("Asset", { }, toggle_reference_doc: function (frm) { - if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { - frm.set_df_property("purchase_invoice", "read_only", 1); - frm.set_df_property("purchase_receipt", "read_only", 1); - } else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { - frm.toggle_reqd("purchase_receipt", 0); - frm.toggle_reqd("purchase_invoice", 0); - frm.set_value("purchase_receipt", ""); - frm.set_value("purchase_invoice", ""); - } else if (frm.doc.purchase_receipt) { - // if purchase receipt link is set then set PI disabled - frm.toggle_reqd("purchase_invoice", 0); - frm.set_df_property("purchase_invoice", "read_only", 1); - } else if (frm.doc.purchase_invoice) { - // if purchase invoice link is set then set PR disabled - frm.toggle_reqd("purchase_receipt", 0); - frm.set_df_property("purchase_receipt", "read_only", 1); - } else { - frm.toggle_reqd("purchase_receipt", 1); - frm.set_df_property("purchase_receipt", "read_only", 0); - frm.toggle_reqd("purchase_invoice", 1); - frm.set_df_property("purchase_invoice", "read_only", 0); + const is_submitted = frm.doc.docstatus === 1; + const is_special_asset = frm.doc.is_existing_asset || frm.doc.is_composite_asset; + + const clear_field = (field) => { + if (frm.doc[field]) { + frm.set_value(field, ""); + } + }; + + ["purchase_receipt", "purchase_receipt_item", "purchase_invoice", "purchase_invoice_item"].forEach( + (field) => { + frm.toggle_reqd(field, 0); + frm.set_df_property(field, "read_only", 0); + } + ); + + if (is_submitted) { + [ + "purchase_receipt", + "purchase_receipt_item", + "purchase_invoice", + "purchase_invoice_item", + ].forEach((field) => { + frm.set_df_property(field, "read_only", 1); + }); + return; } + + if (is_special_asset) { + clear_field("purchase_receipt"); + clear_field("purchase_receipt_item"); + clear_field("purchase_invoice"); + clear_field("purchase_invoice_item"); + return; + } + + if (frm.doc.purchase_receipt) { + frm.toggle_reqd("purchase_receipt_item", 1); + + ["purchase_invoice", "purchase_invoice_item"].forEach((field) => { + clear_field(field); + frm.set_df_property(field, "read_only", 1); + }); + return; + } + + if (frm.doc.purchase_invoice) { + frm.toggle_reqd("purchase_invoice_item", 1); + + ["purchase_receipt", "purchase_receipt_item"].forEach((field) => { + clear_field(field); + frm.set_df_property(field, "read_only", 1); + }); + return; + } + + frm.toggle_reqd("purchase_receipt", 1); + frm.toggle_reqd("purchase_invoice", 1); }, make_journal_entry: function (frm) { From 8434efd11b572f78427e47f5c363c98d97d23af3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Jan 2026 13:22:28 +0530 Subject: [PATCH 071/106] fix: removed duplicate sidebar link: Tree of procedures (#51679) --- erpnext/workspace_sidebar/quality.json | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/erpnext/workspace_sidebar/quality.json b/erpnext/workspace_sidebar/quality.json index 346fa15b3e2..0e9d20d2a91 100644 --- a/erpnext/workspace_sidebar/quality.json +++ b/erpnext/workspace_sidebar/quality.json @@ -124,17 +124,6 @@ "show_arrow": 0, "type": "Link" }, - { - "child": 1, - "collapsible": 1, - "indent": 0, - "keep_closed": 0, - "label": "Tree of Procedures", - "link_to": "Quality Procedure", - "link_type": "DocType", - "show_arrow": 0, - "type": "Link" - }, { "child": 1, "collapsible": 1, @@ -158,7 +147,7 @@ "type": "Link" } ], - "modified": "2026-01-10 00:06:13.140271", + "modified": "2026-01-12 12:59:09.607272", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", From 02a9c54b7489f1b6213a60d94e60e0913319653d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Jan 2026 14:08:30 +0530 Subject: [PATCH 072/106] fix: Subcontracting settings link should point to subcontracting tab of Buying Settings (#51680) --- erpnext/workspace_sidebar/subcontracting.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/workspace_sidebar/subcontracting.json b/erpnext/workspace_sidebar/subcontracting.json index 4cd0bc9003c..31618a4a860 100644 --- a/erpnext/workspace_sidebar/subcontracting.json +++ b/erpnext/workspace_sidebar/subcontracting.json @@ -192,11 +192,12 @@ "label": "Settings", "link_to": "Buying Settings", "link_type": "DocType", + "navigate_to_tab": "subcontract", "show_arrow": 0, "type": "Link" } ], - "modified": "2026-01-10 00:06:12.744529", + "modified": "2026-01-12 13:12:47.927785", "modified_by": "Administrator", "module": "Buying", "name": "Subcontracting", From b5e0b543f771ddc32bd21e499f7c312cea407e2c Mon Sep 17 00:00:00 2001 From: Jacob Salvi <121790568+jacob-salvi@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:48:28 +0530 Subject: [PATCH 073/106] chore: new icons share-management (#51682) --- erpnext/public/icons/desktop_icons/solid/share_management.svg | 4 ++++ .../public/icons/desktop_icons/subtle/share_management.svg | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 erpnext/public/icons/desktop_icons/solid/share_management.svg create mode 100644 erpnext/public/icons/desktop_icons/subtle/share_management.svg diff --git a/erpnext/public/icons/desktop_icons/solid/share_management.svg b/erpnext/public/icons/desktop_icons/solid/share_management.svg new file mode 100644 index 00000000000..bea49a5c5f0 --- /dev/null +++ b/erpnext/public/icons/desktop_icons/solid/share_management.svg @@ -0,0 +1,4 @@ + + + + diff --git a/erpnext/public/icons/desktop_icons/subtle/share_management.svg b/erpnext/public/icons/desktop_icons/subtle/share_management.svg new file mode 100644 index 00000000000..c9bf99f0821 --- /dev/null +++ b/erpnext/public/icons/desktop_icons/subtle/share_management.svg @@ -0,0 +1,4 @@ + + + + From 784e338be4b1fa6d5d204b7a0bad2b71dbd25fce Mon Sep 17 00:00:00 2001 From: Pandiyan5273 Date: Mon, 12 Jan 2026 13:49:19 +0530 Subject: [PATCH 074/106] test(stock-entry): manufacture entry without work order --- .../doctype/stock_entry/test_stock_entry.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 935e86dcdc6..cf1b12a8f28 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -2250,6 +2250,33 @@ class TestStockEntry(IntegrationTestCase): material_request.reload() self.assertEqual(material_request.transfer_status, "Completed") + def test_manufacture_entry_without_wo(self): + from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom + + fg_item = make_item("_Mobiles", properties={"is_stock_item": 1}).name + rm_item1 = make_item("_Temper Glass", properties={"is_stock_item": 1}).name + rm_item2 = make_item("_Battery", properties={"is_stock_item": 1}).name + warehouse = "_Test Warehouse - _TC" + make_stock_entry(item_code=rm_item1, target=warehouse, qty=5, purpose="Material Receipt") + make_stock_entry(item_code=rm_item2, target=warehouse, qty=5, purpose="Material Receipt") + + bom_no = make_bom(item=fg_item, raw_materials=[rm_item1, rm_item2]).name + se = make_stock_entry(item_code=fg_item, qty=1, purpose="Manufacture", do_not_save=True) + se.from_bom = 1 + se.use_multi_level_bom = 1 + se.bom_no = bom_no + se.fg_completed_qty = 1 + se.from_warehouse = warehouse + se.to_warehouse = warehouse + + se.get_items() + rm_items = {d.item_code: d.qty for d in se.items if d.item_code != fg_item} + self.assertEqual(rm_items[rm_item1], 1) + self.assertEqual(rm_items[rm_item2], 1) + se.calculate_rate_and_amount() + se.save() + se.submit() + def make_serialized_item(self, **args): args = frappe._dict(args) From adec5307922aca1ea8dca60aa90dd587dbb99527 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 12 Jan 2026 16:12:46 +0530 Subject: [PATCH 075/106] ci: ignore server tests on svg changes (#51687) --- .github/workflows/server-tests-mariadb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index 036f587dbf9..2bfe5e7065d 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -7,6 +7,7 @@ on: paths-ignore: - '**.js' - '**.css' + - '**.svg' - '**.md' - '**.html' - 'crowdin.yml' From ccb4b1fbc4398fbe96fe2fc7babfe31821ed5d13 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 12 Jan 2026 16:13:46 +0530 Subject: [PATCH 076/106] fix: delete outdated desktop icon & sidebar (#51685) --- erpnext/patches.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dab56f9f552..c82e257dc6c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -456,4 +456,5 @@ erpnext.patches.v16_0.update_tax_withholding_field_in_payment_entry erpnext.patches.v16_0.migrate_tax_withholding_data erpnext.patches.v16_0.update_corrected_cancelled_status erpnext.patches.v16_0.fix_barcode_typo -erpnext.patches.v16_0.set_post_change_gl_entries_on_pos_settings \ No newline at end of file +erpnext.patches.v16_0.set_post_change_gl_entries_on_pos_settings +execute:frappe.delete_doc_if_exists("Workspace Sidebar", "Opening & Closing") From ca97f340923fb53cf1c078bc349cbdc170f5c8bc Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 12 Jan 2026 16:36:34 +0530 Subject: [PATCH 077/106] fix: use new_asset instead of asset_doc when checking values after splitting --- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index f81b06129cb..46edf701c74 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -817,8 +817,8 @@ class TestAsset(AssetSetup): ) # check new asset values after splitting - self.assertEqual(asset_doc.asset_quantity, 5) - self.assertEqual(asset_doc.net_purchase_amount, 500000) + self.assertEqual(new_asset.asset_quantity, 5) + self.assertEqual(new_asset.net_purchase_amount, 500000) self.assertEqual( new_asset_depr_schedule.depreciation_schedule[0].get("depreciation_amount"), 41666.66 ) From eeb6d0e9bf1e6a2d202996bf80a5a37194fc9a18 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 12 Jan 2026 16:45:04 +0530 Subject: [PATCH 078/106] fix: remove the redundant purchase receipt submit --- erpnext/assets/doctype/asset/test_asset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 46edf701c74..600f6126f61 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -734,7 +734,7 @@ class TestAsset(AssetSetup): asset_depr_schedule_before_sale.depreciation_schedule[0].get("depreciation_amount"), 83333.33 ) - # make a partial sales againt the asset + # make a partial sales against the asset si = make_sales_invoice( asset=asset.name, item_code="Macbook Pro", company="_Test Company", sell_qty=5 ) @@ -776,7 +776,6 @@ class TestAsset(AssetSetup): location=asset_location, supplier="_Test Supplier", ) - pr.submit() asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name") asset_doc = frappe.get_doc("Asset", asset) From 73b038084b43a02f614f8148eb2353f4cf78c4fa Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Mon, 12 Jan 2026 18:18:22 +0530 Subject: [PATCH 079/106] fix: prevent manual cancellation of the linked Revaluation Journal Entry --- .../doctype/journal_entry/journal_entry.py | 18 ++++++++++++++++++ .../asset_value_adjustment.py | 1 + 2 files changed, 19 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 5b877e84283..1c10208378a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -184,6 +184,9 @@ class JournalEntry(AccountsController): else: return self._submit() + def before_cancel(self): + pass + def cancel(self): if len(self.accounts) > 100: queue_submission(self, "_cancel") @@ -305,6 +308,7 @@ class JournalEntry(AccountsController): ) self.make_gl_entries(1) JournalTaxWithholding(self).on_cancel() + self.has_asset_adjustment_entry() self.unlink_advance_entry_reference() self.unlink_asset_reference() self.unlink_inter_company_jv() @@ -554,6 +558,20 @@ class JournalEntry(AccountsController): ) frappe.db.set_value("Journal Entry", self.name, "inter_company_journal_entry_reference", "") + def has_asset_adjustment_entry(self): + if self.flags.get("via_asset_value_adjustment"): + return + + asset_value_adjustment = frappe.db.get_value( + "Asset Value Adjustment", {"docstatus": 1, "journal_entry": self.name}, "name" + ) + if asset_value_adjustment: + frappe.throw( + _( + "Cannot cancel this document as it is linked with the submitted Asset Value Adjustment {0}. Please cancel the Asset Value Adjustment to continue." + ).format(frappe.utils.get_link_to_form("Asset Value Adjustment", asset_value_adjustment)) + ) + def unlink_asset_adjustment_entry(self): AssetValueAdjustment = frappe.qb.DocType("Asset Value Adjustment") ( diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 3b0a8a6459b..c033cda05b5 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -175,6 +175,7 @@ class AssetValueAdjustment(Document): if revaluation_entry.docstatus == 1: # Ignore permissions to match Journal Entry submission behavior revaluation_entry.flags.ignore_permissions = True + revaluation_entry.flags.via_asset_value_adjustment = True revaluation_entry.cancel() def update_asset(self): From 3bc58fb46fd7490134f87e32bf2b44d2ad0a4703 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Jan 2026 19:20:27 +0530 Subject: [PATCH 080/106] fix: Redirect to Desktop after signup (#51696) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9d7b7a5ed70..3b0f338cf84 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -8,7 +8,7 @@ app_email = "hello@frappe.io" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" -app_home = "/app/home" +app_home = "/desk" add_to_apps_screen = [ { From bb307dec0aff736a9ed582b479c3e6f9c9fb29b0 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 13 Jan 2026 11:04:17 +0530 Subject: [PATCH 081/106] chore: add v14 remove when EOL reached --- .github/workflows/initiate_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml index 78b3cabe8a5..f57d46b354d 100644 --- a/.github/workflows/initiate_release.yml +++ b/.github/workflows/initiate_release.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - version: ["15", "16"] + version: ["14", "15", "16"] steps: - uses: octokit/request-action@v2.x From 6ec41fa47e097cc7b50da40fada797d44b995fd3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 13 Jan 2026 12:15:21 +0530 Subject: [PATCH 082/106] build: Bump dev version --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6ecfb8df807..7e72b2b585d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -6,7 +6,7 @@ import frappe from frappe.model.document import Document from frappe.utils.user import is_website_user -__version__ = "16.0.0-dev" +__version__ = "17.0.0-dev" def get_default_company(user=None): From d65cd605a1bbc85fd0935c40c7a2c19b9016d736 Mon Sep 17 00:00:00 2001 From: Navin-S-R Date: Tue, 13 Jan 2026 12:16:52 +0530 Subject: [PATCH 083/106] fix: move validation to before_cancel --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 1c10208378a..ce435482ae5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -185,7 +185,7 @@ class JournalEntry(AccountsController): return self._submit() def before_cancel(self): - pass + self.has_asset_adjustment_entry() def cancel(self): if len(self.accounts) > 100: @@ -308,7 +308,6 @@ class JournalEntry(AccountsController): ) self.make_gl_entries(1) JournalTaxWithholding(self).on_cancel() - self.has_asset_adjustment_entry() self.unlink_advance_entry_reference() self.unlink_asset_reference() self.unlink_inter_company_jv() From 9de3b0722390beb73067f1c594c8f2f3ec0369ab Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 13 Jan 2026 17:02:39 +0530 Subject: [PATCH 084/106] fix: stock module not opened when no warehouses --- .../stock_value_by_item_group/stock_value_by_item_group.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.py b/erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.py index 68f64619e7b..e2d0b1e41e8 100644 --- a/erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.py +++ b/erpnext/stock/dashboard_chart_source/stock_value_by_item_group/stock_value_by_item_group.py @@ -53,12 +53,14 @@ def get_stock_value_by_item_group(company): .inner_join(item_doctype) .on(doctype.item_code == item_doctype.name) .select(item_doctype.item_group, stock_value.as_("stock_value")) - .where(doctype.warehouse.isin(warehouses)) .groupby(item_doctype.item_group) .orderby(stock_value, order=frappe.qb.desc) .limit(10) ) + if warehouses: + query = query.where(doctype.warehouse.isin(warehouses)) + results = query.run(as_dict=True) labels = [] From 0da98e6769d5da9b62b27010282e159c8ed6d7f0 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 13 Jan 2026 13:56:50 +0530 Subject: [PATCH 085/106] ci: patch test for v16 branch --- .github/workflows/patch.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index d275899ae39..31e2069aea0 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -114,8 +114,8 @@ jobs: jq 'del(.install_apps)' ~/frappe-bench/sites/test_site/site_config.json > tmp.json mv tmp.json ~/frappe-bench/sites/test_site/site_config.json - wget https://erpnext.com/files/v13-erpnext.sql.gz - bench --site test_site --force restore ~/frappe-bench/v13-erpnext.sql.gz + wget https://frappe.io/files/erpnext-v14.sql.gz + bench --site test_site --force restore ~/frappe-bench/erpnext-v14.sql.gz git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git @@ -143,8 +143,8 @@ jobs: bench --site test_site migrate } - update_to_version 14 3.11 update_to_version 15 3.13 + update_to_version 16 3.14 echo "Updating to latest version" git -C "apps/frappe" fetch --depth 1 upstream "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" From 5e2c7a08d3ebcfc1cb074fc046e0ec4e9aa4a531 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 13 Jan 2026 19:57:11 +0530 Subject: [PATCH 086/106] revert: make CI not run on .github change --- .github/workflows/patch.yml | 1 - .github/workflows/server-tests-mariadb.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 31e2069aea0..96816e59dee 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -11,7 +11,6 @@ on: - 'crowdin.yml' - '.coderabbit.yml' - '.mergify.yml' - - '.github/**' workflow_dispatch: permissions: diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index c6fb19a76b2..2bfe5e7065d 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -13,7 +13,6 @@ on: - 'crowdin.yml' - '.coderabbit.yml' - '.mergify.yml' - - '.github/**' schedule: # Run everday at midnight UTC / 5:30 IST - cron: "0 0 * * *" From b6312bca9c8d163c8771e73a60545d226d3e559e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 13 Jan 2026 17:00:45 +0530 Subject: [PATCH 087/106] fix: valuation rate for non batchwise valuation --- .../purchase_receipt/test_purchase_receipt.py | 148 ++++++++++++++++++ .../serial_and_batch_bundle.py | 21 ++- 2 files changed, 158 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6ee22025565..b26bab3d1c4 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4849,6 +4849,154 @@ class TestPurchaseReceipt(IntegrationTestCase): self.assertEqual(return_entry.items[0].qty, -2) self.assertEqual(return_entry.items[0].rejected_qty, 0) # 3-3=0 + def test_do_not_use_batchwise_valuation_with_fifo(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = make_item( + "Test Item Do Not Use Batchwise Valuation with FIFO", + { + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "BN-TESTDNUBVWF-.#####", + "valuation_method": "FIFO", + }, + ).name + + doc = frappe.new_doc("Batch") + doc.update( + { + "batch_id": "BN-TESTDNUBVWF-00001", + "item": item_code, + } + ).insert() + + doc.db_set("use_batchwise_valuation", 0) + doc.reload() + + self.assertTrue(doc.use_batchwise_valuation == 0) + + doc = frappe.new_doc("Batch") + doc.update( + { + "batch_id": "BN-TESTDNUBVWF-00002", + "item": item_code, + } + ).insert() + + self.assertTrue(doc.use_batchwise_valuation == 1) + + warehouse = "_Test Warehouse - _TC" + make_stock_entry( + item_code=item_code, + qty=10, + rate=100, + target=warehouse, + batch_no="BN-TESTDNUBVWF-00001", + use_serial_batch_fields=1, + ) + + se1 = make_stock_entry( + item_code=item_code, + qty=10, + rate=200, + target=warehouse, + batch_no="BN-TESTDNUBVWF-00001", + use_serial_batch_fields=1, + ) + + stock_queue = frappe.db.get_value( + "Stock Ledger Entry", + { + "item_code": item_code, + "warehouse": warehouse, + "is_cancelled": 0, + "voucher_type": "Stock Entry", + "voucher_no": se1.name, + }, + "stock_queue", + ) + + stock_queue = frappe.parse_json(stock_queue) + + self.assertEqual(stock_queue, [[10, 100.0], [10, 200.0]]) + + se2 = make_stock_entry( + item_code=item_code, + qty=10, + rate=2, + target=warehouse, + batch_no="BN-TESTDNUBVWF-00002", + use_serial_batch_fields=1, + ) + + stock_queue = frappe.db.get_value( + "Stock Ledger Entry", + { + "item_code": item_code, + "warehouse": warehouse, + "is_cancelled": 0, + "voucher_type": "Stock Entry", + "voucher_no": se2.name, + }, + "stock_queue", + ) + + stock_queue = frappe.parse_json(stock_queue) + self.assertEqual(stock_queue, [[10, 100.0], [10, 200.0]]) + + se3 = make_stock_entry( + item_code=item_code, + qty=20, + source=warehouse, + batch_no="BN-TESTDNUBVWF-00001", + use_serial_batch_fields=1, + ) + + ste_details = frappe.db.get_value( + "Stock Ledger Entry", + { + "item_code": item_code, + "warehouse": warehouse, + "is_cancelled": 0, + "voucher_type": "Stock Entry", + "voucher_no": se3.name, + }, + ["stock_queue", "stock_value_difference"], + as_dict=1, + ) + + stock_queue = frappe.parse_json(ste_details.stock_queue) + self.assertEqual(stock_queue, []) + self.assertEqual(ste_details.stock_value_difference, 3000 * -1) + + se4 = make_stock_entry( + item_code=item_code, + qty=20, + rate=0, + target=warehouse, + batch_no="BN-TESTDNUBVWF-00001", + use_serial_batch_fields=1, + do_not_submit=1, + ) + + se4.items[0].basic_rate = 0.0 + se4.items[0].allow_zero_valuation_rate = 1 + se4.submit() + + stock_queue = frappe.db.get_value( + "Stock Ledger Entry", + { + "item_code": item_code, + "warehouse": warehouse, + "is_cancelled": 0, + "voucher_type": "Stock Entry", + "voucher_no": se4.name, + }, + "stock_queue", + ) + + self.assertEqual(frappe.parse_json(stock_queue), [[20, 0.0]]) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 8b50e1666ef..458df2fec38 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -712,17 +712,16 @@ class SerialandBatchBundle(Document): is_packed_item = True stock_queue = [] - batches = [] - if prev_sle and prev_sle.stock_queue: - batches = frappe.get_all( - "Batch", - filters={ - "name": ("in", [d.batch_no for d in self.entries if d.batch_no]), - "use_batchwise_valuation": 0, - }, - pluck="name", - ) + batches = frappe.get_all( + "Batch", + filters={ + "name": ("in", [d.batch_no for d in self.entries if d.batch_no]), + "use_batchwise_valuation": 0, + }, + pluck="name", + ) + if prev_sle and prev_sle.stock_queue and parse_json(prev_sle.stock_queue): if batches and valuation_method == "FIFO": stock_queue = parse_json(prev_sle.stock_queue) @@ -749,7 +748,7 @@ class SerialandBatchBundle(Document): if d.qty: d.stock_value_difference = flt(d.qty) * d.incoming_rate - if stock_queue and valuation_method == "FIFO" and d.batch_no in batches: + if valuation_method == "FIFO" and d.batch_no in batches and d.incoming_rate is not None: stock_queue.append([d.qty, d.incoming_rate]) d.stock_queue = json.dumps(stock_queue) From 8b445e04e57ecfb25a22e767c455c3cabe173cb6 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Wed, 14 Jan 2026 01:34:20 +0530 Subject: [PATCH 088/106] fix(transaction.js): use flt instead of cint for plc_conversion_rate --- erpnext/public/js/controllers/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index a77bd824f2f..cd9341dd9c8 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1530,8 +1530,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } else if ( this.frm.doc.price_list_currency === this.frm.doc.currency && this.frm.doc.plc_conversion_rate && - cint(this.frm.doc.plc_conversion_rate) != 1 && - cint(this.frm.doc.plc_conversion_rate) != cint(this.frm.doc.conversion_rate) + flt(this.frm.doc.plc_conversion_rate) != 1 && + flt(this.frm.doc.plc_conversion_rate) != flt(this.frm.doc.conversion_rate) ) { this.frm.set_value("conversion_rate", this.frm.doc.plc_conversion_rate); } From 6d3f6d73d08b1f96eb65737d70511bb29f98fd56 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 14 Jan 2026 10:22:59 +0530 Subject: [PATCH 089/106] fix: add uom js error --- erpnext/setup/doctype/uom/uom.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/setup/doctype/uom/uom.js b/erpnext/setup/doctype/uom/uom.js index 273e30fd197..eb61f63f034 100644 --- a/erpnext/setup/doctype/uom/uom.js +++ b/erpnext/setup/doctype/uom/uom.js @@ -1,7 +1,2 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - -//--------- ONLOAD ------------- -cur_frm.cscript.onload = function (doc, cdt, cdn) {}; - -cur_frm.cscript.refresh = function (doc, cdt, cdn) {}; From 1d35e2b261a906e64698d8ef23ae855c229e5539 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 14 Jan 2026 11:07:06 +0530 Subject: [PATCH 090/106] build: bump required frappe version (#51738) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e450d675f02..c4029137907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ skip_namespaces = [ ] [tool.bench.frappe-dependencies] -frappe = ">=16.0.0-dev,<17.0.0" +frappe = ">=17.0.0-dev,<18.0.0" [tool.ruff] line-length = 110 From 7ef8c81caf13452099ef8cd397f7f4e198284dfd Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:36:22 +0100 Subject: [PATCH 091/106] fix: docs_path --- erpnext/stock/doctype/item/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 56cdb427acd..ad180a7f2f8 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -1063,7 +1063,7 @@ frappe.tour["Item"] = [ fieldname: "valuation_rate", title: "Valuation Rate", description: __( - "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." + "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." ), }, { From 050ea96cc68e746fbc534c87d8ab6b6fc7f144e8 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 15 Jan 2026 12:33:04 +0530 Subject: [PATCH 092/106] chore(hooks): develop_version bump --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 3b0f338cf84..6a4fcc1d619 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -20,7 +20,7 @@ add_to_apps_screen = [ } ] -develop_version = "15.x.x-develop" +develop_version = "17.x.x-develop" app_include_js = "erpnext.bundle.js" app_include_css = "erpnext.bundle.css" From 1db9ce205f8337c7008d1708bbda8afae641c820 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 15 Jan 2026 17:04:49 +0530 Subject: [PATCH 093/106] fix: Show non-SLE vouchers with GL entries in Stock vs Account Value Comparison report --- .../stock_and_account_value_comparison.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 172e0fa6a41..8afe1d72e27 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -42,9 +42,37 @@ def get_data(report_filters): gl_data = voucher_wise_gl_data.get(key) or {} d.account_value = gl_data.get("account_value", 0) d.difference_value = d.stock_value - d.account_value + d.ledger_type = "Stock Ledger Entry" if abs(d.difference_value) > 0.1: data.append(d) + if key in voucher_wise_gl_data: + del voucher_wise_gl_data[key] + + if voucher_wise_gl_data: + data += get_gl_ledgers_with_no_stock_ledger_entries(voucher_wise_gl_data) + + return data + + +def get_gl_ledgers_with_no_stock_ledger_entries(voucher_wise_gl_data): + data = [] + + for key in voucher_wise_gl_data: + gl_data = voucher_wise_gl_data.get(key) or {} + data.append( + { + "name": gl_data.get("name"), + "ledger_type": "GL Entry", + "voucher_type": gl_data.get("voucher_type"), + "voucher_no": gl_data.get("voucher_no"), + "posting_date": gl_data.get("posting_date"), + "stock_value": 0, + "account_value": gl_data.get("account_value", 0), + "difference_value": gl_data.get("account_value", 0) * -1, + } + ) + return data @@ -88,6 +116,7 @@ def get_gl_data(report_filters, filters): "name", "voucher_type", "voucher_no", + "posting_date", { "SUB": [{"SUM": "debit_in_account_currency"}, {"SUM": "credit_in_account_currency"}], "as": "account_value", @@ -109,10 +138,15 @@ def get_columns(filters): { "label": _("Stock Ledger ID"), "fieldname": "name", - "fieldtype": "Link", - "options": "Stock Ledger Entry", + "fieldtype": "Dynamic Link", + "options": "ledger_type", "width": "80", }, + { + "label": _("Ledger Type"), + "fieldname": "ledger_type", + "fieldtype": "Data", + }, {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date"}, {"label": _("Posting Time"), "fieldname": "posting_time", "fieldtype": "Time"}, {"label": _("Voucher Type"), "fieldname": "voucher_type", "width": "110"}, From 8d188cd32b74b839def62e1200f4573a404cab35 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 12 Jan 2026 18:28:34 +0530 Subject: [PATCH 094/106] refactor: sample retention stock entry --- .../stock/doctype/stock_entry/stock_entry.js | 6 +-- .../stock/doctype/stock_entry/stock_entry.py | 52 +++++++++++-------- erpnext/stock/serial_batch_bundle.py | 2 +- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index d106d097256..b62dce837f1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -568,10 +568,6 @@ frappe.ui.form.on("Stock Entry", { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); - } else { - frappe.msgprint( - __("Retention Stock Entry already created or Sample Quantity not provided") - ); } }, }); @@ -1054,7 +1050,7 @@ frappe.ui.form.on("Stock Entry Detail", { var validate_sample_quantity = function (frm, cdt, cdn) { var d = locals[cdt][cdn]; - if (d.sample_quantity && frm.doc.purpose == "Material Receipt") { + if (d.sample_quantity && d.transfer_qty && frm.doc.purpose == "Material Receipt") { frappe.call({ method: "erpnext.stock.doctype.stock_entry.stock_entry.validate_sample_quantity", args: { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8c200715397..9afb98b61d7 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3408,10 +3408,10 @@ class StockEntry(StockController, SubcontractingInwardController): @frappe.whitelist() def move_sample_to_retention_warehouse(company, items): - from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import ( - get_batch_from_bundle, + from erpnext.stock.serial_batch_bundle import ( + SerialBatchCreation, + get_batch_nos, ) - from erpnext.stock.serial_batch_bundle import SerialBatchCreation if isinstance(items, str): items = json.loads(items) @@ -3422,38 +3422,46 @@ def move_sample_to_retention_warehouse(company, items): stock_entry.set_stock_entry_type() for item in items: if item.get("sample_quantity") and item.get("serial_and_batch_bundle"): - batch_no = get_batch_from_bundle(item.get("serial_and_batch_bundle")) - sample_quantity = validate_sample_quantity( - item.get("item_code"), - item.get("sample_quantity"), - item.get("transfer_qty") or item.get("qty"), - batch_no, + warehouse = item.get("t_warehouse") or item.get("warehouse") + total_qty = 0 + cls_obj = SerialBatchCreation( + { + "type_of_transaction": "Outward", + "serial_and_batch_bundle": item.get("serial_and_batch_bundle"), + "item_code": item.get("item_code"), + "warehouse": warehouse, + "do_not_save": True, + } ) - - if sample_quantity: - cls_obj = SerialBatchCreation( - { - "type_of_transaction": "Outward", - "serial_and_batch_bundle": item.get("serial_and_batch_bundle"), - "item_code": item.get("item_code"), - "warehouse": item.get("t_warehouse"), - } + sabb = cls_obj.duplicate_package() + batches = get_batch_nos(item.get("serial_and_batch_bundle")) + for batch_no in batches.keys(): + sample_quantity = validate_sample_quantity( + item.get("item_code"), + item.get("sample_quantity"), + item.get("transfer_qty") or item.get("qty"), + batch_no, ) - cls_obj.duplicate_package() + if sample_quantity: + total_qty += sample_quantity + sabe = next(item for item in sabb.entries if item.batch_no == batch_no) + sabe.qty = -1 * sample_quantity + if total_qty: + sabb.save() stock_entry.append( "items", { "item_code": item.get("item_code"), - "s_warehouse": item.get("t_warehouse"), + "s_warehouse": warehouse, "t_warehouse": retention_warehouse, - "qty": item.get("sample_quantity"), + "qty": total_qty, "basic_rate": item.get("valuation_rate"), "uom": item.get("uom"), "stock_uom": item.get("stock_uom"), "conversion_factor": item.get("conversion_factor") or 1.0, - "serial_and_batch_bundle": cls_obj.serial_and_batch_bundle, + "serial_and_batch_bundle": sabb.name, }, ) if stock_entry.get("items"): diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 13d7b100855..9cb4c25d782 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1116,7 +1116,7 @@ class SerialBatchCreation: id = self.serial_and_batch_bundle package = frappe.get_doc("Serial and Batch Bundle", id) - new_package = frappe.copy_doc(package) + new_package = frappe.copy_doc(package, ignore_no_copy=False) if self.get("returned_serial_nos"): self.remove_returned_serial_nos(new_package) From b54067e04d171d6c6b8d91760efa53a5270f4f93 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 12 Jan 2026 18:56:14 +0530 Subject: [PATCH 095/106] fix: remove already transferred batch --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 9afb98b61d7..7d5f756e49f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3443,10 +3443,12 @@ def move_sample_to_retention_warehouse(company, items): batch_no, ) + sabe = next(item for item in sabb.entries if item.batch_no == batch_no) if sample_quantity: total_qty += sample_quantity - sabe = next(item for item in sabb.entries if item.batch_no == batch_no) sabe.qty = -1 * sample_quantity + else: + sabb.entries.remove(sabe) if total_qty: sabb.save() From 3d0f6494115331ed4052e025da74eb212ab3b84c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 13 Jan 2026 12:28:56 +0530 Subject: [PATCH 096/106] feat: support for serial item --- erpnext/stock/doctype/stock_entry/stock_entry.py | 12 +++++++++++- erpnext/stock/serial_batch_bundle.py | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 7d5f756e49f..e92ab143588 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3435,6 +3435,7 @@ def move_sample_to_retention_warehouse(company, items): ) sabb = cls_obj.duplicate_package() batches = get_batch_nos(item.get("serial_and_batch_bundle")) + sabe_list = [] for batch_no in batches.keys(): sample_quantity = validate_sample_quantity( item.get("item_code"), @@ -3446,11 +3447,20 @@ def move_sample_to_retention_warehouse(company, items): sabe = next(item for item in sabb.entries if item.batch_no == batch_no) if sample_quantity: total_qty += sample_quantity - sabe.qty = -1 * sample_quantity + if sabb.has_serial_no: + sabe_list.extend( + [entry for entry in sabb.entries if entry.batch_no == batch_no][ + : int(sample_quantity) + ] + ) + else: + sabe.qty = -1 * sample_quantity else: sabb.entries.remove(sabe) if total_qty: + if sabe_list: + sabb.entries = sabe_list sabb.save() stock_entry.append( "items", diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 9cb4c25d782..50603eb609d 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -989,9 +989,10 @@ def get_batch_nos(serial_and_batch_bundle): entries = frappe.get_all( "Serial and Batch Entry", - fields=["batch_no", "qty", "name"], + fields=["batch_no", {"SUM": "qty", "as": "qty"}], filters={"parent": serial_and_batch_bundle, "batch_no": ("is", "set")}, order_by="idx", + group_by="batch_no", ) if not entries: From c5b0787de6c020d8baa5f0eb080a943562360317 Mon Sep 17 00:00:00 2001 From: Jatin3128 <140256508+Jatin3128@users.noreply.github.com> Date: Fri, 16 Jan 2026 12:06:22 +0530 Subject: [PATCH 097/106] Merge pull request #51673 from Jatin3128/ar/ap-future-range-fix fix: add below-0 column in ar/ap report --- .../accounts_receivable/accounts_receivable.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 75e81d3b04c..0b4f59d6aae 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -877,11 +877,15 @@ class ReceivablePayableReport: else: entry_date = row.posting_date + row.range0 = 0.0 + self.get_ageing_data(entry_date, row) - # ageing buckets should not have amounts if due date is not reached if getdate(entry_date) > getdate(self.age_as_on): + row.range0 = row.outstanding [setattr(row, f"range{i}", 0.0) for i in self.range_numbers] + row.total_due = 0 + return row.total_due = sum(row[f"range{i}"] for i in self.range_numbers) @@ -1281,6 +1285,8 @@ class ReceivablePayableReport: ranges = [*self.ranges, _("Above")] prev_range_value = 0 + self.add_column(label=_("<0"), fieldname="range0", fieldtype="Currency") + self.ageing_column_labels.append(_("<0")) for idx, curr_range_value in enumerate(ranges): label = f"{prev_range_value}-{curr_range_value}" self.add_column(label=label, fieldname="range" + str(idx + 1)) @@ -1296,7 +1302,9 @@ class ReceivablePayableReport: for row in self.data: row = frappe._dict(row) if not cint(row.bold): - values = [flt(row.get(f"range{i}", None), precision) for i in self.range_numbers] + values = [flt(row.get("range0", 0), precision)] + [ + flt(row.get(f"range{i}", 0), precision) for i in self.range_numbers + ] rows.append({"values": values}) self.chart = { From b567184dd707b3b5b8aad995007d67bd6f466029 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 12:31:54 +0530 Subject: [PATCH 098/106] test: add test case --- .../stock/doctype/stock_entry/stock_entry.js | 12 ++++-- .../stock/doctype/stock_entry/stock_entry.py | 17 ++++++-- .../doctype/stock_entry/stock_entry_utils.py | 1 + .../doctype/stock_entry/test_stock_entry.py | 40 +++++++++++++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b62dce837f1..7d2ae01357a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -440,12 +440,16 @@ frappe.ui.form.on("Stock Entry", { if ( frm.doc.docstatus == 1 && - frm.doc.purpose == "Material Receipt" && + ["Material Receipt", "Manufacture"].includes(frm.doc.purpose) && frm.get_sum("items", "sample_quantity") ) { - frm.add_custom_button(__("Create Sample Retention Stock Entry"), function () { - frm.trigger("make_retention_stock_entry"); - }); + frm.add_custom_button( + __("Sample Retention Stock Entry"), + function () { + frm.trigger("make_retention_stock_entry"); + }, + __("Create") + ); } frm.trigger("setup_quality_inspection"); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e92ab143588..fc38aff9e2e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2570,6 +2570,7 @@ class StockEntry(StockController, SubcontractingInwardController): "expense_account": expense_account, "cost_center": item.get("buying_cost_center"), "is_finished_item": 1, + "sample_quantity": item.get("sample_quantity"), } if ( @@ -3103,6 +3104,7 @@ class StockEntry(StockController, SubcontractingInwardController): se_child.po_detail = item_row.get("po_detail") se_child.sco_rm_detail = item_row.get("sco_rm_detail") se_child.scio_detail = item_row.get("scio_detail") + se_child.sample_quantity = item_row.get("sample_quantity", 0) for field in [ self.subcontract_data.rm_detail_field, @@ -3415,6 +3417,7 @@ def move_sample_to_retention_warehouse(company, items): if isinstance(items, str): items = json.loads(items) + retention_warehouse = frappe.get_single_value("Stock Settings", "sample_retention_warehouse") stock_entry = frappe.new_doc("Stock Entry") stock_entry.company = company @@ -3449,12 +3452,17 @@ def move_sample_to_retention_warehouse(company, items): total_qty += sample_quantity if sabb.has_serial_no: sabe_list.extend( - [entry for entry in sabb.entries if entry.batch_no == batch_no][ - : int(sample_quantity) - ] + [ + entry + for entry in sabb.entries + if entry.batch_no == batch_no + and frappe.db.exists( + "Serial No", {"name": entry.serial_no, "warehouse": warehouse} + ) + ][: int(sample_quantity)] ) else: - sabe.qty = -1 * sample_quantity + sabe.qty = sample_quantity else: sabb.entries.remove(sabe) @@ -3462,6 +3470,7 @@ def move_sample_to_retention_warehouse(company, items): if sabe_list: sabb.entries = sabe_list sabb.save() + stock_entry.append( "items", { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py index 576d129ee2d..6e54fd4e3b9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py @@ -190,6 +190,7 @@ def make_stock_entry(**args): "cost_center": args.cost_center, "expense_account": args.expense_account, "use_serial_batch_fields": args.use_serial_batch_fields, + "sample_quantity": frappe.get_value("Item", args.item, "sample_quantity") or 0, }, ) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index cf1b12a8f28..7b710514519 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -2277,6 +2277,46 @@ class TestStockEntry(IntegrationTestCase): se.save() se.submit() + @IntegrationTestCase.change_settings( + "Stock Settings", {"sample_retention_warehouse": "_Test Warehouse 1 - _TC"} + ) + def test_sample_retention_stock_entry(self): + from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse + + warehouse = "_Test Warehouse - _TC" + retain_sample_item = make_item( + "Retain Sample Item", + properties={ + "is_stock_item": 1, + "retain_sample": 1, + "sample_quantity": 2, + "has_batch_no": 1, + "has_seral_no": 1, + "create_new_batch": 1, + "batch_number_series": "SAMPLE-RET-.#####", + "serial_no_series": "SAMPLE-RET-SN-.#####", + }, + ) + material_receipt = make_stock_entry( + item_code=retain_sample_item.item_code, target=warehouse, qty=10, purpose="Material Receipt" + ) + + source_sabb = frappe.get_doc( + "Serial and Batch Bundle", material_receipt.items[0].serial_and_batch_bundle + ) + batch = source_sabb.entries[0].batch_no + serial_nos = [entry.serial_no for entry in source_sabb.entries] + + sample_entry = frappe.get_doc( + move_sample_to_retention_warehouse(material_receipt.company, material_receipt.items) + ) + sample_entry.submit() + target_sabb = frappe.get_doc("Serial and Batch Bundle", sample_entry.items[0].serial_and_batch_bundle) + + self.assertEqual(sample_entry.items[0].transfer_qty, 2) + self.assertEqual(target_sabb.entries[0].batch_no, batch) + self.assertEqual([entry.serial_no for entry in target_sabb.entries], serial_nos[:2]) + def make_serialized_item(self, **args): args = frappe._dict(args) From f952b92d71c4b81bf1931e748abbedc7cdc555d6 Mon Sep 17 00:00:00 2001 From: SowmyaArunachalam Date: Fri, 16 Jan 2026 13:15:00 +0530 Subject: [PATCH 099/106] fix: add company filters for warehouse --- .../doctype/supplier_quotation/supplier_quotation.js | 8 ++++++++ erpnext/selling/doctype/quotation/quotation.js | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index 93f52d6ec42..4beb17d7cf6 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -16,6 +16,14 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e return !doc.qty && me.frm.doc.has_unit_price_items ? "yellow" : ""; }); + this.frm.set_query("warehouse", "items", (doc, cdt, cdn) => { + return { + filters: { + company: doc.company, + is_group: 0, + }, + }; + }); super.setup(); } diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 480ca04b6a9..bb82310d9c5 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -36,6 +36,15 @@ frappe.ui.form.on("Quotation", { }; }); + frm.set_query("warehouse", "items", (doc, cdt, cdn) => { + return { + filters: { + company: doc.company, + is_group: 0, + }, + }; + }); + frm.set_indicator_formatter("item_code", function (doc) { return !doc.qty && frm.doc.has_unit_price_items ? "yellow" : ""; }); From 19ae405742efd7e188fb560ed68731d7e6e3ec27 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 13:15:22 +0530 Subject: [PATCH 100/106] fix: bugs --- .../stock/doctype/stock_entry/stock_entry.py | 22 +++++++++---------- .../doctype/stock_entry/test_stock_entry.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index fc38aff9e2e..fbcc43231dd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -3449,19 +3449,19 @@ def move_sample_to_retention_warehouse(company, items): sabe = next(item for item in sabb.entries if item.batch_no == batch_no) if sample_quantity: - total_qty += sample_quantity if sabb.has_serial_no: - sabe_list.extend( - [ - entry - for entry in sabb.entries - if entry.batch_no == batch_no - and frappe.db.exists( - "Serial No", {"name": entry.serial_no, "warehouse": warehouse} - ) - ][: int(sample_quantity)] - ) + new_sabe = [ + entry + for entry in sabb.entries + if entry.batch_no == batch_no + and frappe.db.exists( + "Serial No", {"name": entry.serial_no, "warehouse": warehouse} + ) + ][: int(sample_quantity)] + sabe_list.extend(new_sabe) + total_qty += len(new_sabe) else: + total_qty += sample_quantity sabe.qty = sample_quantity else: sabb.entries.remove(sabe) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 7b710514519..a0ac4f180b2 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -2291,7 +2291,7 @@ class TestStockEntry(IntegrationTestCase): "retain_sample": 1, "sample_quantity": 2, "has_batch_no": 1, - "has_seral_no": 1, + "has_serial_no": 1, "create_new_batch": 1, "batch_number_series": "SAMPLE-RET-.#####", "serial_no_series": "SAMPLE-RET-SN-.#####", From 589a393b5cc099ef6d3b7643a4d428c47348ac13 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 16 Jan 2026 13:15:24 +0530 Subject: [PATCH 101/106] fix: opening stock not working for serial / batch (#51781) --- erpnext/stock/doctype/item/item.py | 24 ++++++++++++++-- erpnext/stock/doctype/item/test_item.py | 37 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 5c92fecbfdf..feedb6a0f8a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -183,7 +183,23 @@ class Item(Document): self.add_price(default.default_price_list) if self.opening_stock: - self.set_opening_stock() + if self.opening_stock > 10000 and self.has_serial_no: + frappe.enqueue( + self.set_opening_stock, + queue="long", + timeout=600, + job_name=f"set_opening_stock_for_{self.name}", + ) + frappe.msgprint( + _( + "Opening stock creation has been queued and will be created in the background. Please check the stock entry after some time." + ), + indicator="orange", + alert=True, + ) + + else: + self.set_opening_stock() def validate(self): if not self.item_name: @@ -264,7 +280,11 @@ class Item(Document): def set_opening_stock(self): """set opening stock""" - if not self.is_stock_item or self.has_serial_no or self.has_batch_no: + if ( + not self.is_stock_item + or (self.has_serial_no and not self.serial_no_series) + or (self.has_batch_no and (not self.create_new_batch or not self.batch_number_series)) + ): return if not self.valuation_rate and not self.standard_rate and not self.is_customer_provided_item: diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 7814efb1c0b..b259f6592c1 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -957,6 +957,43 @@ class TestItem(IntegrationTestCase): msg="Different Variant UOM should not be allowed when `allow_different_uom` is disabled.", ) + def test_opening_stock_for_serial_batch(self): + items = { + "Test Opening Stock for Serial No": { + "has_serial_no": 1, + "opening_stock": 5, + "serial_no_series": "SN-TOPN-.####", + "valuation_rate": 100, + }, + "Test Opening Stock for Batch No": { + "has_batch_no": 1, + "opening_stock": 5, + "batch_number_series": "BCH-TOPN-.####", + "valuation_rate": 100, + "create_new_batch": 1, + }, + "Test Opening Stock for Serial and Batch No": { + "has_serial_no": 1, + "has_batch_no": 1, + "opening_stock": 5, + "batch_number_series": "SN-BCH-TOPN-.####", + "serial_no_series": "BCH-SN-TOPN-.####", + "valuation_rate": 100, + "create_new_batch": 1, + }, + } + + for item_code, properties in items.items(): + make_item(item_code, properties) + + serial_and_batch_bundle = frappe.db.get_value( + "Stock Entry Detail", {"docstatus": 1, "item_code": item_code}, "serial_and_batch_bundle" + ) + self.assertTrue(serial_and_batch_bundle) + + sabb_qty = frappe.db.get_value("Serial and Batch Bundle", serial_and_batch_bundle, "total_qty") + self.assertEqual(sabb_qty, properties["opening_stock"]) + def set_item_variant_settings(fields): doc = frappe.get_doc("Item Variant Settings") From 8fd1d6aec8b7e76881b1394eac70fb6482526756 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 13:38:38 +0530 Subject: [PATCH 102/106] chore: typo --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index b5c1c38729f..81c1b147697 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -266,7 +266,7 @@ erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extend ); } cur_frm.add_custom_button( - __("Retention Stock Entry"), + __("Sample Retention Stock Entry"), this.make_retention_stock_entry, __("Create") ); From 047343ca1164869e17fff21571fc9b6829ac4591 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 15:10:31 +0530 Subject: [PATCH 103/106] fix: js error on customer doctype --- erpnext/selling/doctype/customer/customer.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 5a5f5b27331..a2abaa5527d 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -212,9 +212,11 @@ frappe.ui.form.on("Customer", { frappe.contacts.clear_address_and_contact(frm); } - var grid = cur_frm.get_field("sales_team").grid; - grid.set_column_disp("allocated_amount", false); - grid.set_column_disp("incentives", false); + let grid = frm.get_field("sales_team")?.grid; + if (grid) { + grid.set_column_disp("allocated_amount", false); + grid.set_column_disp("incentives", false); + } frm.set_query("customer_group", () => { return { From f959b2c59ab5682f73e673d0e088b351c0de8415 Mon Sep 17 00:00:00 2001 From: Pandiyan5273 Date: Fri, 16 Jan 2026 15:12:28 +0530 Subject: [PATCH 104/106] fix(stock): resolve quantity issue when adding items via barcode scan --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index cd9341dd9c8..937bb8b8513 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -518,7 +518,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe barcode(doc, cdt, cdn) { let row = locals[cdt][cdn]; - if (row.barcode) { + if (row.barcode && !frappe.flags.trigger_from_barcode_scanner) { erpnext.stock.utils.set_item_details_using_barcode(this.frm, row, (r) => { frappe.model.set_value(cdt, cdn, { item_code: r.message.item_code, From b3db2981de2a128b22b6fcba593abbddf6a46b1c Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 16 Jan 2026 15:58:02 +0530 Subject: [PATCH 105/106] fix: dont show certain fields based on permissions --- erpnext/stock/doctype/item/item.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index ad180a7f2f8..6f13428c22b 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -116,6 +116,11 @@ frappe.ui.form.on("Item", { }, __("View") ); + + frm.toggle_display( + ["opening_stock"], + frappe.model.can_create("Stock Entry") && frappe.model.can_write("Stock Entry") + ); } if (frm.doc.is_fixed_asset) { @@ -239,6 +244,8 @@ frappe.ui.form.on("Item", { }, }; }); + + frm.toggle_display(["standard_rate"], frappe.model.can_create("Item Price")); }, validate: function (frm) { From 3fe5b5c80d2781e2496e93eab4858956f81bb7e6 Mon Sep 17 00:00:00 2001 From: Sowmya <106989392+SowmyaArunachalam@users.noreply.github.com> Date: Sat, 17 Jan 2026 14:55:25 +0530 Subject: [PATCH 106/106] fix: change docfield type to render html format (#51795) --- erpnext/buying/doctype/supplier/supplier.json | 4 ++-- erpnext/buying/doctype/supplier/supplier.py | 2 +- erpnext/public/js/utils/party.js | 2 +- erpnext/selling/doctype/customer/customer.json | 4 ++-- erpnext/selling/doctype/customer/customer.py | 2 +- erpnext/stock/doctype/shipment/shipment.json | 15 ++++++++------- erpnext/stock/doctype/shipment/shipment.py | 8 ++++---- 7 files changed, 19 insertions(+), 18 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 125a8b6adb1..a801a2a601d 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -383,7 +383,7 @@ }, { "fieldname": "primary_address", - "fieldtype": "Text", + "fieldtype": "Text Editor", "label": "Primary Address", "read_only": 1 }, @@ -500,7 +500,7 @@ "link_fieldname": "party" } ], - "modified": "2025-06-29 05:30:50.398653", + "modified": "2026-01-16 15:56:31.139206", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index f5005fbc12a..543b3726089 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -62,7 +62,7 @@ class Supplier(TransactionBase): portal_users: DF.Table[PortalUser] prevent_pos: DF.Check prevent_rfqs: DF.Check - primary_address: DF.Text | None + primary_address: DF.TextEditor | None release_date: DF.Date | None represents_company: DF.Link | None supplier_details: DF.Text | None diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index d86296f1eea..a2e4dbf1da1 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -157,7 +157,7 @@ erpnext.utils.get_address_display = function (frm, address_field, display_field, args: { address_dict: frm.doc[address_field] }, callback: function (r) { if (r.message) { - frm.set_value(display_field, frappe.utils.html2text(r.message)); + frm.set_value(display_field, r.message); } }, }); diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 7ddb8fc74cf..72798f32329 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -335,7 +335,7 @@ }, { "fieldname": "primary_address", - "fieldtype": "Text", + "fieldtype": "Text Editor", "label": "Primary Address", "read_only": 1 }, @@ -625,7 +625,7 @@ "link_fieldname": "party" } ], - "modified": "2025-11-25 09:35:56.772949", + "modified": "2026-01-16 15:56:05.967663", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 628173c2c7d..7003d491c32 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -83,7 +83,7 @@ class Customer(TransactionBase): opportunity_name: DF.Link | None payment_terms: DF.Link | None portal_users: DF.Table[PortalUser] - primary_address: DF.Text | None + primary_address: DF.TextEditor | None prospect_name: DF.Link | None represents_company: DF.Link | None sales_team: DF.Table[SalesTeam] diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json index d7f0877a298..a5e0a2377cf 100644 --- a/erpnext/stock/doctype/shipment/shipment.json +++ b/erpnext/stock/doctype/shipment/shipment.json @@ -115,7 +115,7 @@ }, { "fieldname": "pickup_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "read_only": 1 }, { @@ -135,7 +135,7 @@ }, { "fieldname": "pickup_contact", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "read_only": 1 }, { @@ -193,7 +193,7 @@ }, { "fieldname": "delivery_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "read_only": 1 }, { @@ -214,7 +214,7 @@ { "depends_on": "eval:doc.delivery_contact_name", "fieldname": "delivery_contact", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "read_only": 1 }, { @@ -441,11 +441,11 @@ ], "is_submittable": 1, "links": [], - "modified": "2026-01-07 19:24:23.566312", + "modified": "2026-01-16 14:59:28.547953", "modified_by": "Administrator", "module": "Stock", "name": "Shipment", - "naming_rule": "Expression (old style)", + "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { @@ -477,8 +477,9 @@ "write": 1 } ], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py index a2d6dee50dc..ae5a4214d24 100644 --- a/erpnext/stock/doctype/shipment/shipment.py +++ b/erpnext/stock/doctype/shipment/shipment.py @@ -27,10 +27,10 @@ class Shipment(Document): awb_number: DF.Data | None carrier: DF.Data | None carrier_service: DF.Data | None - delivery_address: DF.SmallText | None + delivery_address: DF.TextEditor | None delivery_address_name: DF.Link delivery_company: DF.Link | None - delivery_contact: DF.SmallText | None + delivery_contact: DF.TextEditor | None delivery_contact_email: DF.Data | None delivery_contact_name: DF.Link | None delivery_customer: DF.Link | None @@ -42,10 +42,10 @@ class Shipment(Document): pallets: DF.Literal["No", "Yes"] parcel_template: DF.Link | None pickup: DF.Data | None - pickup_address: DF.SmallText | None + pickup_address: DF.TextEditor | None pickup_address_name: DF.Link pickup_company: DF.Link | None - pickup_contact: DF.SmallText | None + pickup_contact: DF.TextEditor | None pickup_contact_email: DF.Data | None pickup_contact_name: DF.Link | None pickup_contact_person: DF.Link | None