Compare commits

..

1 Commits

Author SHA1 Message Date
Stéphan Sainléger
ddb1fcd0ad [IMP] add bdd manipulation to migrate credit.request
due to account.invoice transformation in account.move
2025-03-27 09:14:17 +01:00
24 changed files with 73 additions and 657 deletions

1
.gitignore vendored
View File

@@ -1 +0,0 @@
final_404_addons

View File

@@ -1,5 +0,0 @@
#!/bin/bash
echo "Post migration to 13.0..."
#compose --debug run ou13 -u base --stop-after-init --no-http

View File

@@ -17,6 +17,20 @@ EOF
)
query_postgres_container "$PRE_MIGRATE_SQL" ou13 || exit 1
# Digital currency specific - to comment if not needed
INVOICE_NAME_SQL=$(cat <<'EOF'
ALTER TABLE credit_request ADD invoice_name VARCHAR;
UPDATE credit_request
SET invoice_name = (
SELECT move_name
FROM account_invoice
WHERE account_invoice.id = credit_request.invoice_id
);
UPDATE credit_request SET invoice_id = NULL;
EOF
)
query_postgres_container "$INVOICE_NAME_SQL" ou13 || exit 1
# Copy filestores
copy_filestore ou12 ou12 ou13 ou13 || exit 1

View File

@@ -1,3 +1,3 @@
#!/bin/bash
compose -f ../compose.yml run -p 8013:8069 ou13 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou13
compose -f ../compose.yml run -p 8013:8069 ou13 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=warn --max-cron-threads=0 --limit-time-real=10000 --database=ou13

View File

@@ -1,5 +0,0 @@
#!/bin/bash
echo "Post migration to 14.0..."
#compose --debug run ou14 -u base --stop-after-init --no-http

View File

@@ -1,3 +1,3 @@
#!/bin/bash
compose -f ../compose.yml run -p 8014:8069 ou14 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou14 --load=base,web,openupgrade_framework
compose -f ../compose.yml run -p 8014:8069 ou14 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=warn --max-cron-threads=0 --limit-time-real=10000 --database=ou14 --load=web,openupgrade_framework

View File

@@ -1,5 +0,0 @@
#!/bin/bash
echo "Post migration to 15.0..."
#compose --debug run ou15 -u base --stop-after-init --no-http

View File

@@ -1,3 +1,3 @@
#!/bin/bash
compose -f ../compose.yml run -p 8015:8069 ou15 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou15 --load=base,web,openupgrade_framework
compose -f ../compose.yml run -p 8015:8069 ou15 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=warn --max-cron-threads=0 --limit-time-real=10000 --database=ou15 --load=web,openupgrade_framework

View File

@@ -1,5 +1,17 @@
#!/bin/bash
echo "Post migration to 16.0..."
echo "POST migration to 16.0..."
#compose --debug run ou16 -u base --stop-after-init --no-http
# Digital currency specific - to comment if not needed
INVOICE_NAME_SQL=$(cat <<'EOF'
UPDATE credit_request
SET invoice_id = (
SELECT id
FROM account_move
WHERE account_move.name = credit_request.invoice_name
);
EOF
)
query_postgres_container "$INVOICE_NAME_SQL" ou16 || exit 1
echo "END POST migration to 16.0."

View File

@@ -1,3 +1,3 @@
#!/bin/bash
compose -f ../compose.yml run -p 8016:8069 ou16 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou16 --load=base,web,openupgrade_framework
compose -f ../compose.yml run -p 8016:8069 ou16 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=warn --max-cron-threads=0 --limit-time-real=10000 --database=ou16 --load=web,openupgrade_framework

View File

