From f1b4fe12a2d0e6959ef6ac1b614ffe0e87a51518 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 3 Feb 2026 20:21:51 +0530 Subject: [PATCH 01/11] fix: rate comparison in stock reco --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index e5b6a647724..ee8ce3625f5 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -522,6 +522,9 @@ class StockReconciliation(StockController): if abs(difference_amount) > 0: return True + float_precision = frappe.db.get_default("float_precision") or 3 + item_dict["rate"] = flt(item_dict.get("rate"), float_precision) + item.valuation_rate = flt(item.valuation_rate, float_precision) if ( (item.qty is None or item.qty == item_dict.get("qty")) and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate")) From 16f09141da954f16b9ea232c75df1530f0f8644a Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Fri, 16 Jan 2026 00:01:34 +0530 Subject: [PATCH 02/11] fix(manufacturing): handle None value for actual_end_date --- .../production_analytics.py | 160 ++++++++---------- 1 file changed, 70 insertions(+), 90 deletions(-) diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 5c84a2dc8d1..f0fe0e87a78 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -8,6 +8,8 @@ from frappe.utils import getdate, today from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges +WORK_ORDER_STATUS_LIST = ["Not Started", "Overdue", "Pending", "Completed", "Closed", "Stopped"] + def execute(filters=None): columns = get_columns(filters) @@ -16,119 +18,97 @@ def execute(filters=None): def get_columns(filters): - columns = [{"label": _("Status"), "fieldname": "Status", "fieldtype": "Data", "width": 140}] - + columns = [{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 140}] ranges = get_period_date_ranges(filters) for _dummy, end_date in ranges: period = get_period(end_date, filters) - columns.append({"label": _(period), "fieldname": scrub(period), "fieldtype": "Float", "width": 120}) return columns -def get_periodic_data(filters, entry): - periodic_data = { - "Not Started": {}, - "Overdue": {}, - "Pending": {}, - "Completed": {}, - "Closed": {}, - "Stopped": {}, - } +def get_work_orders(filters): + from_date = filters.get("from_date") + to_date = filters.get("to_date") - ranges = get_period_date_ranges(filters) + WorkOrder = frappe.qb.DocType("Work Order") - for from_date, end_date in ranges: - period = get_period(end_date, filters) - for d in entry: - if getdate(from_date) <= getdate(d.creation) <= getdate(end_date) and d.status not in [ - "Draft", - "Submitted", - "Completed", - "Cancelled", - ]: - if d.status in ["Not Started", "Closed", "Stopped"]: - periodic_data = update_periodic_data(periodic_data, d.status, period) - elif getdate(today()) > getdate(d.planned_end_date): - periodic_data = update_periodic_data(periodic_data, "Overdue", period) - elif getdate(today()) < getdate(d.planned_end_date): - periodic_data = update_periodic_data(periodic_data, "Pending", period) - - if ( - getdate(from_date) <= getdate(d.actual_end_date) <= getdate(end_date) - and d.status == "Completed" - ): - periodic_data = update_periodic_data(periodic_data, "Completed", period) - - return periodic_data - - -def update_periodic_data(periodic_data, status, period): - if periodic_data.get(status).get(period): - periodic_data[status][period] += 1 - else: - periodic_data[status][period] = 1 - - return periodic_data + return ( + frappe.qb.from_(WorkOrder) + .select(WorkOrder.creation, WorkOrder.actual_end_date, WorkOrder.planned_end_date, WorkOrder.status) + .where( + (WorkOrder.docstatus == 1) + & (WorkOrder.company == filters.get("company")) + & ( + (WorkOrder.creation.between(from_date, to_date)) + | (WorkOrder.actual_end_date.between(from_date, to_date)) + ) + ) + .run(as_dict=True) + ) def get_data(filters, columns): - data = [] - entry = frappe.get_all( - "Work Order", - fields=[ - "creation", - "actual_end_date", - "planned_end_date", - "status", - ], - filters={"docstatus": 1, "company": filters["company"]}, - ) + ranges = build_ranges(filters) + period_labels = [pd for _fd, _td, pd in ranges] + periodic_data = {status: {pd: 0 for pd in period_labels} for status in WORK_ORDER_STATUS_LIST} + entries = get_work_orders(filters) - periodic_data = get_periodic_data(filters, entry) + for d in entries: + if d.status == "Completed": + if not d.actual_end_date: + continue - labels = ["Not Started", "Overdue", "Pending", "Completed", "Closed", "Stopped"] - chart_data = get_chart_data(periodic_data, columns) - ranges = get_period_date_ranges(filters) + if period := get_period_for_date(getdate(d.actual_end_date), ranges): + periodic_data["Completed"][period] += 1 + continue - for label in labels: - work = {} - work["Status"] = _(label) - for _dummy, end_date in ranges: - period = get_period(end_date, filters) - if periodic_data.get(label).get(period): - work[scrub(period)] = periodic_data.get(label).get(period) + creation_date = getdate(d.creation) + period = get_period_for_date(creation_date, ranges) + if not period: + continue + + if d.status in ("Not Started", "Closed", "Stopped"): + periodic_data[d.status][period] += 1 + else: + if d.planned_end_date and getdate(today()) > getdate(d.planned_end_date): + periodic_data["Overdue"][period] += 1 else: - work[scrub(period)] = 0.0 - data.append(work) + periodic_data["Pending"][period] += 1 - return data, chart_data + data = [] + for status in WORK_ORDER_STATUS_LIST: + row = {"status": _(status)} + for _fd, _td, pd in ranges: + row[scrub(pd)] = periodic_data[status].get(pd, 0) + data.append(row) + + chart = get_chart_data(periodic_data, columns) + return data, chart + + +def get_period_for_date(date, ranges): + for from_date, to_date, period in ranges: + if from_date <= date <= to_date: + return period + return None + + +def build_ranges(filters): + ranges = [] + for from_date, end_date in get_period_date_ranges(filters): + period = get_period(end_date, filters) + ranges.append((getdate(from_date), getdate(end_date), period)) + return ranges def get_chart_data(periodic_data, columns): labels = [d.get("label") for d in columns[1:]] - not_start, overdue, pending, completed, closed, stopped = [], [], [], [], [], [] datasets = [] + for status in WORK_ORDER_STATUS_LIST: + values = [periodic_data.get(status, {}).get(label, 0) for label in labels] + datasets.append({"name": _(status), "values": values}) - for d in labels: - not_start.append(periodic_data.get("Not Started").get(d)) - overdue.append(periodic_data.get("Overdue").get(d)) - pending.append(periodic_data.get("Pending").get(d)) - completed.append(periodic_data.get("Completed").get(d)) - closed.append(periodic_data.get("Closed").get(d)) - stopped.append(periodic_data.get("Stopped").get(d)) - - datasets.append({"name": _("Not Started"), "values": not_start}) - datasets.append({"name": _("Overdue"), "values": overdue}) - datasets.append({"name": _("Pending"), "values": pending}) - datasets.append({"name": _("Completed"), "values": completed}) - datasets.append({"name": _("Closed"), "values": closed}) - datasets.append({"name": _("Stopped"), "values": stopped}) - - chart = {"data": {"labels": labels, "datasets": datasets}} - chart["type"] = "line" - - return chart + return {"data": {"labels": labels, "datasets": datasets}, "type": "line"} From e8d1e9d946ff19f608cd47a77734bbc72248fe56 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 4 Feb 2026 12:02:14 +0530 Subject: [PATCH 03/11] fix: return None instead of 0 if valuation rate is falsy --- .../stock/doctype/stock_reconciliation/stock_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index ee8ce3625f5..bb8f6e9cc7a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -524,7 +524,7 @@ class StockReconciliation(StockController): float_precision = frappe.db.get_default("float_precision") or 3 item_dict["rate"] = flt(item_dict.get("rate"), float_precision) - item.valuation_rate = flt(item.valuation_rate, float_precision) + item.valuation_rate = flt(item.valuation_rate, float_precision) if item.valuation_rate else None if ( (item.qty is None or item.qty == item_dict.get("qty")) and (item.valuation_rate is None or item.valuation_rate == item_dict.get("rate")) From 27091e516881c1749cbaf3101ff53664fcd9d086 Mon Sep 17 00:00:00 2001 From: Sudharsanan11 Date: Wed, 4 Feb 2026 12:18:03 +0530 Subject: [PATCH 04/11] fix(manufacturing): fix chart period keys --- .../production_analytics.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index f0fe0e87a78..41fd4dd0e82 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -51,7 +51,7 @@ def get_work_orders(filters): def get_data(filters, columns): ranges = build_ranges(filters) - period_labels = [pd for _fd, _td, pd in ranges] + period_labels = [scrub(pd) for _fd, _td, pd in ranges] periodic_data = {status: {pd: 0 for pd in period_labels} for status in WORK_ORDER_STATUS_LIST} entries = get_work_orders(filters) @@ -60,12 +60,12 @@ def get_data(filters, columns): if not d.actual_end_date: continue - if period := get_period_for_date(getdate(d.actual_end_date), ranges): + if period := scrub(get_period_for_date(getdate(d.actual_end_date), ranges)): periodic_data["Completed"][period] += 1 continue creation_date = getdate(d.creation) - period = get_period_for_date(creation_date, ranges) + period = scrub(get_period_for_date(creation_date, ranges)) if not period: continue @@ -80,8 +80,8 @@ def get_data(filters, columns): data = [] for status in WORK_ORDER_STATUS_LIST: row = {"status": _(status)} - for _fd, _td, pd in ranges: - row[scrub(pd)] = periodic_data[status].get(pd, 0) + for _fd, _td, period in ranges: + row[scrub(period)] = periodic_data[status].get(scrub(period), 0) data.append(row) chart = get_chart_data(periodic_data, columns) @@ -104,11 +104,12 @@ def build_ranges(filters): def get_chart_data(periodic_data, columns): - labels = [d.get("label") for d in columns[1:]] + period_labels = [d.get("label") for d in columns[1:]] + period_fieldnames = [d.get("fieldname") for d in columns[1:]] datasets = [] for status in WORK_ORDER_STATUS_LIST: - values = [periodic_data.get(status, {}).get(label, 0) for label in labels] + values = [periodic_data.get(status, {}).get(fieldname, 0) for fieldname in period_fieldnames] datasets.append({"name": _(status), "values": values}) - return {"data": {"labels": labels, "datasets": datasets}, "type": "line"} + return {"data": {"labels": period_labels, "datasets": datasets}, "type": "line"} From b755ca12ca2afd3e5587b81fd280820a298fbec5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 4 Feb 2026 16:54:51 +0530 Subject: [PATCH 05/11] fix: enfore permission on make_payment_request --- erpnext/accounts/doctype/payment_request/payment_request.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 7d21a4ba41a..0b119512f7b 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -535,10 +535,12 @@ class PaymentRequest(Document): row_number += TO_SKIP_NEW_ROW -@frappe.whitelist(allow_guest=True) +@frappe.whitelist() def make_payment_request(**args): """Make payment request""" + frappe.has_permission(doctype="Payment Request", ptype="write", throw=True) + args = frappe._dict(args) if args.dt not in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST: frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt))) From 175fe9279c13168c08df8b6d7f5fda44368a1bb0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 4 Feb 2026 16:55:36 +0530 Subject: [PATCH 06/11] fix: not able to complete job card --- .../doctype/job_card/job_card.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index c9010ff54cc..ba2a36e2ad3 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -1251,10 +1251,26 @@ class JobCard(Document): frappe.db.set_value("Workstation", self.workstation, "status", status) def add_time_logs(self, **kwargs): - row = None kwargs = frappe._dict(kwargs) + if not kwargs.employees and kwargs.to_time: + for row in self.time_logs: + if not row.to_time and row.from_time: + row.to_time = kwargs.to_time + row.time_in_mins = time_diff_in_minutes(row.to_time, row.from_time) + if kwargs.completed_qty: + row.completed_qty = kwargs.completed_qty + row.db_update() + else: + self.add_time_logs_for_employess(kwargs) + + self.validate_time_logs(save=True) + self.save() + + def add_time_logs_for_employess(self, kwargs): + row = None update_status = False + for employee in kwargs.employees: kwargs.employee = employee.get("employee") if kwargs.from_time and not kwargs.to_time: @@ -1290,9 +1306,6 @@ class JobCard(Document): self.set_status(update_status=update_status) - self.validate_time_logs(save=True) - self.save() - def update_workstation_status(self): status_map = { "Open": "Off", From f3ea1863aeff6d2bf47d675ffa1a37ef6b4fc093 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 3 Feb 2026 22:42:02 +0530 Subject: [PATCH 07/11] refactor: Cleanup buying module forms --- .../purchase_invoice/purchase_invoice.json | 64 +++++++++++------- .../doctype/purchase_order/purchase_order.js | 21 ------ .../purchase_order/purchase_order.json | 66 +++++++++++-------- erpnext/public/js/controllers/transaction.js | 24 ++++--- .../purchase_receipt/purchase_receipt.json | 51 ++++++++------ .../purchase_receipt_item.json | 5 +- 6 files changed, 128 insertions(+), 103 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 02371e778cf..7af3b92bf75 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -8,12 +8,12 @@ "email_append_to": 1, "engine": "InnoDB", "field_order": [ - "company", "title", "naming_series", "supplier", "supplier_name", "tax_id", + "company", "column_break_6", "posting_date", "posting_time", @@ -85,20 +85,24 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_49", + "totals_section", + "grand_total", + "disable_rounded_total", + "rounding_adjustment", + "column_break8", + "use_company_roundoff_cost_center", + "in_words", + "rounded_total", + "base_totals_section", "base_grand_total", "base_rounding_adjustment", - "base_rounded_total", + "column_break_hcca", "base_in_words", - "column_break8", - "grand_total", - "rounding_adjustment", - "use_company_roundoff_cost_center", - "rounded_total", - "in_words", + "base_rounded_total", + "section_break_ttrv", "total_advance", + "column_break_peap", "outstanding_amount", - "disable_rounded_total", "section_tax_withholding_entry", "tax_withholding_group", "ignore_tax_withholding_threshold", @@ -882,15 +886,10 @@ "options": "currency", "print_hide": 1 }, - { - "fieldname": "section_break_49", - "fieldtype": "Section Break", - "label": "Totals" - }, { "fieldname": "base_grand_total", "fieldtype": "Currency", - "label": "Grand Total (Company Currency)", + "label": "Grand Total", "oldfieldname": "grand_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -901,7 +900,7 @@ "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", - "label": "Rounding Adjustment (Company Currency)", + "label": "Rounding Adjustment", "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, @@ -911,7 +910,7 @@ "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounded_total", "fieldtype": "Currency", - "label": "Rounded Total (Company Currency)", + "label": "Rounded Total", "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, @@ -920,7 +919,7 @@ { "fieldname": "base_in_words", "fieldtype": "Data", - "label": "In Words (Company Currency)", + "label": "In Words", "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", @@ -1625,8 +1624,7 @@ "hidden": 1, "label": "Item Wise Tax Details", "no_copy": 1, - "options": "Item Wise Tax Detail", - "print_hide": 1 + "options": "Item Wise Tax Detail" }, { "collapsible": 1, @@ -1661,6 +1659,28 @@ "fieldname": "override_tax_withholding_entries", "fieldtype": "Check", "label": "Edit Tax Withholding Entries" + }, + { + "fieldname": "column_break_hcca", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_ttrv", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_peap", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_totals_section", + "fieldtype": "Section Break", + "label": "Totals (Company Currency)" + }, + { + "fieldname": "totals_section", + "fieldtype": "Section Break", + "label": "Totals" } ], "grid_page_length": 50, @@ -1668,7 +1688,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2026-02-03 14:23:47.937128", + "modified": "2026-02-04 16:32:19.664287", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 12b0a8c301c..073312a8450 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -461,27 +461,6 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } - get_items_from_open_material_requests() { - erpnext.utils.map_current_doc({ - method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order_based_on_supplier", - args: { - supplier: this.frm.doc.supplier, - }, - source_doctype: "Material Request", - source_name: this.frm.doc.supplier, - target: this.frm, - setters: { - company: this.frm.doc.company, - }, - get_query_filters: { - docstatus: ["!=", 2], - supplier: this.frm.doc.supplier, - }, - get_query_method: - "erpnext.stock.doctype.material_request.material_request.get_material_requests_based_on_supplier", - }); - } - validate() { set_schedule_date(this.frm); } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 25a31ea698d..f528e07391d 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -9,23 +9,20 @@ "engine": "InnoDB", "field_order": [ "supplier_section", - "company", "title", "naming_series", + "supplier", + "supplier_name", "order_confirmation_no", "order_confirmation_date", - "get_items_from_open_material_requests", - "mps", "column_break_7", "transaction_date", "schedule_date", "column_break1", - "supplier", + "company", "is_subcontracted", - "supplier_name", "has_unit_price_items", "supplier_warehouse", - "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", @@ -79,16 +76,19 @@ "taxes_and_charges_deducted", "total_taxes_and_charges", "totals_section", + "grand_total", + "disable_rounded_total", + "rounding_adjustment", + "column_break4", + "in_words", + "rounded_total", + "base_totals_section", "base_grand_total", "base_rounding_adjustment", + "column_break_jkoz", "base_in_words", "base_rounded_total", - "column_break4", - "grand_total", - "rounding_adjustment", - "rounded_total", - "disable_rounded_total", - "in_words", + "section_break_tnkm", "advance_paid", "discount_section", "apply_discount_on", @@ -154,11 +154,13 @@ "auto_repeat", "update_auto_repeat_reference", "additional_info_section", - "is_internal_supplier", + "party_account_currency", "represents_company", "ref_sq", + "amended_from", "column_break_74", - "party_account_currency", + "mps", + "is_internal_supplier", "inter_company_order_reference", "is_old_subcontracting_flow", "connections_tab" @@ -206,13 +208,6 @@ "reqd": 1, "search_index": 1 }, - { - "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", - "description": "Fetch items based on Default Supplier.", - "fieldname": "get_items_from_open_material_requests", - "fieldtype": "Button", - "label": "Get Items from Open Material Requests" - }, { "bold": 1, "fetch_from": "supplier.supplier_name", @@ -773,7 +768,7 @@ { "fieldname": "base_grand_total", "fieldtype": "Currency", - "label": "Grand Total (Company Currency)", + "label": "Grand Total", "no_copy": 1, "oldfieldname": "grand_total", "oldfieldtype": "Currency", @@ -785,7 +780,7 @@ "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", - "label": "Rounding Adjustment (Company Currency)", + "label": "Rounding Adjustment", "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, @@ -794,7 +789,7 @@ { "fieldname": "base_in_words", "fieldtype": "Data", - "label": "In Words (Company Currency)", + "label": "In Words", "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", @@ -804,7 +799,7 @@ { "fieldname": "base_rounded_total", "fieldtype": "Currency", - "label": "Rounded Total (Company Currency)", + "label": "Rounded Total", "oldfieldname": "rounded_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -862,7 +857,7 @@ { "fieldname": "advance_paid", "fieldtype": "Currency", - "label": "Advance Paid", + "label": "Advance Paid (Company Currency)", "no_copy": 1, "options": "party_account_currency", "print_hide": 1, @@ -1301,8 +1296,21 @@ "hidden": 1, "label": "Item Wise Tax Details", "no_copy": 1, - "options": "Item Wise Tax Detail", - "print_hide": 1 + "options": "Item Wise Tax Detail" + }, + { + "fieldname": "column_break_jkoz", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_totals_section", + "fieldtype": "Section Break", + "label": "Totals (Company Currency)", + "options": "Company:company:default_currency" + }, + { + "fieldname": "section_break_tnkm", + "fieldtype": "Section Break" } ], "grid_page_length": 50, @@ -1310,7 +1318,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2026-02-03 14:44:55.192192", + "modified": "2026-02-04 13:01:36.628472", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d4a4577d8a0..f7a53709f79 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1733,13 +1733,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.set_currency_labels( [ + "advance_paid", "base_total", "base_net_total", "base_total_taxes_and_charges", "base_discount_amount", - "base_grand_total", - "base_rounded_total", - "base_in_words", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "total_amount_to_pay", @@ -1750,7 +1748,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "base_raw_material_cost", "base_total_cost", "base_scrap_material_cost", - "base_rounding_adjustment", + "base_totals_section", ], company_currency ); @@ -1761,19 +1759,16 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "net_total", "total_taxes_and_charges", "discount_amount", - "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", "tax_withholding_net_total", - "rounded_total", - "in_words", "paid_amount", "write_off_amount", "operating_cost", "scrap_material_cost", - "rounding_adjustment", "raw_material_cost", "total_cost", + "totals_section", ], this.frm.doc.currency ); @@ -1827,6 +1822,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.doc.price_list_currency != company_currency ); + let taxes_fields = [ + "total_taxes_and_charges", + "taxes_and_charges_deducted", + "taxes_and_charges_added", + "base_taxes_and_charges_added", + "base_taxes_and_charges_deducted", + "base_total_taxes_and_charges", + ]; + + taxes_fields.forEach((field) => { + this.frm.toggle_display(field, this.frm.doc[field] !== 0 || this.frm.doc.docstatus !== 1); + }); + let show = cint(this.frm.doc.discount_amount) || (this.frm.doc.taxes || []).filter(function (d) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 1e93e50b10d..b16dc1b32e1 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -77,17 +77,19 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_46", + "totals_section", + "grand_total", + "disable_rounded_total", + "rounding_adjustment", + "column_break_50", + "in_words", + "rounded_total", + "base_totals_section", "base_grand_total", "base_rounding_adjustment", - "base_rounded_total", + "column_break_ugyv", "base_in_words", - "column_break_50", - "grand_total", - "rounding_adjustment", - "rounded_total", - "in_words", - "disable_rounded_total", + "base_rounded_total", "section_break_42", "apply_discount_on", "base_discount_amount", @@ -772,15 +774,10 @@ "options": "currency", "print_hide": 1 }, - { - "fieldname": "section_break_46", - "fieldtype": "Section Break", - "label": "Totals" - }, { "fieldname": "base_grand_total", "fieldtype": "Currency", - "label": "Grand Total (Company Currency)", + "label": "Grand Total", "oldfieldname": "grand_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -791,7 +788,7 @@ "depends_on": "eval:!doc.disable_rounded_total", "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", - "label": "Rounding Adjustment (Company Currency)", + "label": "Rounding Adjustment", "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, @@ -800,7 +797,7 @@ { "fieldname": "base_in_words", "fieldtype": "Data", - "label": "In Words (Company Currency)", + "label": "In Words", "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", @@ -810,7 +807,7 @@ { "fieldname": "base_rounded_total", "fieldtype": "Currency", - "label": "Rounded Total (Company Currency)", + "label": "Rounded Total", "oldfieldname": "rounded_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -1282,8 +1279,22 @@ "hidden": 1, "label": "Item Wise Tax Details", "no_copy": 1, - "options": "Item Wise Tax Detail", - "print_hide": 1 + "options": "Item Wise Tax Detail" + }, + { + "fieldname": "totals_section", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "base_totals_section", + "fieldtype": "Section Break", + "label": "Totals (Company Currency)", + "options": "Company:company:default_currency" + }, + { + "fieldname": "column_break_ugyv", + "fieldtype": "Column Break" } ], "grid_page_length": 50, @@ -1291,7 +1302,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2026-01-29 21:24:30.652933", + "modified": "2026-02-04 14:36:41.087460", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 70ca56e286f..a01a4841e49 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -146,7 +146,7 @@ }, { "bold": 1, - "columns": 3, + "columns": 2, "fieldname": "item_code", "fieldtype": "Link", "in_global_search": 1, @@ -436,7 +436,6 @@ "columns": 2, "fieldname": "net_amount", "fieldtype": "Currency", - "in_list_view": 1, "label": "Net Amount", "options": "currency", "print_hide": 1, @@ -1141,7 +1140,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2025-10-21 10:39:32.659933", + "modified": "2026-02-04 14:42:10.646809", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 8cf31548f2d1b81cc49c8d7f65adb7b440aefa1e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Jan 2026 11:01:55 +0530 Subject: [PATCH 08/11] refactor: scrub http and use https in sales partner --- .../setup/doctype/sales_partner/sales_partner.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py index 36c24c0f37e..6ef5b6dc646 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.py +++ b/erpnext/setup/doctype/sales_partner/sales_partner.py @@ -50,8 +50,17 @@ class SalesPartner(WebsiteGenerator): if not self.route: self.route = "partners/" + self.scrub(self.partner_name) super().validate() - if self.partner_website and not self.partner_website.startswith("http"): - self.partner_website = "http://" + self.partner_website + if self.partner_website: + from urllib.parse import urlsplit, urlunsplit + + # scrub http + parts = urlsplit(self.partner_website) + if not parts.netloc and parts.path: + parts = parts._replace(netloc=parts.path, path="") + if not parts.scheme or parts.scheme == "http": + parts = parts._replace(scheme="https") + + self.partner_website = urlunsplit(parts) def get_context(self, context): address_names = frappe.db.get_all( From 8db29b0a81325c2c0fc234b8d218427f1b61618b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Jan 2026 12:04:37 +0530 Subject: [PATCH 09/11] refactor: patch partner_website for old data --- erpnext/patches.txt | 3 ++- .../v15_0/replace_http_with_https_in_sales_partner.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e3235379127..941e3a273d9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -461,4 +461,5 @@ erpnext.patches.v15_0.create_accounting_dimensions_in_advance_taxes_and_charges execute:frappe.delete_doc_if_exists("Workspace Sidebar", "Opening & Closing") erpnext.patches.v16_0.migrate_transaction_deletion_task_flags_to_status # 2 erpnext.patches.v16_0.set_ordered_qty_in_quotation_item -erpnext.patches.v16_0.update_company_custom_field_in_bin \ No newline at end of file +erpnext.patches.v16_0.update_company_custom_field_in_bin +erpnext.patches.v15_0.replace_http_with_https_in_sales_partner diff --git a/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py b/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py new file mode 100644 index 00000000000..80bc418920a --- /dev/null +++ b/erpnext/patches/v15_0/replace_http_with_https_in_sales_partner.py @@ -0,0 +1,10 @@ +import frappe +from frappe import qb +from pypika.functions import Replace + + +def execute(): + sp = frappe.qb.DocType("Sales Partner") + qb.update(sp).set(sp.partner_website, Replace(sp.partner_website, "http://", "https://")).where( + sp.partner_website.rlike("^http://.*") + ).run() From e4df0a393abc884833bd9844fa94b6168efb11d5 Mon Sep 17 00:00:00 2001 From: archielister Date: Wed, 4 Feb 2026 12:20:43 +0000 Subject: [PATCH 10/11] fix for obtaining bom_no --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 2e8b7a42046..eeed10e5083 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -496,7 +496,7 @@ class ProductionPlan(Document): item_details = get_item_details(data.item_code, throw=False) if self.combine_items: - bom_no = item_details.bom_no + bom_no = item_details.get("bom_no") if data.get("bom_no"): bom_no = data.get("bom_no") From 812d7de7f717f39a17891eaf1e5183852b1311b2 Mon Sep 17 00:00:00 2001 From: Diptanil Saha Date: Thu, 5 Feb 2026 04:21:14 +0530 Subject: [PATCH 11/11] fix: pos profile form cleanup (#52436) --- .../doctype/pos_profile/pos_profile.json | 180 ++++++++++++++---- 1 file changed, 146 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index c0e5c895403..e3dad450805 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -12,56 +12,76 @@ "disabled", "column_break_9", "warehouse", - "utm_source", - "utm_campaign", - "utm_medium", "company_address", - "section_break_15", - "applicable_for_users", + "accounting_tab", "section_break_11", "payments", + "set_grand_total_to_default_mop", + "price_list_and_currency_section", + "currency", + "column_break_bptt", + "selling_price_list", + "write_off_section", + "write_off_account", + "column_break_ukpz", + "write_off_cost_center", + "column_break_pkca", + "write_off_limit", + "income_and_expense_account", + "income_account", + "column_break_byzk", + "expense_account", + "taxes_section", + "taxes_and_charges", + "column_break_cjpp", + "tax_category", + "section_break_19", + "account_for_change_amount", + "disable_rounded_total", + "column_break_23", + "apply_discount_on", + "allow_partial_payment", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", + "pos_configurations_tab", "section_break_14", - "hide_images", - "hide_unavailable_items", - "auto_add_item_to_cart", - "validate_stock_on_save", - "print_receipt_on_order_complete", "action_on_new_invoice", + "validate_stock_on_save", "column_break_16", "update_stock", "ignore_pricing_rule", + "print_receipt_on_order_complete", + "pos_item_selector_section", + "hide_images", + "column_break_rpny", + "hide_unavailable_items", + "column_break_stcl", + "auto_add_item_to_cart", + "pos_item_details_section", "allow_rate_change", + "column_break_hwfg", "allow_discount_change", - "set_grand_total_to_default_mop", - "allow_partial_payment", + "section_break_15", + "applicable_for_users", "section_break_23", "item_groups", "column_break_25", "customer_groups", + "more_info_tab", "section_break_16", "print_format", "letter_head", "column_break0", "tc_name", "select_print_heading", - "section_break_19", - "selling_price_list", - "currency", - "write_off_account", - "write_off_cost_center", - "write_off_limit", - "account_for_change_amount", - "disable_rounded_total", - "column_break_23", - "income_account", - "expense_account", - "taxes_and_charges", - "tax_category", - "apply_discount_on", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break", - "project" + "campaign_section_break", + "utm_source", + "column_break_tvls", + "utm_campaign", + "column_break_xygw", + "utm_medium" ], "fields": [ { @@ -130,8 +150,7 @@ }, { "fieldname": "section_break_14", - "fieldtype": "Section Break", - "label": "Configuration" + "fieldtype": "Section Break" }, { "description": "Only show Items from these Item Groups", @@ -152,6 +171,7 @@ "options": "POS Customer Group" }, { + "collapsible": 1, "fieldname": "section_break_16", "fieldtype": "Section Break", "label": "Print Settings" @@ -191,7 +211,7 @@ { "fieldname": "section_break_19", "fieldtype": "Section Break", - "label": "Accounting" + "label": "Miscellaneous" }, { "fieldname": "selling_price_list", @@ -427,9 +447,101 @@ }, { "default": "0", + "description": "Applicable on POS Invoice", "fieldname": "allow_partial_payment", "fieldtype": "Check", "label": "Allow Partial Payment" + }, + { + "fieldname": "column_break_tvls", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_xygw", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "campaign_section_break", + "fieldtype": "Section Break", + "label": "Campaign" + }, + { + "fieldname": "accounting_tab", + "fieldtype": "Tab Break", + "label": "Accounting" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "pos_configurations_tab", + "fieldtype": "Tab Break", + "label": "POS Configurations" + }, + { + "fieldname": "price_list_and_currency_section", + "fieldtype": "Section Break", + "label": "Price List & Currency" + }, + { + "fieldname": "column_break_bptt", + "fieldtype": "Column Break" + }, + { + "fieldname": "write_off_section", + "fieldtype": "Section Break", + "label": "Write Off" + }, + { + "fieldname": "column_break_ukpz", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_pkca", + "fieldtype": "Column Break" + }, + { + "fieldname": "income_and_expense_account", + "fieldtype": "Section Break", + "label": "Income and Expense" + }, + { + "fieldname": "column_break_byzk", + "fieldtype": "Column Break" + }, + { + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "label": "Taxes" + }, + { + "fieldname": "column_break_cjpp", + "fieldtype": "Column Break" + }, + { + "fieldname": "pos_item_selector_section", + "fieldtype": "Section Break", + "label": "POS Item Selector" + }, + { + "fieldname": "column_break_rpny", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_stcl", + "fieldtype": "Column Break" + }, + { + "fieldname": "pos_item_details_section", + "fieldtype": "Section Break", + "label": "POS Item Details" + }, + { + "fieldname": "column_break_hwfg", + "fieldtype": "Column Break" } ], "grid_page_length": 50, @@ -458,7 +570,7 @@ "link_fieldname": "pos_profile" } ], - "modified": "2025-06-24 11:19:19.834905", + "modified": "2026-02-05 03:38:13.216277", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile",