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.