From f95e32a581e0866bbd716ffc57b62aa60e88e599 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sun, 21 Jun 2026 05:28:11 +0530 Subject: [PATCH 1/2] fix(controllers): make budget requested-amount aggregate Postgres-valid The Material Request requested-amount query selects `Sum(stock_qty - ordered_qty) * mri.rate` -- an implicit aggregate with no GROUP BY, where mri.rate is neither grouped nor aggregated. MariaDB arbitrary-picks the rate; Postgres rejects it ("must appear in the GROUP BY clause"). Wrap the rate in Max(mri.rate) so the SELECT is a pure aggregate. Behaviour note: for matched MR items with differing rates, Max() picks the highest (vs MariaDB's arbitrary single rate). The underlying Sum(qty) * rate is a pre-existing single-rate aggregation; this preserves it under the accepted arbitrary-pick convention. Covered by erpnext.accounts.doctype.budget.test_budget .test_monthly_budget_crossed_for_mr, which now passes on Postgres (it errors on develop) and is unchanged on MariaDB. Co-Authored-By: Claude Opus 4.8 (1M context) --- erpnext/controllers/budget_controller.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/budget_controller.py b/erpnext/controllers/budget_controller.py index 547edeb1f02..eee9eca7f11 100644 --- a/erpnext/controllers/budget_controller.py +++ b/erpnext/controllers/budget_controller.py @@ -3,7 +3,7 @@ from collections import OrderedDict import frappe from frappe import _, qb from frappe.query_builder import Criterion -from frappe.query_builder.functions import IfNull, Sum +from frappe.query_builder.functions import IfNull, Max, Sum from frappe.utils import fmt_money from erpnext.accounts.doctype.budget.budget import BudgetError, get_accumulated_monthly_budget @@ -260,7 +260,11 @@ class BudgetValidation: qb.from_(mr) .inner_join(mri) .on(mr.name == mri.parent) - .select((Sum(IfNull(mri.stock_qty, 0) - IfNull(mri.ordered_qty, 0)) * mri.rate).as_("amount")) + # rate is outside the Sum (no GROUP BY -> implicit aggregate); Max() keeps it valid on + # postgres and matches MySQL's arbitrary single-rate choice for this aggregate. + .select( + (Sum(IfNull(mri.stock_qty, 0) - IfNull(mri.ordered_qty, 0)) * Max(mri.rate)).as_("amount") + ) .where(Criterion.all(conditions)) .run(as_dict=True) ): From 147a8672b4e35e1f1450693c7a3ae8ce4235ee1b Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sun, 21 Jun 2026 05:28:12 +0530 Subject: [PATCH 2/2] fix(controllers): cast overproduced-qty flag to bool in subcontracting Case The max-allowed-qty Case used `... | ValueWrapper(allow_delivery_of_overproduced_qty)` where the flag is an int (0/1). Postgres rejects `OR ` ("argument of OR must be type boolean"). Wrap it in bool() so the literal renders as true/false. MariaDB behaviour is unchanged. Surgical: only the bool() wrap is applied; develop's weighted-average rate logic and the internal/whitelisted status-helper split are left intact (the staging branch predated both). Covered by test_subcontracting_inward_order.test_over_production_delivery, which now passes on Postgres and is unchanged on MariaDB. Co-Authored-By: Claude Opus 4.8 (1M context) --- erpnext/controllers/subcontracting_inward_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_inward_controller.py b/erpnext/controllers/subcontracting_inward_controller.py index fbacdc95a81..4907f2d8484 100644 --- a/erpnext/controllers/subcontracting_inward_controller.py +++ b/erpnext/controllers/subcontracting_inward_controller.py @@ -509,8 +509,9 @@ class SubcontractingInwardController: ( Case() .when( + # bool() so the literal renders as true/false; postgres rejects `OR ` (table.produced_qty < table.qty) - | ValueWrapper(allow_delivery_of_overproduced_qty), + | ValueWrapper(bool(allow_delivery_of_overproduced_qty)), table.produced_qty, ) .else_(table.qty)