When a previous operation manufactured less than the current job card
is completing, the error always said 'Submit the manufacturing entry
for the operation first' — even when the entry was already submitted
and the missing quantity was booked as process loss, which made the
advice a dead end.
Sum the process loss of the previous operation's job cards alongside
the manufactured quantity. When manufactured + process loss covers the
requested quantity, say the shortfall is process loss so the user
knows to reduce the completed quantity; keep the submit-first message
for genuinely pending manufacturing entries.
Work orders with track_semi_finished_goods were exempt from the
Work-in-Progress Warehouse requirement in three places: the field's
mandatory_depends_on, the fg_warehouse reqd toggle in the form script,
and validate_warehouse on submit.
The exemption was misleading. The flow still transfers materials to a
WIP warehouse when 'Skip Material Transfer' is unchecked: operations
default their WIP warehouse from the work order, and
set_default_warehouse silently restores the company default after the
user clears the field. Make the field genuinely required instead of
pretending it is optional.
validate_transfer_qty uses an empty finished_good to detect legacy job
cards, and unlike validate_semi_finished_goods it ignores
skip_material_transfer. A job card tracking semi finished goods whose
operation had no finished_good fell into the legacy branch and could
not be submitted even with 'Skip Material Transfer' checked on the
work order.
Return early for semi FG job cards; validate_semi_finished_goods
already enforces the transfer requirement for them and honours
skip_material_transfer.
A BOM with track_semi_finished_goods enabled could be saved with no
finished_good on any operation: validate_semi_finished_goods only
checked that one row had 'Is Final Finished Good' set, and a list
containing None passed the emptiness check.
Such a BOM breaks every downstream step. The work order copies the
empty finished_good into its operations, job cards inherit it, and
Make Stock Entry finally fails with 'Item None not found' because the
manufacture entry has no production item.
Derive the finished good where it is unambiguous: an operation that
references a BOM produces that BOM's item, and the final operation
produces the BOM's own item. Otherwise require it on the row, since
each operation's job card books its output through it.
cint truncates, so a stock_qty of 1999.9998 (dust from qty times
conversion factor) compared as abs(1999 - 2000.0) > epsilon and was
rejected as fractional even though it rounds to a whole number at
field precision, with the error confusingly printing the rounded
value: 'Quantity (2000.0) cannot be a fraction'. Round to field
precision first, then require the result to be a whole number.
Dust above an integer already passed; this fixes the asymmetry for
dust below.
stock_qty is stored as raw qty * conversion_factor, so a UOM-converted
order for exactly the minimum (e.g. LB to Kg) produces values like
1999.999999131832 vs a min_order_qty of 2000 and blocks the Purchase
Order. Round both sides to the stock_qty field precision before
comparing, and show the rounded qty in the error message.
Pick List, Material Request, Material Consumption and Additional
Material Transfer were spread across two standalone buttons and a
separate Make menu. Put them all under a single Create menu, and rename
Create Pick List to Pick List since the menu already says Create.
custom_make_buttons is updated to the new label so the connections
shortcut still finds the button.
Covers the _add_remaining_purchase_request path: partial stock in
another warehouse is allocated as a transfer and the residual purchase
qty goes through the second rounding site.
The stock-UOM qty is rounded in _accumulate_so_items, but the purchase
UOM conversion divided it by the conversion factor without re-rounding,
storing values like 5738748.300863984 in mr_items.quantity. The raw
value flowed into Material Request qty and the raw materials CSV, and
make_material_request compares quantity to requested_qty with exact
float equality, so any rounding downstream left dust quantities.
The division by conversion_factor in _adjust_required_qty_for_uom sits
directly after frappe.throw inside the same block, so it can never run.
It has been dead since commit 2a8cd05b44 (#27278) re-indented it into
the throw branch; the actual purchase-UOM conversion happens in
_material_request_item_row via _mr_purchase_conversion_factor.
* feat(accounts): split exchange gain and exchange loss accounts
Add optional Exchange Gain Account and Exchange Loss Account fields on
Company. When set, realized FX gain/loss from settling an invoice in a
foreign currency (via Payment Entry, Payment Reconciliation, or a
Journal-Entry-based advance) books to the matching account instead of
the single Exchange Gain/Loss account. Either field left blank falls
back to the existing Exchange Gain/Loss account, so companies that
don't configure the new fields are unaffected.
New companies get "Exchange Gain" and "Exchange Loss" accounts
auto-created in their chart of accounts and auto-assigned to the new
fields, same as the existing Exchange Gain/Loss account provisioning.
The Payment Reconciliation tool's per-allocation "Difference Account"
override in its reconcile dialog continues to work as before; the
split accounts only change the computed default shown there.
* test(account_balance): account for new Exchange Gain account in income report
The new auto-provisioned Exchange Gain account under Indirect Income
now shows up in the Income root type report for _Test Company 2.
---------
Co-authored-by: test <test@test.com>
* fix: use current batch avg rate for outward returns of batchwise valuation batches
* fix: honor zero batch average and avoid duplicate batch classification query
set_dynamic_labels() unconditionally forced update_stock's hidden
property based only on is_debit_note/has_subcontracted, overwriting
whatever Customize Form had set on every refresh. OR it with the
field's original (property-setter-driven) hidden value instead.
* feat: validate stock value and stock closing entry before period closing
* fix: do not accept scoped stock closing entries as period closing prerequisite
* feat: seed batch valuation from stock closing balance and freeze closed-period stock
get_mapped_doc copies every same-named field that is not no_copy, so the
Sales Order / Purchase Order / Quotation created from a Blanket Order
inherited MFG-BLR-.YYYY.- and was named MFG-BLR-2026-00003 instead of
SAL-ORD-2026-00001.
exclude naming_series from the mapping, same as job card does when it
maps to a Purchase Order.
fix: incorrect batch-wise valuation rate for entries with same posting datetime (#57794)
* fix: incorrect batch-wise valuation rate for entries with same posting datetime
The tie-breaker in get_batch_no_ledgers compared the bundle's creation
against the SLE's creation. These are different timelines - a bundle can
be created (drafted) much before its SLE (created at submission). For
entries sharing a posting datetime (backdated / amended vouchers), this
mis-ordered the entries against the ledger's replay order (SLE creation),
causing double counting or omission of batch qty / value and runaway
outgoing rates that no repost could heal.
Now the tie is broken using the creation of the bundle's own SLE (same
timeline on both sides). When the valuation runs through the bundle
before its SLE exists, the entry is by definition last in its timestamp
group, so all same-timestamp entries already in the ledger precede it.
* test: batch-wise valuation ordering for same posting datetime entries
Covers both tie-breaking branches of get_batch_no_ledgers:
- submission (pre-insertion) branch: same-timestamp inward at a different
rate plus a multi-row outward voucher (same item and warehouse), at
submission and after a backdated repost
- existing-SLE branch: a bundle created after its sibling's SLE, the
ordering must follow the SLE creation and not the bundle creation
Both tests fail with the previous parent.creation < sle.creation
tie-breaker and pass with the fix.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>