* test: cover both repost branches and the no-repost case
* fix: queue repost for entries backdated by a concurrent submit
---------
Co-authored-by: nareshkannasln <nareshkannashanmugam@gmail.com>
The Italy regional setup created Custom Fields first_name/last_name on
Customer. Since #46281 added standard quick-entry fields with the same
names, every Italian site carries duplicate field definitions:
- the setup wizard creates the duplicates silently because it skips
validation, and any later Custom Field on Customer then raises
UniqueFieldnameError (#50915)
- without the duplicates, creating an Italian company aborts inside
install_country_fixtures; on MariaDB an interrupted fixture run
persists Custom Field documents whose columns were never added, after
which every Company insert fails with "Unknown column
'fiscal_regime'" (#57215)
Re-land the rename from #50921 (reverted in #53409): the fields become
italy_customer_first_name/italy_customer_last_name and the e-invoice
template reads the new names. The migration patch runs only on sites
with Italy fixtures, re-runs them, explicitly syncs the schema of every
affected doctype (create_custom_fields skips unchanged fields, so its
own schema sync cannot restore missing columns), copies the old column
values wherever the new field is empty (also on sites that removed the
duplicate fields with the documented manual workaround), and deletes
the duplicate Custom Fields last so an interrupted run stays resumable.
The old insert_after anchor "salutation" no longer exists on Customer;
the renamed fields anchor after customer_type.
update_qc_reference() writes the QI link and bumps the reference
document's modified timestamp via raw db writes, which emit no realtime
event. A reference form (Purchase Receipt, Delivery Note, Stock Entry,
Job Card) still open in the browser keeps the old timestamp and fails
the timestamp conflict check on the next save/submit, forcing a manual
refresh after every QI submit/cancel/delete.
Calling notify_update() on the reference publishes the standard
doc_update event, so an open, unedited form silently reloads and syncs
its timestamp. get_lazy_doc skips child table loading since
notify_update only needs the parent row.
make_bundle_for_material_transfer squares stock_value_difference for
outward rows instead of negating it. multiply by -1, matching the qty
negation on the line above.
no behaviour change: set_incoming_rate and calculate_qty_and_amount both
recompute the field from qty * incoming_rate before the bundle is saved.
covers the case where the percentages are correct but the accumulated
sum is 100.00000000000001. two rows can never drift, since the second
reconstructs exactly as 100 - first, so the case needs three rows.
the total of allocated_percentage was compared to 100 with exact float
equality, so a correct allocation could be rejected when the sum drifts
in binary floating point (10.0 + 58.02 + 31.98 -> 100.00000000000001).
round the total to the field precision before comparing, in both
SellingController.calculate_contribution and Customer.validate.
Cover both shapes: a single operation that books the loss itself, and a
chain where an earlier operation books it and the final operation loses
nothing, so the sum over the operations is the only correct source.
update_work_order_qty() returns early when track_semi_finished_goods is
enabled, so set_process_loss_qty() never ran and Work Order.process_loss_qty
stayed at zero even though the job cards and the work order operations had
booked the loss. The work order also never reached the Completed status,
since that needs produced_qty + process_loss_qty to cover the ordered qty.
Calling set_process_loss_qty() from that early return is not enough: the
final operation has no semi finished good bom, so its manufacture entry is
not from a bom, remove_fg_completed_qty() zeroes fg_completed_qty and
update_work_order_qty() is never reached at all.
The manufacture entries cannot be summed either. Each one is reset to
MAX(Work Order Operation.process_loss_qty), so every entry of a multi
operation chain carries the running maximum instead of the loss of its own
operation. Aggregate the operations instead, and refresh the work order from
the job card, which is where the operation loss is written.
* feat: sync serial no status from stock ledger in Stock Qty vs Serial No Count report
* fix: pick last bundle move in SQL ordered by posting datetime and SLE creation
* fix: derive synced serial no status from stock ledger helper and validate sync args
Existing submitted BOMs may carry operations without a finished good,
and no migration repairs them. Exempting every semi FG job card from
the transfer check let such a card submit after a partial transfer.
Exempt only cards that skip material transfer; legacy cards with
transfer enabled keep the strict transferred qty check.
Every generated entry copied each Job Card Item's full required_qty in
the skip-transfer and BOM-backflush paths, so two entries for one job
card consumed the requirement twice. Scale the rows to the share of
production this entry accounts for and cap them at the requirement
still unconsumed, dropping rows that have nothing left. An entry whose
materials are exhausted then fails the existing at-least-one-raw-material
check instead of minting finished goods from nothing.
Saving a submitted manufacture entry to change an allowed field re-ran
the pending production cap with a manufactured aggregate that already
includes the entry itself, so the save was rejected against the
post-entry remainder. Quantities are not editable after submit, so the
check has nothing to protect there.
The WIP warehouse change also removed the Target Warehouse exemption
for semi FG orders, but those may validly carry the target on each
operation instead. Restore the exemption in the form and the submit
check; the WIP warehouse requirement stays.
After a partial entry booked the job card's full process loss, the
next generated entry was sized qty-to-produce minus manufactured only.
It exceeded the pending production cap, so Make Stock Entry could not
finish the card. Subtract the consumed loss when sizing the entry.
Entries from operations without their own BOM carry no For Quantity,
so the finished-good reconciliation cannot run for them and a draft
created before other entries were submitted could still over-produce.
Validate every job-card manufacture entry against the job card
directly: finished goods plus process loss must fit in what the job
card still has left to produce after earlier submitted entries.
The finished_good derivation ran in validate_semi_finished_goods,
after set_materials_based_on_operation_bom had already expanded
operation BOM materials. A single-pass insert-and-submit (API or
import) with bom_no set but finished_good empty skipped the expansion,
persisting a submitted BOM without the referenced components. The
derivation also let a final operation inherit another item from its
bom_no, so downstream job cards would produce the wrong item.
Move the derivation into set_operation_finished_goods, called before
the expansion, prefer the BOM's own item for the final operation, and
reject a final operation whose FG item is not the BOM's item.
get_item_details returns the whole Item document, so the dialog row's
name became the item code. get_item_data then matched that item code
against every Components row regardless of operation, so adding an item
already used by another operation silently updated that row's qty
instead of appending one for the target operation — which stayed empty
and failed 'please add raw materials or set a BOM' on submit.
Match the existing row by item code within the same operation: same
operation updates the qty, any other match appends a new row.
set_process_loss_qty stamped MAX(process_loss_qty) across every
operation of the work order onto each manufacture entry. With semi
finished goods tracking, one operation's process loss leaked into the
entries of every other operation: validate_fg_completed_qty then
rejected the entry when it had a BOM, or the wrong loss was recorded
silently when it did not, double-counting the loss across operations.
When the entry belongs to a job card, use that job card's loss net of
what its earlier entries already booked. The MAX fallback stays for
work-order level entries without a job card.
Fixesfrappe/erpnext#57892