This commit is contained in:
gsinghpal
2026-05-16 13:18:52 -04:00
parent 191a9c82be
commit 9ebf89bde2
1080 changed files with 0 additions and 1197 deletions

Binary file not shown.

View File

@@ -0,0 +1,20 @@
# fusion_accounting_migration — Cursor / Claude Context
## Purpose
Transitional sub-module that helps clients move from Odoo Enterprise accounting
to Odoo Community + Fusion Accounting without losing data.
## What it does
- Safety guard: blocks uninstall of Enterprise accounting modules until the
migration wizard has run (per-module flag in ir.config_parameter)
- Migration wizard: shell that other fusion sub-modules extend with per-feature
migration logic (Phase 0 ships only the shell)
## Critical
- The safety guard overrides `button_immediate_uninstall` AND `module_uninstall`
on `ir.module.module`. Both paths must be guarded — UI uninstall, CLI uninstall,
and API uninstall all go through one or the other.
- Each fusion feature sub-module that replaces an Enterprise feature MUST extend
the migration wizard's `action_run_migration` to add its own migration step
AND set the corresponding `fusion_accounting.migration.<module>.completed`
flag to True after running.

View File

@@ -0,0 +1,22 @@
# Fusion Accounting Migration
Transitional helper for moving clients from Odoo Enterprise to Community + Fusion.
## When to use
Install this module ONCE per client during the Enterprise->Fusion switchover.
After the switchover is complete and the client is comfortable on Community,
this module can be uninstalled.
## How it works
1. Install fusion_accounting (the meta-module) — pulls in this module
2. Open Fusion Accounting -> Migrate from Enterprise (top-level menu)
3. Wizard shows which Enterprise modules are detected and what migrations are available
4. Run migration; wizard reports counts and warnings
5. Uninstall Enterprise modules in dep-safe order (the safety guard prevents premature uninstall)
## Override the safety guard
If you need to uninstall an Enterprise module WITHOUT migrating (data will be lost),
set `fusion_accounting.migration.<module>.completed` to True in System Parameters.

View File

@@ -0,0 +1,10 @@
# UPGRADE_NOTES — fusion_accounting_migration
## V19.0.1.0.0 (initial — Phase 0)
Skeleton: safety guard + wizard shell. No per-feature migration logic registered yet.
Added by future phases:
- Phase 1: bank-rec migration (verifies account.partial.reconcile rows are intact; sets `account_accountant.completed` flag)
- Phase 5: account_followup migration
- Phase 6: account_asset, account_budget migration

View File

@@ -0,0 +1,2 @@
from . import models
from . import wizards

View File

@@ -0,0 +1,36 @@
{
'name': 'Fusion Accounting Migration',
'version': '19.0.1.1.0',
'category': 'Accounting/Accounting',
'sequence': 27,
'summary': 'Transitional module: migrates Odoo Enterprise accounting data to Fusion Accounting tables before Enterprise uninstall.',
'description': """
Fusion Accounting Migration
===========================
Transitional helper that lives only during Enterprise-to-Fusion switchover.
Provides:
- A safety guard that blocks uninstall of Odoo Enterprise accounting modules
(account_accountant, account_reports, account_followup, account_asset,
account_budget, account_loans) until the Fusion migration wizard has run
- A guided migration wizard accessible at Settings -> Fusion Accounting ->
Migrate from Enterprise (the wizard's per-feature migrations are added
by each Fusion sub-module that replaces an Enterprise feature)
Once the switchover is complete, this module can safely be uninstalled.
Built by Nexa Systems Inc.
""",
'author': 'Nexa Systems Inc.',
'website': 'https://nexasystems.ca',
'support': 'support@nexasystems.ca',
'maintainer': 'Nexa Systems Inc.',
'depends': ['fusion_accounting_core'],
'data': [
'security/ir.model.access.csv',
'wizards/migration_wizard_views.xml',
],
'installable': True,
'application': False,
'license': 'OPL-1',
}

View File