@@ -1,31 +0,0 @@
#!/bin/bash
echo "Post migration to 17.0..."
# Execute SQL post-migration commands
POST_MIGRATE_SQL=$(cat <<'EOF'
DO $$
DECLARE
plan_id INTEGER;
BEGIN
-- Check if the 'Projects' analytic plan exists
SELECT id INTO plan_id FROM account_analytic_plan WHERE complete_name = 'migration_PROJECTS' LIMIT 1;
-- If it does exist, delete it
IF plan_id IS NOT NULL THEN
DELETE FROM account_analytic_plan WHERE complete_name = 'migration_PROJECTS';
SELECT id INTO plan_id FROM account_analytic_plan WHERE complete_name = 'Projects' LIMIT 1;
-- Delete existing system parameter (if any)
DELETE FROM ir_config_parameter WHERE key = 'analytic.project_plan';
-- Insert the system parameter with the correct plan ID
INSERT INTO ir_config_parameter (key, value, create_date, write_date)
VALUES ('analytic.project_plan', plan_id::text, now(), now());
END IF;
END $$;
EOF
)
echo "SQL command = $POST_MIGRATE_SQL"
query_postgres_container "$POST_MIGRATE_SQL" ou17 || exit 1
#compose --debug run ou17 -u base --stop-after-init --no-http

View File

@@ -1,56 +0,0 @@
#!/bin/bash
echo "Prepare migration to 17.0..."
# Copy database
copy_database ou16 ou17 ou17 || exit 1
# Execute SQL pre-migration commands
PRE_MIGRATE_SQL=$(cat <<'EOF'
DO $$
DECLARE
plan_id INTEGER;
BEGIN
-- Check if the 'Projects' analytic plan exists
SELECT id INTO plan_id FROM account_analytic_plan WHERE name = 'Projects' LIMIT 1;
-- If it doesn't exist, create it
IF plan_id IS NULL THEN
INSERT INTO account_analytic_plan (name, complete_name, default_applicability, create_date, write_date)
VALUES ('Projects', 'migration_PROJECTS', 'optional', now(), now())
RETURNING id INTO plan_id;
END IF;
-- Delete existing system parameter (if any)
DELETE FROM ir_config_parameter WHERE key = 'analytic.project_plan';
-- Insert the system parameter with the correct plan ID
INSERT INTO ir_config_parameter (key, value, create_date, write_date)
VALUES ('analytic.project_plan', plan_id::text, now(), now());
END $$;
EOF
)
echo "SQL command = $PRE_MIGRATE_SQL"
query_postgres_container "$PRE_MIGRATE_SQL" ou17 || exit 1
PRE_MIGRATE_SQL_2=$(cat <<'EOF'
DELETE FROM ir_model_fields WHERE name = 'kanban_state_label';
EOF
)
echo "SQL command = $PRE_MIGRATE_SQL_2"
query_postgres_container "$PRE_MIGRATE_SQL_2" ou17 || exit 1
PRE_MIGRATE_SQL_3=$(cat <<'EOF'
DELETE FROM ir_model_fields WHERE name = 'phone' AND model='hr.employee';
DELETE FROM ir_model_fields WHERE name = 'hr_responsible_id' AND model='hr.job';
DELETE FROM ir_model_fields WHERE name = 'address_home_id' AND model='hr.employee';
DELETE FROM ir_model_fields WHERE name = 'manager_id' AND model='project.task';
EOF
)
echo "SQL command = $PRE_MIGRATE_SQL_3"
query_postgres_container "$PRE_MIGRATE_SQL_3" ou17 || exit 1
# Copy filestores
copy_filestore ou16 ou16 ou17 ou17 || exit 1
echo "Ready for migration to 17.0!"

View File

@@ -1,3 +0,0 @@
#!/bin/bash
compose -f ../compose.yml run -p 8017:8069 ou17 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou17 --load=base,web,openupgrade_framework

View File

@@ -1,5 +0,0 @@
#!/bin/bash
echo "Post migration to 18.0..."
#compose --debug run ou18 -u base --stop-after-init --no-http

View File

