Validate inventory deliveries (stock.picking) for Sales Orders, including backorder handling.
Use this skill to deliver goods from inventory for confirmed Sales Orders.
from scripts.config import search_read
so_id = 123 # Replace with SO ID
pickings = search_read('stock.picking',
[('sale_id', '=', so_id), ('state', 'not in', ['done', 'cancel'])],
['id', 'name', 'move_ids']
)
from scripts.config import read, write, execute_kw
picking_id = pickings[0]['id']
# Set Quantity Done for all moves (Odoo 19: use 'quantity' not 'quantity_done')
move_ids = pickings[0]['move_ids']
for move_id in move_ids:
move_data = read('stock.move', [move_id], ['product_uom_qty'])[0]
write('stock.move', move_id, {'quantity': move_data['product_uom_qty']})
# Validate with skip_sms context (REQUIRED in Odoo 19 to avoid SMS wizard popup)
execute_kw('stock.picking', 'button_validate', [[picking_id]],
{'context': {'skip_sms': True, 'skip_backorder': False}})
print(f"Validated delivery {picking_id}")
Important: Do NOT use call_button(...) for button_validate on stock pickings --
it cannot pass context, and the SMS wizard will block execution in Odoo 19.
# Set partial quantity (e.g., deliver only half)
for move_id in move_ids:
move_data = read('stock.move', [move_id], ['product_uom_qty'])[0]
partial_qty = move_data['product_uom_qty'] * 0.5
write('stock.move', move_id, {'quantity': partial_qty})
# Validate (will trigger backorder creation)
execute_kw('stock.picking', 'button_validate', [[picking_id]],
{'context': {'skip_sms': True, 'skip_backorder': False}})
# Check for created backorder
backorders = search_read('stock.picking',
[('sale_id', '=', so_id), ('state', '=', 'assigned')],
['name']
)
if backorders:
print(f"Backorder created: {backorders[0]['name']}")
from scripts.config import call_button, write
# After delivery is validated (state='done'), lock it to prevent edits
# Try button method first (preferred in Odoo 19), fallback to direct write