@@ -0,0 +1,109 @@
# Graph Report - /Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration (2026-04-22)
## Corpus Check
- 8 files · ~3,755 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 29 nodes · 29 edges · 10 communities detected
- Extraction: 93% EXTRACTED · 7% INFERRED · 0% AMBIGUOUS · INFERRED: 2 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
- [[_COMMUNITY_Community 0|Community 0]]
- [[_COMMUNITY_Community 1|Community 1]]
- [[_COMMUNITY_Community 2|Community 2]]
- [[_COMMUNITY_Community 3|Community 3]]
- [[_COMMUNITY_Community 4|Community 4]]
- [[_COMMUNITY_Community 5|Community 5]]
- [[_COMMUNITY_Community 6|Community 6]]
- [[_COMMUNITY_Community 7|Community 7]]
- [[_COMMUNITY_Community 8|Community 8]]
- [[_COMMUNITY_Community 9|Community 9]]
## God Nodes (most connected - your core abstractions)
1. `TestSafetyGuard` - 5 edges
2. `_fusion_check_uninstall_guard()` - 5 edges
3. `IrModuleModule` - 3 edges
4. `FusionMigrationWizard` - 3 edges
5. `Verify the safety guard blocks Enterprise uninstall when migration hasn't run.` - 1 edges
6. `If the per-module migration flag is set, uninstall is allowed. Skip if` - 1 edges
7. `If account_accountant is installed and migration not run, raise.` - 1 edges
8. `Safety guard: blocks Odoo Enterprise accounting uninstall until migration runs.` - 1 edges
9. `Override to invoke the safety guard before allowing uninstall. Both thi` - 1 edges
10. `Override the lower-level uninstall path too (CLI / API uninstall). See` - 1 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
## Communities
### Community 0 - "Community 0"
Cohesion: 0.29
Nodes (5): _fusion_check_uninstall_guard(), IrModuleModule, Override to invoke the safety guard before allowing uninstall. Both thi, Override the lower-level uninstall path too (CLI / API uninstall). See, If the per-module migration flag is set, uninstall is allowed. Skip if
### Community 1 - "Community 1"
Cohesion: 0.33
Nodes (4): If account_accountant is installed and migration not run, raise., Verify the safety guard blocks Enterprise uninstall when migration hasn't run., TestSafetyGuard, TransactionCase
### Community 2 - "Community 2"
Cohesion: 0.4
Nodes (2): Safety guard: blocks Odoo Enterprise accounting uninstall until migration runs., Migration wizard skeleton. Per-feature migration logic (account.asset -> fusion
### Community 3 - "Community 3"
Cohesion: 0.5
Nodes (2): FusionMigrationWizard, Stub: Phase 0 has no migrations to run. Sub-modules extend this method
### Community 4 - "Community 4"
Cohesion: 1.0
Nodes (0):
### Community 5 - "Community 5"
Cohesion: 1.0
Nodes (0):
### Community 6 - "Community 6"
Cohesion: 1.0
Nodes (0):
### Community 7 - "Community 7"
Cohesion: 1.0
Nodes (0):
### Community 8 - "Community 8"
Cohesion: 1.0
Nodes (0):
### Community 9 - "Community 9"
Cohesion: 1.0
Nodes (1): Verify it's safe to uninstall the given modules. Returns True if all ch
## Knowledge Gaps
- **9 isolated node(s):** `Verify the safety guard blocks Enterprise uninstall when migration hasn't run.`, `If the per-module migration flag is set, uninstall is allowed. Skip if`, `If account_accountant is installed and migration not run, raise.`, `Safety guard: blocks Odoo Enterprise accounting uninstall until migration runs.`, `Verify it's safe to uninstall the given modules. Returns True if all ch` (+4 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 4`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 5`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 6`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 7`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 8`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 9`** (1 nodes): `Verify it's safe to uninstall the given modules. Returns True if all ch`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `_fusion_check_uninstall_guard()` connect `Community 0` to `Community 1`, `Community 2`?**
_High betweenness centrality (0.354) - this node is a cross-community bridge._
- **Why does `TestSafetyGuard` connect `Community 1` to `Community 0`?**
_High betweenness centrality (0.164) - this node is a cross-community bridge._
- **Why does `FusionMigrationWizard` connect `Community 3` to `Community 2`?**
_High betweenness centrality (0.156) - this node is a cross-community bridge._
- **Are the 2 inferred relationships involving `_fusion_check_uninstall_guard()` (e.g. with `.test_uninstall_not_blocked_when_migration_completed()` and `.test_uninstall_blocked_when_migration_pending()`) actually correct?**
_`_fusion_check_uninstall_guard()` has 2 INFERRED edges - model-reasoned connections that need verification._
- **What connects `Verify the safety guard blocks Enterprise uninstall when migration hasn't run.`, `If the per-module migration flag is set, uninstall is allowed. Skip if`, `If account_accountant is installed and migration not run, raise.` to the rest of the system?**
_9 weakly-connected nodes found - possible documentation gaps or missing edges._

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/__init__.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/__init__.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/__init__.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,619 @@
{
"directed": false,
"multigraph": false,
"graph": {},
"nodes": [
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py",
"community": 4,
"norm_label": "__init__.py"
},
{
"label": "__manifest__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__manifest__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_manifest_py",
"community": 8,
"norm_label": "__manifest__.py"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py",
"community": 5,
"norm_label": "__init__.py"
},
{
"label": "test_safety_guard.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_test_safety_guard_py",
"community": 1,
"norm_label": "test_safety_guard.py"
},
{
"label": "TestSafetyGuard",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L6",
"id": "test_safety_guard_testsafetyguard",
"community": 1,
"norm_label": "testsafetyguard"
},
{
"label": "TransactionCase",
"file_type": "code",
"source_file": "",
"source_location": "",
"id": "transactioncase",
"community": 1,
"norm_label": "transactioncase"
},
{
"label": ".test_uninstall_not_blocked_when_migration_completed()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L9",
"id": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"community": 0,
"norm_label": ".test_uninstall_not_blocked_when_migration_completed()"
},
{
"label": ".test_uninstall_blocked_when_migration_pending()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L28",
"id": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"community": 1,
"norm_label": ".test_uninstall_blocked_when_migration_pending()"
},
{
"label": "Verify the safety guard blocks Enterprise uninstall when migration hasn't run.",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L7",
"id": "test_safety_guard_rationale_7",
"community": 1,
"norm_label": "verify the safety guard blocks enterprise uninstall when migration hasn't run."
},
{
"label": "If the per-module migration flag is set, uninstall is allowed. Skip if",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L10",
"id": "test_safety_guard_rationale_10",
"community": 0,
"norm_label": "if the per-module migration flag is set, uninstall is allowed. skip if"
},
{
"label": "If account_accountant is installed and migration not run, raise.",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L29",
"id": "test_safety_guard_rationale_29",
"community": 1,
"norm_label": "if account_accountant is installed and migration not run, raise."
},
{
"label": "ir_module_module.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"community": 2,
"norm_label": "ir_module_module.py"
},
{
"label": "IrModuleModule",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L31",
"id": "ir_module_module_irmodulemodule",
"community": 0,
"norm_label": "irmodulemodule"
},
{
"label": "_fusion_check_uninstall_guard()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L35",
"id": "ir_module_module_fusion_check_uninstall_guard",
"community": 0,
"norm_label": "_fusion_check_uninstall_guard()"
},
{
"label": ".button_immediate_uninstall()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L64",
"id": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"community": 0,
"norm_label": ".button_immediate_uninstall()"
},
{
"label": ".module_uninstall()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L76",
"id": "ir_module_module_irmodulemodule_module_uninstall",
"community": 0,
"norm_label": ".module_uninstall()"
},
{
"label": "Safety guard: blocks Odoo Enterprise accounting uninstall until migration runs.",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L1",
"id": "ir_module_module_rationale_1",
"community": 2,
"norm_label": "safety guard: blocks odoo enterprise accounting uninstall until migration runs."
},
{
"label": "Verify it's safe to uninstall the given modules. Returns True if all ch",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L36",
"id": "ir_module_module_rationale_36",
"community": 9,
"norm_label": "verify it's safe to uninstall the given modules. returns true if all ch"
},
{
"label": "Override to invoke the safety guard before allowing uninstall. Both thi",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L65",
"id": "ir_module_module_rationale_65",
"community": 0,
"norm_label": "override to invoke the safety guard before allowing uninstall. both thi"
},
{
"label": "Override the lower-level uninstall path too (CLI / API uninstall). See",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L77",
"id": "ir_module_module_rationale_77",
"community": 0,
"norm_label": "override the lower-level uninstall path too (cli / api uninstall). see"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py",
"community": 6,
"norm_label": "__init__.py"
},
{
"label": "migration_wizard.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"community": 2,
"norm_label": "migration_wizard.py"
},
{
"label": "FusionMigrationWizard",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L16",
"id": "migration_wizard_fusionmigrationwizard",
"community": 3,
"norm_label": "fusionmigrationwizard"
},
{
"label": "._default_notes()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L26",
"id": "migration_wizard_fusionmigrationwizard_default_notes",
"community": 3,
"norm_label": "._default_notes()"
},
{
"label": "_compute_detected()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L37",
"id": "migration_wizard_compute_detected",
"community": 2,
"norm_label": "_compute_detected()"
},
{
"label": ".action_run_migration()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L46",
"id": "migration_wizard_fusionmigrationwizard_action_run_migration",
"community": 3,
"norm_label": ".action_run_migration()"
},
{
"label": "Migration wizard skeleton. Per-feature migration logic (account.asset -> fusion",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L1",
"id": "migration_wizard_rationale_1",
"community": 2,
"norm_label": "migration wizard skeleton. per-feature migration logic (account.asset -> fusion"
},
{
"label": "Stub: Phase 0 has no migrations to run. Sub-modules extend this method",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L47",
"id": "migration_wizard_rationale_47",
"community": 3,
"norm_label": "stub: phase 0 has no migrations to run. sub-modules extend this method"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py",
"community": 7,
"norm_label": "__init__.py"
}
],
"links": [
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py",
"target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_init_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/__init__.py",
"source_location": "L1",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py",
"target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_init_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L6",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_test_safety_guard_py",
"_tgt": "test_safety_guard_testsafetyguard",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_tests_test_safety_guard_py",
"target": "test_safety_guard_testsafetyguard",
"confidence_score": 1.0
},
{
"relation": "inherits",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L6",
"weight": 1.0,
"_src": "test_safety_guard_testsafetyguard",
"_tgt": "transactioncase",
"source": "test_safety_guard_testsafetyguard",
"target": "transactioncase",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L9",
"weight": 1.0,
"_src": "test_safety_guard_testsafetyguard",
"_tgt": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"source": "test_safety_guard_testsafetyguard",
"target": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L28",
"weight": 1.0,
"_src": "test_safety_guard_testsafetyguard",
"_tgt": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"source": "test_safety_guard_testsafetyguard",
"target": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L7",
"weight": 1.0,
"_src": "test_safety_guard_rationale_7",
"_tgt": "test_safety_guard_testsafetyguard",
"source": "test_safety_guard_testsafetyguard",
"target": "test_safety_guard_rationale_7",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L10",
"weight": 1.0,
"_src": "test_safety_guard_rationale_10",
"_tgt": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"source": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"target": "test_safety_guard_rationale_10",
"confidence_score": 1.0
},
{
"relation": "calls",
"confidence": "INFERRED",
"confidence_score": 0.8,
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L25",
"weight": 1.0,
"_src": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"_tgt": "ir_module_module_fusion_check_uninstall_guard",
"source": "test_safety_guard_testsafetyguard_test_uninstall_not_blocked_when_migration_completed",
"target": "ir_module_module_fusion_check_uninstall_guard"
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L29",
"weight": 1.0,
"_src": "test_safety_guard_rationale_29",
"_tgt": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"source": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"target": "test_safety_guard_rationale_29",
"confidence_score": 1.0
},
{
"relation": "calls",
"confidence": "INFERRED",
"confidence_score": 0.8,
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/tests/test_safety_guard.py",
"source_location": "L41",
"weight": 1.0,
"_src": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"_tgt": "ir_module_module_fusion_check_uninstall_guard",
"source": "test_safety_guard_testsafetyguard_test_uninstall_blocked_when_migration_pending",
"target": "ir_module_module_fusion_check_uninstall_guard"
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L31",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"_tgt": "ir_module_module_irmodulemodule",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"target": "ir_module_module_irmodulemodule",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L35",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"_tgt": "ir_module_module_fusion_check_uninstall_guard",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"target": "ir_module_module_fusion_check_uninstall_guard",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L1",
"weight": 1.0,
"_src": "ir_module_module_rationale_1",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"target": "ir_module_module_rationale_1",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L13",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_ir_module_module_py",
"target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L64",
"weight": 1.0,
"_src": "ir_module_module_irmodulemodule",
"_tgt": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"source": "ir_module_module_irmodulemodule",
"target": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L76",
"weight": 1.0,
"_src": "ir_module_module_irmodulemodule",
"_tgt": "ir_module_module_irmodulemodule_module_uninstall",
"source": "ir_module_module_irmodulemodule",
"target": "ir_module_module_irmodulemodule_module_uninstall",
"confidence_score": 1.0
},
{
"relation": "calls",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L73",
"weight": 1.0,
"_src": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"_tgt": "ir_module_module_fusion_check_uninstall_guard",
"source": "ir_module_module_fusion_check_uninstall_guard",
"target": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"confidence_score": 1.0
},
{
"relation": "calls",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L83",
"weight": 1.0,
"_src": "ir_module_module_irmodulemodule_module_uninstall",
"_tgt": "ir_module_module_fusion_check_uninstall_guard",
"source": "ir_module_module_fusion_check_uninstall_guard",
"target": "ir_module_module_irmodulemodule_module_uninstall",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L65",
"weight": 1.0,
"_src": "ir_module_module_rationale_65",
"_tgt": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"source": "ir_module_module_irmodulemodule_button_immediate_uninstall",
"target": "ir_module_module_rationale_65",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/ir_module_module.py",
"source_location": "L77",
"weight": 1.0,
"_src": "ir_module_module_rationale_77",
"_tgt": "ir_module_module_irmodulemodule_module_uninstall",
"source": "ir_module_module_irmodulemodule_module_uninstall",
"target": "ir_module_module_rationale_77",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/models/__init__.py",
"source_location": "L1",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py",
"target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_models_init_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L16",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"_tgt": "migration_wizard_fusionmigrationwizard",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"target": "migration_wizard_fusionmigrationwizard",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L37",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"_tgt": "migration_wizard_compute_detected",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"target": "migration_wizard_compute_detected",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L1",
"weight": 1.0,
"_src": "migration_wizard_rationale_1",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_migration_wizard_py",
"target": "migration_wizard_rationale_1",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L26",
"weight": 1.0,
"_src": "migration_wizard_fusionmigrationwizard",
"_tgt": "migration_wizard_fusionmigrationwizard_default_notes",
"source": "migration_wizard_fusionmigrationwizard",
"target": "migration_wizard_fusionmigrationwizard_default_notes",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L46",
"weight": 1.0,
"_src": "migration_wizard_fusionmigrationwizard",
"_tgt": "migration_wizard_fusionmigrationwizard_action_run_migration",
"source": "migration_wizard_fusionmigrationwizard",
"target": "migration_wizard_fusionmigrationwizard_action_run_migration",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/migration_wizard.py",
"source_location": "L47",
"weight": 1.0,
"_src": "migration_wizard_rationale_47",
"_tgt": "migration_wizard_fusionmigrationwizard_action_run_migration",
"source": "migration_wizard_fusionmigrationwizard_action_run_migration",
"target": "migration_wizard_rationale_47",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_migration/wizards/__init__.py",
"source_location": "L1",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py",
"source": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py",
"target": "users_gurpreet_github_odoo_modules_fusion_accounting_migration_wizards_init_py",
"confidence_score": 1.0
}
],
"hyperedges": []
}

