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.