Commit Graph

3414 Commits

Author SHA1 Message Date
Nabin Hait
734fe874f2 Merge pull request #55647 from nabinhait/stock-controller-refactoring
refactor(stock): extract StockController into focused services
2026-06-05 15:52:00 +05:30
Nabin Hait
7d72d21bbe refactor(stock): add _service suffix to serial_batch_bundle and quality_inspection modules
Consistent service-module naming: serial_batch_bundle_service.py /
quality_inspection_service.py (matching stock_ledger_service.py). Importers updated;
engine-module imports (stock.serial_batch_bundle) untouched.
2026-06-05 15:16:41 +05:30
Nabin Hait
b41eb6876a refactor(stock): rename stock_ledger service module to stock_ledger_service
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.
2026-06-05 14:59:41 +05:30
Nabin Hait
c5ff1009b2 refactor: relocate ledger_preview to controllers (cross-cutting, not stock-only)
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.
2026-06-05 14:41:58 +05:30
Nabin Hait
b82b2c2ebd refactor(stock): use central erpnext/exceptions.py for stock exceptions
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.
2026-06-05 14:34:27 +05:30
Nabin Hait
8db05fc4da refactor(stock): drop 7 in-repo-only StockController delegators
Remove the delegators whose only callers were in-repo StockController subclasses,
repointing every caller to the owning service / free function:

- validate_warehouse_of_sabb, validate_duplicate_serial_and_batch_bundle,
  validate_serialized_batch, clean_serial_nos -> SerialBatchBundleService
- update_inventory_dimensions -> StockLedgerService
- validate_putaway_capacity -> putaway_rule.validate_putaway_capacity (free fn)
- set_landed_cost_voucher_amount -> landed_cost_voucher.set_landed_cost_voucher_amount

Callers repointed: StockController.validate() (base), StockEntry.validate(),
StockReconciliation (validate + reconciliation SLE build), BuyingController.validate(),
and the Landed Cost Voucher submit (doc.set_landed_cost_voucher_amount on the receipt).