@@ -1,19 +0,0 @@
#!/bin/bash
echo "Prepare migration to 18.0..."
# Copy database
copy_database ou17 ou18 ou18 || exit 1
# Execute SQL pre-migration commands
PRE_MIGRATE_SQL=$(cat <<'EOF'
UPDATE account_analytic_plan SET default_applicability=NULL WHERE default_applicability='optional';
EOF
)
echo "SQL command = $PRE_MIGRATE_SQL"
query_postgres_container "$PRE_MIGRATE_SQL" ou18 || exit 1
# Copy filestores
copy_filestore ou17 ou17 ou18 ou18 || exit 1
echo "Ready for migration to 18.0!"

View File

@@ -1,3 +0,0 @@
#!/bin/bash
compose -f ../compose.yml run -p 8018:8069 ou18 --config=/opt/odoo/auto/odoo.conf --stop-after-init -u all --workers 0 --log-level=debug --max-cron-threads=0 --limit-time-real=10000 --database=ou18 --load=base,web,openupgrade_framework

View File

@@ -52,7 +52,7 @@ ou15:
ou16:
charm: odoo-tecnativa
docker-compose:
image: docker.0k.io/mirror/odoo:rc_16.0-ELABORE-LIGHT
image: docker.0k.io/mirror/odoo:rc_16.0-MYC-INIT
## Important to keep as a list: otherwise it'll overwrite charm's arguments.
command:
- "--log-level=debug"
@@ -73,18 +73,6 @@ ou17:
options:
workers: 0
ou18:
charm: odoo-tecnativa
docker-compose:
image: docker.0k.io/mirror/odoo:rc_18.0-ELABORE-LIGHT
## Important to keep as a list: otherwise it'll overwrite charm's arguments.
command:
- "--log-level=debug"
- "--limit-time-cpu=1000000"
- "--limit-time-real=1000000"
options:
workers: 0
postgres:
docker-compose:
image: docker.0k.io/postgres:17.2.0-myc
image: docker.0k.io/postgres:12.15.0-myc

View File

@@ -11,19 +11,10 @@ EOF
)
query_postgres_container "$FINALE_SQL" "$DB_NAME" || exit 1
# Fix duplicated views
PYTHON_SCRIPT=post_migration_fix_duplicated_views.py
echo "Remove duplicated views with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1
# Uninstall obsolette add-ons
PYTHON_SCRIPT=post_migration_cleanup_obsolete_modules.py
echo "Uninstall obsolete add-ons with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1
# Give back the right to user to access to the tables
# docker exec -u 70 "$DB_CONTAINER_NAME" pgm chown "$FINALE_SERVICE_NAME" "$DB_NAME"
# Launch Odoo with database in finale version to run all updates
compose --debug run "$ODOO_SERVICE" -u all --log-level=debug --stop-after-init --no-http
compose --debug run "$ODOO_SERVICE" -u all --stop-after-init --no-http

24
force_uninstall_addons Normal file
View File

@@ -0,0 +1,24 @@
galicea_base
galicea_environment_checkup
mass_editing
mass_mailing_themes
muk_autovacuum
muk_fields_lobject
muk_fields_stream
muk_utils
muk_web_theme_mail
muk_web_utils
account_usability
kpi_dashboard
web_window_title
website_project_kanbanview
project_usability
project_tag
maintenance_server_monitoring_ping
maintenance_server_monitoring_ssh
maintenance_server_monitoring_memory
maintenance_server_monitoring_maintenance_equipment_status
maintenance_server_monitoring_disk
project_task_assignees_avatar
account_partner_reconcile
account_invoice_import_simple_pdf

View File

