Compare commits
7 Commits
bc3907e1d8
...
1d75a0f9db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d75a0f9db | ||
|
|
134b935fa4 | ||
|
|
b6fd05104a | ||
|
|
8cb2fe5fd2 | ||
|
|
7c680b17a4 | ||
|
|
803ae858ea | ||
|
|
6890d19524 |
1
lib/python/file_gc.py
Normal file
1
lib/python/file_gc.py
Normal file
@@ -0,0 +1 @@
|
||||
env['ir.attachment']._gc_file_store_unsafe()
|
||||
@@ -23,6 +23,58 @@ EOF
|
||||
)
|
||||
query_postgres_container "$CLEANUP_SQL" "$DB_NAME"
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Purge orphaned ``ir.ui.view`` images whose parent view was
|
||||
# deleted AND whose ID is not referenced in any other view's
|
||||
# HTML (``arch_db``). An image with ``res_id = 0`` can still
|
||||
# be used by a live page via ``/web/image/<id>``, so we only
|
||||
# remove it when no ``arch_db`` contains that URL.
|
||||
# ────────────────────────────────────────────────────────────
|
||||
echo "Purging ir.ui.view images orphaned of their parent view and unreferenced in any arch_db..."
|
||||
PURGE_VIEW_IMAGES_SQL=$(cat <<'EOF'
|
||||
DELETE FROM ir_attachment a
|
||||
WHERE a.res_model = 'ir.ui.view'
|
||||
AND a.mimetype LIKE 'image/%'
|
||||
AND a.store_fname IS NOT NULL
|
||||
AND a.res_id NOT IN (SELECT id FROM ir_ui_view)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM ir_ui_view v
|
||||
WHERE v.arch_db::text ILIKE '%web/image/' || a.id || '%'
|
||||
OR v.arch_db::text ILIKE '%web/content/' || a.id || '%'
|
||||
);
|
||||
EOF
|
||||
)
|
||||
query_postgres_container "$PURGE_VIEW_IMAGES_SQL" "$DB_NAME"
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Purge ir_logging (Python logger messages, no business value).
|
||||
# ────────────────────────────────────────────────────────────
|
||||
echo "Purging ir_logging..."
|
||||
query_postgres_container "TRUNCATE ir_logging;" "$DB_NAME"
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Purge resized partner images (128, 256, 512, 1024) and
|
||||
# attachments with no res_model (orphaned factur-x, CSS, ICS).
|
||||
# Odoo 13+ regenerates thumbnails automatically from
|
||||
# image_1920 on the next partner write, so only the full-size
|
||||
# source needs to be kept.
|
||||
# ────────────────────────────────────────────────────────────
|
||||
echo "Purging resized partner images and orphaned attachments..."
|
||||
PURGE_ATTACHMENTS_SQL=$(cat <<'EOF'
|
||||
DELETE FROM ir_attachment
|
||||
WHERE res_model = 'res.partner'
|
||||
AND (name ILIKE '%image_128%'
|
||||
OR name ILIKE '%image_256%'
|
||||
OR name ILIKE '%image_512%'
|
||||
OR name ILIKE '%image_1024%');
|
||||
|
||||
DELETE FROM ir_attachment
|
||||
WHERE (res_model IS NULL OR res_model = '')
|
||||
AND store_fname IS NOT NULL;
|
||||
EOF
|
||||
)
|
||||
query_postgres_container "$PURGE_ATTACHMENTS_SQL" "$DB_NAME"
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Fix orphan ir_filters pointing to deleted ir_actions
|
||||
#
|
||||
@@ -93,7 +145,15 @@ SET state = 'installed'
|
||||
WHERE state = 'to upgrade';
|
||||
" "$DB_NAME" || true
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Clean orphaned filestore files left behind by the attachment
|
||||
# deletions above. ``_gc_file_store_unsafe`` scans the filestore
|
||||
# and removes files that no longer have a matching
|
||||
# ``ir_attachment`` record.
|
||||
# ────────────────────────────────────────────────────────────
|
||||
echo "Cleaning orphaned filestore files..."
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
exec_python_script_in_odoo_shell "$ODOO_SERVICE" "$DB_NAME" "$SCRIPT_DIR/lib/python/file_gc.py"
|
||||
|
||||
PYTHON_SCRIPT="${SCRIPT_DIR}/lib/python/fix_duplicated_views.py"
|
||||
echo "Remove duplicated views with script $PYTHON_SCRIPT ..."
|
||||
|
||||
@@ -103,6 +103,69 @@ EOF
|
||||
echo "SQL command = $PRE_MIGRATE_SQL_4"
|
||||
query_postgres_container "$PRE_MIGRATE_SQL_4" ou17 || exit 1
|
||||
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# Remove orphan theme.ir.ui.view templates pointing to deleted ir.ui.view
|
||||
#
|
||||
# In Odoo 17, the asset bundle system was reworked: `web._assets_utils`,
|
||||
# `website.assets_editor`, and `website._assets_frontend_helpers` were
|
||||
# replaced by the new `ir.asset` model. The theme_common/theme_clean 17.0
|
||||
# modules no longer define those templates, but the migrated database still
|
||||
# holds the old `theme_ir_ui_view` records (kept by ir_model_data noupdate).
|
||||
#
|
||||
# Their `inherit_id` Reference field points to ir.ui.view ids that no longer
|
||||
# exist. When website's `_theme_load(website)` runs (ir_module_module.py:95)
|
||||
# during the registry load, `ThemeView._convert_to_base_model`
|
||||
# (theme_models.py:85) accesses `inherit.website_id` on the ghost record and
|
||||
# raises MissingError, aborting the whole registry:
|
||||
#
|
||||
# odoo.exceptions.MissingError: Record does not exist or has been deleted.
|
||||
# (Record: ir.ui.view(174,), User: 1)
|
||||
#
|
||||
# This deletes the ir_model_data + theme_ir_ui_view rows whose inherit_id
|
||||
# targets a missing ir.ui.view. Generic (covers any theme, idempotent).
|
||||
# Scoped to theme_ir_ui_view only. Verified: no ir.ui.view copies exist for
|
||||
# the affected templates, so no side effects.
|
||||
# ────────────────────────────────────────────────────────────
|
||||
PRE_MIGRATE_SQL_5=$(cat <<'EOF'
|
||||
DO $$
|
||||
DECLARE
|
||||
deleted_imd INTEGER;
|
||||
deleted_tpl INTEGER;
|
||||
BEGIN
|
||||
WITH targets AS (
|
||||
SELECT id FROM theme_ir_ui_view
|
||||
WHERE inherit_id LIKE 'ir.ui.view,%'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM ir_ui_view v
|
||||
WHERE v.id = split_part(theme_ir_ui_view.inherit_id, ',', 2)::int
|
||||
)
|
||||
),
|
||||
del AS (
|
||||
DELETE FROM ir_model_data
|
||||
WHERE model = 'theme.ir.ui.view'
|
||||
AND res_id IN (SELECT id FROM targets)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT count(*) INTO deleted_imd FROM del;
|
||||
|
||||
WITH del AS (
|
||||
DELETE FROM theme_ir_ui_view
|
||||
WHERE inherit_id LIKE 'ir.ui.view,%'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM ir_ui_view v
|
||||
WHERE v.id = split_part(theme_ir_ui_view.inherit_id, ',', 2)::int
|
||||
)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT count(*) INTO deleted_tpl FROM del;
|
||||
|
||||
RAISE NOTICE 'Cleaned % stale theme_ir_ui_view record(s) and % ir_model_data entry(ies)', deleted_tpl, deleted_imd;
|
||||
END $$;
|
||||
EOF
|
||||
)
|
||||
echo "SQL command = $PRE_MIGRATE_SQL_5"
|
||||
query_postgres_container "$PRE_MIGRATE_SQL_5" ou17 || exit 1
|
||||
|
||||
# Copy filestores
|
||||
copy_filestore ou16 ou16 ou17 ou17 || exit 1
|
||||
|
||||
|
||||
@@ -6,6 +6,56 @@ echo "Prepare migration to 18.0..."
|
||||
# Copy database
|
||||
copy_database ou17 ou18 ou18 || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# REMOVE contract_payment_mode (depends on obsolete account_payment_partner)
|
||||
#
|
||||
# contract_payment_mode v18 still depends on account_payment_partner (deleted
|
||||
# by the bank-payment renaming below) and uses the obsolete account.payment.mode
|
||||
# model. No v18 replacement exists — account_payment_base_oca provides no
|
||||
# contract integration. Only 2 contracts had payment_mode_id set, and the
|
||||
# migration to the new payment.method.line model is not supported.
|
||||
#
|
||||
# MUST run BEFORE the bank-payment renaming and BEFORE OpenUpgrade's
|
||||
# button_upgrade() which would otherwise crash on the missing dependency.
|
||||
#
|
||||
# We DELETE the module entirely (like the bank-payment merged_modules pattern)
|
||||
# rather than marking it 'to remove', because Odoo's "Transient module states
|
||||
# were reset" in load_modules() converts 'to remove' -> 'installed', causing
|
||||
# button_upgrade() to re-parse the missing account_payment_partner dependency
|
||||
# and abort the registry load. Deleting the row + ir_model_data + dependencies
|
||||
# ensures OpenUpgrade's update_list() rediscovers the physical addon as a fresh
|
||||
# 'uninstalled' module that button_upgrade() never touches.
|
||||
# ============================================================================
|
||||
contract_payment_mode_sql=$(cat <<'EOF'
|
||||
DO $$
|
||||
DECLARE
|
||||
mod_id INTEGER;
|
||||
BEGIN
|
||||
SELECT id INTO mod_id FROM ir_module_module WHERE name = 'contract_payment_mode';
|
||||
IF mod_id IS NOT NULL THEN
|
||||
DELETE FROM ir_module_module_dependency WHERE module_id = mod_id;
|
||||
DELETE FROM ir_model_data WHERE module = 'contract_payment_mode';
|
||||
DELETE FROM ir_module_module WHERE id = mod_id;
|
||||
|
||||
-- Rename the module's own external ID (base.module_contract_payment_mode)
|
||||
-- so update_list() can rediscover the physical contract_payment_mode addon
|
||||
-- (which still exists in v18 addons path) without conflicting on
|
||||
-- (base, module_contract_payment_mode). The orphaned xmlid pointed to
|
||||
-- the deleted ir.module.module row and has no other references.
|
||||
UPDATE ir_model_data
|
||||
SET name = 'module_contract_payment_mode_removed'
|
||||
WHERE module = 'base' AND name = 'module_contract_payment_mode';
|
||||
|
||||
RAISE NOTICE 'Removed contract_payment_mode module (depends on obsolete account_payment_partner)';
|
||||
ELSE
|
||||
RAISE NOTICE 'contract_payment_mode not found, skipping';
|
||||
END IF;
|
||||
END $$;
|
||||
EOF
|
||||
)
|
||||
echo "Removing contract_payment_mode module..."
|
||||
query_postgres_container "$contract_payment_mode_sql" ou18 || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# BANK-PAYMENT -> BANK-PAYMENT-ALTERNATIVE MODULE RENAMING
|
||||
# Migration from OCA/bank-payment to OCA/bank-payment-alternative
|
||||
@@ -96,6 +146,82 @@ else
|
||||
echo "Module account_payment_mode not installed, skipping bank-payment migration."
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# ACCOUNT_FACTORING -> ACCOUNT_FACTORING_OCA MODULE RENAMING
|
||||
#
|
||||
# account_factoring (depends on account_payment_partner + account_payment_mode)
|
||||
# is replaced by account_factoring_oca (depends on account_payment_base_oca
|
||||
# + account_payment_base_oca_sale) in v18.
|
||||
#
|
||||
# Both modules have rigorously identical stored fields:
|
||||
# - account_journal: is_factor, factor_fee, factor_holdback_percent,
|
||||
# factor_partner_id, factor_fee_account_id, factor_holdback_account_id,
|
||||
# factor_limit_holdback_account_id, factor_tax_id, factor_validation
|
||||
# - account_move: factor_transfer_id, factor_payment_id,
|
||||
# payment_state_with_factor
|
||||
# - res_partner: factor_credit_limit
|
||||
#
|
||||
# Renaming (not uninstalling) preserves the 4910 invoice factor statuses,
|
||||
# 311 partner credit limits, and journal factor config. OpenUpgrade will
|
||||
# treat account_factoring_oca as already installed and adopt the existing
|
||||
# columns via ORM _auto_init.
|
||||
#
|
||||
# MUST run AFTER the bank-payment renaming block above, which creates
|
||||
# account_payment_base_oca in the DB.
|
||||
# ============================================================================
|
||||
ACCOUNT_FACTORING_RENAME_SQL=$(cat <<'EOF'
|
||||
DO $$
|
||||
DECLARE
|
||||
factor_module_id INTEGER;
|
||||
BEGIN
|
||||
SELECT id INTO factor_module_id FROM ir_module_module WHERE name = 'account_factoring';
|
||||
IF factor_module_id IS NOT NULL THEN
|
||||
-- Rename the module itself
|
||||
UPDATE ir_module_module SET name = 'account_factoring_oca' WHERE name = 'account_factoring';
|
||||
UPDATE ir_model_data SET module = 'account_factoring_oca' WHERE module = 'account_factoring';
|
||||
|
||||
-- Rename the module's own external ID (base.module_account_factoring)
|
||||
-- so OpenUpgrade can rediscover the physical account_factoring addon dir
|
||||
-- (which still exists in v18 addons path) without conflicting on
|
||||
-- (base, module_account_factoring) — that xmlid now points to the
|
||||
-- renamed module under its new name.
|
||||
UPDATE ir_model_data
|
||||
SET name = 'module_account_factoring_oca'
|
||||
WHERE module = 'base' AND name = 'module_account_factoring';
|
||||
|
||||
-- Redirect dependencies pointing TO the old name
|
||||
UPDATE ir_module_module_dependency SET name = 'account_factoring_oca' WHERE name = 'account_factoring';
|
||||
|
||||
-- Replace obsolete dependencies (account_payment_partner + account_payment_mode
|
||||
-- no longer exist in v18 addons) with the new OCA equivalents
|
||||
DELETE FROM ir_module_module_dependency
|
||||
WHERE module_id = factor_module_id
|
||||
AND name IN ('account_payment_partner', 'account_payment_mode');
|
||||
|
||||
INSERT INTO ir_module_module_dependency (name, module_id)
|
||||
SELECT 'account_payment_base_oca', factor_module_id
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM ir_module_module_dependency
|
||||
WHERE module_id = factor_module_id AND name = 'account_payment_base_oca'
|
||||
);
|
||||
|
||||
INSERT INTO ir_module_module_dependency (name, module_id)
|
||||
SELECT 'account_payment_base_oca_sale', factor_module_id
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM ir_module_module_dependency
|
||||
WHERE module_id = factor_module_id AND name = 'account_payment_base_oca_sale'
|
||||
);
|
||||
|
||||
RAISE NOTICE 'Renamed account_factoring -> account_factoring_oca (data preserved)';
|
||||
ELSE
|
||||
RAISE NOTICE 'account_factoring not found, skipping rename';
|
||||
END IF;
|
||||
END $$;
|
||||
EOF
|
||||
)
|
||||
echo "Executing account_factoring -> account_factoring_oca renaming..."
|
||||
query_postgres_container "$ACCOUNT_FACTORING_RENAME_SQL" ou18 || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# FIX: Rename company-dependent columns before OpenUpgrade runs
|
||||
# In Odoo 18, company-dependent fields are stored as JSONB columns.
|
||||
|
||||
Reference in New Issue
Block a user