Verified green: ledger snapshots, stock_entry (91), stock_reconciliation (34),
landed_cost_voucher (15), subcontracting_receipt (32), delivery_note (71).
2026-06-05 13:32:04 +05:30
Nabin Hait
6a064765d1 refactor(stock): drop zero-caller StockController delegators
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.
2026-06-05 12:46:09 +05:30
Nabin Hait
78d5fbaca4 refactor(stock): address layering/robustness review findings (#8, #9, #10)
#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.
2026-06-05 12:29:55 +05:30
Pandiyan P
34d128d752 fix: prevent selling items from sample retention warehouse (#55613)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-06-04 16:30:57 +00:00
Mihir Kandoi
d6a201ed4a feat: create sales invoice from pick list (#55594)
* 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
2026-06-04 15:49:19 +00:00
Shllokkk
9cecf2e6f9 refactor: convert rfq_transaction_list to query builder (#55497) 2026-06-04 19:36:40 +05:30
Nabin Hait
d1fd91a542 refactor(stock): extract ledger preview helpers to ledger_preview module
Move the read-side GL/SLE preview helpers (get_accounting_ledger_preview,
get_stock_ledger_preview, get_sl_entries_for_preview, get_gl_entries_for_preview,
get_columns, get_data) into erpnext/stock/services/ledger_preview.py. The
whitelisted show_accounting_ledger_preview / show_stock_ledger_preview entry points
stay in stock_controller (client JS hardcodes their dotted path) and call the
relocated helpers.

Behaviour-preserving: ledger characterization snapshots stay green.
2026-06-04 16:51:17 +05:30
Nabin Hait
8e41e75d89 refactor(stock): relocate landed-cost and putaway logic to owning doctypes
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.
2026-06-04 16:22:38 +05:30
Nabin Hait
7c2406077a refactor(stock): extract StockInternalTransferService from StockController
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.
2026-06-04 16:05:42 +05:30
Nabin Hait
926bdf5a20 refactor(stock): extract QualityInspectionService from StockController
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.
2026-06-04 15:54:34 +05:30
Nabin Hait
b447cbc3c1 refactor(stock): move GL-building helpers onto BaseStockGLComposer
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.
2026-06-04 15:50:32 +05:30
Nabin Hait
4affdd51f6 refactor(stock): extract StockLedgerService from StockController
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.
2026-06-04 15:35:04 +05:30
Nabin Hait
a26d8d448c refactor(stock): extract SerialBatchBundleService from StockController
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.
2026-06-04 15:00:44 +05:30
Nabin Hait
dfe3280737 Merge remote-tracking branch 'upstream/develop' into erpnext-refactoring
# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/buying/doctype/purchase_order/purchase_order.py
#	erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
#	erpnext/controllers/accounts_controller.py
#	erpnext/selling/doctype/sales_order/sales_order.py
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/stock/doctype/delivery_note/delivery_note.py
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
2026-06-03 13:23:03 +05:30
Ankush Menat
b72cde73ba fix: Add likely missing escaps (#55574) 2026-06-03 07:28:05 +00:00
Nabin Hait
cfed16ab6c refactor(gl): give SI and PI their own precision-loss GL entry method
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.
2026-06-03 12:45:58 +05:30
Shllokkk
0c61ad4e6d Avoid status updation for purchase invoice from paid to unpaid by issuing a paid debit note against it (#54382) 2026-06-03 12:04:19 +05:30
Shubh Doshi
5074597d00 perf: batch status check for on-hold/closed documents, remove N+1 queries (#54798) 2026-06-03 11:50:49 +05:30
Luis Mendoza
36dc196a1d fix: prevent double rounding in inclusive tax calculations (#52512)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-06-03 11:09:42 +05:30
Nabin Hait
04443ae29e Merge upstream/develop into erpnext-refactoring
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
2026-06-03 11:05:01 +05:30
Vishnu Priya Baskaran
da82ac86b5 fix payment schedule discount date when no discount is applied (#55462) 2026-06-03 10:56:19 +05:30
Shllokkk
efb8336bf8 fix: remove ignore_permissions from get_party_details signature (#55491) 2026-06-03 10:51:44 +05:30
Mihir Kandoi
0a49403838 fix: unable to submit subcontracted job card (#55537) 2026-06-02 09:18:42 +00:00
Mihir Kandoi
78f9434d14 refactor: resolve regression-safe CodeQL code-quality findings (#55531)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 08:25:32 +05:30
rohitwaghchaure
ba477412ea Merge pull request #55415 from aerele/fix/support-#68706
fix(stock): allow to create quality inspection after purchase/delivery
2026-06-01 21:43:24 +05:30
Nabin Hait
530e587bf2 refactor: use mapper paths directly, drop re-export shims
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).
2026-06-01 18:17:56 +05:30
Mihir Kandoi
dbcfac839c chore: rename type field to secondary_item_type (#55469) 2026-06-01 05:54:59 +00:00
Sudharsanan11
c6a88ab1d2 fix(stock): allow to create quality inspection after purchase/delivery 2026-06-01 10:37:25 +05:30
Sudharsanan Ashok
4ef17c9c1b fix(stock): change qb to qb get_query to fix filter issues (#55443) 2026-05-31 12:21:22 +05:30
nishkagosalia
30011963bc fix: over order allowance setting fix 2026-05-29 12:09:19 +05:30
Nabin Hait
a12d666037 refactor: extract child item update cluster into ChildItemUpdater service
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.
2026-05-28 20:38:25 +05:30
Nabin Hait
c7b4806117 refactor: extract party validation and inter-company logic into service classes
- 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.
2026-05-28 20:10:45 +05:30
Nabin Hait
6c1ac51d7a refactor: convert payment schedule and billing validation to service objects
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.
2026-05-28 17:13:23 +05:30
Nabin Hait
8aaa3a72ef refactor: convert tax cluster to TaxService class in taxes.py
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.
2026-05-28 16:53:03 +05:30
nishkagosalia
355d71dbd2 feat: over order allowance setting 2026-05-28 12:54:45 +05:30
Nabin Hait
0ee0d6f0c5 refactor: extract tax cluster from AccountsController into services/taxes.py
Moves set_taxes, is_pos_profile_changed, set_taxes_and_charges,
append_taxes_from_master, append_taxes_from_item_tax_template,
get_tax_row, set_other_charges, validate_enabled_taxes_and_charges,
validate_tax_account_company, get_tax_map, get_amount_and_base_amount,
get_tax_amounts, and make_discount_gl_entries into free functions in
accounts/services/taxes.py. AccountsController retains thin shims.
Removes now-unused parse_json import.
2026-05-28 12:32:04 +05:30
Nabin Hait
bb803a8f82 refactor: extract billing, payment schedule, and exchange gain/loss into services
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.
2026-05-27 23:42:50 +05:30
Nabin Hait
983d80f7c5 refactor(accounts): merge gl_entry_builder.py into base_gl_composer.py
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.
2026-05-27 22:32:06 +05:30
Nabin Hait
cba6a31497 refactor(accounts): extract get_gl_dict and add_gl_entry into gl_entry_builder.py
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.
2026-05-27 21:46:16 +05:30
Nabin Hait
29261c5fc2 refactor(accounts): extract tax helpers into accounts/services/taxes.py
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.
2026-05-27 16:28:02 +05:30
Nabin Hait
58c90ad651 refactor(accounts): extract advance payment logic into accounts/services/advances.py
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.
2026-05-27 15:55:28 +05:30
Nabin Hait
b68daea365 refactor: introduce BaseStockGLComposer, slim StockController.get_gl_entries
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.
2026-05-27 14:47:19 +05:30
Mihir Kandoi
f023bf8a96 fix: single variant creation error (#55286)
* fix: single variant creation error

* feat: allow creation of any number of variants in multiple item variant creation dialog
2026-05-26 13:34:44 +05:30
Mihir Kandoi
090c25d848 feat: allow creation of any number of variants in multiple item variant creation dialog 2026-05-26 13:09:14 +05:30
Pandiyan P
c286a73e0b fix: prevent AttributeError in batch query filters (#55257) 2026-05-26 10:23:18 +05:30