@@ -1,128 +0,0 @@
#!/usr/bin/env python3
"""
Post-Migration Obsolete Module Cleanup
Run this AFTER migration to detect and remove modules that exist in the database
but no longer exist in the filesystem (addons paths).
"""
print("\n" + "="*80)
print("POST-MIGRATION OBSOLETE MODULE CLEANUP")
print("="*80 + "\n")
import odoo.modules.module as module_lib
# Get all modules from database
all_modules = env['ir.module.module'].search([])
print(f"Analyzing {len(all_modules)} modules in database...\n")
# Detect obsolete modules (in database but not in filesystem)
obsolete_modules = []
for mod in all_modules:
mod_path = module_lib.get_module_path(mod.name, display_warning=False)
if not mod_path:
obsolete_modules.append(mod)
if not obsolete_modules:
print("✓ No obsolete modules found! Database is clean.")
print("=" * 80 + "\n")
exit()
# Separate modules by state
safe_to_delete = [m for m in obsolete_modules if m.state != 'installed']
installed_obsolete = [m for m in obsolete_modules if m.state == 'installed']
# Display obsolete modules
print(f"Obsolete modules found: {len(obsolete_modules)}\n")
if installed_obsolete:
print("-" * 80)
print("⚠️ OBSOLETE INSTALLED MODULES (require attention)")
print("-" * 80)
for mod in sorted(installed_obsolete, key=lambda m: m.name):
print(f"{mod.name:40} | ID: {mod.id}")
print()
if safe_to_delete:
print("-" * 80)
print("OBSOLETE UNINSTALLED MODULES (safe to delete)")
print("-" * 80)
for mod in sorted(safe_to_delete, key=lambda m: m.name):
print(f"{mod.name:40} | State: {mod.state:15} | ID: {mod.id}")
print()
# Summary
print("=" * 80)
print("SUMMARY")
print("=" * 80 + "\n")
print(f" • Obsolete uninstalled modules (safe to delete): {len(safe_to_delete)}")
print(f" • Obsolete INSTALLED modules (caution!): {len(installed_obsolete)}")
# Delete uninstalled modules
if safe_to_delete:
print("\n" + "=" * 80)
print("DELETING OBSOLETE UNINSTALLED MODULES")
print("=" * 80 + "\n")
deleted_count = 0
failed_deletes = []
for mod in safe_to_delete:
try:
mod_name = mod.name
mod_id = mod.id
mod.unlink()
print(f"✓ Deleted: {mod_name} (ID: {mod_id})")
deleted_count += 1
except Exception as e:
print(f"✗ Failed: {mod.name} - {e}")
failed_deletes.append({'name': mod.name, 'id': mod.id, 'reason': str(e)})
# Commit changes
print("\n" + "=" * 80)
print("COMMITTING CHANGES")
print("=" * 80 + "\n")
try:
env.cr.commit()
print("✓ All changes committed successfully!")
except Exception as e:
print(f"✗ Commit failed: {e}")
print("Changes were NOT saved!")
exit(1)
# Final result
print("\n" + "=" * 80)
print("RESULT")
print("=" * 80 + "\n")
print(f" • Successfully deleted modules: {deleted_count}")
print(f" • Failed deletions: {len(failed_deletes)}")
if failed_deletes:
print("\n⚠️ Modules not deleted:")
for item in failed_deletes:
print(f"{item['name']} (ID: {item['id']}): {item['reason']}")
if installed_obsolete:
print("\n" + "=" * 80)
print("⚠️ WARNING: OBSOLETE INSTALLED MODULES")
print("=" * 80 + "\n")
print("The following modules are marked 'installed' but no longer exist")
print("in the filesystem. They may cause problems.\n")
print("Options:")
print(" 1. Check if these modules were renamed/merged in the new version")
print(" 2. Manually uninstall them if possible")
print(" 3. Force delete them (risky, may break dependencies)\n")
for mod in sorted(installed_obsolete, key=lambda m: m.name):
# Find modules that depend on this module
dependents = env['ir.module.module'].search([
('state', '=', 'installed'),
('dependencies_id.name', '=', mod.name)
])
dep_info = f" <- Dependents: {dependents.mapped('name')}" if dependents else ""
print(f"{mod.name}{dep_info}")
print("\n" + "=" * 80)
print("CLEANUP COMPLETED!")
print("=" * 80 + "\n")

