changes
This commit is contained in:
BIN
fusion_accounting/fusion_accounting_core/.DS_Store
vendored
Normal file
BIN
fusion_accounting/fusion_accounting_core/.DS_Store
vendored
Normal file
Binary file not shown.
25
fusion_accounting/fusion_accounting_core/CLAUDE.md
Normal file
25
fusion_accounting/fusion_accounting_core/CLAUDE.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# fusion_accounting_core — Cursor / Claude Context
|
||||
|
||||
## Purpose
|
||||
Foundation for the Fusion Accounting sub-module suite. Owns:
|
||||
- Three security groups (User / Manager / Admin) shared across all sub-modules
|
||||
- Shared-field-ownership declarations on `account.move` and `account.reconcile.model`
|
||||
- Runtime Enterprise-detection helper: `env['ir.module.module']._fusion_is_enterprise_accounting_installed()`
|
||||
|
||||
## What lives here
|
||||
- `models/account_move.py` — declares Enterprise-extension fields with identical
|
||||
schemas / relation tables. Pure schema-preservation; no business logic.
|
||||
- `models/account_reconcile_model.py` — same pattern for `created_automatically`
|
||||
- `models/ir_module_module.py` — Enterprise-detection helpers
|
||||
- `security/fusion_accounting_security.xml` — privilege + 3 groups + auto-assignment
|
||||
|
||||
## Critical rules
|
||||
- NEVER add business logic to the shared-field models (account_move.py here).
|
||||
Logic belongs in the feature sub-module that owns it (e.g. fusion_accounting_bank_rec).
|
||||
- NEVER rename the relation tables for shared M2Ms. They must match Enterprise verbatim
|
||||
for the dual-ownership pattern to work.
|
||||
- Shared fields here have NO defaults beyond what Enterprise sets. The point is preservation.
|
||||
|
||||
## Cross-references
|
||||
- Parent design: `fusion_accounting/docs/superpowers/specs/2026-04-18-fusion-accounting-enterprise-takeover-roadmap-design.md` (Section 3)
|
||||
- Workspace conventions: `/Users/gurpreet/Github/Odoo-Modules/CLAUDE.md`
|
||||
39
fusion_accounting/fusion_accounting_core/README.md
Normal file
39
fusion_accounting/fusion_accounting_core/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Fusion Accounting Core
|
||||
|
||||
Foundation module for the Fusion Accounting suite.
|
||||
|
||||
## What it does
|
||||
|
||||
- Defines three security groups: Fusion Accounting User / Manager / Administrator
|
||||
- Auto-promotes Odoo `account.group_account_user` -> Fusion User and
|
||||
`account.group_account_manager` -> Fusion Admin
|
||||
- Declares schema-preservation fields on `account.move` and `account.reconcile.model`
|
||||
so that Enterprise extension fields (deferred revenue links, signing user, etc.)
|
||||
survive an Enterprise uninstall
|
||||
- Exposes the helper `env['ir.module.module']._fusion_is_enterprise_accounting_installed()`
|
||||
|
||||
## Install
|
||||
|
||||
This module never installs alone. Install `fusion_accounting` (the meta-module)
|
||||
or any of the feature sub-modules — they all depend on `fusion_accounting_core`.
|
||||
|
||||
## Uninstall
|
||||
|
||||
Uninstalling `fusion_accounting_core` will remove the security groups and the
|
||||
schema-preservation fields. If Enterprise is also installed, uninstalling
|
||||
`fusion_accounting_core` will cause Odoo to consider the deferred / signing
|
||||
fields owned only by Enterprise — which is the original Enterprise-only state
|
||||
(no data loss, just back to Enterprise-controlled schema).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If users are missing the "Fusion Accounting" privilege section in user settings
|
||||
after install, the `implied_ids` mechanism only fires for newly-added users.
|
||||
Backfill existing users via SQL:
|
||||
|
||||
INSERT INTO res_groups_users_rel (gid, uid)
|
||||
SELECT g.res_id, gu.uid
|
||||
FROM res_groups_users_rel gu
|
||||
JOIN ir_model_data g ON g.module = 'fusion_accounting_core' AND g.name = 'group_fusion_accounting_user'
|
||||
JOIN ir_model_data ag ON ag.module = 'account' AND ag.name = 'group_account_user' AND gu.gid = ag.res_id
|
||||
ON CONFLICT DO NOTHING;
|
||||
28
fusion_accounting/fusion_accounting_core/UPGRADE_NOTES.md
Normal file
28
fusion_accounting/fusion_accounting_core/UPGRADE_NOTES.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# UPGRADE_NOTES — fusion_accounting_core
|
||||
|
||||
## V19.0.1.0.0 (initial — Phase 0)
|
||||
|
||||
### Reference sources
|
||||
- `RePackaged-Odoo/accounting/account_accountant/models/account_move.py` (Enterprise extension fields read for schema match)
|
||||
- `RePackaged-Odoo/accounting/account_accountant/models/account_reconcile_model.py` (same)
|
||||
|
||||
### Mirror-zone files (none in _core — _core has no Mirror zone)
|
||||
|
||||
### Abstract-zone files (all of _core is abstract)
|
||||
- `models/account_move.py`
|
||||
- `models/account_reconcile_model.py`
|
||||
- `models/ir_module_module.py`
|
||||
|
||||
### Intentional deltas from Odoo
|
||||
- Shared-field declarations have NO compute methods, NO @api decorators beyond
|
||||
basic field types. Enterprise's account_move.py adds compute methods and
|
||||
business logic; we deliberately do not duplicate them. When Enterprise is
|
||||
installed, its compute methods run; when it's not, the fields are simply
|
||||
unused (until a fusion sub-module decides to own that behavior).
|
||||
|
||||
### Migrations
|
||||
- `migrations/19.0.1.0.0/pre-migration.py` — rehome fusion security xml-ids
|
||||
from module='fusion_accounting' to module='fusion_accounting_core' BEFORE
|
||||
data-load (avoids unique-constraint crash on upgrade from pre-Phase-0)
|
||||
- `migrations/19.0.1.0.0/post-migration.py` — idempotent safety-net for the
|
||||
same rehome (zero-op if pre-migration already ran)
|
||||
6
fusion_accounting/fusion_accounting_core/__init__.py
Normal file
6
fusion_accounting/fusion_accounting_core/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from . import models
|
||||
|
||||
|
||||
def post_init_hook(env):
|
||||
"""Initialize coexistence group membership based on current Enterprise install state."""
|
||||
env['res.users']._fusion_recompute_coexistence_group()
|
||||
34
fusion_accounting/fusion_accounting_core/__manifest__.py
Normal file
34
fusion_accounting/fusion_accounting_core/__manifest__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
'name': 'Fusion Accounting Core',
|
||||
'version': '19.0.1.1.0',
|
||||
'category': 'Accounting/Accounting',
|
||||
'sequence': 24,
|
||||
'summary': 'Shared base for the Fusion Accounting sub-module suite (security, shared schema, runtime helpers).',
|
||||
'description': """
|
||||
Fusion Accounting Core
|
||||
======================
|
||||
Foundation for the Fusion Accounting sub-modules. Owns:
|
||||
- Three security groups (User, Manager, Admin) shared across all fusion sub-modules
|
||||
- Shared-field declarations on Community account models so deferred-revenue,
|
||||
signing-user, and similar Enterprise-extension fields survive Enterprise uninstall
|
||||
- Runtime helper for detecting Odoo Enterprise accounting modules
|
||||
|
||||
This module never works alone. Install fusion_accounting (the meta-module)
|
||||
or one of fusion_accounting_ai, fusion_accounting_bank_rec, etc.
|
||||
|
||||
Built by Nexa Systems Inc.
|
||||
""",
|
||||
'author': 'Nexa Systems Inc.',
|
||||
'website': 'https://nexasystems.ca',
|
||||
'support': 'support@nexasystems.ca',
|
||||
'maintainer': 'Nexa Systems Inc.',
|
||||
'depends': ['account', 'mail'],
|
||||
'data': [
|
||||
'security/fusion_accounting_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'license': 'OPL-1',
|
||||
'post_init_hook': 'post_init_hook',
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
# Graph Report - /Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core (2026-04-22)
|
||||
|
||||
## Corpus Check
|
||||
- 15 files · ~4,588 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 66 nodes · 62 edges · 17 communities detected
|
||||
- Extraction: 87% EXTRACTED · 13% INFERRED · 0% AMBIGUOUS · INFERRED: 8 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]]
|
||||
- [[_COMMUNITY_Community 10|Community 10]]
|
||||
- [[_COMMUNITY_Community 11|Community 11]]
|
||||
- [[_COMMUNITY_Community 12|Community 12]]
|
||||
- [[_COMMUNITY_Community 13|Community 13]]
|
||||
- [[_COMMUNITY_Community 14|Community 14]]
|
||||
- [[_COMMUNITY_Community 15|Community 15]]
|
||||
- [[_COMMUNITY_Community 16|Community 16]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `TestSharedFieldOwnership` - 8 edges
|
||||
2. `TestCoexistenceGroup` - 6 edges
|
||||
3. `_fusion_recompute_coexistence_group()` - 6 edges
|
||||
4. `TestEnterpriseDetection` - 5 edges
|
||||
5. `_fusion_is_enterprise_accounting_installed()` - 5 edges
|
||||
6. `TestSharedFieldBankStatementLine` - 4 edges
|
||||
7. `IrModuleModule` - 4 edges
|
||||
8. `post_init_hook()` - 3 edges
|
||||
9. `Initialize coexistence group membership based on current Enterprise install stat` - 1 edges
|
||||
10. `Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.` - 1 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `post_init_hook()` --calls--> `_fusion_recompute_coexistence_group()` [INFERRED]
|
||||
/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py → /Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/res_users.py
|
||||
- `_fusion_recompute_coexistence_group()` --calls--> `_fusion_is_enterprise_accounting_installed()` [INFERRED]
|
||||
/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/res_users.py → /Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/ir_module_module.py
|
||||
|
||||
## Communities
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.18
|
||||
Nodes (6): The recompute helper must be callable on res.users., The 'show when Enterprise absent' group must exist and have computed membership., TestCoexistenceGroup, Verify fusion_accounting_core declares the Enterprise extension fields on ac, TestSharedFieldBankStatementLine, TransactionCase
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.24
|
||||
Nodes (5): IrModuleModule, Recompute the coexistence group after install state changes., Recompute the coexistence group after uninstall state changes. The MRO, Recompute the coexistence group after the lower-level uninstall., _fusion_recompute_coexistence_group()
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.22
|
||||
Nodes (3): The shared M2M relation table must be named identically to Enterprise's, Verify fusion_accounting_core declares the Enterprise extension fields on ac, TestSharedFieldOwnership
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.25
|
||||
Nodes (5): _fusion_is_enterprise_accounting_installed(), A user is in the group iff Enterprise accounting is NOT installed. We c, Helper should return True iff one of the known Enterprise modules is installed., Verify the helper that detects Odoo Enterprise accounting installs., TestEnterpriseDetection
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.67
|
||||
Nodes (1): Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.67
|
||||
Nodes (1): Safety-net reassignment of security xml-ids to fusion_accounting_core. The actu
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): post_init_hook(), Initialize coexistence group membership based on current Enterprise install stat
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): AccountMove, Shared-field-ownership declarations for account.move. Per the roadmap (Section
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): AccountReconcileModel, Shared-field-ownership for account.reconcile.model. Mirrors the single field En
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): Coexistence group membership recomputation., ResUsers
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): AccountBankStatementLine, Shared-field-ownership for account.bank.statement.line. Enterprise's account_ac
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 1.0
|
||||
Nodes (0):
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): True if any Odoo Enterprise accounting module is installed in this DB.
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): True if a specific module is installed.
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): Maintain the two coexistence groups based on Enterprise presence. - ``g
|
||||
|
||||
## Knowledge Gaps
|
||||
- **25 isolated node(s):** `Initialize coexistence group membership based on current Enterprise install stat`, `Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.`, `Safety-net reassignment of security xml-ids to fusion_accounting_core. The actu`, `Verify fusion_accounting_core declares the Enterprise extension fields on ac`, `Verify fusion_accounting_core declares the Enterprise extension fields on ac` (+20 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **Thin community `Community 11`** (1 nodes): `__init__.py`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 12`** (1 nodes): `__init__.py`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 13`** (1 nodes): `__manifest__.py`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 14`** (1 nodes): `True if any Odoo Enterprise accounting module is installed in this DB.`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 15`** (1 nodes): `True if a specific module is installed.`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 16`** (1 nodes): `Maintain the two coexistence groups based on Enterprise presence. - ``g`
|
||||
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_is_enterprise_accounting_installed()` connect `Community 3` to `Community 1`?**
|
||||
_High betweenness centrality (0.232) - this node is a cross-community bridge._
|
||||
- **Why does `_fusion_recompute_coexistence_group()` connect `Community 1` to `Community 9`, `Community 3`, `Community 6`?**
|
||||
_High betweenness centrality (0.200) - this node is a cross-community bridge._
|
||||
- **Why does `TestSharedFieldOwnership` connect `Community 2` to `Community 0`?**
|
||||
_High betweenness centrality (0.151) - this node is a cross-community bridge._
|
||||
- **Are the 5 inferred relationships involving `_fusion_recompute_coexistence_group()` (e.g. with `post_init_hook()` and `.button_immediate_install()`) actually correct?**
|
||||
_`_fusion_recompute_coexistence_group()` has 5 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 4 inferred relationships involving `_fusion_is_enterprise_accounting_installed()` (e.g. with `.test_membership_matches_enterprise_state()` and `.test_helper_returns_bool()`) actually correct?**
|
||||
_`_fusion_is_enterprise_accounting_installed()` has 4 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **What connects `Initialize coexistence group membership based on current Enterprise install stat`, `Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.`, `Safety-net reassignment of security xml-ids to fusion_accounting_core. The actu` to the rest of the system?**
|
||||
_25 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_test_shared_field_bank_statement_py", "label": "test_shared_field_bank_statement.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L1"}, {"id": "test_shared_field_bank_statement_testsharedfieldbankstatementline", "label": "TestSharedFieldBankStatementLine", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L5"}, {"id": "transactioncase", "label": "TransactionCase", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "test_shared_field_bank_statement_testsharedfieldbankstatementline_test_cron_last_check_field_exists", "label": ".test_cron_last_check_field_exists()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L9"}, {"id": "test_shared_field_bank_statement_rationale_6", "label": "Verify fusion_accounting_core declares the Enterprise extension fields on ac", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L6"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_test_shared_field_bank_statement_py", "target": "odoo_tests_common", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L1", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_test_shared_field_bank_statement_py", "target": "test_shared_field_bank_statement_testsharedfieldbankstatementline", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L5", "weight": 1.0}, {"source": "test_shared_field_bank_statement_testsharedfieldbankstatementline", "target": "transactioncase", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L5", "weight": 1.0}, {"source": "test_shared_field_bank_statement_testsharedfieldbankstatementline", "target": "test_shared_field_bank_statement_testsharedfieldbankstatementline_test_cron_last_check_field_exists", "relation": "method", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L9", "weight": 1.0}, {"source": "test_shared_field_bank_statement_rationale_6", "target": "test_shared_field_bank_statement_testsharedfieldbankstatementline", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "test_shared_field_bank_statement_testsharedfieldbankstatementline_test_cron_last_check_field_exists", "callee": "assertIn", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L11"}, {"caller_nid": "test_shared_field_bank_statement_testsharedfieldbankstatementline_test_cron_last_check_field_exists", "callee": "assertEqual", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/test_shared_field_bank_statement.py", "source_location": "L14"}]}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_reconcile_model_py", "label": "account_reconcile_model.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L1"}, {"id": "account_reconcile_model_accountreconcilemodel", "label": "AccountReconcileModel", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L10"}, {"id": "account_reconcile_model_rationale_1", "label": "Shared-field-ownership for account.reconcile.model. Mirrors the single field En", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_reconcile_model_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_reconcile_model_py", "target": "account_reconcile_model_accountreconcilemodel", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L10", "weight": 1.0}, {"source": "account_reconcile_model_rationale_1", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_reconcile_model_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_reconcile_model.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_post_migration_py", "label": "post-migration.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L1"}, {"id": "post_migration_migrate", "label": "migrate()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L31"}, {"id": "post_migration_rationale_1", "label": "Safety-net reassignment of security xml-ids to fusion_accounting_core. The actu", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_post_migration_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L17", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_post_migration_py", "target": "post_migration_migrate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L31", "weight": 1.0}, {"source": "post_migration_rationale_1", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_post_migration_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L1", "weight": 1.0}], "raw_calls": [{"caller_nid": "post_migration_migrate", "callee": "execute", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L32"}, {"caller_nid": "post_migration_migrate", "callee": "list", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L39"}, {"caller_nid": "post_migration_migrate", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/post-migration.py", "source_location": "L42"}]}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_tests_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/tests/__init__.py", "source_location": "L4", "weight": 1.0}], "raw_calls": []}
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/__init__.py", "source_location": "L5", "weight": 1.0}], "raw_calls": []}
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_bank_statement_line_py", "label": "account_bank_statement_line.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L1"}, {"id": "account_bank_statement_line_accountbankstatementline", "label": "AccountBankStatementLine", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L12"}, {"id": "account_bank_statement_line_rationale_1", "label": "Shared-field-ownership for account.bank.statement.line. Enterprise's account_ac", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_bank_statement_line_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L9", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_bank_statement_line_py", "target": "account_bank_statement_line_accountbankstatementline", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L12", "weight": 1.0}, {"source": "account_bank_statement_line_rationale_1", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_bank_statement_line_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_bank_statement_line.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_move_py", "label": "account_move.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L1"}, {"id": "account_move_accountmove", "label": "AccountMove", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L19"}, {"id": "account_move_rationale_1", "label": "Shared-field-ownership declarations for account.move. Per the roadmap (Section", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_move_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L16", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_move_py", "target": "account_move_accountmove", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L19", "weight": 1.0}, {"source": "account_move_rationale_1", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_models_account_move_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/models/account_move.py", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_pre_migration_py", "label": "pre-migration.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L1"}, {"id": "pre_migration_migrate", "label": "migrate()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L45"}, {"id": "pre_migration_rationale_1", "label": "Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_pre_migration_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L31", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_pre_migration_py", "target": "pre_migration_migrate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L45", "weight": 1.0}, {"source": "pre_migration_rationale_1", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_migrations_19_0_1_0_0_pre_migration_py", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L1", "weight": 1.0}], "raw_calls": [{"caller_nid": "pre_migration_migrate", "callee": "execute", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L46"}, {"caller_nid": "pre_migration_migrate", "callee": "list", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L53"}, {"caller_nid": "pre_migration_migrate", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/migrations/19.0.1.0.0/pre-migration.py", "source_location": "L56"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L1"}, {"id": "init_post_init_hook", "label": "post_init_hook()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L4"}, {"id": "init_rationale_5", "label": "Initialize coexistence group membership based on current Enterprise install stat", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L5"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_init_py", "target": "users_gurpreet_github_odoo_modules_fusion_accounting_core_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L1", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_fusion_accounting_core_init_py", "target": "init_post_init_hook", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "init_rationale_5", "target": "init_post_init_hook", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L5", "weight": 1.0}], "raw_calls": [{"caller_nid": "init_post_init_hook", "callee": "_fusion_recompute_coexistence_group", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__init__.py", "source_location": "L6"}]}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_fusion_accounting_core_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/fusion_accounting_core/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
257
fusion_accounting/fusion_accounting_core/graphify-out/graph.html
Normal file
257
fusion_accounting/fusion_accounting_core/graphify-out/graph.html
Normal file
File diff suppressed because one or more lines are too long
1348
fusion_accounting/fusion_accounting_core/graphify-out/graph.json
Normal file
1348
fusion_accounting/fusion_accounting_core/graphify-out/graph.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
"""Safety-net reassignment of security xml-ids to fusion_accounting_core.
|
||||
|
||||
The actual rename lives in pre-migration.py — it MUST run before data-load
|
||||
to avoid creating duplicate res.groups records and hitting the (module,
|
||||
name) unique constraint on ir_model_data. This post-migration is a
|
||||
belt-and-suspenders no-op for the common case: if pre-migration already
|
||||
ran, this UPDATE matches zero rows.
|
||||
|
||||
It also catches a rare edge case: fusion_accounting_ai.post-migration.py
|
||||
runs an identical UPDATE to cover cross-module upgrade ordering, so both
|
||||
modules redundantly ensure the rows land in the right module regardless
|
||||
of which upgrade runs first.
|
||||
|
||||
Idempotent: running it a second time matches zero rows.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
SECURITY_NAMES = (
|
||||
'module_category_fusion_accounting',
|
||||
'res_groups_privilege_fusion_accounting',
|
||||
'group_fusion_accounting_user',
|
||||
'group_fusion_accounting_manager',
|
||||
'group_fusion_accounting_admin',
|
||||
)
|
||||
|
||||
|
||||
def migrate(cr, version):
|
||||
cr.execute(
|
||||
"""
|
||||
UPDATE ir_model_data
|
||||
SET module = 'fusion_accounting_core'
|
||||
WHERE module = 'fusion_accounting'
|
||||
AND name = ANY(%s)
|
||||
""",
|
||||
(list(SECURITY_NAMES),),
|
||||
)
|
||||
moved = cr.rowcount
|
||||
_logger.info(
|
||||
"fusion_accounting_core post-migration: reassigned %d security rows "
|
||||
"from module='fusion_accounting' to module='fusion_accounting_core' "
|
||||
"(usually zero; pre-migration already handled the rename)",
|
||||
moved,
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Rehome the fusion security xml-ids to fusion_accounting_core BEFORE data-load.
|
||||
|
||||
Pre-Phase-0, the three fusion security groups (user, manager, admin), the
|
||||
module category and the privilege all lived in module='fusion_accounting'.
|
||||
Post-Phase-0 (Task 16) they moved into module='fusion_accounting_core'.
|
||||
|
||||
Running this rename in pre-migration (rather than post-migration) is
|
||||
essential: Odoo's XML data-load looks up records by (module, name) in
|
||||
ir_model_data. If the old row still has module='fusion_accounting' when
|
||||
data-load runs, Odoo will not find a match for
|
||||
'fusion_accounting_core.group_fusion_accounting_user' and will CREATE a
|
||||
brand-new res.groups record plus a new ir_model_data row. That leaves the
|
||||
database with two groups per name:
|
||||
|
||||
1. The ORIGINAL group (still tagged module='fusion_accounting') that every
|
||||
existing user is linked to via res_groups_users_rel.
|
||||
2. A FRESH empty group (newly tagged module='fusion_accounting_core').
|
||||
|
||||
The subsequent post-migration UPDATE...SET module='fusion_accounting_core'
|
||||
would then violate the (module, name) unique constraint on ir_model_data
|
||||
and the upgrade transaction would roll back.
|
||||
|
||||
By renaming ir_model_data rows BEFORE data-load, Odoo finds the existing
|
||||
row (now tagged fusion_accounting_core.*), UPDATEs the res.groups record
|
||||
in place with the XML-defined values, and the user-group links are
|
||||
preserved untouched.
|
||||
|
||||
Idempotent: running this a second time matches zero rows.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
SECURITY_NAMES = (
|
||||
'module_category_fusion_accounting',
|
||||
'res_groups_privilege_fusion_accounting',
|
||||
'group_fusion_accounting_user',
|
||||
'group_fusion_accounting_manager',
|
||||
'group_fusion_accounting_admin',
|
||||
)
|
||||
|
||||
|
||||
def migrate(cr, version):
|
||||
cr.execute(
|
||||
"""
|
||||
UPDATE ir_model_data
|
||||
SET module = 'fusion_accounting_core'
|
||||
WHERE module = 'fusion_accounting'
|
||||
AND name = ANY(%s)
|
||||
""",
|
||||
(list(SECURITY_NAMES),),
|
||||
)
|
||||
moved = cr.rowcount
|
||||
_logger.info(
|
||||
"fusion_accounting_core pre-migration: renamed %d security rows "
|
||||
"from module='fusion_accounting' to module='fusion_accounting_core' "
|
||||
"before data-load (idempotent; non-zero only on first upgrade from "
|
||||
"pre-Phase-0)",
|
||||
moved,
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
from . import ir_module_module
|
||||
from . import res_users
|
||||
from . import account_move
|
||||
from . import account_reconcile_model
|
||||
from . import account_bank_statement_line
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Shared-field-ownership for account.bank.statement.line.
|
||||
|
||||
Enterprise's account_accountant adds cron_last_check (timestamp of the last
|
||||
auto-reconcile cron run for the line). By declaring it here with the same
|
||||
schema, fusion_accounting_core becomes a co-owner so the column persists
|
||||
when account_accountant uninstalls.
|
||||
"""
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountBankStatementLine(models.Model):
|
||||
_inherit = "account.bank.statement.line"
|
||||
|
||||
cron_last_check = fields.Datetime(copy=False)
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Shared-field-ownership declarations for account.move.
|
||||
|
||||
Per the roadmap (Section 3.3), these fields exist in Odoo Enterprise's
|
||||
account_accountant module. By declaring them here with identical schemas
|
||||
and identical relation tables, fusion_accounting_core becomes a co-owner.
|
||||
When Enterprise uninstalls, Odoo's module registry sees fusion still owns
|
||||
the fields and preserves the columns / relation tables, so the data
|
||||
(deferred revenue links, signing user, etc.) survives uninstall.
|
||||
|
||||
The fields here have NO compute methods, NO defaults beyond what Enterprise
|
||||
provides, NO views. They're pure schema-preservation declarations. Any
|
||||
business logic that operates on these fields lives in Enterprise (when
|
||||
present) or in a future fusion sub-module that opts to own that behavior.
|
||||
"""
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
deferred_move_ids = fields.Many2many(
|
||||
comodel_name='account.move',
|
||||
relation='account_move_deferred_rel',
|
||||
column1='original_move_id',
|
||||
column2='deferred_move_id',
|
||||
copy=False,
|
||||
string="Deferred Entries",
|
||||
)
|
||||
deferred_original_move_ids = fields.Many2many(
|
||||
comodel_name='account.move',
|
||||
relation='account_move_deferred_rel',
|
||||
column1='deferred_move_id',
|
||||
column2='original_move_id',
|
||||
copy=False,
|
||||
string="Original Invoices",
|
||||
)
|
||||
deferred_entry_type = fields.Selection(
|
||||
selection=[
|
||||
('expense', 'Deferred Expense'),
|
||||
('revenue', 'Deferred Revenue'),
|
||||
],
|
||||
copy=False,
|
||||
string="Deferred Entry Type",
|
||||
)
|
||||
|
||||
signing_user = fields.Many2one(
|
||||
comodel_name='res.users',
|
||||
copy=False,
|
||||
string="Signing User",
|
||||
)
|
||||
|
||||
payment_state_before_switch = fields.Char(
|
||||
copy=False,
|
||||
string="Payment State Before Switch",
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Shared-field-ownership for account.reconcile.model.
|
||||
|
||||
Mirrors the single field Enterprise's account_accountant adds to the
|
||||
Community account.reconcile.model: created_automatically.
|
||||
"""
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountReconcileModel(models.Model):
|
||||
_inherit = "account.reconcile.model"
|
||||
|
||||
created_automatically = fields.Boolean(
|
||||
default=False,
|
||||
copy=False,
|
||||
string="Created Automatically",
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
# Modules considered "Odoo Enterprise accounting" for the purpose of feature gating.
|
||||
# A client is "on Enterprise" if any of these are installed; fusion_accounting_*
|
||||
# replacement modules will hide their menus when Enterprise is present (replace mode
|
||||
# vs. augment mode is configurable in Settings).
|
||||
ENTERPRISE_ACCOUNTING_MODULES = (
|
||||
'account_accountant',
|
||||
'account_reports',
|
||||
'accountant',
|
||||
)
|
||||
|
||||
|
||||
class IrModuleModule(models.Model):
|
||||
_inherit = "ir.module.module"
|
||||
|
||||
@api.model
|
||||
def _fusion_is_enterprise_accounting_installed(self):
|
||||
"""True if any Odoo Enterprise accounting module is installed in this DB."""
|
||||
return bool(self.sudo().search_count([
|
||||
('name', 'in', list(ENTERPRISE_ACCOUNTING_MODULES)),
|
||||
('state', '=', 'installed'),
|
||||
]))
|
||||
|
||||
@api.model
|
||||
def _fusion_is_module_installed(self, module_name):
|
||||
"""True if a specific module is installed."""
|
||||
return bool(self.sudo().search_count([
|
||||
('name', '=', module_name),
|
||||
('state', '=', 'installed'),
|
||||
]))
|
||||
|
||||
def button_immediate_install(self):
|
||||
"""Recompute the coexistence group after install state changes."""
|
||||
result = super().button_immediate_install()
|
||||
self.env['res.users']._fusion_recompute_coexistence_group()
|
||||
return result
|
||||
|
||||
def button_immediate_uninstall(self):
|
||||
"""Recompute the coexistence group after uninstall state changes.
|
||||
|
||||
The MRO chains into fusion_accounting_migration's override (which runs
|
||||
the safety guard before calling super); we recompute only after the
|
||||
whole chain completes.
|
||||
"""
|
||||
result = super().button_immediate_uninstall()
|
||||
self.env['res.users']._fusion_recompute_coexistence_group()
|
||||
return result
|
||||
|
||||
def module_uninstall(self):
|
||||
"""Recompute the coexistence group after the lower-level uninstall."""
|
||||
result = super().module_uninstall()
|
||||
self.env['res.users']._fusion_recompute_coexistence_group()
|
||||
return result
|
||||
53
fusion_accounting/fusion_accounting_core/models/res_users.py
Normal file
53
fusion_accounting/fusion_accounting_core/models/res_users.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""Coexistence group membership recomputation."""
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = "res.users"
|
||||
|
||||
@api.model
|
||||
def _fusion_recompute_coexistence_group(self):
|
||||
"""Maintain the two coexistence groups based on Enterprise presence.
|
||||
|
||||
- ``group_fusion_show_when_enterprise_absent``: members = all internal
|
||||
users when NO Enterprise accounting module is installed. Used to
|
||||
unhide Fusion menus that would conflict with Enterprise UIs.
|
||||
|
||||
- ``group_fusion_show_when_enterprise_present``: members = all internal
|
||||
users when AT LEAST ONE Enterprise accounting module IS installed.
|
||||
Used to hide migration/transitional UIs once Enterprise has been
|
||||
uninstalled (so the user doesn't see "Migrate from Enterprise" with
|
||||
nothing to migrate).
|
||||
|
||||
The two groups are mutually exclusive at any moment in time, but a
|
||||
user can transition between them as Enterprise modules are installed
|
||||
or uninstalled. Idempotent; safe to call multiple times.
|
||||
|
||||
Called from ir.module.module.button_immediate_install / uninstall
|
||||
overrides.
|
||||
"""
|
||||
absent_group = self.env.ref(
|
||||
'fusion_accounting_core.group_fusion_show_when_enterprise_absent',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
present_group = self.env.ref(
|
||||
'fusion_accounting_core.group_fusion_show_when_enterprise_present',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
if not absent_group and not present_group:
|
||||
return
|
||||
|
||||
enterprise_installed = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed()
|
||||
all_internal = self.sudo().search([('share', '=', False)])
|
||||
|
||||
if enterprise_installed:
|
||||
if absent_group:
|
||||
absent_group.sudo().write({'user_ids': [(5, 0, 0)]})
|
||||
if present_group:
|
||||
present_group.sudo().write({'user_ids': [(6, 0, all_internal.ids)]})
|
||||
else:
|
||||
if absent_group:
|
||||
absent_group.sudo().write({'user_ids': [(6, 0, all_internal.ids)]})
|
||||
if present_group:
|
||||
present_group.sudo().write({'user_ids': [(5, 0, 0)]})
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Module Category -->
|
||||
<record id="module_category_fusion_accounting" model="ir.module.category">
|
||||
<field name="name">Fusion Accounting</field>
|
||||
<field name="sequence">25</field>
|
||||
</record>
|
||||
|
||||
<!-- Groups Privilege -->
|
||||
<record id="res_groups_privilege_fusion_accounting" model="res.groups.privilege">
|
||||
<field name="name">Fusion Accounting</field>
|
||||
<field name="category_id" ref="module_category_fusion_accounting"/>
|
||||
</record>
|
||||
|
||||
<!-- User Group (Staff) -->
|
||||
<record id="group_fusion_accounting_user" model="res.groups">
|
||||
<field name="name">User</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="implied_ids" eval="[(4, ref('account.group_account_user'))]"/>
|
||||
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
|
||||
</record>
|
||||
|
||||
<!-- Manager Group -->
|
||||
<record id="group_fusion_accounting_manager" model="res.groups">
|
||||
<field name="name">Manager</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
|
||||
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
|
||||
</record>
|
||||
|
||||
<!-- Admin Group -->
|
||||
<record id="group_fusion_accounting_admin" model="res.groups">
|
||||
<field name="name">Administrator</field>
|
||||
<field name="sequence">30</field>
|
||||
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_manager'))]"/>
|
||||
<field name="privilege_id" ref="res_groups_privilege_fusion_accounting"/>
|
||||
</record>
|
||||
|
||||
<!-- Auto-assign: Accounting users get Fusion User; Advisers get Admin -->
|
||||
<record id="account.group_account_user" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_user'))]"/>
|
||||
</record>
|
||||
<record id="account.group_account_manager" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('group_fusion_accounting_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Phase 1: dynamic coexistence group -->
|
||||
<record id="group_fusion_show_when_enterprise_absent" model="res.groups">
|
||||
<field name="name">Fusion: Show menus when Enterprise absent</field>
|
||||
<field name="comment">Computed group. Membership: all internal users when no Enterprise accounting module is installed. Used to hide fusion sub-module menus that would conflict with Enterprise UIs.</field>
|
||||
</record>
|
||||
|
||||
<!-- Phase 8: inverse coexistence group \u2014 visible only when Enterprise IS present.
|
||||
Used to hide migration/transitional UIs once the migration is complete and
|
||||
Enterprise has been uninstalled. -->
|
||||
<record id="group_fusion_show_when_enterprise_present" model="res.groups">
|
||||
<field name="name">Fusion: Show menus when Enterprise present</field>
|
||||
<field name="comment">Computed group. Membership: all internal users WHEN at least one Enterprise accounting module is installed. Used to hide migration/transitional UIs that are irrelevant once Enterprise has been uninstalled.</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,4 @@
|
||||
from . import test_enterprise_detection
|
||||
from . import test_shared_field_ownership
|
||||
from . import test_shared_field_bank_statement
|
||||
from . import test_coexistence_group
|
||||
@@ -0,0 +1,46 @@
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestCoexistenceGroup(TransactionCase):
|
||||
"""The 'show when Enterprise absent' group must exist and have computed membership."""
|
||||
|
||||
def test_group_exists(self):
|
||||
group = self.env.ref(
|
||||
'fusion_accounting_core.group_fusion_show_when_enterprise_absent',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
self.assertTrue(group, "Coexistence group must exist")
|
||||
|
||||
def test_membership_matches_enterprise_state(self):
|
||||
"""A user is in the group iff Enterprise accounting is NOT installed.
|
||||
|
||||
We can't toggle Enterprise mid-test, so just assert the current state
|
||||
matches: if Enterprise is installed, group should have 0 members; if
|
||||
not, the group should include all internal users.
|
||||
"""
|
||||
group = self.env.ref(
|
||||
'fusion_accounting_core.group_fusion_show_when_enterprise_absent'
|
||||
)
|
||||
enterprise_installed = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed()
|
||||
all_internal = self.env['res.users'].sudo().search([('share', '=', False)])
|
||||
if enterprise_installed:
|
||||
self.assertEqual(
|
||||
len(group.user_ids), 0,
|
||||
"Enterprise installed -> coexistence group should be empty",
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
set(group.user_ids.ids), set(all_internal.ids),
|
||||
"Enterprise absent -> coexistence group should contain all internal users",
|
||||
)
|
||||
|
||||
def test_recompute_method_exists(self):
|
||||
"""The recompute helper must be callable on res.users."""
|
||||
self.assertTrue(
|
||||
callable(getattr(
|
||||
self.env['res.users'],
|
||||
'_fusion_recompute_coexistence_group',
|
||||
None,
|
||||
))
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestEnterpriseDetection(TransactionCase):
|
||||
"""Verify the helper that detects Odoo Enterprise accounting installs."""
|
||||
|
||||
def test_helper_returns_bool(self):
|
||||
result = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed()
|
||||
self.assertIsInstance(result, bool)
|
||||
|
||||
def test_helper_matches_actual_state(self):
|
||||
"""Helper should return True iff one of the known Enterprise modules is installed."""
|
||||
installed = self.env['ir.module.module'].sudo().search_count([
|
||||
('name', 'in', ['account_accountant', 'account_reports', 'accountant']),
|
||||
('state', '=', 'installed'),
|
||||
])
|
||||
expected = bool(installed)
|
||||
actual = self.env['ir.module.module']._fusion_is_enterprise_accounting_installed()
|
||||
self.assertEqual(actual, expected)
|
||||
@@ -0,0 +1,14 @@
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestSharedFieldBankStatementLine(TransactionCase):
|
||||
"""Verify fusion_accounting_core declares the Enterprise extension fields
|
||||
on account.bank.statement.line so they survive Enterprise uninstall."""
|
||||
|
||||
def test_cron_last_check_field_exists(self):
|
||||
Line = self.env['account.bank.statement.line']
|
||||
self.assertIn('cron_last_check', Line._fields,
|
||||
"cron_last_check must be declared on account.bank.statement.line "
|
||||
"(shared-field-ownership with account_accountant)")
|
||||
self.assertEqual(Line._fields['cron_last_check'].type, 'datetime')
|
||||
@@ -0,0 +1,32 @@
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestSharedFieldOwnership(TransactionCase):
|
||||
"""Verify fusion_accounting_core declares the Enterprise extension fields
|
||||
on account.move and account.reconcile.model, so they survive Enterprise uninstall."""
|
||||
|
||||
def test_account_move_deferred_fields_exist(self):
|
||||
Move = self.env['account.move']
|
||||
for fname in ('deferred_move_ids', 'deferred_original_move_ids', 'deferred_entry_type'):
|
||||
self.assertIn(fname, Move._fields, f"{fname!r} must exist on account.move")
|
||||
|
||||
def test_account_move_signing_user_exists(self):
|
||||
Move = self.env['account.move']
|
||||
self.assertIn('signing_user', Move._fields)
|
||||
|
||||
def test_account_move_payment_state_before_switch_exists(self):
|
||||
Move = self.env['account.move']
|
||||
self.assertIn('payment_state_before_switch', Move._fields)
|
||||
|
||||
def test_account_reconcile_model_created_automatically_exists(self):
|
||||
Model = self.env['account.reconcile.model']
|
||||
self.assertIn('created_automatically', Model._fields)
|
||||
|
||||
def test_deferred_relation_table_name_matches_enterprise(self):
|
||||
"""The shared M2M relation table must be named identically to Enterprise's
|
||||
so dual ownership works (Enterprise drops field => fusion preserves table)."""
|
||||
f = self.env['account.move']._fields['deferred_move_ids']
|
||||
self.assertEqual(f.relation, 'account_move_deferred_rel')
|
||||
self.assertEqual(f.column1, 'original_move_id')
|
||||
self.assertEqual(f.column2, 'deferred_move_id')
|
||||
Reference in New Issue
Block a user