Pick Lists transferred before this feature have transferred_qty = 0 and
their Stock Entry rows carry no pick_list_item link, so the new
is_fully_transferred check would never fire and, with the old
duplicate-entry guard removed, they could be transferred again. Set
transferred_qty = picked_qty for non-Delivery submitted pick lists that
already have a linked Stock Entry so they stay completed and locked.
work order status was decided using a stale transferred-qty value,
computed before the current stock entry's transfer got recomputed.
this left work orders stuck at "not started" for pick-list-driven
transfers, since those entries never set fg_completed_qty and their
transferred qty can only be known from actual item-level transfers.
an earlier attempt fixed this by setting fg_completed_qty from the pick
list's for_qty, but that broke two things tied to fg_completed_qty
being zero: the excess-transfer guard, and the partial-transfer
fraction logic used to avoid marking a work order as fully supplied too
early.
recompute the transferred qty first, then decide status from the fresh
value. revert the fg_completed_qty change since it's no longer needed.
Extend the boundary rule to callers: non-decorated code that built or
annotated with ItemDetailsCtx now uses frappe._dict directly, and drops
the now-unused import. asset_capitalization keeps ItemDetailsCtx for its
own normalize_ctx_input-decorated functions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ItemDetailsCtx signals the normalize_ctx_input boundary, so keep it only
on the decorator and the ctx param of decorated functions. Every other
annotation/constructor in non-decorated code becomes plain frappe._dict.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ensure the recursion guard only applies to the nested save() and is cleared
afterwards, so a later save() on the same doc instance still creates periods.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_item_price is an internal, non-decorated helper: the "| dict" and
"pctx = frappe._dict(pctx)" were load-bearing (callers may pass a plain
dict; the body does attribute access). Restore both. Also restore the
"| dict" on set_valuation_rate/update_party_blanket_order out params
(these are not normalize_ctx_input-decorated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
on_update() called self.save(), which re-enters on_update() via
run_post_save_methods(), recursing indefinitely when make_all_scorecards()
keeps returning newly created periods. Guard the re-save with an in_rescore
flag so the nested on_update() short-circuits, while still running the full
validate() once to refresh score and standings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Python 3.14 (PEP 649/749) replaced "__annotations__" with "__annotate__"
in functools.WRAPPER_ASSIGNMENTS. normalize_ctx_input excluded only
"__annotations__" when wrapping, so functools.wraps copied the wrapped
function's __annotate__ and the wrapper's permissive ctx annotation
(_dict | Document | dict | str) was overwritten by the narrow
ItemDetailsCtx | str. Now that Frappe casts whitelisted args via
typing_validations, a dict ctx failed the isinstance-only frappe._dict
check and raised FrappeTypeError. Exclude "__annotate__" too.
Cleanup while here:
- Merge the three identical frappe._dict aliases (ItemDetails,
ItemDetailsCtx, ItemPriceCtx) into ItemDetailsCtx.
- Drop the now-redundant "| str" from decorated signatures; the
decorator's wrapper union is what typing_validations enforces.
- Decorate get_batch_based_item_price with normalize_ctx_input instead
of a manual parse_json, renaming its arg pctx -> ctx (JS caller
updated) so a dict/string payload is normalized to frappe._dict.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>