Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use the same explicit db-aware conditional-add as work_order/mapper.py for the
end_of_life check (shared _item_is_alive helper in reorder_item.py; inline in
stock_projected_qty.py) instead of the inline ternary that became == None on
postgres. Identical SQL, no behaviour change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "item is not discontinued" checks treat an item as alive when its
`end_of_life` is unset, in the future, or the MariaDB zero-date `'0000-00-00'`.
`'0000-00-00'` is an invalid date literal on PostgreSQL (it errors), and a
"not set" end_of_life is `NULL` there anyway — already covered by the existing
`end_of_life IS NULL` term. So the zero-date comparison is applied on MariaDB
only; PostgreSQL keeps the `IS NULL` / future-date terms. No behaviour change on
MariaDB.
Sites: work order item-master selection (`mapper.py`), reorder-level item
selection (`reorder_item.py`), and the Stock Projected Qty report.
Part of the staged MariaDB<->PostgreSQL parity rollout (one problem class).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Acquire the same row locks on postgres that MariaDB takes, via a separate plain
SELECT <pk> ... FOR UPDATE before each grouped/aggregate read (FOR UPDATE is
invalid with GROUP BY on postgres). Applied to all 6 aggregate lock sites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PostgreSQL rejects `SELECT ... FOR UPDATE` when combined with `GROUP BY`,
aggregates or `DISTINCT`, has no concept of MySQL's locking semantics for those
shapes, and its server-side (unbuffered) cursors can't run nested queries
mid-iteration. This makes the row-locking / cursor paths db-aware so they keep
the exact MariaDB behaviour there and use the valid PostgreSQL form on Postgres.
One problem class, applied across the codebase:
- **`FOR UPDATE` + GROUP BY/aggregate** — keep `.for_update()` on MariaDB; on
Postgres acquire the lock in a separate plain `SELECT ... FOR UPDATE` pass (or
skip where the grouped read isn't a lock point). Deprecated serial/batch,
serial-batch-bundle, pick list, stock reservation entry.
- **Unbuffered/server-side cursor** — Stock Ageing streamed via an unbuffered
cursor and then ran nested queries; on Postgres that invalidates the cursor, so
process the buffered result directly there.
- **Transaction savepoints** — Opening Invoice Creation Tool rolled back the whole
transaction per failed invoice (which on Postgres also discards sibling rows and
earlier error logs); scope each invoice to a savepoint instead.
No behaviour change on MariaDB (the locking/cursor path is unchanged there).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(manufacturing, projects): make raw SQL portable to PostgreSQL
Convert the MariaDB-only raw `frappe.db.sql` in the Manufacturing and Projects
modules to the cross-database query builder / ORM, and fix the non-portable
constructs that remain. Every change is a no-op on MariaDB (identical rendered
SQL / identical results) and only brings PostgreSQL — standards-strict where
MySQL is lax — in line.
Areas: BOM (cost/where-used/explosion), Work Order (operations, required items,
mapper, stock report), Workstation, Production Plan sub-assembly/explosion
queries, BOM Stock Analysis / Process Loss / Work Order Stock reports; Projects
(project, task, timesheet, activity cost, project update), Daily Timesheet
Summary and Project-wise Stock Tracking reports.
Part of the staged MariaDB<->PostgreSQL parity rollout (module 2 of 9).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The test_prevention_of_cancelled_transaction_riv test sets
frappe.flags.dont_execute_stock_reposts = True without resetting it,
which leaked into the recalculate_valuation_rate tests and made
repost() a no-op (incoming rate stayed unchanged). Reset the flag
in tearDown so reposts run for subsequent tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert the MariaDB-only raw `frappe.db.sql` in the Selling and Buying
modules to the cross-database query builder / ORM, and fix the few
non-portable constructs that remain. Every change is a no-op on MariaDB
(identical rendered SQL / identical results) and only brings PostgreSQL —
which is standards-strict where MySQL is lax — in line.
Patterns addressed in these modules:
- Strict GROUP BY — PostgreSQL rejects SELECTing a non-aggregated column
that isn't functionally dependent on the grouped key. Sales Order
Analysis, Sales Analytics, Purchase Order Analysis and Procurement
Tracker now group by the PK (1:1 with the existing key, so no behaviour
change) or aggregate genuinely-independent columns.
- App clock vs DB clock — Sales Order Analysis computed delay against the
database CURRENT_DATE, which differs from the app's today by a day when
the DB runs a different timezone; switched to `nowdate()` (deterministic,
identical on both DBs).
- Portable date math / functions — DATEDIFF and friends via the db-aware
query-builder functions.
- Raw SQL → query builder for the remaining self-contained selling/buying
reads (POS item search, customer naming suffix, packing-items
availability, customer credit/acquisition reports).
Part of the staged MariaDB↔PostgreSQL parity rollout (module 1 of 9).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>