Most queries are straight raw-SQL -> query-builder ports. One rider:
get_future_payments_from_journal_entry sums future amounts with no GROUP BY,
so its non-aggregated identity columns (invoice_no/party/future_date/future_ref)
are wrapped in Max() to satisfy postgres strict GROUP BY. The summed amount is
unchanged; the attributed invoice/party label stays within MariaDB's existing
arbitrary-row indeterminacy for that already-aggregated single-row query.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Also sort the distinct income / unrealized P&L account lists in python:
frame drops ORDER BY for distinct queries on postgres, so the generated
account-column order must be pinned in python to stay deterministic on
both backends.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
disable_rounded_total and is_pos are smallint Check fields; postgres rejects
using them as bare boolean conditions in CASE WHEN / bitwise-AND, so compare
explicitly against 1. No-op on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use the same explicit db-aware conditional-add as work_order/mapper.py for the
end_of_life check (shared _item_is_alive helper in reorder_item.py; inline in
stock_projected_qty.py) instead of the inline ternary that became == None on
postgres. Identical SQL, no behaviour change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "item is not discontinued" checks treat an item as alive when its
`end_of_life` is unset, in the future, or the MariaDB zero-date `'0000-00-00'`.
`'0000-00-00'` is an invalid date literal on PostgreSQL (it errors), and a
"not set" end_of_life is `NULL` there anyway — already covered by the existing
`end_of_life IS NULL` term. So the zero-date comparison is applied on MariaDB
only; PostgreSQL keeps the `IS NULL` / future-date terms. No behaviour change on
MariaDB.
Sites: work order item-master selection (`mapper.py`), reorder-level item
selection (`reorder_item.py`), and the Stock Projected Qty report.
Part of the staged MariaDB<->PostgreSQL parity rollout (one problem class).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Acquire the same row locks on postgres that MariaDB takes, via a separate plain
SELECT <pk> ... FOR UPDATE before each grouped/aggregate read (FOR UPDATE is
invalid with GROUP BY on postgres). Applied to all 6 aggregate lock sites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PostgreSQL rejects `SELECT ... FOR UPDATE` when combined with `GROUP BY`,
aggregates or `DISTINCT`, has no concept of MySQL's locking semantics for those
shapes, and its server-side (unbuffered) cursors can't run nested queries
mid-iteration. This makes the row-locking / cursor paths db-aware so they keep
the exact MariaDB behaviour there and use the valid PostgreSQL form on Postgres.
One problem class, applied across the codebase:
- **`FOR UPDATE` + GROUP BY/aggregate** — keep `.for_update()` on MariaDB; on
Postgres acquire the lock in a separate plain `SELECT ... FOR UPDATE` pass (or
skip where the grouped read isn't a lock point). Deprecated serial/batch,
serial-batch-bundle, pick list, stock reservation entry.
- **Unbuffered/server-side cursor** — Stock Ageing streamed via an unbuffered
cursor and then ran nested queries; on Postgres that invalidates the cursor, so
process the buffered result directly there.
- **Transaction savepoints** — Opening Invoice Creation Tool rolled back the whole
transaction per failed invoice (which on Postgres also discards sibling rows and
earlier error logs); scope each invoice to a savepoint instead.
No behaviour change on MariaDB (the locking/cursor path is unchanged there).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace per-test company creation in setUp() with persistent master data
from BootStrapTestData. Add Test PCV Company to test_records.json so it
becomes a persistent fixture rather than a throwaway created per test run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor(manufacturing, projects): make raw SQL portable to PostgreSQL
Convert the MariaDB-only raw `frappe.db.sql` in the Manufacturing and Projects
modules to the cross-database query builder / ORM, and fix the non-portable
constructs that remain. Every change is a no-op on MariaDB (identical rendered
SQL / identical results) and only brings PostgreSQL — standards-strict where
MySQL is lax — in line.
Areas: BOM (cost/where-used/explosion), Work Order (operations, required items,
mapper, stock report), Workstation, Production Plan sub-assembly/explosion
queries, BOM Stock Analysis / Process Loss / Work Order Stock reports; Projects
(project, task, timesheet, activity cost, project update), Daily Timesheet
Summary and Project-wise Stock Tracking reports.
Part of the staged MariaDB<->PostgreSQL parity rollout (module 2 of 9).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The test_prevention_of_cancelled_transaction_riv test sets
frappe.flags.dont_execute_stock_reposts = True without resetting it,
which leaked into the recalculate_valuation_rate tests and made
repost() a no-op (incoming rate stayed unchanged). Reset the flag
in tearDown so reposts run for subsequent tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert the MariaDB-only raw `frappe.db.sql` in the Selling and Buying
modules to the cross-database query builder / ORM, and fix the few
non-portable constructs that remain. Every change is a no-op on MariaDB
(identical rendered SQL / identical results) and only brings PostgreSQL —
which is standards-strict where MySQL is lax — in line.
Patterns addressed in these modules:
- Strict GROUP BY — PostgreSQL rejects SELECTing a non-aggregated column
that isn't functionally dependent on the grouped key. Sales Order
Analysis, Sales Analytics, Purchase Order Analysis and Procurement
Tracker now group by the PK (1:1 with the existing key, so no behaviour
change) or aggregate genuinely-independent columns.
- App clock vs DB clock — Sales Order Analysis computed delay against the
database CURRENT_DATE, which differs from the app's today by a day when
the DB runs a different timezone; switched to `nowdate()` (deterministic,
identical on both DBs).
- Portable date math / functions — DATEDIFF and friends via the db-aware
query-builder functions.
- Raw SQL → query builder for the remaining self-contained selling/buying
reads (POS item search, customer naming suffix, packing-items
availability, customer credit/acquisition reports).
Part of the staged MariaDB↔PostgreSQL parity rollout (module 1 of 9).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All test classes inheriting AccountsTestMixin that called create_company(),
create_item(), create_customer(), create_supplier(), create_usd_receivable_account(),
and create_usd_payable_account() in setUp() now set instance attributes directly
using master data pre-created by BootStrapTestData, eliminating redundant DB
inserts on every test run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reformat generator expression in add_alt_uom_columns to satisfy ruff
line-length rule (pre-commit was auto-fixing this and failing CI)
- Create "Carton" UOM before use in test_alt_uom_balance_uses_first_alternate_uom
to avoid LinkValidationError when "Carton" doesn't exist in test DB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When creating a pick list for a work order with partially available stock,
the resulting Material Transfer for Manufacture stock entry was setting
fg_completed_qty = for_qty (= wo.qty), causing material_transferred_for_manufacturing
to reach wo.qty after just one partial transfer and blocking further pick lists.
Fix:
- Set fg_completed_qty = 0 on stock entries created from pick lists so the
old SUM(fg_completed_qty) path never fires prematurely
- Recompute material_transferred_for_manufacturing after each transfer:
use SUM(fg_completed_qty) when > 0 (direct entries / excess transfer),
otherwise use min(transferred/required) × wo.qty (pick list flow)
- Add _validate_no_excess_transfer for pick list entries (fg_completed_qty=0)
to prevent transferring more than pending qty; skip for return entries
and when backflush is based on Material Transferred for Manufacture
- Remove the zero-qty prompt in pick list work_order trigger; skip the
qty dialog in work_order.js when max transferable qty is already 0
- Hide fg_completed_qty field in Stock Entry for Material Transfer for
Manufacture purpose since it is unused in that flow
Fixes: #70713, #63846
- Remove `depends_on` restriction from `inspection_required` field so it
is visible for all Stock Entry purposes, not just Manufacture
- Fix `check_item_quality_inspection` to return items for Stock Entry
(was returning [] for unknown doctypes, blocking QI creation flow)
- Fix `inspection_type` in transaction.js to be purpose-aware: Manufacture
and Material Receipt → "Incoming"; all other purposes → "Outgoing"
The framework ignores `no_copy` while amending, so a reconciled voucher
carried a stale clearance date into its amendment even though the linked
bank transaction gets unreconciled on cancellation. Reset it via a shared
`before_insert` hook on AccountsController.
Fixes#54909
Float fields default to 0, so qty is never None. Per review feedback,
remove the validation entirely.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Display the actual company name in bold within the confirmation dialog
label so users immediately know which company they must type to confirm,
reducing the risk of accidental data loss.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Fixed Asset Turnover Ratio in the Financial Ratios report divided
Net Sales by Total Assets (the root-level Asset group), which actually
computes the Total Asset Turnover Ratio.
Populate a `fixed_asset` balance from the asset account carrying the
`Fixed Asset` account_type (mirroring how `current_asset` is derived for
`Current Asset`) and use it as the denominator, so the ratio reflects
Net Sales / Net Fixed Assets per the standard definition.
Fixes#54529
* feat(invoices): add tooltip description to Update Stock checkbox
Adds a description below the Update Stock checkbox on both Sales Invoice
and Purchase Invoice so users understand when to use the field without
consulting documentation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(invoices): replace Update Stock description with hover info tooltip
Removes the inline description text and adds an ℹ icon next to the
Update Stock checkbox label on both Sales Invoice and Purchase Invoice.
Hovering the icon shows the contextual tooltip via Bootstrap tooltip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(invoices): use Frappe native tooltip-content class for Update Stock icon
Replace Bootstrap .tooltip() (pure black bg) with Frappe's own
.tooltip-content CSS class so the hover tooltip matches the rest of
the ERPNext UI — uses var(--bg-dark-gray) and var(--text-dark).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(invoices): use frappe.ui.SidebarCard for Update Stock info tooltip
Replace custom CSS tooltip with the same SidebarCard + Popper approach
Frappe's InfoCard uses for field description tooltips — gives the native
ERPNext card appearance (white card, border, shadow) on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(invoices): use built-in field description for Update Stock tooltip
Replace custom SidebarCard JS tooltip with Frappe's native
description + show_description_on_click field property on the
update_stock field in Sales Invoice and Purchase Invoice.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: remove duplicate description in purchase_invoice update_stock field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* revert: restore custom tooltip in purchase_invoice.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* revert: remove all changes from purchase_invoice.js
Keep purchase_invoice.js identical to upstream develop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reduce from 2 alternate UOM columns to 1 (first alt UOM by idx)
- Fix broken translation strings: replace _(f"...{slot}") with
_("...") — f-strings inside _() are never extracted by bench
get-untranslated, breaking non-English installations
- Simplify fieldnames: alt_uom_1/alt_uom_1_bal_qty → alt_uom/alt_uom_bal_qty
- Add 4 test cases covering: single alt UOM, no alt UOM, disabled filter,
and multiple alt UOMs (first-wins behaviour)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The informational toast is not required for the feature to work.
The core fix (reqd removed from JSON, validation relaxed in bom.py)
is sufficient to allow zero qty on BOM secondary items.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a BOM secondary item has cost_allocation_per = 0 (the default), the
previous code unconditionally computed `0 / transfer_qty = 0`, wiping any
rate the user had entered for the item. Now the allocation formula only runs
when cost_allocation_per > 0, allowing the valuation-rate fallback (or a
manually entered rate) to apply instead.
Additionally, secondary items with transfer_qty = 0 now short-circuit the
entire rate pipeline: they get rate = 0 and amount = 0 immediately, avoiding
a ZeroDivisionError and the spurious "enter basic rate" prompt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Secondary output items in a BOM do not always guarantee output during
manufacture. The actual qty is only known when manufacturing completes,
so setting zero in the BOM is a valid way to express "output is
non-deterministic".
Changes:
- Remove `reqd: 1` from the qty field in BOM Secondary Item so that 0
is accepted as an explicit value (non_negative constraint is kept, so
negative values are still rejected).
- Relax validate_secondary_items() in bom.py to only reject qty that is
None/missing, not qty that is explicitly 0.
- Add a qty event handler in bom.js that shows a blue informational
alert when the user sets qty to 0, explaining that the actual output
will be recorded at manufacture time.
Fixes https://github.com/frappe/erpnext/issues/55401
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#52953
The Stock Balance report previously showed balance qty only in the item's
stock UOM. To view balance in an alternate UOM, users had to set the
"Include UOM" filter — which applies a single UOM to all items. This breaks
down when different items use different alternate UOMs (e.g., Pens in Box,
Ink in Milliliters).
This change adds a new "Show Alternate UOM Balance" checkbox filter. When
enabled, up to two alternate UOM columns are injected right after the
Balance Qty column:
Balance Qty | Alt UOM 1 | Balance Qty (Alt UOM 1) | Alt UOM 2 | Balance Qty (Alt UOM 2)
Each row resolves its own alternate UOMs from `tabUOM Conversion Detail`
(ordered by idx, excluding the item's stock UOM). The converted balance
qty is computed as: stock qty / conversion_factor.
Items with fewer than 2 alternate UOMs leave the extra columns blank.
The existing "Include UOM" filter behaviour is unchanged.
When a Purchase Invoice is created with `update_stock = 1`, the system
automatically replaces the item's expense account with the correct
inventory account for perpetual inventory. This is expected behaviour,
but a `frappe.msgprint` warning was being shown to the user:
"Expense Head changed to Stock In Hand because account Cost of Goods
Sold is not linked to warehouse Stores or it is not the default
inventory account."
The message is purely informational, provides no actionable guidance,
and confuses users who deliberately enable Update Stock. The underlying
account substitution logic is unchanged; only the popup is suppressed.
The two other `msgprint` calls (for the Purchase-Receipt-linked and
no-Purchase-Receipt flows) are intentionally preserved — those surface
a genuine change in behaviour that users may not expect.
Fixes: https://github.com/frappe/erpnext/issues/...
fix(bom): fetch routing operations when routing is selected
frm.doc.operations is always an array in Frappe, so !frm.doc.operations
was always false (empty array [] is truthy in JS), causing get_routing()
to never fire when a Routing is selected on a BOM with no existing
operations.
Changed the guard to !frm.doc.operations.length so the fetch triggers
correctly when the operations table is empty.
Also wired the same fetch into the with_operations handler so that
enabling the checkbox after a Routing is already set will populate
operations without requiring the user to re-select the Routing.
Co-authored-by: Umair Sayed <umairsayed@Umairs-MacBook-Air-2.local>
row is a plain dict subclass, so row["item_code"] raised an unhandled
KeyError (500) when the payload had neither key. get_active_product_bundle
already returns None for falsy input, yielding an empty item list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy item_code path now resolves the active bundle's name via
get_active_product_bundle (same filters as the old joined query) so
frappe.has_permission can validate the specific document on both
branches. The orphaned get_product_bundle_items helper is removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The whitelisted get_items_from_product_bundle endpoint now verifies read
permission on Product Bundle (doc-level when a name is passed, doctype-
level for the legacy item_code path) so authenticated users can't
enumerate bundle components. The disabled-bundle test also restores the
disabled flag via addCleanup.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Since Product Bundles became versioned, their names are PB-prefixed and
no longer double as the parent item code. The buying dialog kept passing
the picked bundle name as `item_code`, so the component lookup (which
filters `new_item_code`) matched nothing and the dialog silently added
no items.
The dialog now sends the selection as `product_bundle` and the endpoint
fetches that version's components by document name (rejecting
unsubmitted versions); passing `item_code` still resolves the parent
item's active version, preserving the legacy contract of the
whitelisted endpoint. The picker is also restricted to submitted
bundles.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mutation testing on gl_composer surfaced that the foreign-row
debit/credit_in_transaction_currency conversion (amount / exchange_rate) was
unverified -- a / vs * bug survived. Assert those fields in test_multi_currency
and add a foreign-debit case so both conversion directions are now caught.
Add characterization tests for get_payment_entry_against_order (the Sales/
Purchase Order advance path, previously untested) and make_inter_company_journal_entry
(previously fully uncovered).
Split AssetService.unlink_asset_reference into _is_depreciation_asset_row /
_reverse_asset_depreciation / _restore_scheduled_depreciation /
_restore_finance_book_value / _block_scrap_journal_cancel, and add return type
hints and docstrings across the service. Behaviour preserved (netted by the
asset suite).
Split get_payment_entry into _reference_exchange_rate / _append_party_row /
_append_bank_row, and add return type hints and docstrings to all mapper
document builders. Behaviour preserved.
Add return type hints and option-A docstrings to JournalEntryReferenceValidator,
and split JournalEntryGLComposer.compose into _set_transaction_currency and
_gl_row helpers. Behaviour preserved.
Resolution skips disabled bundles, transactions referencing a disabled
version are blocked, rows without an explicit version stop packing, and
the Item Where Used report surfaces the disabled flag on bundle rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Un-deprecate the `disabled` checkbox: it is now editable (also after
submit) and parks a bundle version without ceding its active slot, so
re-enabling restores it without re-activation.
- `get_active_product_bundle` (the single resolution entry point) skips
disabled bundles, so every consumer stops treating the item as a bundle
while it is disabled
- the version pickers on transaction item rows and the buying "Get Items
from Product Bundle" dialog filter out disabled bundles
- an explicitly selected disabled version blocks the transaction with a
validation error instead of silently re-packing another version
- Product Bundle Balance report excludes disabled bundles
- list view indicator: Disabled (grey) / Active (green), falling back to
docstatus for drafts, cancelled and inactive submitted versions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stock Ageing iterates stock ledger entries through an unbuffered
(streaming) cursor. _get_batchwise_valuation() lazily queried
Batch.use_batchwise_valuation from inside that loop whenever a row
carried the legacy batch_no field, and the nested query invalidated
the active streaming result set — crashing the report (or silently
dropping the remaining rows, depending on the driver version).
Resolve the valuation flags in a single query before entering the
unbuffered cursor block; the lazy lookup now only serves callers that
pass stock ledger entries in directly, where no streaming is active.
Fixes https://github.com/frappe/erpnext/issues/55786
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the single opaque `args` parameter of the whitelisted get_outstanding
with explicit named parameters (the supported interface), splitting the body
into _get_journal_entry_outstanding / _get_invoice_outstanding. The legacy
`args` payload is still accepted via kwargs for backward compatibility with
custom apps. Resolves the overusing-args semgrep finding.
Add characterization tests for the previously untested get_balance (difference
on a blank row), get_outstanding_invoices (write-off rows) and
unlink_advance_entry_reference (reference cleared on cancel). Remove the unused
get_average_exchange_rate, which has no callers in erpnext.
Add a class docstring plus docstrings for the lifecycle hooks and the public
API helpers (get_outstanding, get_against_jv, get_exchange_rate, etc.).
Self-evident one-line methods are intentionally left undocumented.
Decompose update_invoice_discounting, set_print_format_fields,
get_balance_for_periodic_accounting, set_exchange_rate, get_balance and
get_outstanding_invoices into focused per-row / row-building helpers (verb
prefixed, with docstrings). The nested closure in update_invoice_discounting
that ignored its row id is dropped. Behaviour preserved.
Split create_remarks into _cheque_remark / _reference_remark / _bill_remark
helpers, and validate_against_jv into _validate_jv_reference,
_validate_jv_reference_direction and _against_jv_entries. Add docstrings.
Behaviour preserved.
Add return annotations to the module-level helpers and to make_gl_entries,
get_balance and set_total_amount, plus parameter types for set_total_amount
and make_gl_entries.
Convert the five raw frappe.db.sql calls to Query Builder / ORM: the
against-JV lookup, the write-off invoice listing (get_values, now a single
query), the JV outstanding aggregate (get_outstanding), and the bill-no
lookup (get_value). Behaviour preserved.
Update erpnext's own importers (asset depreciation, invoice discounting and the
JE tests) to import the builders from mapper.py directly. Drop
make_inter_company_journal_entry and make_reverse_journal_entry from the
backward-compat re-export in journal_entry.py -- they are not part of the
custom-app call surface; only the payment-entry builders remain re-exported.
Move the Payment Entry / Journal Entry builders (get_payment_entry and its
against-order/against-invoice helpers, make_inter_company_journal_entry,
make_reverse_journal_entry) into mapper.py. The whitelisted builders are
re-exported from journal_entry.py so existing call paths -- including custom
apps -- keep working, and the erpnext client calls now point at the mapper
path. get_payment_entry imports the exchange-rate/bank-account helpers lazily
to avoid a circular import with the re-export.
Move the nine asset/depreciation coupling methods (depreciation-account
validation, asset value updates on depreciation and disposal, and the
unlink-on-cancel logic) out of the controller into a JournalEntryAssetLinkage
service under services/. Pure behaviour-preserving move, netted by the asset
suite (asset, asset_value_adjustment) plus the JE module.
Move validate_reference_doc and its helpers, plus validate_orders and
validate_invoices, out of the controller into a JournalEntryReferenceValidator
service under services/. Behaviour preserved; the per-reference totals stay on
the document. The order/invoice validators are split into <=15-line helpers.
Rename three private helpers for intent and to drop an abbreviation:
_is_validatable_reference -> _has_party_reference,
_accumulate_reference -> _register_reference,
_reference_dr_or_cr -> _reference_amount_field.
Extract the 100-line, CC-27 validate_reference_doc into a thin orchestrator
loop plus focused per-row private methods, and lift the inline reference
field map to a module constant. Behaviour preserved; complexity drops from
27 to 3 and no extracted function exceeds 15 lines.
Pin every branch of validate_reference_doc before refactoring: Sales Order
debit / Purchase Order credit rejection, non-existent reference handling,
Sales/Purchase Invoice and Order party mismatches, and population of the
reference_totals/types/accounts side effects.
The FG item produced by a BOM should not also appear as a secondary
item (Co-Product/By-Product/Scrap/Additional Finished Good). When an
Additional Finished Good shared the main FG's item code, the resulting
Stock Entry ended up with two rows of the same item carrying different
valuation rates. Validate against it instead, exempting legacy rows so
migrated BOMs can still be re-saved.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Rename single-letter query-builder aliases (C, DT) to readable names
(customer, sales) and add report tests covering the column contract,
validation guards, and the days-since-last-order threshold.
Move PR billing sync and provisional-entry cancellation into
accounts/doctype/purchase_invoice/services/billing_status.py:
- update_billing_status_in_pr, get_pr_details_billed_amt and
cancel_provisional_entries move into the service (internal-only;
on_submit/on_cancel and make_gl_entries repointed)
- the service imports the shared allocation helpers from
purchase_receipt/services/billing_status.py (PR owns the shared
buying billing logic)
- also repoints the validate_expense_account call in
validate_for_repost missed in the ExpenseAccountService commit
No behaviour change.
Move expense-account resolution into
accounts/doctype/purchase_invoice/services/expense_account.py:
- set_expense_account stays as a controller delegator (dispatched from
accounts_controller.py) and force_set_against_expense_account stays
(called by repost_accounting_ledger)
- validate_expense_account and set_against_expense_account move into the
service; validate() repointed (the unused force kwarg on
set_against_expense_account is dropped with the method)
Pre-existing raw SQL (SRBNB-booked-in-PR check) moved verbatim.
No behaviour change.
Move stock reservation on PR submission into
stock/doctype/purchase_receipt/services/stock_reservation.py:
- reserve_stock, reserve_stock_for_sales_order,
reserve_stock_for_production_plan and get_production_plan_references
move into the service (internal-only; on_submit repointed)
- delegates to the Sales Order controller contract
(create_stock_reservation_entries) and the shared StockReservation
class
No behaviour change.
Move provisional accounting for non-stock items into
stock/doctype/purchase_receipt/services/provisional_accounting.py:
- add_provisional_gl_entry stays as a controller delegator (called as a
doc method by both the PR and PI GL composers)
- validate_provisional_expense_account moves into the service;
validate() repointed
No behaviour change.
Move PR↔PI billed-amount allocation into
stock/doctype/purchase_receipt/services/billing_status.py. Purchase
Receipt owns the shared buying billing logic; Purchase Invoice imports
from the service module:
- update_billing_status stays as a controller delegator (called by
Purchase Invoice flows and v13 patches)
- the module-function family moves verbatim:
update_billed_amount_based_on_po, update_billing_percentage,
get_billed_amount_against_pr/_po,
get_purchase_receipts_against_po_details,
get_billed_qty_amount_against_purchase_receipt/_order,
adjust_incoming_rate_for_pr, get_item_wise_returned_qty
- imports repointed in purchase_invoice.py (top-level + lazy) and
patches/v15_0/recalculate_amount_difference_field.py
No behaviour change.
Move status transitions and receiving progress into
buying/doctype/purchase_order/services/status.py:
- update_status (module-level whitelisted wrapper + list view) and
update_receiving_percentage (called by child_item_update) stay as
controller delegators
- check_modified_date moves into the service (internal to update_status)
No behaviour change.
Move drop-ship item handling into
buying/doctype/purchase_order/services/drop_ship.py:
- update_dropship_received_qty stays as a whitelisted controller
delegator (called from purchase_order.js)
- update_delivered_qty_in_sales_order, has_drop_ship_item and
set_received_qty_to_zero_for_drop_ship_items move into the service
(internal-only; on_cancel and the module-level update_status wrapper
repointed)
No behaviour change.
Move subcontracting integration into
buying/doctype/purchase_order/services/subcontracting.py:
- set_service_items_for_finished_goods (called by production plan
work-order planning) and can_update_items (onload + child_item_update)
stay as controller delegators
- validate_fg_item_for_subcontracting, auto_create_subcontracting_order
and update_subcontracting_order_status move into the service;
validate(), on_submit and update_status repointed
No behaviour change.
Move packing slip / product bundle handling into
stock/doctype/delivery_note/services/packing.py:
- validate_packed_qty stays as a controller delegator (called via
hasattr contract in accounts/utils.py); has_unpacked_items stays
for onload/JS
- get_product_bundle_list and cancel_packing_slips move into the
service (internal-only; on_cancel repointed)
Pre-existing raw SQL in cancel_packing_slips moved verbatim.
No behaviour change.
Move billing status tracking and return invoicing into
stock/doctype/delivery_note/services/billing_status.py:
- update_status and update_billing_status stay as controller delegators
(whitelisted update_delivery_note_status wrapper, v13 patches and
Sales Invoice call them)
- make_return_invoice moves into the service (internal to on_submit)
- the update_billed_amount_based_on_so module function moves to the
service module; the sales_invoice.py import is repointed
- drops stale Document/DocType/Abs imports
Pre-existing raw SQL in update_billed_amount_based_on_so moved verbatim.
No behaviour change.
Move subcontracting (inward) integration into
selling/doctype/sales_order/services/subcontracting.py:
- can_update_items stays as a controller delegator (onload data +
child_item_update.py caller)
- validate_fg_item_for_subcontracting and
update_subcontracting_order_status move into the service; validate()
and StatusService.update_status repointed
No behaviour change.
Move delivery schedule management into
selling/doctype/sales_order/services/delivery_schedule.py:
- get_delivery_schedule and create_delivery_schedule stay as whitelisted
controller delegators (called from sales_order.js)
- update_delivery_date_based_on_schedule, delete_delivery_schedule_items
and delete_removed_delivery_schedule_items move into the service
(internal-only; on_submit/on_cancel repointed)
No behaviour change.
Move status computation and progress tracking into
selling/doctype/sales_order/services/status.py:
- update_status, update_delivery_status, update_picking_status and
set_indicator stay as controller delegators (whitelisted wrapper,
purchase_order/pick_list/child_item_update callers, portal contract)
- check_modified_date moves into the service (internal to update_status)
- the billing/delivery/advance status defaults in validate() move to
StatusService.set_default_statuses()
No behaviour change.
Move stock reservation logic out of the Sales Order controller into
selling/doctype/sales_order/services/stock_reservation.py:
- validate_reserved_stock, enable_auto_reserve_stock and the
get_unreserved_qty module function move into the service (internal-only,
callers repointed; stock_reservation_entry.py import updated)
- has_unreserved_stock, create/cancel_stock_reservation_entries and
update_reserved_qty stay on the controller as thin delegators
(whitelisted/JS-reachable or called from selling_controller and
child_item_update)
No behaviour change.
* feat: add item prices tab to Item doctype
* feat: item form pricing tab
* fix: remove action button for edit item price
* fix: prevent stale item price rendering after form navigation
* fix: remove stale call to deleted edit_prices_button function
* fix: item price list fixes
* fix: show filtered price list
* fix: show filtered price list
Avoids basename collision with the core SLE engine erpnext/stock/stock_ledger.py
(the service even imports make_sl_entries from it). File now maps 1:1 to its class,
StockLedgerService.
Phase 0 golden-master safety net for the stock_controller refactor. It served its
purpose (every extraction verified byte-identical GL + Stock Ledger output) and is
removed before shipping, mirroring the earlier GL characterization cleanup.
The preview feature serves both accounts and stock vouchers (SI/PI/PE + DN/PR/SE)
and its show_*_preview entry points live in controllers/stock_controller, so the
cohesive GL+SLE preview module belongs in controllers/, not stock/services/. Pure
move + import-path update; GL and stock previews stay together (shared get_columns/
get_data formatters; read-side, kept out of the write-path services).
Verified: ledger snapshots green; module resolves at new path.
Merge the stock exceptions into the existing app-wide erpnext/exceptions.py (under a
'# stock' section) instead of a separate erpnext/stock/exceptions.py, matching the
established convention. stock_controller still re-exports them for backward
compatibility; services import from erpnext.exceptions.
Verified: ledger snapshots, quality inspection suite, stock_entry batch-expiry stay green.
Re-audited the kept delegators for true external callers. Two had none:
- has_landed_cost_amount: no caller anywhere (the landed_cost_voucher.py free
function is what the composers use) — pure dead delegator, removed.
- validate_internal_transfer: only StockController.validate() called it; inline that
one hook to StockInternalTransferService(self).validate_internal_transfer() and
remove the delegator.
All other kept delegators have real external/subclass/run_method callers and remain
as the stock extension contract.
Verified: ledger snapshots + DN/PR internal-transfer suites stay green.
#8 ledger_preview: wrap the submit-in-memory dry run in a savepoint inside
get_accounting_ledger_preview / get_stock_ledger_preview and roll back to it in a
finally, so the preview never persists entries regardless of caller (previously
only the whitelisted show_*_preview wrappers' full rollback made it safe).
#9 exceptions: move BatchExpiredError and the QualityInspection* errors into a new
erpnext/stock/exceptions.py and re-export them from stock_controller for backward
compatibility (job_card and tests still import from the controller; identity is
preserved). Services now import from the neutral module instead of back from the
controller they were extracted out of.
#10 quality inspection: extract the duplicated doctype->inspection-field map into a
single INSPECTION_FIELDNAME_MAP constant in the service, consumed by both
validate_inspection and check_item_quality_inspection.
Verified: ledger snapshots, quality inspection suite, stock_entry batch-expiry test
stay green; preview smoke-tested to persist nothing and not roll back the caller.
validate_internal_transfer_qty stashed the value on a name-mangled instance
attribute (self.__inter_company_reference) that get_item_wise_inter_transfer_qty
read back, creating an implicit call-ordering contract: calling the latter on a
fresh service without the former first raised AttributeError. Compute it as a local
and pass it as a method argument, removing the hidden cross-method state.
Verified: ledger snapshots + PR internal-transfer suite stay green.
get_serialized_items had zero callers anywhere (Python, JS, run_method) on develop
and after the refactor; it was relocated into SerialBatchBundleService by mistake
instead of being dropped. Delete it — also removes a raw frappe.db.sql_list query
that duplicated the ORM helper get_serial_or_batch_items.
The @frappe.request_cache decorator keyed on `self`, which after the service
extraction is a transient SerialBatchBundleService built per delegated call, so the
request-wide dedup was lost and dead instances were pinned in request_cache. Use
frappe.get_cached_value on the Item instead: caching is keyed by the item (request-
local + redis), effective regardless of service-instance churn, and the redundant
frappe.db.exists query is dropped.
Verified: ledger snapshots + serial and batch bundle suite stay green.
Address code-review findings on the Phase-0 safety net:
- Per-test savepoint/rollback isolation so cumulative SLE fields
(qty_after_transaction, stock_value, valuation_rate) are deterministic
regardless of test order or leftover state (were order-coupled before).
- Backdate prerequisite stock to PREREQUISITE_DATE so balances are positive and
independent of the wall-clock date.
- Capture has_serial_and_batch_bundle (boolean linkage, not the volatile docname)
so a dropped serial/batch bundle link is caught.
- Add pr_batch_item and pr_serial_item scenarios to exercise SerialBatchBundleService
(the largest extraction, previously uncovered).
Goldens regenerated. Verified deterministic across repeated assert runs.
validate_putaway_capacity selected the 'disable' field but checked rule.get('disabled')
(always None), so disabled rules still enforced capacity and could wrongly raise
'Over Receipt'. Use the correct 'disable' key. Pre-existing bug surfaced during the
stock_controller refactor review.
* feat: create sales invoice from pick list
* test: cover sales invoice creation from pick list
* fix: require update stock for pick list invoices
* fix: return SI should not bother with pick list
These clusters are really other doctypes' logic parked on StockController, so they
move next to the doctype that owns them rather than into a stock service:
- set_landed_cost_voucher_amount / get_item_account_wise_lcv_entries /
has_landed_cost_amount -> landed_cost_voucher.py (free functions). Controller keeps
thin delegators (called as doc.X from 4 GL composers, buying_controller and the LCV
doctype).
- validate_putaway_capacity -> putaway_rule.py (free function, next to
get_available_putaway_capacity it already used). Controller keeps a delegator
(validate hook + Stock Entry/Reconciliation); prepare_over_receipt_message becomes a
private helper there.
Drops now-unused Sum/defaultdict imports from stock_controller.
Behaviour-preserving: ledger snapshots, putaway and landed-cost suites stay green.
Move internal-transfer warehouse/currency/packed-item/over-receipt-qty validation
into erpnext/stock/services/internal_transfer.py as a delegating service. This is
the stock-side counterpart to accounts/services/internal_transfer.py (party/rate/
pricing). validate_internal_transfer keeps a controller delegator (validate hook);
the other 7 methods are internal-only. The is_internal_transfer() predicate is
already consolidated on AccountsController.
Behaviour-preserving: ledger snapshots + DN/PR internal-transfer suites stay green.
Move quality-inspection validation (validate_inspection + validate_qi_presence/
submission/rejection) into erpnext/stock/services/quality_inspection.py as a
delegating service. validate_inspection keeps a controller delegator (called from
validate() and 3 other doctypes); the three row-level helpers are internal-only.
The whitelisted module fns check_item_quality_inspection / make_quality_inspections
stay in stock_controller (stable endpoint paths).
Behaviour-preserving: ledger snapshots + quality inspection suite stay green.
Relocate get_voucher_details, check_expense_account and get_debit_field_precision
from StockController to BaseStockGLComposer, where they are only used (by compose()
and AssetCapitalizationGLComposer). Call sites flipped from doc.X to self.X.
Inventory-account resolution (get_inventory_account_map/_dict, etc.) stays on the
controller: it is a doc-contract method called as doc.X from non-stock-composer code
(PI controller/composer, accounts/utils, repost_accounting_ledger), so it cannot fold
into BaseStockGLComposer. make_gl_entries / make_gl_entries_on_cancel / add_gl_entry
likewise stay (contract entry points).
Behaviour-preserving: ledger snapshots, subcontracting receipt and asset
capitalization suites stay green.
Move SLE building and reposting (get_sl_entries, update_inventory_dimensions,
get_stock_ledger_details, get_items_and_warehouses, make_sl_entries,
repost_future_sle_and_gle) into erpnext/stock/services/stock_ledger.py as a
delegating service. All six keep thin controller delegators (each has external
callers). The repost helper *functions* stay module-level in stock_controller
(imported widely); the service calls them. Also drop import orphaned by this and
the prior bundle extraction.
Behaviour-preserving: ledger characterization snapshots and the repost item
valuation suite stay green.
Move serial & batch bundle handling (creation, validation, return bundles,
teardown) out of StockController into erpnext/stock/services/serial_batch_bundle.py
as a delegating service. The controller keeps thin delegators for the 10 methods
reached from other doctypes or run_method; the 12 internal-only helpers live in
the service. make_bundle_for_material_transfer stays a module fn in stock_controller
(imported by stock/serial_batch_bundle.py).
Behaviour-preserving: ledger characterization snapshots and the full Serial and
Batch Bundle test suite stay green.
Phase 0 safety net for the stock_controller service refactor. Captures the
combined GL + Stock Ledger output of representative stock vouchers (DN, Stock
Entry, Stock Reconciliation, Purchase Receipt incl. returns/taxes) as golden
snapshots, so later phases can prove ledger behaviour stays byte-identical while
stock_controller is split into services.
Run: bench --site <site> run-tests --module erpnext.stock.test_ledger_characterization
Regenerate goldens: REGEN_LEDGER_SNAPSHOTS=1 (after intentional changes only).
frappe-modifying-but-not-comitting anchors on the method definition, so the
suppression must sit on the def line (matching the convention used elsewhere
in the codebase); inner-line comments did not suppress it.
The golden-master snapshots, capture harness, characterization test, and
refactor spec existed to prove the accounts refactor preserved GL output.
All 29 characterization tests pass against the merged code, so the
scaffolding has served its purpose and is removed before merge.
Removes:
- erpnext/accounts/gl_snapshot.py
- erpnext/accounts/gl_snapshots/ (29 snapshots)
- erpnext/accounts/test_gl_characterization.py
- specs/accounts_refactor_spec.md
Both patterns are unchanged from develop but newly appear in the diff
because the refactoring relocated them:
- purchase_order/mapper.make_purchase_invoice_from_portal: portal flow
needs commit before redirect (matches develop behaviour)
- asset_repair.on_cancel: ignore_linked_doctypes is a runtime cancel flag,
not a persisted field
After moving mapping functions into per-doctype mapper.py modules and POS
logic into POSService, several call sites still referenced the old
locations, breaking import/collection in CI:
- bulk_transaction: import mapper modules for make_* transitions
- test_purchase_order / test_purchase_receipt / test_stock_entry: import
make_purchase_receipt, make_purchase_invoice, make_inter_company_purchase_receipt
and make_stock_entry from their mapper modules
- order.html: point portal API URL to purchase_order.mapper
- sales_invoice: add validate_full_payment delegating wrapper (called by POSInvoice)
It is Sales-Invoice-specific GL assembly and was the only TaxService
method called by the composer. Move it to SalesInvoiceGLComposer (verbatim),
call it as self.make_discount_gl_entries, drop the now-unused composer-level
TaxService local and the orphaned get_account_currency import in taxes.py.
Remove the doctype-branching make_precision_loss_gl_entry from
exchange_gain_loss.py (and its accounts_controller wrapper); add a
dedicated method to each of SalesInvoiceGLComposer and
PurchaseInvoiceGLComposer. The SI variant now passes 'Sales Invoice'
as the round-off voucher type (output-equivalent) and the throwaway
return value no longer shadows the gettext _ helper.
The make_/delete_/apply_loyalty_points methods on SalesInvoice only existed
as an inheritance surface for POSInvoice (self.X()). Route all callers
through LoyaltyService(doc).X() directly, consistent with how related-doc
cases already worked, and remove the three forwarding methods.
Resolve 4 conflicts from Phase 7 service/mapper extraction vs upstream:
- asset.py: take extraction; repoint dangling make_asset_movement JS to mapper
- job_card: port upstream field_no_map(naming_series) into mapper.make_subcontracting_po
- sales_order: port upstream rows-index fix into mapper.make_delivery_note
- sales_invoice (Phase 7): take service delegations; port upstream SQL->QB/ORM
changes for get_warehouse, get_all_mode_of_payments, get_discounting_status,
clear_unallocated_mode_of_payments, and set_pos_fields(POS DN skip) into services
Repoint all JS method strings and Python imports for mapper functions
across 18 doctypes from the doctype module to its mapper module, and
remove the now-unused re-export shims from each doctype file (keeping
only names used internally).
* ci: re-fetch before push to avoid force-push on translations_hotfix
If upstream/translations_hotfix moved forward while the script was
running (e.g., a concurrent run or manual push), git push would fail
with "behind remote". Re-fetch right before pushing and merge any new
remote commits with -X ours so our .po changes and the main.pot from
${HOTFIX_BRANCH} (set by the initial -X theirs merge) are preserved
without needing a force-push.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: define HOTFIX_BRANCH once as job env; pass it to sync script
version-16-hotfix is now declared as env.HOTFIX_BRANCH at the job level
so the checkout ref and the script argument both derive from the same
value. Quoting the GITHUB_WORKSPACE path guards against spaces on
self-hosted runners.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: define HOTFIX_BRANCH as job env and pass to sync script via env
Declares version-16-hotfix once as env.HOTFIX_BRANCH at the job level
so the checkout ref and the script both derive from the same value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: read HOTFIX_BRANCH from env instead of hardcoding in script
Removes the hardcoded branch name from the script; reads it from the
HOTFIX_BRANCH env var set by the workflow. Fails immediately with a
clear message if the var is not set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: use ls-remote to check branch existence instead of swallowing fetch errors
Replace `git fetch ... 2>/dev/null || true` + `git rev-parse --verify`
with `git ls-remote --exit-code --heads upstream translations_hotfix`.
ls-remote queries the remote directly so the check is never based on
stale local state, and real failures (auth, network) propagate through
set -e instead of being silently ignored. Applied to both the initial
branch setup and the pre-push re-fetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: rewrite orchestrator to dispatch runner per hotfix branch via matrix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: add per-branch runner workflow for hotfix translation sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: generalise sync script to use APP_NAME and GITHUB_REPOSITORY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: remove push trigger from runner workflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: rename working branch to sync_translations_{hotfix_branch}
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Separates all make_*/create_* document-creation functions from the
SalesOrder controller into a dedicated mapper.py for better separation
of concerns. Re-exports from sales_order.py preserve backward compat.
Semgrep rule frappe-modifying-but-not-comitting-other-method flags
setting self.ignore_linked_doctypes inside make_gl_entries() instead
of in the calling on_cancel method. Follows the same pattern used by
AssetCapitalization.
accounts/services/child_item_update.py:
- ChildItemUpdater class with update() entry point encapsulating all
the logic from the old update_child_qty_rate free function; nested
closures (check_doc_permissions, validate_workflow_conditions,
validate_quantity_and_rate, validate_fg_item_for_subcontracting)
become private methods on the class
- update_child_qty_rate kept as @frappe.whitelist() thin wrapper;
re-exported from accounts_controller.py so the JS whitelist path
"erpnext.controllers.accounts_controller.update_child_qty_rate"
and test imports continue to work
- Free functions: set_order_defaults, validate_child_on_delete,
update_bin_on_delete, validate_and_delete_children, get_allow_zero_qty,
get_child_item_change_state, is_child_item_unchanged,
update_child_item_rate_and_discount, update_child_item_uom_and_weight,
check_if_child_table_updated
accounts_controller.py drops from ~2356 to ~1796 lines.
- accounts/services/party_validation.py: PartyValidator class with
single validate() entry point covering party frozen/disabled check,
party accounts, currency, party account currency, address/contact,
and company-linked addresses. AccountsController.get_party() kept
as a shim (called by advances and payment_schedule services).
- accounts/services/internal_transfer.py: InternalTransferService
class with validate() (reference + transaction rate + pricing/tax
disablers), set_account() for unrealized P&L, is_internal_transfer(),
process_common_party_accounting(), and get_common_party_link().
Shims retained on AccountsController for the three methods called
by selling/buying/stock controllers and GL composers.
accounts_controller.py drops from ~2722 to ~2356 lines.
Introduce PaymentScheduleService and BillingValidationService classes so
call sites read PaymentScheduleService(doc).set_payment_schedule() instead
of the opaque self.set_payment_schedule() shim. Removes 15 shim methods
from AccountsController and updates all 11 call sites across the codebase.
Replaces the shim+free-function pattern with a TaxService class so
callers like TaxService(self).set_taxes() make the source location
explicit. Class lives in taxes.py above the existing free functions.
Deletes the intermediate tax_service.py. Updates AccountsController,
sales_invoice, pos_invoice, subscription, and both GL composers to
call TaxService directly.
Move billing validation, payment schedule, and exchange gain/loss logic from
AccountsController into dedicated service modules under accounts/services/.
AccountsController retains thin shim methods that delegate to the services.
The free functions (get_gl_dict, add_gl_entry, get_voucher_subtype, etc.) live
in the same module as BaseGLComposer — they are all about building GL entries,
so there is no reason to split them across two files. Removes gl_entry_builder.py
and updates all import references to base_gl_composer.
Move the get_gl_dict/add_gl_entry logic from AccountsController/StockController
into free functions in accounts/services/gl_entry_builder.py with doc as first arg.
BaseGLComposer gains get_gl_dict and add_gl_entry methods that delegate to the free
functions — GL composers now call self.get_gl_dict/self.add_gl_entry directly
without going through the doc. AccountsController and StockController keep thin
shims for backward compatibility with unrefactored callers.
Also move update_gl_dict_with_regional_fields and update_gl_dict_with_app_based_fields
to gl_entry_builder.py, re-exporting them from accounts_controller.py to avoid a
circular import.
Move validate_conversion_rate, validate_taxes_and_charges, validate_account_head,
validate_cost_center, validate_inclusive_tax, set_balance_in_account_currency,
set_child_tax_template_and_map, add_taxes_from_tax_template, merge_taxes,
get_tax_rate, get_default_taxes_and_charges, and get_taxes_and_charges out of
accounts_controller into a dedicated accounts/services/taxes.py module.
Re-export all symbols from accounts_controller for backward compatibility.
Moves all advance-related query and management logic out of the 4500-line
AccountsController into a dedicated module-level service:
- get_advance_journal_entries, get_advance_payment_entries,
get_advance_payment_entries_for_regional, get_common_query
- set_advances, get_advance_entries, validate_advance_entries,
set_advance_gain_or_loss, calculate_total_advance_from_ledger,
set_total_advance_paid, set_advance_payment_status,
delink_advance_entries, create_advance_and_reconcile
AccountsController methods become thin shims; module-level functions in
accounts_controller.py are replaced with re-exports for backward
compatibility. payment_reconciliation.py updated to import directly from
the new service.
All 29 GL snapshots, 121 SI tests, 53 PE tests, and 37 payment
reconciliation tests pass.
Extracts get_gl_entries logic from SubcontractingReceipt,
AssetCapitalization, and AssetRepair into dedicated GL composer classes
under each doctype's services/ package. Each composer follows the
established BaseGLComposer / BaseStockGLComposer pattern, and the
original get_gl_entries becomes a 3-line shim.
- SubcontractingReceiptGLComposer(BaseStockGLComposer): moves
make_item_gl_entries and make_item_gl_entries_for_lcv
- AssetCapitalizationGLComposer(BaseStockGLComposer): moves
get_gl_entries_for_consumed_{stock,asset,service}_items and
get_gl_entries_for_target_item; inventory_account_map/sle_map/precision
become composer instance attributes
- AssetRepairGLComposer(BaseGLComposer): moves
get_gl_entries_for_repair_cost and get_gl_entries_for_consumed_items
(AR inherits AccountsController, not StockController)
All 29 GL snapshot tests and existing doctype test suites (32 SCR,
5 AC, 18 AR) pass.
purchase_receipt/services/gl_composer.py → PurchaseReceiptGLComposer(BaseStockGLComposer).
compose() orchestrates the four builder steps: _make_item_gl_entries,
_make_tax_gl_entries, set_gl_entry_for_purchase_expense (stays on doc),
update_regional_gl_entries (module-level).
_make_item_gl_entries preserves the original closure structure (six inner
functions: make_item_asset_inward_gl_entry, make_stock_received_but_not_billed_entry,
make_landed_cost_gl_entries, make_amount_difference_entry,
make_sub_contracting_gl_entries, make_divisional_loss_gl_entry); all doc
calls go through self.doc. _make_tax_gl_entries is a direct port.
Helpers that stay on the document: add_provisional_gl_entry (public —
PI composer calls it via purchase_receipt_doc.add_provisional_gl_entry),
add_gl_entry, get_item_account_wise_lcv_entries, update_assets,
is_landed_cost_booked_for_any_item.
PurchaseReceipt.get_gl_entries is now a 3-line shim; make_item_gl_entries
and make_tax_gl_entries removed from the class.
Verified: 29 GL snapshots byte-identical on test-erpnext-v17;
101 PR tests green on test-site-ai.
Extends the Phase-0 characterization suite with 3 PR scenarios:
pr_basic, pr_with_taxes, pr_return — all using _Test Company with
perpetual inventory (TCP1) so stock-received GL entries are produced.
Also refreshes se_material_issue.json (cumulative stock on test-erpnext-v17
shifted the outgoing valuation rate). 29 snapshots total, all green.
Stock Entry
stock_entry/services/gl_composer.py → StockEntryGLComposer(BaseStockGLComposer)
compose() calls super().compose() for the base warehouse↔expense GL pairs,
then adds additional-cost entries (_build_additional_cost_per_item_account +
_append_additional_cost_gl_entries) and LCV adjustments (_append_lcv_gl_entries).
get_item_account_wise_lcv_entries stays on StockController (called via self.doc).
StockEntry.get_gl_entries is now a 3-line shim.
Removed private helpers from StockEntry; dropped unused process_gl_map and
get_account_currency imports.
Stock Reconciliation
stock_reconciliation/services/gl_composer.py → StockReconciliationGLComposer(BaseStockGLComposer)
compose() guards cost_center and delegates to
super().compose(inventory_account_map, doc.expense_account, doc.cost_center).
StockReconciliation.get_gl_entries is now a 3-line shim.
Verified: 26 GL snapshots byte-identical on test-erpnext-v17;
89 SE tests and 33/34 SR tests green on test-site-ai
(1 pre-existing SR failure in test_serial_no_status_with_backdated_stock_reco,
unrelated to GL — IndexError in serial bundle setup).
Extends the Phase-0 characterization suite with 4 scenarios:
se_material_receipt, se_material_issue, se_material_transfer, sr_basic.
All use _Test Company with perpetual inventory (TCP1) so stock accounting
GL entries are produced. 26 snapshots total, all green on test-erpnext-v17.
Moves the StockController.get_gl_entries body into
erpnext/stock/services/base_stock_gl_composer.py → BaseStockGLComposer(BaseGLComposer).
compose(inventory_account_map, default_expense_account, default_cost_center) contains
all warehouse↔expense-account GL pair building and the internal-transfer rounding-diff
block; all helpers (get_inventory_account_dict, get_stock_ledger_details, etc.) remain
on self.doc and are called via doc.<method>.
StockController.get_gl_entries becomes a 3-line shim. Delivery Note, Stock Entry, and
Stock Reconciliation continue to work unchanged — DN inherits the shim directly; SE and
SR override and call super(), which now delegates to the composer.
Verified: 22 GL snapshots byte-identical on test-erpnext-v17.
Extends the Phase-0 characterization suite with 2 DN scenarios (basic
delivery and return) using _Test Company with perpetual inventory so
stock accounting GL entries are produced. Uses stock_entry_utils.make_stock_entry
directly (avoids importing test_delivery_note and its conflicting test-record deps).
Run: bench --site test-erpnext-v17 run-tests --module erpnext.accounts.test_gl_characterization
Move the Journal Entry GL assembly into a new JournalEntryGLComposer(
BaseGLComposer); compose() projects the accounts child rows into GL dicts,
mirroring the former build_gl_map, which is now a thin shim delegating to
the composer. Drop the now-unused get_advance_payment_doctypes import.
Extend the Phase-0 GL safety net with three representative Journal Entry
scenarios (basic two-line, multi-currency, against a Sales Invoice with
party and reference) ahead of moving JE onto the composer.
Move the Payment Entry GL row builders (party, bank, deductions, tax)
onto a new PaymentEntryGLComposer(BaseGLComposer); compose() mirrors the
former build_gl_map, which is now a thin shim delegating to the composer.
The builders operate on self.doc and shared helpers stay on the document.
Advance-posting builders are left on the controller; they post in a
separate pass and move with the advances service in a later phase.
Extend the Phase-0 GL safety net with five representative Payment Entry
scenarios (receive against SI, pay against PI, with deductions, with
taxes, multi-currency) ahead of moving PE onto the composer.
Move make_item_gl_entries, make_stock_adjustment_entry,
get_provisional_accounts, make_provisional_gl_entry, and
update_net_purchase_amount_for_linked_assets from PurchaseInvoice onto
PurchaseInvoiceGLComposer, completing the full GL builder migration.
purchase_invoice.py no longer contains any GL row-building logic;
PurchaseInvoiceGLComposer is the single authoritative source for all
PI GL entries, mirroring the SalesInvoiceGLComposer pattern.
All 12 GL characterization snapshots pass.
Move make_supplier_gl_entry, add_supplier_gl_entry, make_tax_gl_entries,
make_internal_transfer_gl_entries, make_gl_entries_for_tax_withholding,
make_payment_gl_entries, make_write_off_gl_entry, and
make_gle_for_rounding_adjustment from PurchaseInvoice onto
PurchaseInvoiceGLComposer.
compose() now calls self.X for all moved builders; the make_item cluster
(make_item_gl_entries, make_provisional_gl_entry, get_provisional_accounts,
update_net_purchase_amount_for_linked_assets, make_stock_adjustment_entry)
still lives on doc pending batch-2 migration.
All 12 GL characterization snapshots pass.
Phase 3 of the accounts/controller refactor. Adds PurchaseInvoiceGLComposer;
PI's get_gl_entries body moves into compose() and the method becomes a thin
shim. Row-builder methods still live on the document (invoked via self.doc)
and migrate onto the composer next.
After comparing the SI and PI compose() flows, BaseGLComposer is kept minimal:
the two differ in step order, builders, and per-doctype regional function, so a
shared template is not warranted. No behavior change (Phase 0 snapshots and PI
GL tests stay green).
Relocates all 11 Sales Invoice-specific GL entry builders from the document
onto SalesInvoiceGLComposer, operating on self.doc. The perpetual-inventory
super().get_gl_entries() call becomes super(SalesInvoice, doc).get_gl_entries().
Shared bucket-A helpers (get_gl_dict, make_discount_gl_entries, etc.) remain on
AccountsController for now, invoked via self.doc, until all doctypes use a
composer. No behavior change: Phase 0 snapshots and the SI tests covering
perpetual inventory, POS, write-off, returns, fixed assets, internal transfer
and loyalty all stay green.
Phase 2 (pilot) of the accounts/controller refactor. Adds BaseGLComposer
and SalesInvoiceGLComposer; Sales Invoice's get_gl_entries body moves into
compose() and the method becomes a thin shim. Row-builder methods still live
on the document (invoked via self.doc) and migrate onto the composer next.
No behavior change (Phase 0 GL snapshots remain byte-identical).
make_reverse_gl_entries passed adv_adj as the company argument to
check_freezing_date, so the freeze-date check silently no-op'd on
cancellation (no company matched). Pass company explicitly so
cancellations respect the freezing date like submissions do.
Adds a regression test covering cancellation after the freeze date.
Phase 1 of the accounts/controller refactor. Moves the six pure
list-level validators (validate_disabled_accounts, validate_accounting_period,
validate_cwip_accounts, check_freezing_date, validate_against_pcv,
validate_allowed_dimensions) out of general_ledger.py into the new
erpnext/accounts/services/gl_validator.py. general_ledger.py imports and
calls them at the existing sites; no behavior change (Phase 0 GL snapshots
remain byte-identical).
The debit/credit balance trio stays in general_ledger.py for now since
get_debit_credit_difference mutates entries and is interleaved with the
round-off repair.
Phased plan to decompose accounts_controller.py and the sales_invoice.py
monolith into composed services. Documents the frozen GL-layer design
(GLComposer / gl_validator / general_ledger sink), method bucketing, and
the 8-phase rollout.
Extracted validation into validate_debit_note_with_update_stock().
Hide update_stock in JS via set_dynamic_labels() and is_debit_note handler.
Added unit test asserting ValidationError on save.
Fixes#54891
* refactor(queries): migrate item_query to Query Builder
Use Frappe Query Builder to ensure compatibility with PostgreSQL.
The implementation still relies on raw SQL for fcond and mcond through
LiteralValue to maintain compatibility with legacy filter builders.
* refactor(queries): migrate item_query to Query Builder
Fix the bugs found by coderabbit.
For the eol condition: PostgreSQL raises DatetimeFieldOverflow when evaluating '0000-00-00' as
a date literal, even inside NULLIF(). Added a db_type guard to skip the
zero-date condition on PostgreSQL, where it can never be stored anyway.
No generic cross-db solution found for this case; open to revisiting
* refactor(queries): Rework item_query to use get_query
Rework the item_query method to use get_query with the ignore_permissions flag at False
* refactor(controller): Fix the query builder
Fix the build query in item_query according to coderabbit
* refactor(queries): explicitly add has_variants
Explicitely add has_variants==0 to the query according to coderabbit feedback
* perf: get payment ledger and remove update from delink when immutable ledger is enabled
* revert: changes of get_payment_ledger_entries
* perf: skip delink_original_entry during cancellation when Immutable Ledger is enabled
* test: for immutable ledger
* test: add posting_date in create_sales_invoice
* fix: link validation err with immutable ledger on
* test: update testcase of the immutable ledger
* refactor(test): simpler test for immutable invariants
---------
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
This account holds a debit balance (inventory value delivered but not yet
invoiced) and clears to COGS on Sales Invoice, so it is economically a
short-term clearing asset rather than a trade payable. Move it from the
Stock Liabilities group to Stock Assets under Current Assets, with
account_category "Stock Assets" (and account_number 1420 in the numbered
chart). The account_type "Stock Delivered But Not Billed" is unchanged,
so posting logic in Sales Invoice and Delivery Note continues to key off
the correct account.
Existing tests hardcoded "Cost of Goods Sold" as expected GL account,
but SDBNB overrides it on DN submission. Use dn.items[0].expense_account
to work with both SDBNB-enabled and legacy companies.
Ensures regular transactions only match tax rules where
use_for_shopping_cart = 0, preventing webshop-specific rules
from applying to standard documents.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrates from raw frappe.db.sql with string interpolation to frappe.qb.
Adds hierarchical supplier_group matching (mirrors customer_group behaviour).
Removes unused get_customer_group_condition helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: use route_options instead of filters for Credit Note and Debit Note sidebar links
Filters with ["=", value] format produce broken URLs like
`?is_return=%3D%2C1` instead of `?is_return=1`. Switching to
route_options with a plain JSON object generates correct URLs.
* Revert "fix: debit credit not equal in purchase transactions for multi currency"
This reverts commit 75bcea57f4.
* Revert "test: add test case"
This reverts commit 1d30a202c3.
* Revert "fix: include rejected qty in tax (purchase receipt)"
This reverts commit 8c9a88abbe.
* feat: Added philipinnes chart of account json file
Signed-off-by: Soham-ambibuzz <soham.pawar@ambibuzz.com>
* feat: made changes as per review comments and corrected indentation
* feat: made changes as per review comments
* feat: made changes as per review comments to resolve the issues
* fix: fixed changes as per review comments
Signed-off-by: soham7117 <sohampawar626@gmail.com>
* fix: fixed changes as per review comments on bank group account
Signed-off-by: soham7117 <sohampawar626@gmail.com>
---------
Signed-off-by: Soham-ambibuzz <soham.pawar@ambibuzz.com>
Signed-off-by: soham7117 <sohampawar626@gmail.com>
Co-authored-by: soham7117 <sohampawar626@gmail.com>
replace raw SQL with frappe.get_all in get_criteria_list to leverage
the standard Frappe API. This improves code readability and follows
framework best practices.
replace raw SQL with frappe orm to leverage the framework's native
capabilities. this improves code maintainability and adheres to frappe
best practices.
* refactor(material-request): replace raw SQL with Frappe Query Builder
Replace frappe.db.sql with frappe.qb in get_linked_material_requests
to improve readability and leverage the ORM's built-in SQL injection protection.
* removes unused import
* fix: enforce user permissions on bank account get_list
* feat: auto-select last used bank account
* fix: skeleton loaders in bank balance
* fix: show empty state for no bank transactions
* chore: add Stripe and PayPal logos
* fix: alignment of header text in list-view
* fix: wrap words in transaction description
* fix: change file-dropzone color on hover
* feat: initial SPA setup for banking
* wip: bring over new banking module
* feat: added Espresso design tokens
* feat: button styles
* fix: add all ink colors
* wip: espresso design system changes
* feat: button and badge espresso components
* fix: button styling for reconcile
* feat: Espresso progress bar
* feat: Espresso toggle switch
* feat: Espresso tabs design
* fix: vertical tab support
* fix: button sizing across modals
* feat: Espresso style table layout
* feat: Espresso tooltip
* feat: Espresso elevations and checkbox
* feat: Dialog with Espresso styles
* feat: Espresso textarea
* fix: input styles
* fix: colors on bank picker
* fix: breadcrumb styling
* fix: bank picker styling
* feat: create doctypes and fields for bank reconciliation
* feat: APIs for banking
* fix: use date format parser
* fix: font styling to match Espresso
* wip: settings modal
* feat: settings dialog component
* fix: icons and invalid requests
* feat: preferences tab
* fix: adjust icon stroke width to 1.5
* feat: rule configuration in settings
* fix: remove sheet component
* feat: alert and error banner component
* feat: dropdown in Espresso
* feat: popover and select in Espresso
* fix: cleanup more styles
* fix: match size of link fields
* feat: command styling
* fix: remove unused style tokens
* fix: styles for global date picker dropdown
* fix: styles for match and reconcile
* feat: table Espresso component
* feat: remove all other design tokens
* fix: remove unused tokens
* fix: form elements
* fix: remove unused styles and fix filters in bank transaction list
* feat: fetch bank rec doctypes for filtering
* fix: record payment modal
* feat: support for dark mode switching
* fix: move bank logos to public folder
* feat: add support for RTL
* feat: support for RTL
* chore: send layout direction in dev boot
* fix: make checkbox work in RTL
* feat: dark mode support
* fix: dark mode style
* feat: bank logos in dark mode
* feat: dark mode bank logos
* chore: use dark mode bank logos everywhere
* chore: move rule evaluation to controller
* chore: add tests for bank transaction rules
* fix: move deps to fix actions errors
* fix: move tw-animate-css to deps
* fix: remove shadcn
* fix: do not open modal if no transactions selected
* fix: add translation strings
* feat: add banner on existing bank reconciliation tool
* feat: bank statement import
* fix: translations and layout directions
* fix: validation for transaction matching rule
* fix: styles
* fix: show conflicting transactions in alert
* fix: show help text for new banking module forms
* feat: show total debits and credits
* fix: dark mode colors in automatic config
* feat: add keyboard shortcuts help
* feat: added keyboard shortcut for settings
* fix: decrease size of progress bar
* chore: bump packages
* feat: add tests for statement import
* fix: settings dialog
* fix: show banner on small screens
* fix: show banner when no bank account set
* refactor(purchase-order): use ORM syntax for min order quantity query
Use frappe.get_all instead of raw SQL with manual string formatting
to fetch min_order_qty. This improves code readability and leverages
the framework's built-in database abstraction.
* chore: fix formatting
* chore: fix formatting
* chore: fix formatting by adding a space
Add support for the new PaymentController interface from frappe/payments,
enabling Payment Request to work with v2 gateways while maintaining
backward compatibility with v1.
Related: frappe/payments#192
* fix: mark item tax templates as not applicable
For new German charts of accounts, mark accounts for different tax rates as *Not Applicable* in **Item Tax Templates**.
* fix: wrong applicable rate 19 in template 7
* fix: UI improvements for item form
* fix: add descriptions and tooltips to all checkboxes
* feat: show toast notification when item price is created
* fix: do not use selling rate for opening stock entry
* fix: add descriptions and tooltips to item default fields
* fix(test): give valuation rate for opening stock entry creation
* fix: moving naming series toggle before the return
* refactor: more changes in the form UI
Fix negative quantity check in validate_item_qty
When saving a Blanket Order with a blank qty field in the items table, the following error is raised:
TypeError: '<' not supported between instances of 'NoneType' and 'int'
Root cause: The validate_item_qty method compares d.qty < 0 directly. When the qty field is left empty, its value is None, and Python cannot compare None with an integer.
Fix
Wrap d.qty with flt(), which safely converts None (and any non-numeric value) to 0.0 before the comparison.
# Before
if d.qty < 0:
# After
if flt(d.qty) < 0:
* fix(dashboard-trends): set default fiscal year and company before validating filters Ensure and are populated with default values
* fix(dashboard-trends): ensure fiscal_year and company are properly set before validation to avoid empty filter issues
* Update erpnext/controllers/trends.py
---------
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
* Revise feature request template for clarity and structure
- Updated `about` field to mention "enhancement"
- Fixed typo: "many many requests" → "many requests"
- Fixed typo: "urgent need to" → "urgent need of"
- Added "Before Submitting" checklist to reduce duplicate issues
- Improved problem statement placeholder with a role-based example
- Added Environment section for ERPNext and Frappe version info
* chore: fix case
---------
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
* feat: add support for 'not applicable' tax in item tax templates
* refactor: remove unused imports
* fix: import NOT_APPLICABLE_TAX in get_item_tax_map function
* fix: add item wise tax details for not applicable taxes
* test: added test case for `not_applicable`
* fix: do not create item wise tax details for not applicable tax
* fix: ensure tax rate is set to 0 for not applicable tax rows
* refactor: changes as per review
* test: update selling settings
* test: correct settings
* fix: return both net and current tax amounts for not applicable tax
* fix: add tax_id handling in Tax Withholding Entry
* fix: update get_tax_id_for_party to handle absence of tax_id in payment and journal entries
* fix: refactor tax_id handling in Tax Withholding Entry and Details
* test: correct function address
* fix(manufacturing): close work order status when stock reservation is enabled
* chore: better syntax
---------
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
* feat(employee): Create User button and form.
* feat(employee): Add automatic user creation feature and related validations. Create User on Import.
* refactor(employee): create user function -removed useless function calls
* refactor(employee): reorganize joining and employee exit tabs at the end.
* feat(employee): Add birthdays and work anniversaries indicator in form ,list view enhancements and new empty state.
* fix: add missing type hints to whitelisted function arguments
* fix(employee): add 'set_only_once' property to 'Create User Automatically' field
* refactor(employee): remove anniversary indicator logic from employee form
* fix: move Joining section before Exit, relabel Employee Exit -> Exit
* fix: reset employee listview empty state, add import btn instead
* fix: employee user creation
- consider prefered email as default in employee creation
- remove unused user parameter from `create_user` API
- remove unnecessary validations on user ID, already checked by user doctype hooks
- set company email only if empty
* fix: only validate auto user creation before insert
* fix: uncollapse User Details section in new form
* fix: hide Create User Automatically checkbox if user is already selected
* fix: set create user perm to 1 by default + persist option while saving employee
* fix: avoid setting unnecessary fields
* fix: fallback to Personal Email for user creation just like client-side
* fix: reset User ID and make it read-only if 'Create User Automatically' is set
* test: Create User Automatically
* test(fix): set company in employee
---------
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
- consider prefered email as default in employee creation
- remove unused user parameter from `create_user` API
- remove unnecessary validations on user ID, already checked by user doctype hooks
- set company email only if empty
* fix(banking): include paid purchase invoices in reports and bank clearance
* fix: condition for amounts not reflected in system
* fix: set Sales Invoice to be the payment document in bank rec
* fix: add additional filter for `is_paid`
* fix: added is_paid
* fix: added invoice number in bank clearance tool
* chore: make requested changes
* fix: exclude opening JEs
* fix: bring back banking icon in desktop
* fix: enable submittability for ledger entries and cleanup invalid test submissions
* fix: reverted child table submittability
* fix: added ignore_links flag for back gl entry
* fix: add ignore_links for reconcile,cancelled PLE,cancelled SLE
* fix: update test_recreate_stock_ledgers to use db.delete instead of doc.delete
* chore: temporary test against frappe PR 37009
* fix: make Advance Payment Ledger Entry submittable
* refactor: add extra line for create_shipping_rule
* chore: revert temporary test against frappe PR 37009
* fix: use parent doc save with ignore_validate_update_after_submit for child table updates
* chore: temporary test against frappe PR 37009
* chore: revert temporary test against frappe PR 37009
* fix: use skip_docstatus_validation
System checks valuation rate in incoming_rate field, since SO has it in valuation_rate field of item row,
need to handle the field name dynamically based upon the doctype name in Selling Controller.
When a Document Naming Rule is configured in QI with a condition based on the company,
the system was not passing the company value properly. As a result, the naming rule
condition was skipped and the document name was generated using the default series.
This fix ensures that the company is passed correctly so that the configured
Document Naming Rule is evaluated and applied as expected.
* fix: avoid hardcoded column slicing for Profit & Loss chart data
* refactor: improve parameter naming and reduce code repetion by using same function get_period_columns()
* refactor: improved parameter naming in get_data() and get_chart_data()
* fix(manufacturing): remove delete query of batch and serial no
* fix(manufacturing): remove delete query of job card
* fix: remove delete function call for work order
* feat: add Bank Transaction as Reference Type to Journal Entry Account
* fix: take care of existing property setters
* fix: cancelling Bank Transactions should still be possible
* fix: handle blank options in patch
* fix: hide Reference Due Date for Bank Transaction
In their default state, the fields can be `None`. When a user enters something and deletes it afterwards, the fields contain an empty string.
This fixes the comparison.
The 'Incorrect Serial and Batch Bundle' report had a hardcoded
letter_head value of 'Test', preventing users from deleting a
Letter Head named 'Test' due to link check.
Standard reports should not reference specific Letter Head names.
Fixes#52569
* fix(global_defaults): set disable rounded total on POS Profiles
* fix: removed re-setting fields on toggle functions
* fix: removed property setter for print_hide
* fix: merge taxes in purchase receipt when get items from multiple purchase invoices
* fix: make merge tax configurable
* chore: follow standard merge taxes method
* chore: follow standard merge taxes method
* feat: add editable DocTypes To Delete list with import/export
Add user control over transaction deletion with reviewable and reusable deletion templates.
- New "DocTypes To Delete" table allows users to review and customize what will be deleted before submission
- Import/Export CSV templates for reusability across environments
- Company field rule: only filter by company if field is specifically named "company", otherwise delete all records
- Child tables (istable=1) automatically excluded from selection
- "Remove Zero Counts" helper button to clean up list
- Backward compatible with existing deletion records
* refactor: improve Transaction Deletion Record code quality
- Remove unnecessary chatty comments from AI-generated code
- Add concise docstrings to all new methods
- Remove redundant @frappe.whitelist() decorators from internal methods
- Improve CSV import validation (header check, child table filtering)
- Add better error feedback with consolidated skip messages
- Reorder form fields: To Delete list now appears before Excluded list
- Add conditional visibility for Summary table (legacy records only)
- Improve architectural clarity: single API entry point per feature
Technical improvements:
- export_to_delete_template_method and import_to_delete_template_method
are now internal helpers without whitelist decorators
- CSV import now validates format and provides detailed skip reasons
- Summary table only shows for submitted records without To Delete list
- Maintains backward compatibility for existing deletion records
* fix: field order
* test: fix broken tests and add new ones
* fix: adapt create_transaction_deletion_request
* test: fix assertRaises trigger
* fix: conditionally execute Transaction Deletion pre-tasks based on selected DocTypes
* refactor: replace boolean task flags with status fields
* fix: remove UI comment
* fix: don't allow virtual doctype selection and improve protected Doctype List
* fix: replace outdated frappe.db.sql by frappe.qb
* feat: add support for multiple company fields
* fix: autofill comapny field, add docstrings, filter for company_field
* fix: add edge case handling for update_naming_series and add tests for prefix extraction
* fix: use redis for running deletion validation, check per doctype instead of company
* Algeria chart of accounts
Algeria chart of accounts
* Update Algeria Chart Of Account
* Algeria chart of account
* Algeria Chart of Account
Algeria Chart of Account
* Modify Algeria tax entries in country_wise_tax.json
Updated tax rates and account names for Algeria.
* Rename account for Algeria tax from VAT to TVA
Rename account for Algeria tax from VAT to TVA
Thank you for your interest in raising an Issue with ERPNext. An Issue could mean a bug report or a request for a missing feature. By raising a bug report, you are contributing to the development of ERPNext and this is the first step of participating in the community. Bug reports are very helpful for developers as they quickly fix the issue before other users start facing it.
Thank you for your interest in raising an issue with ERPNext. An issue can be either a bug report or a feature request.
Feature requests are also a great way to take the product forward. New ideas can come in any user scenario and the issue list also acts a roadmap of future features.
By reporting bugs, you contribute directly to improving ERPNext. Bug reports help developers identify and fix issues quickly before they affect more users.
When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want.
Feature requests are also valuable. They help shape the future of the product by introducing new ideas and improvements based on real-world use cases.
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.frappe.io](https://discuss.frappe.io/c/erpnext/6).
When raising an issue, keep in mind that developers do not have access to your environment. Therefore, provide as much relevant information as possible.
If you are suggesting a feature, clearly describe what you expect and how it should behave.
> ⚠️ The issue tracker is not the right place for general questions or discussions.
> Please use the forum instead: https://discuss.frappe.io/c/erpnext/6
---
### Reply and Closing Policy
If your issue is not clear or does not meet the guidelines, then it will be closed. If it is closed, please supply the information asked and re-open it.
If your issue is unclear or does not meet the guidelines, it may be closed.
If that happens, please provide the requested information and reopen the issue.
---
### General Issue Guidelines
1.**Search existing Issues:**Before raising a Issue, search if it has been raised before. Maybe add a 👍 or give additional help by creating a mockup if it is not already created.
1.**Report each issue separately:** Don't club multiple, unreleated issues in one note.
1.**Brief:** Please don't include long explanations. Use screenshots and bullet points instead of descriptive paragraphs.
1.**Search existing issues:**
Before creating a new issue, check if it already exists. You can support existing issues with a 👍 or contribute additional details or mockups.
2.**Report issues separately:**
Do not combine multiple unrelated issues into a single report.
3.**Be concise:**
Avoid long explanations. Use bullet points and screenshots where possible.
---
### Bug Report Guidelines
1.**Steps to Reproduce:**The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it.
1.**Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version
1.**Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit"
1.**Screenshots:** Screenshots are a great way of communicating issues. Try adding annotations or using LiceCAP to take a screencast in `gif`.
1.**Steps to reproduce:**
Clearly list the steps required to reproduce the issue. If the issue cannot be reproduced, it cannot be fixed.
2.**Version number:**
Include the ERPNext version. The issue may already be fixed in a newer release.
3.**Clear title:**
Use a descriptive title (e.g., "Unable to submit Purchase Order without Basic Rate" instead of "Cannot submit").
4.**Screenshots:**
Add screenshots or screen recordings (e.g., `.gif`) to illustrate the issue.
---
### Feature Request Guidelines
1.**Clarity:**Clearly specify how do you want the feature to behave. Don't just say "I would like multiple PDF formats", say that "Ability to add multiple print formats for customers with different languages".
1.**Solution:** Try and identify how the feature should look like.
1.**Mockups:** Mockups are a great way to explain your requirement.
1.**Clarity:**
Clearly describe the expected behavior. Avoid vague statements.
### What if my Issue is closed
2.**Proposed solution:**
Suggest how the feature should work.
Don't worry, take the feedback, supply the correct information and re-open it!
3.**Mockups:**
Provide mockups or examples whenever possible.
---
### What if my issue is closed?
Don't worry. Review the feedback, provide the required information, and reopen the issue.
@@ -17,17 +17,21 @@ Welcome to ERPNext issue tracker! Before creating an issue, please heed the foll
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
Please keep in mind that we get many many requests and we can't possibly work on all of them, we prioritize development based on the goals of the product and organization. Feature requests are still welcome as it helps us in research when we do decide to work on the requested feature.
Please keep in mind that we get many requests and we can't possibly work on all of them, we prioritize development based on the goals of the product and organization. Feature requests are still welcome as it helps us in research when we do decide to work on the requested feature.
If you're in urgent need to a feature, please try the following channels to get paid developments done quickly:
If you're in urgent need of a feature, please try the following channels to get paid developments done quickly:
echo"PR already open — branch updated in place. No new PR needed."
exit0
fi
gh pr create \
--base "${HOTFIX_BRANCH}"\
--head "sync_translations_${HOTFIX_BRANCH}"\
--title "chore: sync translations to ${HOTFIX_BRANCH}"\
--body "Automated sync of Crowdin translations from \`develop\` to \`${HOTFIX_BRANCH}\`.
A 3-way merge is performed per language, then \`bench update-po-files\` reconciles each \`.po\` against hotfix's \`main.pot\`:
| Case | Condition | Result |
|------|-----------|--------|
| **a** | \`msgid\` in hotfix's \`main.pot\`, **not** in develop's \`.po\` | Hotfix's existing \`msgstr\` is **preserved** (string removed from develop but still needed in hotfix) |
| **b** | \`msgid\` **not** in hotfix's \`main.pot\` | **Dropped** from hotfix's \`.po\` |
| **c** | \`msgid\` in both hotfix's \`main.pot\` and develop's \`.po\` | Develop's \`msgstr\` is used (Crowdin translation wins) |
Generated by the \`sync-hotfix-translations\` workflow."\
100% Open-Source ERP system to help you run your business.
100% Open-Source ERP System to help you run your business.
### Motivation
Running a business is a complex task - handling invoices, tracking stock, managing personnel and even more ad-hoc activities. In a market where software is sold separately to manage each of these tasks, ERPNext does all of the above and more, for free.
Running a business is a complex task - handling invoices, tracking stock, managing personnel, and other daily operations. In a market where software is sold separately to manage each of these tasks, ERPNext does all of the above and more, for free.
### Key Features
- **Accounting**: All the tools you need to manage cash flow in one place, right from recording transactions to summarizing and analyzing financial reports.
- **Order Management**: Track inventory levels, replenish stock, and manage sales orders, customers, suppliers, shipments, deliverables, and order fulfillment.
- **Manufacturing**: Simplifies the production cycle, helps track material consumption, exhibits capacity planning, handles subcontracting, and more!
- **Asset Management**: From purchase to perishment, IT infrastructure to equipment. Cover every branch of your organization, all in one centralized system.
- **Projects**: Delivery both internal and external Projects on time, budget and Profitability. Track tasks, timesheets, and issues by project.
- **Asset Management**: From purchase to disposal, IT infrastructure to equipment. Covers every branch of your organization, all in one centralized system.
- **Projects**: Deliver both internal and external projects on time, budget and profitability. Track tasks, timesheets, and issues by project.
<details open>
@@ -52,7 +53,7 @@ Running a business is a complex task - handling invoices, tracking stock, managi
### Under the Hood
- [**Frappe Framework**](https://github.com/frappe/frappe): A full-stack web application framework written in Python and Javascript. The framework provides a robust foundation for building web applications, including a database abstraction layer, user authentication, and a REST API.
- [**Frappe Framework**](https://github.com/frappe/frappe): A full-stack web application framework written in Python and JavaScript. The framework provides a robust foundation for building web applications, including a database abstraction layer, user authentication, and a REST API.
- [**Frappe UI**](https://github.com/frappe/frappe-ui): A Vue-based UI library, to provide a modern user interface. The Frappe UI library provides a variety of components that can be used to build single-page applications on top of the Frappe Framework.
@@ -60,12 +61,12 @@ Running a business is a complex task - handling invoices, tracking stock, managi
### Managed Hosting
You can try [Frappe Cloud](https://frappecloud.com), a simple, user-friendly and sophisticated [open-source](https://github.com/frappe/press) platform to host Frappe applications with peace of mind.
You can try [Frappe Cloud](https://frappecloud.com), a simple, user-friendly, and sophisticated [open-source](https://github.com/frappe/press) platform to host Frappe applications reliably and securely.
It takes care of installation, setup, upgrades, monitoring, maintenance and support of your Frappe deployments. It is a fully featured developer platform with an ability to manage and control multiple Frappe deployments.
It handles installation, setup, upgrades, monitoring, maintenance, and support of your Frappe deployments. It is a fully featured developer platform with an ability to manage and control multiple Frappe deployments.
> For Docker basics and best practices refer to Docker's [documentation](https://docs.docker.com)
### Try on your environment
> **⚠️ Disposable demo only**
>
> **This setup is intended for quick evaluation. Expect to throw the environment away.** You will not be able to install custom apps to this setup. For production deployments, custom configurations, and detailed explanations, see the full documentation.
First clone the repo:
```sh
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
docker compose -f pwd.yml up -d
```
After a couple of minutes, site should be accessible on your localhost port: 8080. Use below default login credentials to access the site.
- Username: Administrator
- Password: admin
Then run:
See [Frappe Docker](https://github.com/frappe/frappe_docker?tab=readme-ov-file#to-run-on-arm64-architecture-follow-this-instructions) for ARM based docker setup.
```sh
docker compose -f pwd.yml up -d
```
Wait for a couple of minutes for ERPNext site to be created or check the `create-site` container logs before opening browser on port `8080`. (username: `Administrator`, password: `admin`)
See [Frappe Docker](https://github.com/frappe/frappe_docker/blob/main/docs/01-getting-started/03-arm64.md) for ARM based docker setup
## Development Setup
@@ -100,7 +116,7 @@ See [Frappe Docker](https://github.com/frappe/frappe_docker?tab=readme-ov-file#t
The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details.
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the Frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
### Local
@@ -129,20 +145,20 @@ To setup the repository locally follow the steps mentioned below:
4. Open the URL `http://erpnext.localhost:8000/app` in your browser, you should see the app running
## Learning and community
## Learning and Community
1. [Frappe School](https://school.frappe.io) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
3. [Discussion Forum](https://discuss.frappe.io/c/erpnext/6) - Engage with community of ERPNext users and service providers.
3. [Discussion Forum](https://discuss.frappe.io/c/erpnext/6) - Engage with the community of ERPNext users and service providers.
4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users.
The ERPNext team and community take security issues seriously. To report a security issue, fill out the format [https://erpnext.com/security/report](https://erpnext.com/security/report).
The ERPNext team and community take security issues seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
You can help us make ERPNext and all it's users more secure by following the [Reporting guidelines](https://erpnext.com/security).
You can help us make ERPNext and all its users more secure by following the [Reporting guidelines](https://frappe.io/security).
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
@@ -18,8 +18,9 @@ We will grant permission to use the ERPNext name and logo for projects that meet
- The primary purpose of your project is to promote the spread and improvement of the ERPNext software.
- Your project is non-commercial in nature (it can make money to cover its costs or contribute to non-profit entities, but it cannot be run as a for-profit project or business).
Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
- If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
-Your project neither promotes nor is associated with entities that currently fail to comply with the GPL license under which ERPNext is distributed.
If your project meets these criteria, you will be permitted to use the ERPNext name and logo to promote your project in any way you see fit with one exception: Please do not use ERPNext as part of a domain name.
Use of the ERPNext name and logo is additionally allowed in the following situations:
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
exportdefaultdefineConfig([
globalIgnores(['dist']),
{
files:['**/*.{ts,tsx}'],
extends:[
// Other configs...
// Remove tseslint.configs.recommended and replace with this
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
<AlertDialogDescription>{type==='match'?_("Are you sure you want to unmatch the voucher from this transaction?"):_("Are you sure you want to cancel this {} {}?",[_(item.voucher.reference_doctype),item.voucher.reference_name])}</AlertDialogDescription>
<DialogTitle>{_("Set closing balance as per bank statement")}</DialogTitle>
<DialogDescription>
{_("Enter the closing balance you see in your bank statement for {0} as of the {1}",[bankAccount?.account_name??bankAccount?.name??'',formatDate(date,'Do MMM YYYY')])}
__html: _("Below is a list of all accounting entries posted against the bank account {0} between {1} and {2}.",[`<strong>${bankAccount?.account}</strong>`,`<strong>${formattedFromDate}</strong>`,`<strong>${formattedToDate}</strong>`])
<TableRowkey={field.id}className={index===0?'bg-surface-gray-1 cursor-not-allowed':''}title={index===0?_("This is the bank account entry. You cannot edit it."):''}>
__html: _("Below is a list of all entries posted against the bank account {0} which have not been cleared till {1}.",[`<strong>${bankAccount?.account}</strong>`,`<strong>${formatDate(dates.toDate)}</strong>`])
__html: _("Below is a list of all bank transactions imported in the system for the bank account {0} between {1} and {2}.",[`<strong>${bankAccount?.account_name}</strong>`,`<strong>${formattedFromDate}</strong>`,`<strong>${formattedToDate}</strong>`])
}}/>
</Paragraph>
<Buttonsize='md'variant='subtle'asChild>
<Linkto="/statement-importer">
<ImportIcon/>
{_("Import Bank Statement")}
</Link>
</Button>
</div>
{error&&<ErrorBannererror={error}/>}
<Filters
onSearchChange={onSearchChange}
search={search}
results={filteredResults}
setAmountFilter={setAmountFilter}
amountFilter={amountFilter}
onTypeFilterChange={setTypeFilter}
typeFilter={typeFilter}
status={status}
setStatus={setStatus}
/>
<ListView
data={filteredResults}
columns={transactionColumns}
getRowId={(row)=>row.name}
maxHeight="calc(100vh - 200px)"
scrollAreaClassName="min-h-[calc(100vh-200px)]"
emptyState={<Empty>
<EmptyMedia>
<ListIcon/>
</EmptyMedia>
<EmptyHeader>
<EmptyTitle>{_("No bank transactions found")}</EmptyTitle>
<EmptyDescription>{_("There are no transactions in the system for the selected bank account and dates that match the filters.")}</EmptyDescription>
__html: _("This report shows all entries in the system where the <strong>clearance date is before the posting date</strong> which is incorrect.")
}}/>
<br/>
{data&&data.message.result.length>0&&<span>
<spandangerouslySetInnerHTML={{
__html: _("Entries below have a posting date after {0} but the clearance date is before {1}.",[`<strong>${formattedToDate}</strong>`,`<strong>${formattedToDate}</strong>`])
}}/>
<br/>
{_("You can reset the clearing dates of these entries here.")}
</span>}
</Paragraph>
</div>
{error&&<ErrorBannererror={error}/>}
{data&&data.message.result.length>0&&(
<divclassName="space-y-2">
<pclassName="text-ink-gray-5 text-sm">{_("Incorrectly cleared entries as per the report.")}</p>
<TableRowclassName="bg-surface-gray-1 cursor-not-allowed"title={_("This is the row for the bank account. It will be auto populated based on the bank transaction.")}>
<TableCell>
<Checkboxdisabled/>
</TableCell>
<TableCellclassName="align-top">
</TableCell>
<TableCellclassName="align-top text-ink-gray-5">
<spanclassName="px-2">
BankGLAccount
</span>
</TableCell>
<TableCellclassName="align-top">
</TableCell>
<TableCellclassName={"align-top text-end"}>
<spanclassName="text-ink-gray-5 text-sm">
{transaction_type==="Withdrawal"||transaction_type==="Any"?_("Will be auto-populated"):""}
</span>
</TableCell>
<TableCellclassName={"text-end align-top"}>
<spanclassName="text-ink-gray-5 text-sm">
{transaction_type==="Deposit"||transaction_type==="Any"?_("Will be auto-populated"):""}
<ParagraphclassName="text-p-sm">{(_("You can set up the rule to split the transaction across multiple accounts."))}
<br/>{_("You can also add credit or debit values to pre-fill - these support both static values (like 200) or formulas (like transaction_amount * 0.25).")}
{_("In this case, the amount will be calculated as 25% of the transaction amount. If the transaction amount is 200, then this will be calculated as 200 * 0.25 = 50.")}
<spanclassName="text-sm font-medium">{_("Suggested Transfer to {0}",[data.message.account])}</span>
</div>
<divclassName='flex flex-col gap-1'>
<spanclassName='text-p-sm'>{_("The system found a mirror transaction ({0}) in another account with the same amount and date.",[data.message.name])}</span>
<spanclassName='text-p-sm'>{_("Accepting the suggestion will reconcile both transactions.")}</span>
<ParagraphclassName='text-p-sm'>{_("We've found 1 transaction in the statement file that will be imported into the system. Please review the details below and click the 'Import' button to proceed.")}</Paragraph>
):(
<ParagraphclassName='text-p-sm'>{_("{0} transactions will be imported into the system. Please review the details below and click the 'Import' button to proceed.",[data.final_transactions?.length?.toString()||"0"])}</Paragraph>
{transactions.length===1?_("We've found 1 existing transaction in the system that conflicts with the transactions in the statement file. Are you sure you want to proceed with the import?")
:_("We've found {0} existing transactions in the system that conflict with the transactions in the statement file. Are you sure you want to proceed with the import?",[transactions.length.toString()])}
{transactions.length===1?_("We've found 1 existing transaction in the system that conflicts with the transactions in the statement file. Are you sure you want to proceed with the import?")
:_("We've found {0} existing transactions in the system that conflict with the transactions in the statement file. Are you sure you want to proceed with the import?",[transactions.length.toString()])}
{_('Review each page. In the Table view, map each column, click a row number to set/clear the header row, and exclude anything that is not transactions (ads, summaries).')}
<LabelhtmlFor="transfer_match_days"className="text-p-base text-ink-gray-6">{_("Number of days to match transfers")}</Label>
<pclassName="text-p-sm text-ink-gray-5">
{_("For example, if set to 4, the system will try to find matching transfer transactions in other banks 4 days before and after the transaction date. This is because transactions can clear on different days on different bank accounts.")}
<LabelhtmlFor="automatically_run_rules_on_unreconciled_transactions"className="text-p-base text-ink-gray-6">{_("Automatically run rules on unreconciled transactions")}</Label>
<pclassName="text-p-sm text-ink-gray-5">
{_("This will automatically run transaction matching rules on unreconciled transactions every hour.")}
formDescription={_("For example, if set to 4, the system will try to find matching transactions in other banks 4 days before and after the transaction date. This is because transactions can clear on different days on different bank accounts.")}
<DropdownMenuItemonClick={()=>handleRunRules(false)}disabled={isRunningRules}title={_("Run rules on unreconciled transactions that haven't been evaluated yet")}>
<Play/>
{_("Run on new transactions")}
</DropdownMenuItem>
<DropdownMenuItemonClick={()=>handleRunRules(true)}disabled={isRunningRules}title={_("Force re-evaluate all unreconciled transactions, even if they were previously evaluated")}>
success: checked?_("Scheduled job enabled. Transactions will be auto classified."):_("Scheduled job disabled. Transactions will not be auto classified."),
error: _("Failed to update auto classify transactions settings")
<divtitle={rule.transaction_type==="Any"?_("Applies to withdrawals and deposits"):rule.transaction_type==="Withdrawal"?_("Applies to withdrawals"):_("Applies to deposits")}>
{files.length===0?<pclassName='text-sm text-ink-gray-5 text-center h-8 flex items-center justify-center'>{multiple?_("Drop some files here, or click to select files"):_("Drop a file here, or click to select a file")}</p>:null}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.