Commit Graph

13176 Commits

Author SHA1 Message Date
ruthra kumar
314aa303e5 Merge pull request #56417 from ruthra-kumar/configurable_timeout_on_process_pcv
refactor: configurable timeout on process pcv
2026-06-24 13:27:43 +05:30
ruthra kumar
3da7eefebb refactor: patch, display depends on and json changes 2026-06-24 13:03:07 +05:30
ruthra kumar
13b6c4a165 feat(accounts): add configurable job timeout for Process Period Closing Voucher
Adds a `pcv_job_timeout` Int field (default 3600s) to Accounts Settings
so admins can tune the enqueue timeout for PCV background jobs without
a code change. All three `frappe.enqueue` calls in
`process_period_closing_voucher.py` now read this value at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 12:57:31 +05:30
Ravibharathi
1e37c4b9ac feat(opening invoice creation tool): add project to opening invoice child row (#54662) 2026-06-24 02:16:05 +05:30
Ravibharathi
32e971e374 fix(payment_entry): recompute base amount when exchange rate changes (#56136)
Co-authored-by: ervishnucs <ervishnucs369@gmail.com>
2026-06-24 01:59:16 +05:30
Diptanil Saha
b356dbd59e fix(budget): ambiguous error message for budget assignment validation (#56390)
Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
2026-06-23 17:18:08 +00:00
Shllokkk
934b1ff7dd Merge pull request #56337 from Shllokkk/cust-supp-dashboard
fix: show contextual balance label on party dashboard for net balances
2026-06-23 21:22:35 +05:30
Mihir Kandoi
8591a0b6ad Merge pull request #56378 from mihir-kandoi/pg-audit13-fixes
fix(postgres): three parity fixes — POS NULL ordering, traceability div-by-zero, LIKE on non-text
2026-06-23 20:10:20 +05:30
Mihir Kandoi
8960e3ff4a fix(accounts): cast non-text Account fields for LIKE filters (Postgres)
Financial Report Template calculation_formula filters are user-authored and
only validated for field existence + operator membership, not that a
like/ilike operator targets a text field. A filter such as
["is_group", "like", "1"] builds `is_group ILIKE '%1%'`; PostgreSQL has no
LIKE/ILIKE operator for a smallint/int/numeric column
(`operator does not exist: smallint ~~* unknown`) and aborts the report, while
MariaDB implicitly casts the numeric column to text and matches.

For like-family operators, cast a numeric/Check Account field to varchar
(`Cast_(field, "varchar")`), reproducing MariaDB's implicit numeric->text
coercion on both engines. Text-field filters (the normal account_name/
account_number case) are left untouched, so MariaDB output is unchanged.
2026-06-23 19:38:27 +05:30
Mihir Kandoi
9fdeb5f991 fix(accounts): make two Query Report SQLs valid on Postgres (loose GROUP BY + no-op ORDER BY) (#56369)
* fix(accounts): wrap loose finance_book in max() in Trial Balance (Simple) (Postgres)

The Trial Balance (Simple) Query Report selects `finance_book` but groups only
by `fiscal_year, company, posting_date, account`. PostgreSQL rejects the
non-grouped, non-aggregated column:

    column "tabGL Entry.finance_book" must appear in the GROUP BY clause or be
    used in an aggregate function

MariaDB tolerates it and returns an arbitrary finance_book per group.

Wrapping it in `max(finance_book)` keeps the row count identical (the GROUP BY
is unchanged) and makes PostgreSQL valid. Adding finance_book to GROUP BY would
split each group into N rows and change the MariaDB row count, so it is not an
option. This replaces MariaDB's previously arbitrary finance_book value with a
deterministic one (the only sanctioned MariaDB-output change); the row count is
preserved.

* fix(accounts): make Sales Partners Commission valid on Postgres (ORDER BY + div-by-zero)

The Sales Partners Commission Query Report had two PostgreSQL problems:

1. It ended with `ORDER BY "Total Commission:Currency:120"`, but the alias the
   query produces is `"Total Commission:Currency:170"` (width 170, not 120), so
   the ORDER BY never referenced a real output column. On MariaDB a double-quoted
   token is a string literal — a no-op sort that never errored. On PostgreSQL a
   double-quoted token is an identifier, so it errors with
   `column "Total Commission:Currency:120" does not exist` (and single-quoting it
   instead trips `non-integer constant in ORDER BY`). Since the clause was always
   a no-op on MariaDB, it is removed — MariaDB's group-order output is unchanged
   and the report runs on PostgreSQL.

2. `sum(total_commission)*100 / sum(amount_eligible_for_commission)` has an
   unguarded divisor: the inner query filters `total_commission`/`base_net_total`
   but not `amount_eligible_for_commission`, so a partner whose rows sum to 0
   there makes MariaDB return NULL but PostgreSQL raise `division by zero`. Wrap
   it in `NULLIF(sum(amount_eligible_for_commission), 0)` — NULL on both engines.

Both verified live on MariaDB and PostgreSQL.
2026-06-23 13:53:38 +00:00
Mihir Kandoi
4aef3aa5b3 Merge pull request #56368 from mihir-kandoi/pg-divzero-nullif-guards
fix: guard division-by-zero divisors across reports/doctypes (Postgres parity)
2026-06-23 19:21:12 +05:30
Mihir Kandoi
df6437c49d Merge pull request #56335 from aerele/fix/skip-over-allowance-for-non-stock-items
fix: skip over-allowance qty validation for non-stock items
2026-06-23 19:14:42 +05:30
Mihir Kandoi
71685532bd Merge pull request #56367 from mihir-kandoi/pg-asset-depr-coalesce-reship
fix(accounts): stop coalescing a DATE with an int in Asset Depreciations report
2026-06-23 18:45:00 +05:30
Mihir Kandoi
297153264b fix(accounts): guard last-GLE exchange-rate division against a zero divisor (Postgres)
calculate_exchange_rate_using_last_gle divides (debit - credit) by
(debit_in_account_currency - credit_in_account_currency). The GL row is
re-selected by (voucher_type, voucher_no, account) ordered by posting_date
WITHOUT the "(debit_in_account_currency > 0) | (credit_in_account_currency > 0)"
filter the first query used, so the chosen row can have equal/zero account-
currency amounts, making the divisor 0.

MariaDB returns NULL for x/0 (the caller maps it via `or 0.0`); PostgreSQL
raises `division by zero` and aborts. Wrapping the divisor in NullIf(divisor, 0)
yields NULL on both engines, so MariaDB output is unchanged and PostgreSQL no
longer errors.
2026-06-23 17:49:12 +05:30
Mihir Kandoi
27ec5eabc6 fix(accounts): stop coalescing a DATE with an int in Asset Depreciations report
The Asset Depreciations and Balances report tested disposal status with
IfNull(asset.disposal_date, 0) != 0 / == 0 — coalescing the DATE column
disposal_date with the integer 0. frappe.qb renders this as
COALESCE("disposal_date", 0); PostgreSQL requires COALESCE arguments to
share a type and raises:

    psycopg2.errors.DatatypeMismatch: COALESCE types date and integer
    cannot be matched

The predicate is in the WHERE/CASE of every query the report runs (both
group_by=Asset Category and group_by=Asset), so the whole report errored
on PostgreSQL. MariaDB's IFNULL(date, 0) is permissive and worked.

Replace each comparison with the null-test form already used elsewhere in
this same file: IfNull(disposal_date, 0) != 0 -> disposal_date.isnotnull(),
== 0 -> disposal_date.isnull(). Semantically identical (a stored date is
never 0), valid on both engines, MariaDB output unchanged.
2026-06-23 17:43:49 +05:30
Lakshit Jain
7b659ee6af fix: whitelist get_payment_terms api (#55850)
Co-authored-by: Abdeali Chharchhoda <abdealiking786@gmail.com>
2026-06-23 17:37:15 +05:30
Lakshit Jain
4de1064ef6 Merge pull request #56345 from vorasmit/fix-get-tax-rate
fix: whitelist `get_tax_rate`
2026-06-23 17:36:59 +05:30
pandiyan
733e24faef test: add tests for non stock item over billing against so/po 2026-06-23 16:38:38 +05:30
NaviN
d1ffac36c1 fix(payment reconciliation): honour user permissions on accounting dimensions (#55803) 2026-06-23 13:36:29 +05:30
Nabin Hait
ca908b69cf Merge pull request #56246 from nabinhait/commission-fields-depends-on-sales-partner
fix(selling): hide commission fields without a sales partner and stop copying them
2026-06-23 10:42:16 +05:30
vorasmit
48d5e8732b fix: whitelist get_tax_rate 2026-06-23 10:38:58 +05:30
Shllokkk
3251b40365 fix: show contextual balance label on party dashboard for net balances 2026-06-23 01:44:42 +05:30
Mihir Kandoi
32216bd75b fix(pos): return raw Item Group names from get_item_groups (double-escape regression)
The Postgres-portability change moved the POS item-group filters to the query builder
(item.item_group.isin(...)) and frappe.get_all(["name","in",...]), which escape values
once. get_item_groups() still pre-escaped each name with frappe.db.escape(), so the
names were escaped TWICE -> `item_group IN ('''Products''')`, matching nothing. Any POS
Profile that restricts item groups returned ZERO items, on both MariaDB and Postgres.

Return raw names; the parameterized callers escape them correctly. (get_parent_item_group
also returned the quoted literal before this fix.) Add a regression test: a POS Profile
restricted to an item group must still surface that group's items — it returns 0 before
the fix and passes after, on both engines.
2026-06-22 19:00:35 +05:30
Nishka Gosalia
c24fc063fc Merge pull request #56309 from nishkagosalia/migrating-document-naming-setting
fix: Removing the document naming series dialog and moving to framework
2026-06-22 16:07:08 +05:30
nishkagosalia
aa7402b1e3 fix: removing the document naming series dialog and moving to framework 2026-06-22 15:46:15 +05:30
rohitwaghchaure
9b8c363bed feat: capitalize full actual charge on stock items only for Purchase Invoice (#56223)
* feat: capitalize full actual charge on stock items only for Purchase Invoice

Extends #56102 (Purchase Receipt) to the Purchase Invoice GL: an actual
valuation charge (e.g. Freight) flagged 'Allocate Full Amount to Stock Items'
is fully capitalized onto stock/asset items only; when unchecked, only the
stock items' share of a spread-across-all-items charge is capitalized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: aggregate GL rows per account in PI freight test

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 15:39:10 +05:30
Nabin Hait
d0184e07b3 fix(selling): hide commission fields without a sales partner and stop copying them
Across Sales Order, Delivery Note, Sales Invoice and POS Invoice, the
Commission section's commission_rate, total_commission and
amount_eligible_for_commission are sales-partner commission fields:
- depends_on eval:doc.sales_partner so they only show when a Sales Partner
  is set;
- no_copy so a duplicated/amended document does not carry a stale commission
  rate or computed commission amount (the sales partner itself still copies).

depends_on is client-only, so the server-side commission calculation is
unchanged. Add a Sales Order test for the no_copy behaviour.
2026-06-22 14:32:28 +05:30
Nabin Hait
c5ff32aa2f refactor: tidy update_coupon_code_count
Drop the dead 'if coupon:' guard (get_doc would have thrown) and collapse the
duplicate increment branches into a single exhausted-check plus increment.
No behaviour change.
2026-06-22 14:27:16 +05:30
Nabin Hait
eaab71a99e test(coupon_code): cover coupon validation and usage-count edges
Add tests for the previously-untested branches of validate_coupon_code
(not-yet-valid, expired, maximum-use exhausted) and update_coupon_code_count
(releasing a use on cancel, and rejecting use beyond the maximum). Both
functions are now fully covered.
2026-06-22 14:20:41 +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
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
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
Mihir Kandoi
549a24f7b9 ci(postgres): add a static pre-commit check for MySQL-only SQL
The Postgres test job is label-gated, so it does not run on every PR. This adds an
always-on pre-commit hook that statically flags the *mechanical* breaks: MySQL-only
functions (timestamp(date,time), timediff, str_to_date, date_format/add/sub,
group_concat, period_diff, SQL IF()), SHOW INDEX/TABLES/COLUMNS, single-quoted
aliases, UPDATE..JOIN, interpolated/f-string SQL carrying MySQL-isms,
set_value/db_set(<Check>, bool), and MySQL SHOW INDEX result keys.

It deliberately does NOT flag the framework auto-translations (ifnull->coalesce,
backtick/locate/REGEXP, .like()->ILIKE) nor the *semantic* divergences (loose GROUP
BY, case-sensitive ==/IN, NULL ordering, tiebreakers) — those need the test suite,
which remains the backstop. AST + structure-gated regex keep false positives near
zero (docstrings and prose skipped); '# pg-ok' exempts intentional MariaDB-only
branches. Scoped to erpnext/ excluding patches/. Includes a unit test of the checker.
2026-06-21 16:53:53 +05:30
Mihir Kandoi
b97a0c9a13 test(accounts): set Check field with int, not bool (Postgres)
set_value(Company, ..., "book_advance_payments_in_separate_party_account", True)
errored on Postgres (smallint column, boolean expression). Use 1; MariaDB unchanged.
2026-06-21 15:29:25 +05:30
Mihir Kandoi
e076a78003 fix(accounts): set Check field 'reconciled' with int, not bool (Postgres)
frappe.db.set_value(..., "reconciled", True) renders SET reconciled=true; the
column is smallint, which Postgres rejects (DatatypeMismatch). MariaDB coerces the
boolean to 1. Pass 1 so both engines store the same value.
2026-06-21 15:29:19 +05:30
Mihir Kandoi
8a5f659681 test(postgres): make test-helper SQL Postgres-valid across the suite
The repo-wide query audit fixed runtime/source queries, but test files carry their
own raw SQL helpers that were never swept and only fail when the suite runs on
Postgres. Port the staging branch's already-green fixes for them:

- timestamp(posting_date, posting_time) (raw + qb Timestamp) -> posting_datetime /
  CombineDatetime (test_stock_ledger_entry, test_stock_balance, test_utils)
- HAVING <select-alias> -> qb .having(<expr>) (test_asset_capitalization, test_purchase_order)
- capital-cased identifiers ("Status", "Name") -> lowercase (test_delivery_note,
  test_purchase_order, test_employee)
- raw GL/SLE select helpers -> frappe.get_all / qb, with order-independent
  comparisons where account ordering is collation-dependent across engines
  (test_purchase_invoice, test_sales_invoice, test_payment_entry, test_asset,
  test_purchase_receipt, test_payment_request, test_repost_accounting_ledger,
  test_journal_entry)

All changes are test-only and behaviour-identical on MariaDB (lowercase column names
resolve the same; posting_datetime == timestamp(posting_date, posting_time); HAVING on
the expression is the same computation). Verified: the heavy modules pass on both
MariaDB and Postgres, and MariaDB output is unchanged.
2026-06-21 14:05:49 +05:30
Nabin Hait
97acd4b33b Merge pull request #56141 from frappe/refactor/journal-entry-client-script
refactor: simplify Journal Entry client script
2026-06-21 11:54:38 +05:30
Nabin Hait
1de903143a Merge pull request #56150 from nabinhait/refactor-si-intercompany-fixedassets
refactor(sales_invoice): simplify fixed-asset and inter-company validations
2026-06-21 11:48:20 +05:30
Mihir Kandoi
acbf453def fix(accounts): make General Ledger remarks alias Postgres-valid
When Accounts Settings -> general_ledger_remarks_length is set, the GL report
adds `substr(remarks, 1, n) as 'remarks'` to its raw SQL. Postgres treats a
single-quoted column alias as a string literal and raises a syntax error, so
the General Ledger report is broken on Postgres whenever that setting is on.

Use a bare alias (`as remarks`). substr() itself is portable.

Adds a test that sets general_ledger_remarks_length and runs the report,
asserting it executes (and returns rows) on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 09:58:06 +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
Nabin Hait
0bcafa1fde fix: use full refresh instead of refresh_fields for multi currency
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-19 23:16:27 +05:30
rohitwaghchaure
8479a8b4d3 Merge pull request #56102 from rohitwaghchaure/feat-allocate-full-amount-to-stock-items
feat: allocate full actual charge to stock items only (e.g. Freight)
2026-06-19 18:05:18 +05:30
Nabin Hait
be05e01bd7 Merge pull request #56151 from nabinhait/refactor-si-mapper
refactor(sales_invoice): shrink make_inter_company_transaction mapper
2026-06-19 16:47:56 +05:30
Nabin Hait
61927b61fe Merge pull request #56139 from nabinhait/refactor-si-timesheet-billing
refactor(sales_invoice): simplify TimesheetBillingService link decision
2026-06-19 16:47:16 +05:30
Nabin Hait
b8e699b226 refactor(sales_invoice): simplify fixed-asset and inter-company validations
validate_fixed_asset (C/11) is flattened with a guard clause and the per-item
checks move into _validate_fixed_asset_item. validate_inter_company_party
(C/12) splits into _get_inter_company_party_config plus _validate_against_reference
and _validate_internal_party_company (conditions preserved verbatim). No C-rank
function remains in either module.

Characterize the previously-untested asset-sale throws (missing asset, update
stock, return without return-against, selling a sold/scrapped asset) and the
asset-restore note text before the move; behaviour is unchanged (asset and
inter-company suites green).
2026-06-19 16:41:39 +05:30
ruthra kumar
f8550838a3 Merge pull request #55265 from aerele/bank-guarantee-type
fix: update reference doctype mapping and field visibility in bank guarantee
2026-06-19 16:38:53 +05:30
Rohit Waghchaure
9e15e52847 feat: allocate full actual charge to stock items only (e.g. Freight)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 16:35:45 +05:30
Nabin Hait
f24ea74ef8 refactor: simplify Journal Entry client script
- Replace the JournalEntry controller class and cur_frm.cscript free
  functions with frappe.ui.form.on event blocks plus a namespaced
  erpnext.journal_entry helper object
- Drop deprecated APIs: cur_frm/script_manager, add_fetch, $.each and var
- Move the bank_account -> account fetch to fetch_from on the
  Journal Entry Account "account" field
- Keep totals/difference and company-currency conversion on the client
  (cheap, race-free); call the server only to fetch exchange rates
- get_balance now computes its own difference instead of trusting the
  client-sent value, with a regression test
2026-06-19 16:34:31 +05:30