* feat(accounts): add Bank Charges account for Payment Entry deductions
Add an optional Bank Charges Account field on Company. When a Payment
Entry has a difference between the paid and received amount (e.g. a
same-currency Internal Transfer where the bank deducted a fee), that
amount now books to the Bank Charges account in the deductions table
instead of always going to the Exchange Gain/Loss account. Left blank,
behavior is unchanged.
Mirrors the resolution on both the server (set_exchange_gain_loss) and
client (set_exchange_gain_loss_deduction) so the deduction row is
pre-filled consistently before and after save. A user's manual account
edit on an existing deduction row is preserved across recalculation,
same as before this change.
* fix(accounts): only route Payment Entry difference to Bank Charges for same-currency transfers
Cross-currency Payment Entries were also matching the unconditional
bank_charges_account precedence, misrouting a genuine exchange
gain/loss into the Bank Charges account. Only prefer Bank Charges
Account when paid_from and paid_to share a currency; cross-currency
differences continue to book to Exchange Gain/Loss Account.
* test(payment_entry): assert against actual exchange gain/loss account, not a hardcoded name
CI failed: _Test Company's exchange_gain_loss_account is auto-provisioned
as "Exchange Gain/Loss - _TC" by the standard chart of accounts, not the
"_Test Exchange Gain/Loss - _TC" account used only by a sibling test.
* fix(accounts): auto-set Bank Charges Account from chart of accounts default
The standard chart of accounts already ships a "Bank Charges" ledger
account, but set_default_accounts() never picked it up into the
Company's bank_charges_account field, unlike its write_off_account and
exchange_gain_loss_account siblings. New and existing companies now
get it auto-populated the same way.
---------
Co-authored-by: test <test@test.com>
boot_session used raw `frappe.db.sql`, including a MySQL-only
`ifnull(account_type, '')` over Party Type that is invalid on Postgres.
- customer_count: `SELECT count(*)` → `frappe.db.count`
- setup_complete: `SELECT name ... LIMIT 1` → `frappe.db.get_all(limit=1)`
- companies: raw select → `frappe.get_all`, preserving the `:Company`
virtual-doc marker
- party_account_types: `ifnull(account_type,'')` → `frappe.get_all` with a
Python `account_type or ""`, which collapses NULL→'' and ''→''
identically on both engines (handles Postgres storing '' as NULL)
Adds a test (no test file existed) that runs boot_session and asserts the
company list and party_account_types are populated, on both engines.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
authorization_control.py used MySQL-only `ifnull()` in its raw rule
lookups (invalid on Postgres) and several raw `frappe.db.sql` selects.
- Replace every `ifnull(...)` with the portable `coalesce(...)` in the
rule-lookup statements that remain raw (they interpolate dynamic
conditions and rely on Frappe's Postgres backtick translation).
- Convert the user/role based_on lookups in validate_approving_authority
and the four value-based lookups in get_value_based_rule to frappe.qb
(Coalesce, isin, and a fresh Employee-designation subquery per use).
Behaviour is unchanged on MariaDB; the queries now run on Postgres.
Adds a test (no test file existed): a not-authorized case that exercises
the based_on + coalesce rule lookups (run as a non-admin user, since
Administrator implicitly holds every role), and a get_value_based_rule
call that exercises all four query-builder lookups.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: date filter for fiscal year
* fix: rename fieldtypes to valid_for_fieldtypes
* Update utils.py
Co-authored-by: Nabin Hait <nabinhait@gmail.com>