#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Demo Pool Cleanup Script Removes the Studio-created x_demo_pool_tracking model and all related database artifacts (tables, ir.model entries, fields, views, menus, etc.). WARNING: Only run this AFTER verifying the import_demo_pool.py migration was successful and all data is correct in Loaner Products. Run via Odoo shell: docker exec -i odoo-mobility-app odoo shell -d mobility < cleanup_demo_pool.py Copyright 2024-2026 Nexa Systems Inc. License OPL-1 (Odoo Proprietary License v1.0) """ import logging _logger = logging.getLogger(__name__) DEMO_POOL_MODELS = [ 'x_demo_pool_tracking', 'x_demo_pool_tracking_line_4a032', 'x_demo_pool_tracking_line_629cc', 'x_demo_pool_tracking_line_b4ec9', 'x_demo_pool_tracking_stage', 'x_demo_pool_tracking_tag', ] DEMO_POOL_TABLES = DEMO_POOL_MODELS + [ 'x_demo_pool_tracking_tag_rel', ] PRODUCT_TEMPLATE_STUDIO_FIELDS_TO_REMOVE = [ 'x_studio_many2one_field_V8rnk', ] def run_cleanup(env): cr = env.cr print(f"\n{'='*60}") print("Demo Pool Cleanup") print(f"{'='*60}") print() # ===================================================================== # Step 1: Remove ir.ui.menu entries referencing demo pool actions # ===================================================================== print("[1/7] Removing menu items...") cr.execute(""" DELETE FROM ir_ui_menu WHERE action IN ( SELECT CONCAT('ir.actions.act_window,', id) FROM ir_act_window WHERE res_model IN %s ) """, (tuple(DEMO_POOL_MODELS),)) print(f" Removed {cr.rowcount} menu items") # ===================================================================== # Step 2: Remove ir.actions.act_window entries # ===================================================================== print("[2/7] Removing window actions...") cr.execute(""" DELETE FROM ir_act_window WHERE res_model IN %s """, (tuple(DEMO_POOL_MODELS),)) print(f" Removed {cr.rowcount} window actions") # ===================================================================== # Step 3: Remove ir.ui.view entries # ===================================================================== print("[3/7] Removing views...") cr.execute(""" DELETE FROM ir_ui_view WHERE model IN %s """, (tuple(DEMO_POOL_MODELS),)) print(f" Removed {cr.rowcount} views") # ===================================================================== # Step 4: Remove ir.model.access entries # ===================================================================== print("[4/7] Removing access rules...") cr.execute(""" SELECT id FROM ir_model WHERE model IN %s """, (tuple(DEMO_POOL_MODELS),)) model_ids = [row[0] for row in cr.fetchall()] if model_ids: cr.execute(""" DELETE FROM ir_model_access WHERE model_id IN %s """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} access rules") else: print(" No model IDs found (already cleaned?)") # ===================================================================== # Step 5: Remove ir.model.fields and ir.model entries # ===================================================================== print("[5/7] Removing field definitions and model registry entries...") if model_ids: cr.execute(""" DELETE FROM ir_model_fields_selection WHERE field_id IN ( SELECT id FROM ir_model_fields WHERE model_id IN %s ) """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} field selection values") cr.execute(""" DELETE FROM ir_model_fields WHERE model_id IN %s """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} field definitions") cr.execute(""" DELETE FROM ir_model_constraint WHERE model IN %s """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} constraints") cr.execute(""" DELETE FROM ir_model_relation WHERE model IN %s """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} relations") cr.execute(""" DELETE FROM ir_model WHERE id IN %s """, (tuple(model_ids),)) print(f" Removed {cr.rowcount} model registry entries") # ===================================================================== # Step 6: Remove Studio field on product.template that linked to demo pool # ===================================================================== print("[6/7] Removing demo pool Studio fields from product.template...") for field_name in PRODUCT_TEMPLATE_STUDIO_FIELDS_TO_REMOVE: cr.execute(""" SELECT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'product_template' AND column_name = %s ) """, (field_name,)) exists = cr.fetchone()[0] if exists: cr.execute(""" DELETE FROM ir_model_fields_selection WHERE field_id IN ( SELECT id FROM ir_model_fields WHERE model = 'product.template' AND name = %s ) """, (field_name,)) cr.execute(""" DELETE FROM ir_model_fields WHERE model = 'product.template' AND name = %s """, (field_name,)) cr.execute(f'ALTER TABLE product_template DROP COLUMN IF EXISTS "{field_name}"') print(f" Removed {field_name} from product.template") else: print(f" {field_name} not found on product.template (already removed)") # ===================================================================== # Step 7: Drop the actual database tables # ===================================================================== print("[7/7] Dropping demo pool tables...") for table in DEMO_POOL_TABLES: cr.execute(f"DROP TABLE IF EXISTS \"{table}\" CASCADE") print(f" Dropped {table}") cr.commit() print(f"\n{'='*60}") print("Cleanup Complete") print(f"{'='*60}") print() print("The following have been removed:") print(" - x_demo_pool_tracking model and all line/stage/tag tables") print(" - All related views, menus, actions, and access rules") print(" - x_studio_many2one_field_V8rnk from product.template") print() print("NOT touched:") print(" - x_studio_adp_price, x_studio_adp_sku on product.template") print(" - x_studio_msrp, x_studio_product_description_long on product.template") print(" - x_studio_product_details on product.template") print() print("Restart the Odoo server to clear the model registry cache.") print(f"{'='*60}\n") run_cleanup(env)