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>
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.