Tower: upload laundry_management 19.0.19.0.4 (via marketplace)
This commit is contained in:
228
addons/laundry_management/migrations/19.0.11.0.0/pre_migrate.py
Normal file
228
addons/laundry_management/migrations/19.0.11.0.0/pre_migrate.py
Normal file
@@ -0,0 +1,228 @@
|
||||
"""
|
||||
Pre-migration script for laundry_management 19.0.11.0.0
|
||||
|
||||
Architecture change: standalone laundry.order / laundry.order.line / laundry.payment
|
||||
models are replaced by _inherit = 'sale.order' / 'sale.order.line'.
|
||||
|
||||
This script runs BEFORE Odoo's model sync so that:
|
||||
1. FK constraints from old wizard tables referencing laundry_order are dropped
|
||||
2. Old wizard transient records are purged (they reference non-existent rows)
|
||||
3. Stale ir.model.fields records pointing to old models are removed
|
||||
4. Old ir.model entries are deleted (unblocking the ORM delete check)
|
||||
5. Old physical tables are dropped
|
||||
|
||||
Without this, the ORM raises:
|
||||
"Another model is using the record you are trying to delete.
|
||||
Blocking model: Laundry Print Wizard (laundry.print.wizard),
|
||||
Blocking field: order_id"
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Old standalone models being removed in this version
|
||||
_OLD_MODELS = [
|
||||
'laundry.order',
|
||||
'laundry.order.line',
|
||||
'laundry.payment',
|
||||
'laundry.order.line.addon',
|
||||
'laundry.register.payment.wizard',
|
||||
'laundry.product.wizard',
|
||||
'laundry.product.wizard.line',
|
||||
'laundry.category',
|
||||
'laundry.item.type',
|
||||
]
|
||||
|
||||
# Physical tables that correspond to the old models
|
||||
_OLD_TABLES = [
|
||||
'laundry_order',
|
||||
'laundry_order_line',
|
||||
'laundry_payment',
|
||||
'laundry_order_line_addon',
|
||||
'laundry_register_payment_wizard',
|
||||
'laundry_product_wizard',
|
||||
'laundry_product_wizard_line',
|
||||
'laundry_category',
|
||||
'laundry_item_type',
|
||||
]
|
||||
|
||||
# Transient/wizard tables that may hold rows with FK refs to laundry_order
|
||||
_WIZARD_TABLES = [
|
||||
'laundry_print_wizard',
|
||||
'laundry_session_wizard',
|
||||
'laundry_whatsapp_wizard',
|
||||
'laundry_register_payment_wizard',
|
||||
'laundry_product_wizard',
|
||||
'laundry_product_wizard_line',
|
||||
]
|
||||
|
||||
|
||||
def _table_exists(cr, table):
|
||||
cr.execute(
|
||||
"SELECT 1 FROM information_schema.tables "
|
||||
"WHERE table_schema = 'public' AND table_name = %s",
|
||||
(table,),
|
||||
)
|
||||
return bool(cr.fetchone())
|
||||
|
||||
|
||||
def _column_exists(cr, table, column):
|
||||
cr.execute(
|
||||
"SELECT 1 FROM information_schema.columns "
|
||||
"WHERE table_schema = 'public' AND table_name = %s AND column_name = %s",
|
||||
(table, column),
|
||||
)
|
||||
return bool(cr.fetchone())
|
||||
|
||||
|
||||
def _drop_fk_constraints_referencing(cr, referenced_table):
|
||||
"""Drop all FK constraints in the DB that point at referenced_table."""
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT tc.table_name, tc.constraint_name
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.referential_constraints rc
|
||||
ON tc.constraint_name = rc.constraint_name
|
||||
AND tc.table_schema = rc.constraint_schema
|
||||
JOIN information_schema.table_constraints tc2
|
||||
ON rc.unique_constraint_name = tc2.constraint_name
|
||||
AND rc.unique_constraint_schema = tc2.table_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc2.table_name = %s
|
||||
AND tc.table_schema = 'public'
|
||||
""",
|
||||
(referenced_table,),
|
||||
)
|
||||
rows = cr.fetchall()
|
||||
for src_table, constraint_name in rows:
|
||||
_logger.info(
|
||||
'pre_migrate: dropping FK constraint %s on %s (referenced %s)',
|
||||
constraint_name, src_table, referenced_table,
|
||||
)
|
||||
cr.execute(
|
||||
'ALTER TABLE "%s" DROP CONSTRAINT IF EXISTS "%s"' %
|
||||
(src_table, constraint_name)
|
||||
)
|
||||
|
||||
|
||||
def migrate(cr, version):
|
||||
if not version:
|
||||
# Fresh install — nothing to clean up
|
||||
return
|
||||
|
||||
_logger.info('pre_migrate laundry_management %s → 19.0.11.0.0 : start', version)
|
||||
|
||||
# ── Step 1: Purge all transient wizard records ────────────────────────────
|
||||
# TransientModel rows expire naturally but DB rows persist during upgrade;
|
||||
# they hold FK refs that block constraint drops and table drops.
|
||||
for tbl in _WIZARD_TABLES:
|
||||
if _table_exists(cr, tbl):
|
||||
_logger.info('pre_migrate: truncating wizard table %s', tbl)
|
||||
cr.execute('TRUNCATE TABLE "%s" CASCADE' % tbl)
|
||||
|
||||
# ── Step 2: Drop FK constraints pointing at old tables ────────────────────
|
||||
for tbl in _OLD_TABLES:
|
||||
if _table_exists(cr, tbl):
|
||||
_drop_fk_constraints_referencing(cr, tbl)
|
||||
|
||||
# Also drop FKs on wizard tables that point at laundry_order (the primary blocker)
|
||||
# Example: laundry_print_wizard.order_id -> laundry_order.id
|
||||
for wizard_tbl in _WIZARD_TABLES:
|
||||
if not _table_exists(cr, wizard_tbl):
|
||||
continue
|
||||
# Find and drop any FK on this wizard table that references an old table
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT kcu.column_name, ccu.table_name AS foreign_table_name, tc.constraint_name
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name = %s
|
||||
AND ccu.table_name = ANY(%s)
|
||||
""",
|
||||
(wizard_tbl, _OLD_TABLES),
|
||||
)
|
||||
for col, ref_tbl, constraint_name in cr.fetchall():
|
||||
_logger.info(
|
||||
'pre_migrate: dropping FK %s on %s.%s -> %s',
|
||||
constraint_name, wizard_tbl, col, ref_tbl,
|
||||
)
|
||||
cr.execute(
|
||||
'ALTER TABLE "%s" DROP CONSTRAINT IF EXISTS "%s"' %
|
||||
(wizard_tbl, constraint_name)
|
||||
)
|
||||
|
||||
# ── Step 3: Remove stale ir.model.fields that ref old models ─────────────
|
||||
# These are the records that cause "Blocking model: laundry.print.wizard,
|
||||
# Blocking field: order_id" — the field record itself still has
|
||||
# relation = 'laundry.order', which makes the ORM think laundry.print.wizard
|
||||
# still depends on laundry.order.
|
||||
cr.execute(
|
||||
"""
|
||||
DELETE FROM ir_model_fields
|
||||
WHERE relation = ANY(%s)
|
||||
""",
|
||||
(_OLD_MODELS,),
|
||||
)
|
||||
deleted = cr.rowcount
|
||||
_logger.info('pre_migrate: deleted %d stale ir.model.fields rows', deleted)
|
||||
|
||||
# Also remove fields whose model itself is one of the old models
|
||||
cr.execute(
|
||||
"""
|
||||
DELETE FROM ir_model_fields
|
||||
WHERE model IN %s
|
||||
""",
|
||||
(tuple(_OLD_MODELS),),
|
||||
)
|
||||
_logger.info('pre_migrate: deleted %d ir.model.fields for old models', cr.rowcount)
|
||||
|
||||
# ── Step 4: Remove stale ir.model.fields_by_name cache ───────────────────
|
||||
# In some Odoo versions there is a separate constraint/index table.
|
||||
# Safe to attempt; ignore if table doesn't exist.
|
||||
if _table_exists(cr, 'ir_model_constraint'):
|
||||
cr.execute(
|
||||
"""
|
||||
DELETE FROM ir_model_constraint imc
|
||||
USING ir_model im
|
||||
WHERE imc.model = im.id
|
||||
AND im.model = ANY(%s)
|
||||
""",
|
||||
(_OLD_MODELS,),
|
||||
)
|
||||
_logger.info('pre_migrate: removed %d ir.model.constraint rows', cr.rowcount)
|
||||
|
||||
if _table_exists(cr, 'ir_model_relation'):
|
||||
cr.execute(
|
||||
"""
|
||||
DELETE FROM ir_model_relation imr
|
||||
USING ir_model im
|
||||
WHERE imr.model = im.id
|
||||
AND im.model = ANY(%s)
|
||||
""",
|
||||
(_OLD_MODELS,),
|
||||
)
|
||||
_logger.info('pre_migrate: removed %d ir.model.relation rows', cr.rowcount)
|
||||
|
||||
# ── Step 5: Remove ir.model entries for the old models ───────────────────
|
||||
cr.execute(
|
||||
"DELETE FROM ir_model WHERE model = ANY(%s)",
|
||||
(_OLD_MODELS,),
|
||||
)
|
||||
_logger.info('pre_migrate: deleted %d ir.model rows', cr.rowcount)
|
||||
|
||||
# ── Step 6: Drop old physical tables ─────────────────────────────────────
|
||||
for tbl in reversed(_OLD_TABLES): # reverse to respect FK order
|
||||
if _table_exists(cr, tbl):
|
||||
_logger.info('pre_migrate: dropping table %s', tbl)
|
||||
cr.execute('DROP TABLE IF EXISTS "%s" CASCADE' % tbl)
|
||||
|
||||
_logger.info('pre_migrate laundry_management: complete')
|
||||
Reference in New Issue
Block a user