View File

@@ -0,0 +1 @@
from . import ir_module_module

View File

@@ -0,0 +1,84 @@
"""Safety guard: blocks Odoo Enterprise accounting uninstall until migration runs.
For each Enterprise accounting module the user attempts to uninstall, the
guard checks an ir.config_parameter flag named:
fusion_accounting.migration.<module_name>.completed
If the flag is False/unset and the module is currently installed, the guard
raises UserError pointing the user to the top-level
Fusion Accounting -> Migrate from Enterprise menu.
The migration wizard sets the flag to True after a successful migration run
for that module.
"""
from odoo import _, api, models
from odoo.exceptions import UserError
GUARDED_MODULES = (
'account_accountant',
'account_reports',
'accountant',
'account_followup',
'account_asset',
'account_budget',
'account_loans',
)
class IrModuleModule(models.Model):
_inherit = "ir.module.module"
@api.model
def _fusion_check_uninstall_guard(self, module_names):
"""Verify it's safe to uninstall the given modules.
Returns True if all checks pass; raises UserError otherwise.
"""
Param = self.env['ir.config_parameter'].sudo()
for name in module_names:
if name not in GUARDED_MODULES:
continue
installed = self.sudo().search_count([
('name', '=', name), ('state', '=', 'installed'),
])
if not installed:
continue
flag_key = f'fusion_accounting.migration.{name}.completed'
if Param.get_param(flag_key, default='False').lower() != 'true':
raise UserError(_(
"Cannot uninstall %s: the Fusion Accounting migration "
"for this module has not run yet. Please open\n"
" Fusion Accounting -> Migrate from Enterprise\n"
"and run the migration before uninstalling. Once the "
"migration has completed, the safety guard will allow "
"uninstall.\n\n"
"If you genuinely want to uninstall WITHOUT migrating "
"(data will be lost), set the parameter %s to True manually.",
name, flag_key,
))
return True
def button_immediate_uninstall(self):
"""Override to invoke the safety guard before allowing uninstall.
Both this and ``module_uninstall`` below can fire in a single UI
uninstall call (button_immediate_uninstall -> module_uninstall). The
guard is a pure read + raise, so re-running it is idempotent: on the
happy path it just re-confirms; on the blocked path the first call
already raised and the second is never reached.
"""
self._fusion_check_uninstall_guard(self.mapped('name'))
return super().button_immediate_uninstall()
def module_uninstall(self):
"""Override the lower-level uninstall path too (CLI / API uninstall).
See ``button_immediate_uninstall`` above -- both overrides may run in
the same UI uninstall; the guard is idempotent so double-invocation
is safe.
"""
self._fusion_check_uninstall_guard(self.mapped('name'))
return super().module_uninstall()

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_fusion_migration_wizard_admin,fusion.migration.wizard admin,model_fusion_migration_wizard,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_fusion_migration_wizard_admin fusion.migration.wizard admin model_fusion_migration_wizard fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -0,0 +1 @@
from . import test_safety_guard

