Commit Graph

58875 Commits

Author SHA1 Message Date
Mihir Kandoi
0b4e52e8d7 fix(buying): make Purchase Order Analysis GROUP BY Postgres-valid
get_data() grouped only by Purchase Order Item while selecting Purchase
Order parent columns. MariaDB allows this loose GROUP BY; Postgres rejects
it with "column ... must appear in the GROUP BY clause".

Add the Purchase Order PK (po.name) to the GROUP BY. po.name is 1:1 with
the already-grouped po_item.name, so groups are unchanged and the result
is identical on MariaDB.

Adds a test (no test file existed) that runs the report and asserts the PO
is listed, exercising the GROUP BY on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 04:40:35 +05:30
Mihir Kandoi
8b4845d272 fix(buying): make Procurement Tracker GROUP BY Postgres-valid
get_po_entries() grouped only by (Purchase Order, material_request_item)
while selecting other Purchase Order Item columns. MariaDB allows this
loose GROUP BY (arbitrary-picking the extra columns); Postgres rejects it
with "column ... must appear in the GROUP BY clause".

Add the Purchase Order Item PK (child.name) to the GROUP BY so the
selected child columns are functionally determined by a grouped key.

Behaviour note: this is not a MariaDB no-op. When one PO has multiple
items sharing the same/blank material_request_item, MariaDB collapsed
them into one arbitrary row; now there is one row per PO line. The
downstream report already keys rows by purchase_order, so totals are
unaffected and the per-line breakdown is more correct.

Adds a test that runs the report and asserts the PO is listed, exercising
the GROUP BY on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 04:40:33 +05:30
Mihir Kandoi
4255059846 Merge pull request #56196 from mihir-kandoi/pg-bom-groupby-fix
fix(manufacturing): make get_bom_items_as_dict Postgres-valid (GROUP BY)
2026-06-20 22:33:30 +05:30
Mihir Kandoi
cfedcc06c8 Merge pull request #56197 from mihir-kandoi/pg-stock-entry-groupby-fix
fix(stock): Postgres GROUP-BY validity for Job Card secondary-item & disassembly queries
2026-06-20 22:24:10 +05:30
Mihir Kandoi
79cbefb088 fix(manufacturing): make get_bom_items_as_dict Postgres-valid (GROUP BY)
_query_bom_items / _build_base_bom_items_query / _add_*_item_columns selected
non-grouped columns (idx, item_name, image, project, item-default fields, BOM
Item attributes) alongside `group by item_code` -> arbitrary pick on MariaDB,
GroupingError on Postgres. Wrap them in Max() (Min() for idx, preserving the
original ordering). Every wrapped column is functionally dependent on the
grouped item_code (item attributes / the single BOM's project / one Item
Default per item+company), so Max()/Min() returns exactly the value MySQL
picked arbitrarily -> MariaDB output unchanged.

This was previously shipped in #56008 and reverted with that batch; re-applied
in isolation here. Verified: test_work_order 85/85 on BOTH MariaDB (no change)
and Postgres (was 85/85 failing on this query).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:13:29 +05:30
Mihir Kandoi
911a27e8e6 Merge pull request #56195 from mihir-kandoi/pg-stock-reconciliation
refactor(stock): port Stock Reconciliation raw SQL to qb/ORM (Postgres compat)
2026-06-20 22:12:14 +05:30
Mihir Kandoi
810e93758e fix(stock): make disassembly manufacture-entry query Postgres-valid (GROUP BY)
get_items_from_manufacture_stock_entry aggregated Stock Entry Detail rows by
item_code while selecting item_name/description/warehouses/etc. (and an orderby
on the non-grouped idx) -> arbitrary pick on MariaDB, GroupingError on Postgres.
Wrap the non-grouped columns in Max() (Min(idx) for the orderby), preserving the
one-row-per-item shape the disassembly expects; an item plays one role with one
uom/warehouse across the WO's manufacture entries, so MariaDB output is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:04:46 +05:30
Mihir Kandoi
4d29bfbe07 fix(stock): make Job Card secondary-item query Postgres-valid (GROUP BY)
get_secondary_items_from_job_card selected item_name/description/stock_uom/
bom_secondary_item alongside `group by item_code, secondary_item_type` (and an
orderby on the non-grouped idx) -> arbitrary pick on MariaDB, GroupingError on
Postgres. Wrap the non-grouped columns in Max() (Min(idx) for the orderby);
they are item attributes / the secondary-item BOM link, constant per group, so
MariaDB output is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:53:50 +05:30
Mihir Kandoi
554c196870 refactor(stock): convert Stock Reconciliation raw SQL to qb/ORM
validate_expense_account SLE existence check -> frappe.db.get_all(limit=1);
get_items_for_stock_reco's two comma-join SELECTs -> frappe.qb inner_joins, with
the correlated Warehouse-subtree EXISTS replaced by a precomputed
warehouses_in_tree subquery + isin and ifnull(disabled,0)=0 -> disabled==0|isnull.
The Item-Default query's `group by i.name` is dropped (sound: validate_item_defaults
enforces one Item Default per (item, company), so the company-filtered query already
returns one row per item; the downstream (item_code, warehouse) de-dup is unchanged).
Same result on MariaDB; valid under Postgres.