View File

@@ -1,192 +0,0 @@
#!/usr/bin/env python3
"""
Post-Migration Duplicate View Fixer
Run this AFTER migration to fix duplicate views automatically.
"""
print("\n" + "="*80)
print("POST-MIGRATION DUPLICATE VIEW FIXER")
print("="*80 + "\n")
from collections import defaultdict
# Find all duplicate views
all_views = env['ir.ui.view'].search(['|', ('active', '=', True), ('active', '=', False)])
keys = defaultdict(list)
for view in all_views:
if view.key:
keys[view.key].append(view)
duplicates = {k: v for k, v in keys.items() if len(v) > 1}
print(f"Found {len(duplicates)} keys with duplicate views\n")
if not duplicates:
print("✓ No duplicate views found! Database is clean.")
print("=" * 80 + "\n")
exit()
# Process duplicates
views_to_delete = []
redirect_log = []
for key, views in sorted(duplicates.items()):
print(f"\nProcessing key: {key}")
print("-" * 80)
# Sort views: module views first, then by ID (older first)
sorted_views = sorted(views, key=lambda v: (
0 if v.model_data_id else 1, # Module views first
v.id # Older views first (lower ID = older)
))
# Keep the first view (should be module view or oldest)
keep = sorted_views[0]
to_delete = sorted_views[1:]
module_keep = keep.model_data_id.module if keep.model_data_id else "Custom/DB"
print(f"KEEP: ID {keep.id:>6} | Module: {module_keep:<20} | {keep.name}")
for view in to_delete:
module = view.model_data_id.module if view.model_data_id else "Custom/DB"
print(f"DELETE: ID {view.id:>6} | Module: {module:<20} | {view.name}")
# Find and redirect children
children = env['ir.ui.view'].search([('inherit_id', '=', view.id)])
if children:
print(f" → Redirecting {len(children)} children {children.ids} to view {keep.id}")
for child in children:
child_module = child.model_data_id.module if child.model_data_id else "Custom/DB"
redirect_log.append({
'child_id': child.id,
'child_name': child.name,
'child_module': child_module,
'from': view.id,
'to': keep.id
})
try:
children.write({'inherit_id': keep.id})
print(f" ✓ Redirected successfully")
except Exception as e:
print(f" ✗ Redirect failed: {e}")
continue
views_to_delete.append(view)
# Summary before deletion
print("\n" + "="*80)
print("SUMMARY")
print("="*80 + "\n")
print(f"Views to delete: {len(views_to_delete)}")
print(f"Child views to redirect: {len(redirect_log)}\n")
if redirect_log:
print("Redirections that will be performed:")
for item in redirect_log[:10]: # Show first 10
print(f" • View {item['child_id']} ({item['child_module']})")
print(f" '{item['child_name']}'")
print(f" Parent: {item['from']}{item['to']}")
if len(redirect_log) > 10:
print(f" ... and {len(redirect_log) - 10} more redirections")
# Delete duplicate views
print("\n" + "="*80)
print("DELETING DUPLICATE VIEWS")
print("="*80 + "\n")
deleted_count = 0
failed_deletes = []
# Sort views by ID descending (delete newer/child views first)
views_to_delete_sorted = sorted(views_to_delete, key=lambda v: v.id, reverse=True)
for view in views_to_delete_sorted:
try:
# Create savepoint to isolate each deletion
env.cr.execute('SAVEPOINT delete_view')
view_id = view.id
view_name = view.name
view_key = view.key
# Double-check it has no children
remaining_children = env['ir.ui.view'].search([('inherit_id', '=', view_id)])
if remaining_children:
print(f"⚠️ Skipping view {view_id}: Still has {len(remaining_children)} children")
failed_deletes.append({
'id': view_id,
'reason': f'Still has {len(remaining_children)} children'
})
env.cr.execute('ROLLBACK TO SAVEPOINT delete_view')
continue
view.unlink()
env.cr.execute('RELEASE SAVEPOINT delete_view')
print(f"✓ Deleted view {view_id}: {view_key}")
deleted_count += 1
except Exception as e:
env.cr.execute('ROLLBACK TO SAVEPOINT delete_view')
print(f"✗ Failed to delete view {view.id}: {e}")
failed_deletes.append({
'id': view.id,
'name': view.name,
'reason': str(e)
})
# Commit changes
print("\n" + "="*80)
print("COMMITTING CHANGES")
print("="*80 + "\n")
try:
env.cr.commit()
print("✓ All changes committed successfully!")
except Exception as e:
print(f"✗ Commit failed: {e}")
print("Changes were NOT saved!")
exit(1)
# Final verification
print("\n" + "="*80)
print("FINAL VERIFICATION")
print("="*80 + "\n")
# Re-check for duplicates
all_views_after = env['ir.ui.view'].search([('active', '=', True)])
keys_after = defaultdict(list)
for view in all_views_after:
if view.key:
keys_after[view.key].append(view)
duplicates_after = {k: v for k, v in keys_after.items() if len(v) > 1}
print(f"Results:")
print(f" • Successfully deleted: {deleted_count} views")
print(f" • Failed deletions: {len(failed_deletes)}")
print(f" • Child views redirected: {len(redirect_log)}")
print(f" • Remaining duplicates: {len(duplicates_after)}")
if failed_deletes:
print(f"\n⚠️ Failed deletions:")
for item in failed_deletes:
print(f" • View {item['id']}: {item['reason']}")
if duplicates_after:
print(f"\n⚠️ Still have {len(duplicates_after)} duplicate keys:")
for key, views in sorted(duplicates_after.items())[:5]:
print(f"{key}: {len(views)} views")
for view in views:
module = view.model_data_id.module if view.model_data_id else "Custom/DB"
print(f" - ID {view.id} ({module})")
print(f"\n Run this script again to attempt another cleanup.")
else:
print(f"\n✓ All duplicates resolved!")
print("\n" + "="*80)
print("FIX COMPLETED!")
print("="*80)