View File

@@ -0,0 +1,42 @@
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestSafetyGuard(TransactionCase):
"""Verify the safety guard blocks Enterprise uninstall when migration hasn't run."""
def test_uninstall_not_blocked_when_migration_completed(self):
"""If the per-module migration flag is set, uninstall is allowed.
Skip if account_accountant isn't installed -- otherwise the guard's
`if not installed: continue` short-circuit would make this test pass
vacuously without exercising the flag-check branch.
"""
Module = self.env['ir.module.module'].sudo()
if not Module.search_count([
('name', '=', 'account_accountant'),
('state', '=', 'installed'),
]):
self.skipTest("account_accountant not installed in this DB")
self.env['ir.config_parameter'].sudo().set_param(
'fusion_accounting.migration.account_accountant.completed', 'True'
)
guard = Module._fusion_check_uninstall_guard(['account_accountant'])
self.assertTrue(guard, "Guard should pass when migration flag is set")
def test_uninstall_blocked_when_migration_pending(self):
"""If account_accountant is installed and migration not run, raise."""
self.env['ir.config_parameter'].sudo().set_param(
'fusion_accounting.migration.account_accountant.completed', 'False'
)
Module = self.env['ir.module.module'].sudo()
installed = Module.search_count([
('name', '=', 'account_accountant'),
('state', '=', 'installed'),
])
if not installed:
self.skipTest("account_accountant not installed in this DB")
with self.assertRaises(UserError) as ctx:
Module._fusion_check_uninstall_guard(['account_accountant'])
self.assertIn('migration', str(ctx.exception).lower())