Tests: get_items_for_stock_reco Bin branch (stocked item) and Item-Default branch
(default_warehouse, no stock).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:19:36 +05:30
Mihir Kandoi
8c1c8a3cee Merge pull request #56192 from mihir-kandoi/pg-purchase-receipt
refactor(stock): port Purchase Receipt + LCV raw SQL to qb/ORM + #39 GROUP-BY fixes (Postgres)
2026-06-20 20:56:56 +05:30
Mihir Kandoi
b811dba5c2 fix(stock): aggregate non-grouped cols in get_items_to_be_repost (PG #39)
get_items_to_be_repost selected posting_date/posting_time/creation/posting_datetime
alongside `group_by item_code, warehouse` with no aggregation -> arbitrary pick on
MariaDB, GroupingError on Postgres. Wrap the four columns in `Min()` (earliest row
per item+warehouse, the correct repost-start point; a single voucher's SLEs share
posting_date/time per group -> MariaDB-identical). This is reached by every stock
transaction submit/cancel via repost_future_sle_and_gle, so it unblocks the whole
transaction-heavy stock suite on Postgres (e.g. test_purchase_receipt 105/105).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:09:05 +05:30
Mihir Kandoi
afb7c25141 refactor(stock): convert LCV serial-rate update to qb + fix cost_center GROUP BY (PG)
Convert the `update tabSerial No set purchase_rate ... where name in (...)` to
frappe.qb.update(isin). Also fix the #39 Postgres bug in
set_landed_cost_voucher_amount: `.select(Sum(applicable_charges), cost_center)`
selected a non-grouped column with no GROUP BY (GroupingError on PG) -> wrap it
in `Max(cost_center)` (deterministic representative; per (receipt_document,
receipt_item) the matching LCV items share a cost_center -> MariaDB-identical).
Covered by the existing landed-cost tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:09:05 +05:30
Mihir Kandoi
c76c0d85ba refactor(stock): convert PR get_invoiced_qty_map to qb aggregate
Replace the raw `select pr_detail, qty from Purchase Invoice Item` (summed in
Python) with a frappe.qb GROUP BY Sum(qty) per pr_detail, matching the sibling
get_returned_qty_map. Same result on MariaDB; valid under Postgres. Covered by
the existing make_purchase_invoice tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:09:05 +05:30
Mihir Kandoi
a65aa27225 refactor(stock): convert Purchase Receipt raw SQL to ORM
get_already_received_qty (sum over Purchase Receipt Item, parent != self.name)
and the two Purchase-Invoice-against-receipt existence checks (implicit
comma-joins -> child-table get_all on Purchase Invoice Item, docstatus=1).
Also fixes a pre-existing `self.submit_rv` -> `submit_rv` typo in the (dead)
check_next_docstatus that staging carried forward. Same result on MariaDB;
valid under Postgres.

Tests: get_already_received_qty (parent-exclusion sum) and check_next_docstatus
(blocks on a submitted Purchase Invoice; also locks the typo fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:08:43 +05:30
Diptanil Saha
5505ae43d4 Merge pull request #56191 from diptanilsaha/fix/perms_on_whitelisted_functions
fix: added missing permission validation on whitelisted function and removed unnecessary whitelisted decorator
2026-06-20 19:50:28 +05:30
diptanilsaha
e29535f29c fix(report_utils): remove unnecessary whitelist decorator on get_invoiced_item_gross_margin 2026-06-20 19:28:49 +05:30
diptanilsaha
9bf1e847d2 fix(err): add missing permission check on get_account_details 2026-06-20 19:28:49 +05:30
Mihir Kandoi
bfffed0f52 Merge pull request #56186 from mihir-kandoi/pg-stock-valuation-core
refactor(stock): port valuation-core helpers raw SQL to qb/ORM (Postgres compat)
2026-06-20 19:10:11 +05:30
Mihir Kandoi
46c1b49be1 refactor(stock): convert backdated-entry SLE check to qb (PG fix)
The authorized-user backdated-entry guard used MariaDB-only
`MAX(timestamp(posting_date, posting_time))`, invalid on Postgres. Convert to
`Max(posting_datetime)` (the precomputed column) via frappe.qb. Same result on
MariaDB; now valid under Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:01:33 +05:30
Mihir Kandoi
aa73606ed2 refactor(stock): convert get_warehouse_account lookup to get_all
Replace the raw nearest-ancestor warehouse-account SELECT with frappe.get_all;
`account is not null and ifnull(account,'')!=''` -> filter ["account","is","set"]
(IS NOT NULL AND != ''), order_by lft desc, limit 1, pluck. Same result on
MariaDB; valid under Postgres. Covered by the existing get_warehouse_account tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:01:33 +05:30
Mihir Kandoi
57ea0ff6aa refactor(stock): convert stock/utils.py raw SQL to qb/ORM
Convert get_stock_value_from_bin (comma-join + internal ifnull/warehouse-subtree
fragments -> inner_join + qb subquery), get_latest_stock_qty, get_latest_stock_balance,
get_avg_purchase_rate and get_incoming_outgoing_rate_for_cancel (Case/Abs) to
frappe.qb / get_all. Same result on MariaDB; valid under Postgres.

Tests: get_latest_stock_qty and get_stock_value_from_bin against received stock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:01:32 +05:30
Mihir Kandoi
17108d8a37 refactor(stock): convert stock_balance raw SQL to qb/ORM
Convert the repost item/warehouse UNION, get_balance_qty_from_sle,
get_reserved_qty (UNION of correlated subqueries -> two qb Sum branches with an
inner_join to Sales Order Item, added in Python; qty!=0 guards the divide and
mirrors the original `qty>=delivered_qty` which on MariaDB excluded x/0 NULL
rows), get_indented_qty, get_planned_qty and set_stock_balance_as_per_serial_no
to frappe.qb / get_all / db.count. Same result on MariaDB; valid under Postgres.

Tests (new test_stock_balance.py): get_reserved_qty SO-item + packed-bundle
branches and get_indented_qty, all without delivery so they avoid the unrelated
#39 SLE-repost path and pass on Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 09:39:43 +05:30
Mihir Kandoi
85556913d6 Merge pull request #56185 from mihir-kandoi/pg-material-request
refactor(stock): port Material Request raw SQL to qb/ORM (Postgres compat + TIMEDIFF fix)
2026-06-20 09:36:54 +05:30
Mihir Kandoi
4062f72bdb refactor(stock): convert Material Request raw SQL to ORM
validate_qty_against_so: the already-indented (Material Request Item) and
Sales-Order-qty (Sales Order Item) sum lookups -> frappe.get_all({SUM}).
check_modified_date: raw `select modified` + MariaDB-only `TIMEDIFF` ->
frappe.db.get_value + a get_datetime() comparison. The TIMEDIFF removal also
fixes a real Postgres bug: update_status() (Stop/Reopen/Cancel) ran TIMEDIFF,
which errors on PG (`function timediff does not exist`); this greens 7
previously-failing status-change tests on Postgres.

Same result on MariaDB. Tests: concurrent-modification guard (pass + throw
branches) and the over-request-against-SO throw (both converted SUM queries +
the boundary). mapper.py is intentionally left untouched (no raw SQL; its
staging copy predates develop's RFQ cost_center field-map).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 09:17:07 +05:30
Mihir Kandoi
4479d7ff18 Merge pull request #56181 from mihir-kandoi/pg-delivery-note
refactor(stock): port Delivery Note & Delivery Trip raw SQL to qb/ORM (Postgres compat)
2026-06-20 00:21:37 +05:30
Mihir Kandoi
5b8ba4bd52 Merge pull request #56179 from mihir-kandoi/pg-stock-masters
refactor(stock): port masters/settings/dashboards raw SQL to qb/ORM (Postgres compat)
2026-06-20 00:21:25 +05:30
Mihir Kandoi
7f81ffca23 refactor(stock): convert Delivery Trip contact/address lookups to qb
get_default_contact / get_default_address: raw correlated-subquery SELECTs over
Dynamic Link -> frappe.qb with a LEFT join (preserving the original
correlated-subquery semantics: a Dynamic Link whose parent Contact/Address is
missing still returns, with a NULL flag). Same result on MariaDB; valid under
Postgres.

Tests: pin the converted query output (real linked Contact/Address) and lock
the LEFT-join choice with an orphaned-Dynamic-Link case (fails under an inner
join).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:01:05 +05:30
Mihir Kandoi
28f6994520 refactor(stock): convert DN billed-amount SUM to get_all
update_billed_amount_based_on_so: raw "select sum(amount) ... where
dn_detail=%s and docstatus=1" -> frappe.get_all(fields=[{SUM: amount}]); the
bare aggregate needs no GROUP BY and the NULL-sum still resolves to 0. Same
result on MariaDB; valid under Postgres. Covered by the existing billing tests
in test_delivery_note.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:01:05 +05:30
Mihir Kandoi
6c96606c18 refactor(stock): convert packing-slip cancellation lookup to get_all
cancel_packing_slips: raw "SELECT name FROM `tabPacking Slip` WHERE
delivery_note=%s AND docstatus=1" -> frappe.get_all(pluck="name") with
pluck-aware iteration. Same result on MariaDB; valid under Postgres.

Covered by test_cancel_packing_slips_cancels_submitted_slips in
test_delivery_note.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:01:05 +05:30
Mihir Kandoi
ff4adce91b refactor(stock): convert Delivery Note raw SQL to ORM
set_actual_qty Bin lookup -> frappe.db.get_value; validate_proj_cust raw
"customer=%s OR ifnull(customer,'')=''" -> get_all or_filters with
[customer, is, not set] (correct PG empty-string/NULL handling); the two
check_next_docstatus implicit comma-joins -> get_all on the child table
(Sales Invoice Item / Installation Note Item, docstatus=1). Same result on
MariaDB; valid under Postgres.

Tests: validate_proj_cust mismatch + no-customer (the or_filters branch), and
check_next_docstatus blocking cancel when a submitted Sales Invoice draws from
the DN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:01:05 +05:30
Mihir Kandoi
48bbf66422 refactor(stock): convert packing slip item search to qb.get_query
Replace the raw SELECT with get_match_cond in item_details with
frappe.qb.get_query(ignore_permissions=False) plus a Delivery Note Item
subquery; get_query applies the permission match conditions. Same result on
MariaDB; valid under Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:58 +05:30
Mihir Kandoi
f9732efb23 refactor(stock): convert stock settings checks to ORM/qb
Replace the get_all(limit=1, pluck) existence checks with frappe.db.exists and
the no-own-valuation-method Stock Ledger Entry EXISTS with a frappe.qb
subquery (null-or-empty valuation_method preserved). Same result on MariaDB;
valid under Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:58 +05:30
Mihir Kandoi
3e801a2067 refactor(stock): convert serial_no lookups to ORM
Replace the on_trash Stock Ledger Entry serial-match SELECT and the
update_maintenance_status expiry SELECT with frappe.get_all (or_filters for
the amc/warranty expiry OR). Same result on MariaDB; valid under Postgres.

Tests: maintenance-status expiry transition, the not-in exclusion (with a
negative-control candidate), and NULL-status handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:58 +05:30
Mihir Kandoi
d61720c3e2 refactor(stock): convert Job Card reference update to qb
Replace the raw f-string UPDATE (name + production_item match) that links a
Quality Inspection back to its Job Card with frappe.qb.update. Same result on
MariaDB; valid under Postgres.

Tests: the Job Card reference update and its production_item scoping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:57 +05:30
Mihir Kandoi
d955122c88 refactor(stock): convert Item Price bulk update to qb
Replace the raw UPDATE ... modified=NOW() in update_item_price with
frappe.qb.update (now() for modified). Same result on MariaDB; valid under
Postgres.

Tests: currency/buying/selling/modified propagation and price-list scoping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:57 +05:30
Mihir Kandoi
b0d9208561 refactor(stock): convert get_alternative_items UNION to ORM
Replace the raw UNION of forward/two-way alternative-item matches with two
frappe.get_all calls, order-preserving dedup (dict.fromkeys) and Python
pagination. Each leg is bounded to start+page_len rows so the per-keystroke
search round trip stays small (the original bounded with LIMIT/OFFSET);
ItemAlternative forbids duplicate (item_code, alternative_item_code) pairs, so
each leg is internally distinct and that bound is exact. Same result on
MariaDB; valid under Postgres.

Tests: both-direction dedup, txt filtering, pagination, bounded-and-exact
page-walk reconstruction, and case-insensitive (ILIKE-on-Postgres) matching.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:57 +05:30
Mihir Kandoi
f03a81b943 refactor(stock): use get_all for warehouse subtree in capacity dashboard
Replace the raw lft/rgt SELECT with frappe.get_all(pluck="name"). Same result
on MariaDB; valid under Postgres.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:57 +05:30
Mihir Kandoi
497a0abb07 refactor(stock): build item-group filter via qb in item_dashboard
Replace the raw EXISTS subquery (items within an item-group subtree) with
frappe.qb (item_group.isin(subquery)). Same result on MariaDB; valid under
Postgres' stricter SQL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:00:57 +05:30
Mihir Kandoi
884f57d5f6 Merge pull request #56182 from mihir-kandoi/pg-fix-timesheet-summary-flaky
test(projects): fix time-of-day flaky daily-timesheet-summary test
2026-06-20 00:00:17 +05:30
Mihir Kandoi
9dcd561778 test(projects): fix time-of-day flaky daily-timesheet-summary test
make_timesheet(simulate=True) logs at now_datetime(); when the suite runs late
in the day under the site timezone the 2h log crosses midnight, so its to_time
falls outside the report's `to_time <= end-of-day` bound and the submitted
timesheet is (correctly) excluded — making test_submitted_timesheet_in_summary
fail in an evening window (observed at 22:51 IST in CI). Pin the log to a fixed
mid-day window on today so the assertion is time-of-day independent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 23:29:45 +05:30
Mihir Kandoi
a5f21331a4 Merge pull request #56178 from mihir-kandoi/pg-misc
refactor(postgres): port Telephony/Quality/Bulk-Transaction/Utilities/Portal queries to the query builder
2026-06-19 21:41:37 +05:30
Mihir Kandoi
be21f56771 refactor(postgres): port payment_setup_certification query to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:13 +05:30
Mihir Kandoi
fbcec6e75f refactor(postgres): port rename_tool get_doctypes to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:11 +05:30
Mihir Kandoi
5fcaa54f04 refactor(postgres): port bulk_transaction_log existence check to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:10 +05:30
Mihir Kandoi
c18ca7af22 refactor(postgres): port quality_procedure on_trash to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:08 +05:30
Mihir Kandoi
1cfae33fb0 refactor(postgres): port transaction_base delete_events to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:06 +05:30
Mihir Kandoi
5548c3a713 refactor(postgres): port support index favorite-articles query to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:21:03 +05:30
Mihir Kandoi
928bbf22d2 refactor(postgres): port call_log link_existing_conversations to the query builder
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 21:10:00 +05:30
Mihir Kandoi
57e44b3a5f Merge pull request #56173 from mihir-kandoi/pg-manufacturing-maintenance
refactor(postgres): port Manufacturing & Maintenance module queries to the query builder
2026-06-19 18:57:06 +05:30
mergify[bot]
a01da137ba Merge pull request #56066 from frappe/l10n_develop
fix: sync translations from crowdin
2026-06-19 13:24:25 +00:00