Commit Graph

59031 Commits

Author SHA1 Message Date
Mihir Kandoi
7d205c89ea test: add test case 2026-06-22 14:26:28 +05:30
Mihir Kandoi
98f5116a09 fix: party specific item doesnt work if there are 2 suppliers with same item 2026-06-22 14:10:45 +05:30
Nabin Hait
fc0dee5730 Merge pull request #56291 from frappe/chore/rfq-sq-test-coverage
test: RFQ + Supplier Quotation coverage; fix broken SQ expiry task
2026-06-22 13:10:19 +05:30
Mihir Kandoi
14b1250ea7 Merge pull request #56284 from mihir-kandoi/party-alias
feat: party aliases
2026-06-22 12:43:48 +05:30
Nabin Hait
dd4371e35b fix: repair broken Supplier Quotation expiry scheduled task
set_expired_status passed filters= and fieldname= kwargs that
frappe.db.set_value does not accept, so the daily scheduled task threw
TypeError on every run and quotations were never marked Expired. Pass
the filter dict as the positional docname argument, and scope it to
submitted documents so draft quotations aren't wrongly expired (matching
the selling Quotation behaviour).

Adds coverage for valid-till validation, the expiry task, and the
RFQ quote-status round-trip on submit/cancel.
2026-06-22 12:38:32 +05:30
Nabin Hait
618ee6ddeb test: add RFQ duplicate-supplier, scorecard-block and status coverage 2026-06-22 12:38:31 +05:30
Ankush Menat
7256fc98e9 ci: Wait for processes to die (#56288) 2026-06-22 12:25:16 +05:30
Mihir Kandoi
5e16d41387 feat: party aliases 2026-06-22 12:23:44 +05:30
Mihir Kandoi
73459af908 Merge pull request #56283 from frappe/revert-56261-qcs-uae-regional
revert: "feat: Enhance UAE VAT Reports, UAE FTA Audit File and Add VAT Register"
2026-06-22 11:03:17 +05:30
Mihir Kandoi
cfaaf43381 Revert "feat: Enhance UAE VAT Reports, UAE FTA Audit File and Add VAT Register" 2026-06-22 10:42:57 +05:30
Bibin
7b84478c70 Merge pull request #56261 from bibinqcs/qcs-uae-regional
feat: Enhance UAE VAT Reports, UAE FTA Audit File and Add VAT Register
2026-06-22 10:32:38 +05:30
Mihir Kandoi
bae0263990 Merge pull request #56281 from mihir-kandoi/pg-greptile-config
ci(greptile): enforce MariaDB↔PostgreSQL parity in PR review
2026-06-22 09:54:53 +05:30
Mihir Kandoi
c1a3685d14 Merge pull request #56279 from mihir-kandoi/subcontracting-inward-controller-cleanup
fix(subcontracting): correct FG-warehouse validation message + controller cleanup
2026-06-22 09:38:48 +05:30
Mihir Kandoi
3e2d61262a ci(greptile): widen guide scope to SQL-bearing non-Python files; fix HAVING-alias wording
Address Greptile review:
- customContext.files scope was **/*.py only, so Query Report SQL in .js/.sql/report .json
  files didn't get the guide attached as context (the global instructions still applied).
  Widen to .py/.js/.sql/report **/*.json.
- The guide's HAVING-alias rule said "with no GROUP BY"; PostgreSQL rejects a SELECT-alias in
  HAVING regardless of GROUP BY. Reworded to match (repeat the expression, or move a
  non-aggregate predicate to WHERE).
2026-06-22 09:34:23 +05:30
Mihir Kandoi
4a690c86d2 ci(greptile): teach the review bot to enforce MariaDB↔PostgreSQL parity
The PostgreSQL server-test job is label-gated, so until it is required the Greptile
PR-review bot is the always-on guard against cross-engine breaks. Extend
.greptile/config.json with `instructions` (and a `customContext` reference to a new
guide) so every review flags new/changed queries that would error on PostgreSQL or
silently diverge from MariaDB, under the prime rule that MariaDB output must not change.

- .github/POSTGRES_COMPATIBILITY.md — the catalogue the bot (and contributors) follow:
  hard breaks (loose GROUP BY, MySQL-only funcs, UPDATE..JOIN, HAVING-on-alias,
  DISTINCT+ORDER BY, single-quoted alias, varchar bitwise OR, capital identifiers,
  set_value(Check,bool)), silent divergences (text case-sensitivity, name-lookup case,
  empty-string↔NULL, NULL ordering, ORDER BY..LIMIT 1 tiebreakers, integer division,
  distinct-drops-ORDER-BY-on-PG + casefold sorting, function-rewrite parity, UnixTimestamp
  TZ), the GROUP BY row-count trap (Max()-wrap vs add-to-GROUP-BY; FD-by-source-table),
  the InFailedSqlTransaction/savepoint rule, and the false positives NOT to flag
  (.like→ILIKE, ifnull/backtick/LOCATE/REGEXP auto-translation, MariaDB-changing tiebreakers).
- Existing disabledLabels and frappe/frappe context are preserved.
2026-06-22 09:29:19 +05:30
Mihir Kandoi
b129daedc8 refactor(subcontracting): dedup validate_manufacture consumption checks
The `skip_transfer` and transfer branches of `validate_manufacture` ran the
same per-item validation loop — look the row up or throw "not a part of",
check overconsumption, guard against duplicates, record — differing only in
the data source (SCIO Received Item vs Work Order Item), the available-qty
basis, the source-warehouse check (skip_transfer only) and the message text.

Split each branch into a small method that builds a normalised
`{item_code: {consumed_qty, available_qty}}` lookup, and share the loop via
`_validate_customer_provided_consumption`. Branch-specific throw messages are
passed as callbacks so the user-facing strings (and their translations) are
unchanged, and the order in which checks fire is preserved. Also drops the
unused `name` column from the skip_transfer query.

Adds a test for the non-skip-transfer manufacture flow (Material Transfer for
Manufacture -> Manufacture), which exercises the Work Order branch that the
existing suite — all of whose manufacture tests set skip_transfer=1 — never
covered. Full subcontracting-inward suite passes on MariaDB and PostgreSQL.
2026-06-22 09:18:45 +05:30
Mihir Kandoi
23f1fc6235 refactor(subcontracting): use f-string for fg reference search filter
`get_fg_reference_names` built its LIKE filter with old-style
`"%%%s%%" % txt`. Use an f-string (`f"%{txt}%"`) for readability; the value
is still passed as a parameterised filter, so behaviour is unchanged.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
19466b24b0 perf(subcontracting): compute child idx once per insert loop
Each new child row was given `idx=frappe.db.count(...) + 1`, issuing a count
query per inserted row across three insert loops (received items on receipt,
self-procured RM on manufacture, secondary items on manufacture). Compute the
starting index once before each loop and increment a local counter, producing
the same idx sequence with a single count query.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
63c5dccb4b fix(subcontracting): guard empty raw-material list before strict zip
`update_inward_order_received_items_for_manufacture` unpacks
`zip(*item_code_wh.keys(), strict=True)`. When the manufacture entry has no
raw-material rows (all rows are finished/secondary/scrap), `item_code_wh` is
empty and the unpack raises `ValueError: not enough values to unpack`.

Return early when there are no such rows, mirroring the `if secondary_items:`
guard already present in `update_inward_order_secondary_items`.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
8218875733 refactor(subcontracting): fetch customer_warehouse only when needed
In `validate_manufacture`, `customer_warehouse` is read only inside the
`skip_transfer` branch but was fetched unconditionally, wasting a lookup on
the non-skip-transfer path. Move it inside the branch that uses it.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
e422c4d2ab perf(subcontracting): hoist Work Order Item lookup out of transfer loop
`validate_material_transfer` ran the `Work Order Item` query and rebuilt
`wo_item_dict` inside the per-item loop, even though both depend only on
`self.work_order`. For an entry with N customer-provided rows that meant N
identical queries. Build the lookup once before the loop.

`validate_manufacture` already builds the analogous dict once up front, so
this also aligns the two methods.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
a342db38de refactor(subcontracting): drop redundant scio_item_name check
In `update_inward_order_item`, the walrus assignment `scio_item_name :=` is
already part of the truthy `if` condition, so the nested `if scio_item_name:`
is always true. Remove it and dedent the body.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
57f5186dff refactor(subcontracting): hoist ValueWrapper import to module level
`validate_delivery_on_save` imported `pypika.terms.ValueWrapper` inside its
per-item loop, re-running the import on every iteration. Move it to the
module-level imports.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
8fc7cb0117 refactor(subcontracting): drop unused format arg in overconsumption message
The "exceeds quantity available" throw in `validate_manufacture` passes a
third positional arg (`item.transfer_qty`), but the message only has `{0}`
and `{1}` placeholders, so `str.format` silently discards it. Remove the
dead argument; no behaviour change.
2026-06-22 08:00:15 +05:30
Mihir Kandoi
48d49cdcd2 fix(subcontracting): fix format placeholders in FG warehouse validation message
`validate_manufacture` builds its "Target Warehouse for Finished Good must
be same as Finished Good Warehouse ..." message with placeholders `{1}` and
`{2}`, but only passes two positional args (indices 0 and 1). `str.format`
raises `IndexError: Replacement index 2 out of range` instead of rendering
the message, so a user who sets the wrong FG target warehouse gets an opaque
traceback rather than the intended validation error.

Renumber the placeholders to `{0}` and `{1}` to match the args.
2026-06-22 08:00:14 +05:30
Mihir Kandoi
f001d13447 Merge pull request #56278 from mihir-kandoi/pg-purchase-register-colorder
fix(accounts): keep Purchase Register account-column order identical across engines
2026-06-22 07:53:03 +05:30
Mihir Kandoi
6ef9020134 Merge pull request #56277 from mihir-kandoi/pg-sales-register-colorder
fix(accounts): keep Sales Register account-column order MariaDB-faithful on both engines
2026-06-22 07:49:49 +05:30
Mihir Kandoi
c8e294b416 Merge pull request #56276 from mihir-kandoi/pg-customer-name-suffix
fix(selling): match MariaDB's customer-name suffix extraction on Postgres
2026-06-22 07:48:20 +05:30
Mihir Kandoi
9abdf8527e Merge pull request #56275 from mihir-kandoi/pg-procurement-tracker-rowcount
fix(buying): keep Procurement Tracker one row per (PO, material_request_item) (MariaDB parity)
2026-06-22 07:47:52 +05:30
Mihir Kandoi
cf075bd67e fix(accounts): keep Purchase Register account-column order identical across engines
get_account_columns fetched the dynamic expense / unrealized-P&L account lists with
frappe.get_all(distinct=True, order_by=...). frappe silently drops ORDER BY for
distinct queries on postgres (db_query), so the generated account columns came back
in arbitrary order on Postgres while MariaDB kept them ordered — a cross-engine
parity gap (the sibling Sales Register had already moved to a python sort).

Sort the lists in python with key=str.casefold (dropping the ignored order_by) so the
column order is deterministic, case-insensitive (matching MariaDB's collation), and
identical on both engines. Add a regression test with two case-colliding expense
account names asserting the casefold column order on both engines.
2026-06-22 07:30:35 +05:30
Mihir Kandoi
e26a499923 fix(accounts): keep Sales Register account-column order MariaDB-faithful on both engines
get_account_columns sorts the dynamic income / unrealized-P&L account columns with
python sorted() (the original raw SQL used ORDER BY, which frappe drops for distinct
queries on postgres). Plain sorted() is case-sensitive (ASCII), so it reordered the
columns versus the pre-effort MariaDB output, whose ORDER BY ran under the
case-insensitive utf8mb4 collation.

Sort with key=str.casefold so the column order matches MariaDB's collation and is
identical on MariaDB and Postgres. Add a regression test with two case-colliding
account names ("aaa ..." / "ZZZ ...") that fails on case-sensitive sort and passes
after, on both engines.
2026-06-22 07:30:16 +05:30
Mihir Kandoi
53491e2008 fix(selling): match MariaDB's customer-name suffix extraction on Postgres
get_customer_name's Postgres branch extracted the PURE TRAILING digits of the
name (regexp '^.*?(\d*)$'), while the MariaDB branch uses
CAST(SUBSTRING_INDEX(name, ' ', -1) AS UNSIGNED) — the LEADING digits of the last
whitespace token. For a scanned name like "<base> - 3a" MariaDB yields 3 but
Postgres yielded NULL→0, so the next de-duplicated number (and thus the generated
Customer name) diverged between engines.

Make the Postgres branch take the last whitespace token then its leading digits,
mirroring MariaDB exactly ("X - 3a"->3, "X - 1.5"->1, "X - Foo"->0). Add a
regression test with a "<base> - 3a" name asserting the next name is "<base> - 4"
on both engines (it produced "<base> - 1" on the old Postgres regex).
2026-06-22 07:29:56 +05:30
Mihir Kandoi
29c29fd335 fix(buying): keep Procurement Tracker one row per (PO, material_request_item)
The Postgres-portability change added the Purchase Order Item PK (child.name) to
get_po_entries' GROUP BY. material_request_item is blank for PO lines not sourced
from a Material Request, so a multi-line PO previously collapsed to ONE row per
(PO, blank) on MariaDB but now produced one row PER LINE — changing the MariaDB
row count (and the add_total_row totals).

Group only by (PO, material_request_item) — the pre-effort key — and Max()-
aggregate the other selected columns so the query stays valid on Postgres while
restoring the prior one-row-per-group MariaDB output (per-column arbitrary→
deterministic, row count preserved). Add a regression test with a two-line PO
that fails on the multi-column GROUP BY (2 rows) and passes after (1 row), on
both MariaDB and Postgres.
2026-06-22 07:29:24 +05:30
Diptanil Saha
c188ed59ec fix(lead): added missing read permission check on get_lead_details (#56272) 2026-06-21 21:46:53 +00:00
MochaMind
8fef286327 fix: sync translations from crowdin (#56205) 2026-06-21 21:39:11 +00:00
Diptanil Saha
ea45d41314 fix: escape user image url on various templates (#56269) 2026-06-22 02:52:38 +05:30
Mihir Kandoi
ef53319183 Merge pull request #56267 from mihir-kandoi/pg-wo-stock-report-test
test(manufacturing): cover Work Order Stock report duplicate-item row count
2026-06-22 01:31:19 +05:30
Mihir Kandoi
b8bbcda047 Merge pull request #56265 from mihir-kandoi/pg-trends-rowcount
fix(controllers): keep Sales/Purchase Trends one row per based-on key (MariaDB parity)
2026-06-22 01:25:01 +05:30
Mihir Kandoi
d65098fe24 test(manufacturing): cover Work Order Stock report duplicate-item row count
Add a regression test for the one-row-per-item invariant: a BOM that lists the
same raw item on two lines at different qty must still be counted once in the
report ("# Req'd Items" == 1). The test fails on the pre-fix multi-column GROUP
BY (which split the item into one row per distinct stock_qty -> 2) and passes
after the fix, on both MariaDB and Postgres.
2026-06-22 01:11:01 +05:30
Mihir Kandoi
8f1c703871 fix(controllers): keep Supplier trends one row per supplier + add regression tests
The earlier parity fix aggregated the non-key descriptive columns for the Item
and Customer based-on paths but left Supplier grouping by all three selected
columns (supplier, supplier_name, supplier_group). supplier_name is a stored
per-transaction field, so historical purchase docs holding a divergent value for
the same supplier would split one supplier into multiple rows — diverging from
the original MariaDB output, which grouped by t1.supplier only.

Aggregate supplier_name with Max() and keep only supplier + the FD master column
supplier_group in GROUP BY, restoring one row per supplier on both engines.

Add regression tests for the Supplier (purchase) and Customer (sales) paths that
assert a single row per key even when stored descriptive fields diverge; both
fail on the pre-fix multi-column GROUP BY and pass after the fix, on MariaDB and
Postgres.
2026-06-22 00:58:56 +05:30
Mihir Kandoi
7f6004bfd9 Merge pull request #56128 from Henil666/fix/mt940-label-typo
fix: correct typo in Bank Statement Import MT940 label
2026-06-22 00:34:07 +05:30
Bibin
d0988dc32c fix(UAE VAT 201): bypass helper cache in tests
frappe.local is request-scoped, not test-scoped — it survives
across unit-test methods. Two tests calling get_standard_rated_
expenses_total({"company": "_Test Company UAE VAT"}) hit the
same cache key, so the second test (foreign-currency PI, expected
917.5) was seeing 250 carried over from the first.

Short-circuit @_cached on frappe.flags.in_test so each test method
queries fresh. Production callers run one execute() per request and
have the cache cleared at the top of that call, so the optimisation
still applies there.
2026-06-21 17:25:19 +00:00
Mihir Kandoi
dbd1388b40 fix(controllers): keep Sales/Purchase Trends one row per based-on key (MariaDB parity)
#56192 made the trends queries Postgres-strict-GROUP-BY-valid by widening based_on_group_by
to include the selected descriptive columns. For Item it added t2.item_name, for Customer
t1.territory (and customer_name) — but item_name is an editable per-line field and territory an
editable per-document field, not functionally dependent on the item_code/customer key. On MariaDB
(ONLY_FULL_GROUP_BY off) this SPLITS the single row per key into one row per distinct
(key, item_name)/(key, territory), so a customer transacting across two territories (or an item
with an edited item_name) now shows duplicate rows with fractured per-period subtotals.

Group by the KEY only and aggregate the non-key descriptive columns with Max(): one row per
based-on key (identical to the pre-#56192 MariaDB output) and still Postgres-valid. Supplier
columns are master-joined / fetch-locked (functionally dependent) so they stay unchanged.
2026-06-21 22:38:30 +05:30
Mihir Kandoi
6395d968ad Merge pull request #56260 from mihir-kandoi/pg-wo-stock-report-rowcount
fix(manufacturing): keep Work Order Stock report one row per item (MariaDB parity)
2026-06-21 22:21:21 +05:30
Bibin
a8b6bcacc5 fix(FTA Audit File): block regeneration from Generated state
The JS button only renders the Generate/Retry action for Draft and
Error; the REST endpoint, however, still let an authenticated caller
silently overwrite the attached CSV on a Generated FAF. Tighten the
server-side guard to match the UI lifecycle so the destructive
action has to be explicit (delete and create a new doc to regenerate).
2026-06-21 16:43:13 +00:00
Sudharsanan Ashok
130c2594e1 fix(stock): update voucher valuaion rate in sle (#55960) 2026-06-21 21:50:19 +05:30
Bibin
f78683c14b fix(UAE Regional): address greptile review findings
- Gate generate_faf() and mark_as_submitted() on write permission so
  REST callers without write access can no longer trigger state
  changes via the whitelisted endpoints.
- Drop test_generate_faf_excise_not_yet_implemented; the Excise file
  type is no longer a valid Select option, so doc.insert() now fails
  before generate_faf() is reached.
- Stream GL Entry rows in pages of GL_PAGE_SIZE to bound memory on
  multi-year exports against large companies; running balance,
  account-name cache, and totals carry across batches so output is
  byte-identical to the single-fetch implementation.
- Move the VAT 201 helper cache from a module-level dict to
  frappe.local so concurrent requests on threaded workers no longer
  race or leak data across users.
2026-06-21 15:33:26 +00:00
Bibin
73166979a2 test(FTA Audit File): drop redundant tearDown override
ERPNextTestSuite already calls frappe.db.rollback() in its base
tearDown; overriding (even with the same call) trips the
semgrep "Dont-override-teardown" rule.
2026-06-21 15:26:10 +00:00
Bibin
dffe4bd22d feat(FTA Audit File): Enhance FAF generation logic and error handling; update currency handling in VAT reports 2026-06-21 15:19:48 +00:00
Bibin
806f30fa87 refactor: FTA Audit File and UAE VAT Reports 2026-06-21 15:19:48 +00:00