* feat: inline serial and batch entries editor in Purchase Receipt
* feat: grid-style UX, deferred saves, scan and range options for inline serial batch editor
* feat: extend inline serial batch editor to all bundle doctypes with auto fetch
* fix: address review comments on inline serial batch editor
* fix: escape untrusted values in inline editor alerts
* fix: clear child bundle reference only when the row owns the bundle
* fix: keep inline serial batch editor disabled on existing sites via patch
Replaces the Global Defaults toggle. Each Item/Customer/Supplier now
carries a Restrict to Companies checkbox: the Allowed Companies table
only shows (and is mandatory) when checked, is cleared on uncheck, and
permission filtering, read denial and write validation apply only to
masters that have the checkbox set.
get_stock_balance_for fetched serial nos across every batch in the
warehouse, so reconciling one batch of a serial+batch item compared the
selected serials against the pool of all batches and failed whenever
multiple batches existed.
get_mapped_doc copies same-named fields by default. work order item's
transferred_qty (cumulative across the whole work order) was leaking into
the new pick list item's transferred_qty (meant to track how much of
that pick list row has been converted into a stock entry, starting at 0).
the leaked value then got subtracted again in
get_pending_transfer_stock_qty(), so every pick list after the first
under-transferred raw materials by whatever was already recorded on the
work order, driving material_transferred_for_manufacturing towards zero
across repeated partial pick-list/finish cycles.
fixes#57236, related to #56596
Repost Item Valuation and BOM Update Log cleared old logs with a raw
delete on the parent table, orphaning timeline comments, versions,
attachments and other reference records.
Fixes#57237
update_item_rates passed price_not_uom_dependent, a key
get_price_list_rate_for never reads, and omitted conversion_factor, so a
stock-UOM price was never converted to the row UOM. The function's
(historically misnamed) price_list_uom_dependant ctx key carries the
Price List's price_not_uom_dependent value: truthy returns the found
rate as-is, falsy multiplies by conversion_factor.
Also guard on_update with is_new(): has_value_changed returns True when
there is no doc_before_save, so every first save re-wrote item rates.
* feat: block sales invoice submit when customer overdue exceeds threshold
Adds an opt-in, per-customer Overdue Billing Threshold. When enabled in
Accounts Settings, submitting a Sales Invoice is blocked if the customer's
overdue amount exceeds their threshold, unless the current user holds a
configured bypass role. Modeled on the existing credit limit feature.
- Accounts Settings (Credit Limits tab): enable toggle + bypass role.
- Per-customer threshold on the Customer Credit Limit table, shown only
when the feature is enabled via a property setter (same mechanism as
subscription / accounting dimension sections). Table relabeled to
"Credit & Overdue Limits".
- Overdue is read live from the ledger via get_outstanding_invoices
(payments already netted), summing Sales Invoices past their due date.
- Enforced in Sales Invoice on_submit, after the credit-limit check;
returns are exempt.
- validate_credit_limit_on_change no longer trips when a row sets only
the overdue threshold (credit_limit = 0).
Fixes#52960
* fix: compute overdue amount in company currency and format with fmt_money
get_customer_overdue_amount now sums GL Entry debit - credit grouped per
invoice, which is always booked in company currency, instead of using
get_outstanding_invoices which returns the receivable-account currency.
The threshold is in company currency, so the previous comparison could mix
currencies for customers with a foreign-currency receivable account. This
mirrors how get_customer_outstanding computes the figure for the existing
credit-limit check.
The blocking message now formats both amounts with fmt_money using the
company currency.
Adds a test asserting a 100 USD invoice at a conversion rate of 50 is
counted as 5000 in company currency.
* refactor: drop redundant threshold coercion and dead test cleanup
- Coerce the overdue threshold with flt() once when reading it, instead of
calling flt() on it at each of the three use sites.
- Remove a no-op set_overdue_billing_threshold() call in the feature-disabled
block (the threshold was already set to that value) and the trailing reset,
which is dead since each test is rolled back.
No behaviour change.
* fix: compute overdue amount from payment terms, matching the Overdue status
The overdue amount keyed on Sales Invoice.due_date, which set_due_date() sets
to the LAST payment term. An invoice whose first term was past due and unpaid
was therefore counted as zero, even though ERPNext already shows it as Overdue
in the invoice list. The gate and the UI could disagree.
get_customer_overdue_amount now follows the same rule as is_overdue(): per
invoice, the amount that has fallen due (sum of payment schedule terms past
their due date) minus what has been paid, clamped to the outstanding balance.
Invoices without a schedule (POS, opening) still fall back to the invoice due
date, mirroring is_overdue()'s own guard.
The ledger stays the source of truth for what is unpaid: the outstanding per
invoice is still SUM(debit) - SUM(credit) from GL Entry. base_payment_amount is
always stored in company currency, so no currency conversion is needed and the
comparison against the threshold stays consistent.
Adds a test covering a two-term invoice: only the past-due term counts, and
paying it off clears the overdue amount.
* feat: honour the overdue billing threshold set on the customer group
The threshold lives on Customer Credit Limit, which is also rendered on
Customer Group. A threshold set there was stored but never evaluated, so the
configuration was a silent no-op.
get_overdue_billing_threshold now reads the customer's row and falls back to
its customer group, mirroring get_credit_limit. The group's
bypass_credit_limit_check is deliberately not consulted: it is labelled for the
credit limit check at sales order and is unrelated to overdue billing.
get_customer_group_details also dropped the threshold when copying group rows
onto a customer, because it copied a single hardcoded field per table. It now
copies a list of fields per table, so credit_limit and overdue_billing_threshold
both carry over.
The work_order link filter in Stock Entry passed the string
`tabWork Order`.produced_qty as a filter value. It was never a real
column comparison: db_query coerces string values on numeric fields
with flt(), so the condition silently degraded to qty > 0, and on
backends that don't coerce text to numeric (postgres) such filters
fail with InvalidTextRepresentation.
Move the condition into a whitelisted search query that compares the
columns properly (qty > produced_qty), mirroring pick_list's
get_pending_work_orders.
set_transit_warehouse re-tested from_warehouse inside a guard that
already requires it, so the Company branch of its ternary was
unreachable: with no default on the source warehouse the field stayed
empty and Company.default_in_transit_warehouse was silently ignored.
Try the warehouse's default first, then the company's.
Workspace re-export in #56864 duplicated every link in the Home and
Projects workspaces, so desk renders each link twice. Same issue as
55afd95b20. Bumped modified so existing sites re-sync.
* feat: book Expenses Added To Stock GL entries for Stock Entry, Stock Reconciliation and LCV
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat: make stock expense GL booking configurable via Accounts Settings
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: skip stock expense booking for unconfigured companies, check flag once per compose
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The transfer flow ignored Consider Minimum Order Qty twice: the JS
handler force-reset the checkbox before fetching items, and the
purchase remainder left after allocating transfers from other
warehouses was never raised to min_order_qty (the check runs on the
total requirement before the split).
Drop the JS reset and apply min order qty to the purchase remainder,
in stock UOM before the purchase UOM conversion.
Two simultaneous allocations for the same item can both claim the same stock
on postgres: the picked-items locking read cannot see the rows another
in-flight creation is inserting, while MariaDB's gap locks make the creations
take turns. Advisory-gate set_item_locations per item (sorted against
deadlocks) so the second allocation waits, then subtracts the first's claim.
MariaDB unchanged.
Same hasattr pattern as repost_gate: an ERPNext ahead of its frappe build keeps
the status-quo serialization-failure retries instead of failing every stock
submission on postgres.