View File

@@ -1,126 +0,0 @@
#!/usr/bin/env python3
"""
Pre-Migration Cleanup Script for Odoo
Run this BEFORE migrating to identify and clean up custom views.
Usage: odoo shell -d dbname < pre_migration_cleanup.py
"""
print("\n" + "="*80)
print("PRE-MIGRATION CLEANUP - VIEW ANALYSIS")
print("="*80 + "\n")
# 1. Find all custom (COW) views
print("STEP 1: Identifying Custom/COW Views")
print("-"*80)
all_views = env['ir.ui.view'].search(['|', ('active', '=', True), ('active', '=', False)])
cow_views = all_views.filtered(lambda v: not v.model_data_id)
print(f"Total views in database: {len(all_views)}")
print(f"Custom views (no module): {len(cow_views)}")
print(f"Module views: {len(all_views) - len(cow_views)}\n")
if cow_views:
print("Custom views found:\n")
print(f"{'ID':<8} {'Active':<8} {'Key':<50} {'Name':<40}")
print("-"*120)
for view in cow_views[:50]: # Show first 50
active_str = "" if view.active else ""
key_str = view.key[:48] if view.key else "N/A"
name_str = view.name[:38] if view.name else "N/A"
print(f"{view.id:<8} {active_str:<8} {key_str:<50} {name_str:<40}")
if len(cow_views) > 50:
print(f"\n... and {len(cow_views) - 50} more custom views")
# 2. Find duplicate views
print("\n" + "="*80)
print("STEP 2: Finding Duplicate Views (Same Key)")
print("-"*80 + "\n")
from collections import defaultdict
keys = defaultdict(list)
for view in all_views.filtered(lambda v: v.key and v.active):
keys[view.key].append(view)
duplicates = {k: v for k, v in keys.items() if len(v) > 1}
print(f"Found {len(duplicates)} keys with duplicate views:\n")
if duplicates:
for key, views in sorted(duplicates.items()):
print(f"\nKey: {key} ({len(views)} duplicates)")
for view in views:
module = view.model_data_id.module if view.model_data_id else "⚠️ Custom/DB"
print(f" ID {view.id:>6}: {module:<25} | {view.name}")
# 3. Find views that might have xpath issues
print("\n" + "="*80)
print("STEP 3: Finding Views with XPath Expressions")
print("-"*80 + "\n")
import re
views_with_xpath = []
xpath_pattern = r'<xpath[^>]+expr="([^"]+)"'
for view in all_views.filtered(lambda v: v.active and v.inherit_id):
xpaths = re.findall(xpath_pattern, view.arch_db)
if xpaths:
views_with_xpath.append({
'view': view,
'xpaths': xpaths,
'is_custom': not bool(view.model_data_id)
})
print(f"Found {len(views_with_xpath)} views with xpath expressions")
custom_xpath_views = [v for v in views_with_xpath if v['is_custom']]
print(f" - {len(custom_xpath_views)} are custom views (potential issue!)")
print(f" - {len(views_with_xpath) - len(custom_xpath_views)} are module views\n")
if custom_xpath_views:
print("Custom views with xpaths (risk for migration issues):\n")
for item in custom_xpath_views:
view = item['view']
print(f"ID {view.id}: {view.name}")
print(f" Key: {view.key}")
print(f" Inherits from: {view.inherit_id.key}")
print(f" XPath count: {len(item['xpaths'])}")
print(f" Sample xpaths: {item['xpaths'][:2]}")
print()
# 4. Summary and recommendations
print("=" * 80)
print("SUMMARY AND RECOMMENDATIONS")
print("=" * 80 + "\n")
print(f"📊 Statistics:")
print(f" • Total views: {len(all_views)}")
print(f" • Custom views: {len(cow_views)}")
print(f" • Duplicate view keys: {len(duplicates)}")
print(f" • Custom views with xpaths: {len(custom_xpath_views)}\n")
print(f"\n📋 RECOMMENDED ACTIONS BEFORE MIGRATION:\n")
if custom_xpath_views:
print(f"1. Archive or delete {len(custom_xpath_views)} custom views with xpaths:")
print(f" • Review each one and determine if still needed")
print(f" • Archive unnecessary ones: env['ir.ui.view'].browse([ids]).write({{'active': False}})")
print(f" • Plan to recreate important ones as proper module views after migration\n")
if duplicates:
print(f"2. Fix {len(duplicates)} duplicate view keys:")
print(f" • Manually review and delete obsolete duplicates, keeping the most appropriate one")
print(f" • Document the remaining appropriate ones as script post_migration_fix_duplicated_views.py will run AFTER the migration and delete all duplicates.\n")
if cow_views:
print(f"3. Review {len(cow_views)} custom views:")
print(f" • Document which ones are important")
print(f" • Export their XML for reference")
print(f" • Consider converting to module views\n")
print("=" * 80 + "\n")

