Commit Graph

13210 Commits

Author SHA1 Message Date
Mihir Kandoi
dd600c3a79 fix: rewrite user-facing messages in Accounts module
Conservative cleanup of frappe.throw/msgprint messages per the message
style guide; meaning, severity, and .format() arguments are unchanged:

- index bare {} placeholders as {0}/{1}/... so translators can reorder
- move f-strings / .format() / concatenation out of _() (they break
  gettext extraction and never translate)
- wrap translatable dynamic values (DocType/Select labels) in _()
- fix grammar and colloquialisms ("doesn't belongs" -> "does not belong",
  "till" -> "until", "Rules exists" -> "Rules exist", exclusive "one of
  X and Y" -> "one of X, Y, or Z")
- drop no-op _() wrapping runtime-built HTML strings

Part of #53976.
2026-06-25 15:37:49 +05:30
Smit Vora
cb0689bd1e fix: rewrite item rate calculation (#56315)
Co-authored-by: Harsh Patadia <harsh@Harshs-MacBook-Air.local>
Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
2026-06-25 14:49:11 +05:30
Mihir Kandoi
e8e50edbed fix(postgres): pricing rule priority order diverges on Postgres
_get_pricing_rules orders by priority desc; get_pricing_rules then reads
pricing_rules[0].has_priority. priority is a Select (varchar): unset is '' on
MariaDB but NULL on Postgres, which sorts to the top under DESC and flips the
selection. Order by coalesce(priority, '') desc so the unset value sorts last
('' is the text minimum) on both backends.
2026-06-25 14:39:08 +05:30
ruthra kumar
bd53db61cc Merge pull request #56304 from ruthra-kumar/reports_on_duckdb
feat: faster (synced) financial statements using duckdb
2026-06-25 12:40:08 +05:30
ruthra kumar
963bbc8729 refactor: synced reports should be enabled on sites based on requirements 2026-06-25 12:03:42 +05:30
Khushi Rawat
ce44d9192d Merge pull request #56432 from iamejaaz/fix-letterhead-no-company
fix(letter-head): guard company lookups when doc has no company field
2026-06-25 11:11:23 +05:30
ruthra kumar
6a93baacf0 feat(profit-and-loss): implement execute_synced_report with full parity to normal report
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:01:32 +05:30
ruthra kumar
bb19540816 feat(balance-sheet): implement execute_synced_report with full parity to normal report
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:01:32 +05:30
ruthra kumar
6b4895bcc9 feat(general-ledger): implement execute_synced_report with full parity to normal report
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:01:32 +05:30
ruthra kumar
f40cd41801 refactor: DB agnostic method names 2026-06-25 11:01:32 +05:30
ruthra kumar
5c536b8ad1 refactor: maintain sync dependency in report master 2026-06-25 11:01:32 +05:30
ruthra kumar
55862f98f4 refactor(trial-balance): execute_duckdb only reads GL Entry from duckdb
Replaces the previous over-engineered stub with 7 short functions.
Account data, Account Closing Balance, and all metadata come from
frappe.db as normal; only tabGL Entry is read from the duckdb_conn.

Reuses get_opening_balance() for Account Closing Balance unchanged,
reuses all downstream compute helpers (calculate_values, prepare_data,
etc.) unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:01:32 +05:30
ruthra kumar
b1c8e2cb5c feat(trial-balance): implement execute_duckdb with full parity to normal report
Replaces the placeholder stub with 8 focused functions that mirror the
normal execute() flow using parameterized DuckDB SQL queries: account
fetch, period GL entries, opening balances (with Period Closing Voucher
path), and all filters (cost center, project, finance book, accounting
dimensions). Reuses existing pure-Python processing functions unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:01:32 +05:30
ruthra kumar
adb768505a refactor: reports on duckdb 2026-06-25 11:01:32 +05:30
Mihir Kandoi
3fa7ec656b fix: parse native JSON schedules arg in make_payment_request
make_payment_request(**args) is whitelisted and the client passes
`schedules` as a list, so json.loads(args.get("schedules")) raised
TypeError under JSON body mode. Use frappe.parse_json.
2026-06-24 20:55:01 +05:30
Mihir Kandoi
0e862d61d1 refactor: parse native JSON request args in accounts/services/child_item_update.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
fe8be87200 refactor: parse native JSON request args in accounts/doctype/unreconcile_payment/unreconcile_payment.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
4fc952badf refactor: parse native JSON request args in accounts/doctype/purchase_invoice/mapper.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
668ca62ea5 refactor: parse native JSON request args in accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
ffce7aff55 refactor: parse native JSON request args in accounts/doctype/pricing_rule/utils.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
2bc943c7e2 refactor: parse native JSON request args in accounts/doctype/pricing_rule/pricing_rule.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
2985a8b263 refactor: parse native JSON request args in accounts/doctype/pos_invoice/pos_invoice.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
3b25c2b7c2 refactor: parse native JSON request args in accounts/doctype/payment_request/payment_request.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
28770e3988 refactor: parse native JSON request args in accounts/doctype/payment_entry/payment_entry.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
ecbf8632aa refactor: parse native JSON request args in accounts/doctype/invoice_discounting/invoice_discounting.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
348e3ac4ae fix: handle native JSON request args in financial report template
Replace json.loads(object_hook=...) with frappe.parse_json and wrap each
row in frappe._dict, fixing attribute access when args arrive as native
JSON (list of dicts) instead of a JSON string.
2026-06-24 20:37:31 +05:30
Mihir Kandoi
e37c7e9b32 refactor: parse native JSON request args in accounts/doctype/dunning/dunning.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
475cd83861 refactor: parse native JSON request args in accounts/doctype/bank_transaction/bank_transaction_upload.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
9d8f6d4ed9 refactor: parse native JSON request args in accounts/doctype/bank_statement_import_log/bank_statement_import_log.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
2f0367807f refactor: parse native JSON request args in accounts/doctype/bank_statement_import/bank_statement_import.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
ec496c42b5 refactor: parse native JSON request args in accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
a847d15748 refactor: parse native JSON request args in accounts/doctype/accounting_dimension/accounting_dimension.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Mihir Kandoi
a869b748f1 refactor: parse native JSON request args in accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:30 +05:30
Ejaaz Khan
7cb03a427a fix(letter-head): guard company lookups when doc has no company field 2026-06-24 18:23:21 +05:30
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