From 314f028ecf1be9f5bc6ed4f8ad4f88289fd4edf5 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Thu, 19 Mar 2026 13:44:34 +0700 Subject: [PATCH] fix: Add script to repair corrupted `stock.move.line` records missing `product_id`. --- fix_corrupted_move_lines.py | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 fix_corrupted_move_lines.py diff --git a/fix_corrupted_move_lines.py b/fix_corrupted_move_lines.py new file mode 100644 index 0000000..faa8a86 --- /dev/null +++ b/fix_corrupted_move_lines.py @@ -0,0 +1,57 @@ +# Save this script and run it using the Odoo shell: +# odoo-bin shell -c -d < fix_corrupted_move_lines.py + +import logging +_logger = logging.getLogger(__name__) + +def fix_corrupted_move_lines(env): + _logger.info("Scanning for corrupted stock.move.lines with missing product_id...") + + # Search for all move lines that do not have a product_id + # We use a raw SQL query to aggressively find them even if ORM hides them, + # but we will try to use ORM first. + + env.cr.execute(""" + SELECT id, move_id + FROM stock_move_line + WHERE product_id IS NULL AND move_id IS NOT NULL + """) + + corrupted_lines = env.cr.fetchall() + + if not corrupted_lines: + _logger.info("No corrupted move lines found!") + print("No corrupted move lines found.") + return + + _logger.info(f"Found {len(corrupted_lines)} corrupted move lines. Fixing...") + print(f"Found {len(corrupted_lines)} corrupted move lines. Fixing...") + + fixed_count = 0 + for line_id, move_id in corrupted_lines: + try: + # Find the parent move to get the correct product_id + move = env['stock.move'].browse(move_id) + if not move.exists() or not move.product_id: + _logger.warning(f"Could not fix move line {line_id}: Parent move {move_id} is missing or has no product.") + continue + + # Perform a hard SQL update to bypass any ORM constraints that might crash + env.cr.execute(""" + UPDATE stock_move_line + SET product_id = %s + WHERE id = %s + """, (move.product_id.id, line_id)) + + fixed_count += 1 + print(f"Fixed move line {line_id} -> Assigned product_id {move.product_id.id} ({move.product_id.display_name})") + + except Exception as e: + _logger.error(f"Error fixing line {line_id}: {e}") + + env.cr.commit() + _logger.info(f"Successfully fixed {fixed_count} move lines.") + print(f"Successfully fixed {fixed_count} move lines. Please refresh your Odoo browser.") + +# Execute the fix +fix_corrupted_move_lines(env)