View File

@@ -77,26 +77,6 @@ Do you accept to migrate the database with all these add-ons still installed? (Y
echo "Y - Yes, let's go on with the upgrade."
echo "N - No, stop the upgrade"
read -n 1 -p "Your choice: " choice
case "$choice" in
[Yy] ) echo "
Let's go on!";;
[Nn] ) echo "
Upgrade cancelled!"; exit 1;;
* ) echo "
Please answer by Y or N.";;
esac
# Check the views
PYTHON_SCRIPT=pre_migration_view_checking.py
echo "Check views with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1
echo "
Do you accept to migrate the database with the current views states? (Y/N/R)"
echo "Y - Yes, let's go on with the upgrade."
echo "N - No, stop the upgrade"
read -n 1 -p "Your choice: " choice
case "$choice" in
[Yy] ) echo "
Upgrade confirmed!";;

View File

@@ -17,21 +17,8 @@ export FINALE_DB_NAME="ou${FINAL_VERSION}"
# Define finale odoo service name
FINALE_SERVICE_NAME="${FINALE_DB_NAME}"
# Service postgres name (dynamically retrieved from running containers)
POSTGRES_CONTAINERS=$(docker ps --format '{{.Names}}' | grep postgres)
POSTGRES_COUNT=$(echo "$POSTGRES_CONTAINERS" | grep -c .)
if [ "$POSTGRES_COUNT" -eq 0 ]; then
echo "ERROR: No running PostgreSQL container found. Please start a PostgreSQL container and try again." >&2
exit 1
elif [ "$POSTGRES_COUNT" -gt 1 ]; then
echo "ERROR: Multiple PostgreSQL containers found:" >&2
echo "$POSTGRES_CONTAINERS" >&2
echo "Please ensure only one PostgreSQL container is running." >&2
exit 1
fi
export POSTGRES_SERVICE_NAME="$POSTGRES_CONTAINERS"
# Service postgres name
export POSTGRES_SERVICE_NAME="lokavaluto_postgres_1"
#############################################
# DISPLAYS ALL INPUTS PARAMETERS
@@ -84,22 +71,13 @@ copy_filestore(){
local FROM_DB="$2"
local TO_SERVICE="$3"
local TO_DB="$4"
sudo mkdir -p /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
sudo rm -rf /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
sudo cp -a /srv/datastore/data/"$FROM_SERVICE"/var/lib/odoo/filestore/"$FROM_DB" /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
mkdir -p /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
rm -rf /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
cp -a /srv/datastore/data/"$FROM_SERVICE"/var/lib/odoo/filestore/"$FROM_DB" /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
echo "Filestore $FROM_SERVICE/$FROM_DB copied."
}
export -f copy_filestore
# Function to launch python scripts in Odoo Shell
exec_python_script_in_odoo_shell(){
local SERVICE_NAME="$1"
local DB_NAME="$2"
local PYTHON_SCRIPT="$3"
compose --debug run "$SERVICE_NAME" shell -d "$DB_NAME" --no-http --stop-after-init < "$PYTHON_SCRIPT"
}
export -f exec_python_script_in_odoo_shell
##############################################
# CHECKS ALL NEEDED COMPONENTS ARE AVAILABLE #
##############################################
@@ -107,6 +85,14 @@ export -f exec_python_script_in_odoo_shell
echo "
==== CHECKS ALL NEEDED COMPONENTS ARE AVAILABLE ===="
# Check POSTGRES container is running
if ! docker ps | grep -q "$POSTGRES_SERVICE_NAME"; then
printf "Docker container %s is not running.\n" "$POSTGRES_SERVICE_NAME" >&2
return 1
else
echo "UPGRADE: container $POSTGRES_SERVICE_NAME running."
fi
# Check origin database is in the local postgres
DB_EXISTS=$(docker exec -it -u 70 $POSTGRES_SERVICE_NAME psql -tc "SELECT 1 FROM pg_database WHERE datname = '$ORIGIN_DB_NAME'" | tr -d '[:space:]')
if [ "$DB_EXISTS" ]; then
@@ -132,7 +118,7 @@ fi
# Remove finale database and datastore if already exists (we need a virgin Odoo)
if docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm ls | grep -q "$FINALE_SERVICE_NAME"; then
docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm rm -f "$FINALE_SERVICE_NAME"
sudo rm -rf /srv/datastore/data/"$FINALE_SERVICE_NAME"/var/lib/odoo/filestore/"$FINALE_SERVICE_NAME"
rm -rf /srv/datastore/data/"$FINALE_SERVICE_NAME"/var/lib/odoo/filestore/"$FINALE_SERVICE_NAME"
fi
compose --debug run "$FINALE_SERVICE_NAME" -i base --stop-after-init --no-http