View File

@@ -0,0 +1 @@
from . import migration_wizard

View File

@@ -0,0 +1,66 @@
"""Migration wizard skeleton.
Per-feature migration logic (account.asset -> fusion.asset, etc.) is added
by each fusion sub-module that replaces an Enterprise feature, by extending
this wizard via _inherit.
Phase 0 ships the wizard with no migrations registered. Phase 1 will add
the bank-rec verification check. Phase 6 will add asset migration, etc.
"""
from odoo import _, api, fields, models
from ..models.ir_module_module import GUARDED_MODULES
class FusionMigrationWizard(models.TransientModel):
_name = "fusion.migration.wizard"
_description = "Migrate from Odoo Enterprise to Fusion Accounting"
enterprise_modules_detected = fields.Char(
compute='_compute_detected',
string="Enterprise Modules Detected",
)
notes = fields.Text(default=lambda self: self._default_notes())
def _default_notes(self):
return _(
"This wizard migrates data from Odoo Enterprise accounting modules "
"to Fusion Accounting tables. Run before uninstalling Enterprise. "
"After a successful run, each migrated module is marked complete "
"and the Enterprise uninstall safety guard will allow uninstall.\n\n"
"Phase 0 of the roadmap ships this wizard as a shell. As Phase 1, "
"Phase 5, Phase 6, etc. ship, each adds its own migration step here."
)
@api.depends_context('uid')
def _compute_detected(self):
Mod = self.env['ir.module.module'].sudo()
installed = Mod.search([
('name', 'in', list(GUARDED_MODULES)),
('state', '=', 'installed'),
])
for w in self:
w.enterprise_modules_detected = ', '.join(installed.mapped('name')) or _("None")
def action_run_migration(self):
"""Stub: Phase 0 has no migrations to run.
Sub-modules extend this method to perform their per-module migration,
then set the corresponding fusion_accounting.migration.<name>.completed
config param to True.
"""
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'info',
'title': _("Nothing to migrate (yet)"),
'message': _(
"Phase 0 ships the migration framework but no per-feature "
"migrations are registered yet. Each fusion sub-module that "
"replaces an Enterprise feature (Phase 1+) will register its "
"own migration step here."
),
},
}

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_migration_wizard_form" model="ir.ui.view">
<field name="name">fusion.migration.wizard.form</field>
<field name="model">fusion.migration.wizard</field>
<field name="arch" type="xml">
<form string="Migrate from Enterprise">
<sheet>
<group>
<field name="enterprise_modules_detected" readonly="1"/>
<field name="notes" readonly="1"/>
</group>
</sheet>
<footer>
<button name="action_run_migration" type="object" string="Run Migration" class="btn-primary"/>
<button special="cancel" string="Close" class="btn-secondary"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_migration_wizard" model="ir.actions.act_window">
<field name="name">Migrate from Enterprise</field>
<field name="res_model">fusion.migration.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--
Migration wizard lives under Accounting > Configuration, and is
ONLY visible while at least one Enterprise accounting module is
still installed. Once the operator has uninstalled Enterprise, the
wizard is hidden \u2014 there's nothing left to migrate.
Visibility is gated by the intersection of:
- group_fusion_accounting_admin (admin-only feature)
- group_fusion_show_when_enterprise_present (computed: members
iff at least one Enterprise accounting module is installed)
-->
<!-- Note: gating uses ONLY group_fusion_show_when_enterprise_present.
Admin-restriction is enforced via the model ACL
(ir.model.access.csv only grants access to group_fusion_accounting_admin).
Odoo `groups=` on menuitems uses OR semantics, so listing both groups
would let any admin see the menu even after Enterprise is uninstalled. -->
<menuitem id="menu_fusion_migration_root"
name="Migrate from Enterprise"
parent="account.menu_finance_configuration"
sequence="95"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_present"/>
<menuitem id="menu_fusion_migration_wizard"
name="Run Migration Wizard"
parent="menu_fusion_migration_root"
action="action_fusion_migration_wizard"
sequence="10"
groups="fusion_accounting_core.group_fusion_show_when_enterprise_present"/>
</odoo>