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