Skip to content

stock: move_line.picking_id not propagated when stock.move is reassigned to another picking (_assign_picking) #262764

Description

@xbentan

Impacted versions

17.0, 18.0, master (verified in addons/stock/models/stock_move.py)

Steps to reproduce

  1. Confirm a Purchase Order — a receipt picking WH/IN/A is created with a move M and its move line ML (ML.picking_id == A, M.picking_id == A).
  2. Cancel picking A (UI: Cancel).
  3. Trigger a re-creation of the receipt — for example by editing the PO so the procurement runs again, or by any flow that ends up calling stock.move._action_confirm() on the still-open moves.
  4. Odoo runs stock.move._assign_picking() (addons/stock/models/stock_move.py, ~line 1369). A new picking WH/IN/B is created (or a compatible existing one is found) and the line:
    moves.write({'picking_id': picking.id})
    moves M.picking_id from A to B.
  5. ML.picking_id is NOT updated — it still points to the cancelled picking A.

Current behavior

  • M.picking_id == B (new active picking)
  • ML.picking_id == A (old cancelled picking) — orphan
  • Picking B shows move_line_ids == [] because stock.move.line.picking_id is a stored Many2one, not a related/computed field, so the UI's Detailed Operations tab is empty even though M has reservations and a quantity. Users cannot validate the receipt or edit lots/quantities through the standard UI.

Expected behavior

When a stock.move is reassigned to a new picking, its move_line_ids should follow. Either:

  • stock.move.line.picking_id should be related='move_id.picking_id', store=True, or
  • stock.move.write (or _assign_picking itself) should propagate picking_id changes to move_line_ids.

Real-world impact (production data)

Detected on Odoo 18 Enterprise (odoo.sh). 4 stock.move.line records found in this state across 3 active receipt pickings — all triggered by the same pattern (origin shared between cancelled and active picking, both created by the same user within the same second, indicating an automated reassignment). One example:

Record picking_id state
stock.move.line(192650) WH/IN/07254 cancel (orphan)
stock.move.line(192650).move_id (stock.move(127472)) WH/IN/07446 assigned (correct)

The active picking WH/IN/07446 had move_line_ids == [] and the Detailed Operations view was empty until we manually wrote picking_id on the move_line.

Suggested fix

Minimal patch in addons/stock/models/stock_move.py::StockMove.write:

res = super().write(vals)
if vals.get('picking_id'):
    stale = self.move_line_ids.filtered(lambda ml: ml.picking_id.id != vals['picking_id'])
    if stale:
        stale.write({'picking_id': vals['picking_id']})

Happy to provide a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions