[16.0][FIX] stock_available_mrp: avoid infinite recursion on sibling-variant kits#77
Open
alvaro-gmz wants to merge 1 commit into
Open
Conversation
…t kits When a phantom BoM uses another variant of its own template as a component, ``explode_bom_quantities`` followed ``first(.bom_ids)`` on that component and looped on a sibling phantom BoM (``bom_ids`` is template-wide via ``_inherits``). After ~130 iterations the accumulated quantity overflowed to ``inf``, produced ``NaN`` and the final ``math.ceil`` raised ``ValueError: cannot convert float NaN to integer``. Use ``mrp.bom._bom_find(..., bom_type='phantom')`` so the lookup honours the variant scope and never surfaces sibling-variant BoMs. Results are cached per call to avoid the SELECT volume warned about in the docstring. A regression test reproduces the trap with a minimal template carrying two variants where one is a phantom kit of the other.
62e9883 to
0feac6f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a phantom BoM uses another variant of its own
product.templateas acomponent,
explode_bom_quantitiesenters an infinite recursion thateventually raises
ValueError: cannot convert float NaN to integerfrommath.ceilinsidetools.float_round.Reproducer (covered by the new regression test):
unit variant.
The bug is triggered when reading
immediately_usable_qty(or itsdownstream computed fields such as
immediately_usable_qty_todayonsale.order.line) on the pack variant.Root cause
explode_bom_quantitieswalks the BoM tree by callingfirst(current_line.product_id.bom_ids)on every component to detectnested phantom BoMs.
product.product.bom_idsis template-wide through_inherits, so for the unit variant it returns the phantom BoM of itssibling (the pack) as well. The loop never terminates: each iteration
re-yields the same sibling phantom BoM and multiplies
current_qtybythe line quantity. After ~130 iterations the float overflows to
inf,the next arithmetic step produces
NaNandmath.ceilraises.The same pattern is silently masked when the template has at least one
normalBoM whose id is lower than the phantom one:first()thenreturns the normal BoM and the
if sub_bom.type == "phantom"branch isskipped. The fragility hides behind ordering luck.
Fix
Replace
first(.bom_ids)lookups withmrp.bom._bom_find(..., bom_type="phantom"), which honours the variant scope and does notsurface sibling-variant BoMs. The result is cached per
(product, bom_type)tuple inside the call to keep the SELECT volume bounded — thedocstring already warned about
_bom_findbeing chatty if callednaively per line.
Test
A
TransactionCasereproduces the trap with a minimal templatecarrying two variants where one is a phantom kit of the other. It
asserts three things:
explode_bom_quantities(pack)terminates and yields exactly oneline (
unit × 12).immediately_usable_qtyon the pack variant completeswithout
ValueErrorand yields a real number.explode_bom_quantities(unit)(the component) terminates with anempty result instead of recursing on its sibling's BoM.
Compatibility
Behaviour is unchanged for the legitimate cases where the previous
first(.bom_ids)happened to return the right BoM._bom_findreturnsexactly the BoM that applies to the variant, either variant-specific
(
product_id = variant) or template-level (product_id = False).Sibling-variant BoMs are intentionally excluded — surfacing them on a
variant was the bug.