Mirrors Phase 1 + 2 doc layout. CLAUDE.md captures architecture, the 7-method engine API, persisted models, controllers, OWL frontend, performance baselines (Tasks 23 + 41 numbers), test counts (140), and Phase 3.5 backlog. UPGRADE_NOTES.md anchors the Odoo 19 reference and records V19 deprecations applied. README.md is the user-facing intro. Made-with: Cursor
131 lines
6.1 KiB
Markdown
131 lines
6.1 KiB
Markdown
# fusion_accounting_assets — Cursor / Claude Context
|
|
|
|
## Purpose
|
|
|
|
AI-augmented fixed asset management with depreciation schedules — a
|
|
Fusion-native replacement for (and coexisting with) Odoo Enterprise's
|
|
`account_asset` module. Ships in Phase 3 of the fusion_accounting roadmap.
|
|
|
|
## Architecture
|
|
|
|
Hybrid: the engine (`fusion.asset.engine`, AbstractModel) is the SINGLE
|
|
write surface for the asset lifecycle. Everything else (controllers, OWL
|
|
widget, AI tools, wizards, cron) routes through the engine's 7-method
|
|
public API:
|
|
|
|
- `compute_depreciation_schedule(asset, recompute=False)`
|
|
- `post_depreciation_entry(asset, period_date=None)`
|
|
- `dispose_asset(asset, sale_amount=0, sale_date=None, sale_partner=None, disposal_type='sale')`
|
|
- `partial_sale(asset, sold_amount, sold_qty=None, sale_date=None, sale_partner=None)`
|
|
- `pause_asset(asset, pause_date=None)`
|
|
- `resume_asset(asset, resume_date=None)`
|
|
- `reverse_disposal(asset)`
|
|
|
|
Pure-Python services live in `services/`:
|
|
|
|
- `depreciation_methods` — straight_line, declining_balance, units_of_production
|
|
- `prorate` — first/last-period prorating: full_month, days_365, days_period
|
|
- `salvage_value` — % of cost, fixed amount, zero
|
|
- `anomaly_detection` — variance vs expected schedule, low utilization
|
|
- `useful_life_predictor` + `useful_life_prompt` — LLM-suggested useful life with templated fallback
|
|
|
|
Persisted models in `models/`:
|
|
|
|
- `fusion.asset` — main model, state machine: draft → running → paused → disposed
|
|
- `fusion.asset.depreciation.line` — board lines
|
|
- `fusion.asset.category` — templates
|
|
- `fusion.asset.disposal` — disposal records
|
|
- `fusion.asset.anomaly` — flagged variances
|
|
- `fusion.asset.book.values.mv` — pre-aggregated materialized view
|
|
- `fusion.asset.engine` — AbstractModel (the API)
|
|
- `fusion.assets.cron` — cron handlers (post depreciations, MV refresh, anomaly scan)
|
|
- `account.move.line` (inherits) — adds `fusion_asset_id` linkage
|
|
- `fusion.migration.wizard` (inherits in `models/`) — adds asset backfill step
|
|
|
|
Wizards (TransientModel) in `wizards/`:
|
|
|
|
- `fusion.create.asset.wizard` — assisted creation with AI useful-life suggestion
|
|
- `fusion.disposal.wizard` — full disposal flow
|
|
- `fusion.partial.sale.wizard` — partial-quantity disposal
|
|
- `fusion.depreciation.run.wizard` — period close runner
|
|
|
|
Controller: `controllers/assets_controller.py` exposes 8 JSON-RPC
|
|
endpoints under `/fusion/assets/*` (list, get_detail, compute_schedule,
|
|
post_depreciation, dispose, get_anomalies, suggest_useful_life,
|
|
get_partner_history). All calls route through the engine.
|
|
|
|
OWL frontend: `static/src/`
|
|
|
|
- `services/assets_service.js` — central reactive state + RPC wrappers
|
|
- `views/asset_dashboard/*` — top-level dashboard view
|
|
- `components/asset_card`, `asset_detail_panel`, `depreciation_board`,
|
|
`disposal_dialog`, `ai_useful_life_panel`, `anomaly_strip` — 6 components
|
|
- `scss/_variables.scss` + `assets.scss` + `dark_mode.scss`
|
|
- `tours/assets_tours.js` — 5 OWL tour smoke tests
|
|
|
|
## Coexistence
|
|
|
|
When `account_asset` is installed the Asset Management menu hides via
|
|
`fusion_accounting_core.group_fusion_show_when_enterprise_absent` (a
|
|
computed group). The engine + AI tools remain available for the chat.
|
|
The migration wizard backfills `fusion.asset` from existing
|
|
`account.asset` records (verified live: 2 records, Task 35).
|
|
|
|
## Conventions
|
|
|
|
- **V19 deprecations to avoid:** `_sql_constraints` (use
|
|
`models.Constraint`), `@api.depends('id')` (raises
|
|
`NotImplementedError`), `@route(type='json')` (use `type='jsonrpc'`),
|
|
`numbercall` field on `ir.cron` (removed), `groups_id` on `res.users`
|
|
(use `all_group_ids` for searching), `users` field on `res.groups`
|
|
(use `user_ids`), `groups_id` on `ir.ui.menu` (use `group_ids`).
|
|
- **Materialized view refresh:** `fusion.asset.book.values.mv` is
|
|
refreshed by cron (REFRESH CONCURRENTLY in an autocommit cursor since
|
|
it can't run inside a regular Odoo transaction).
|
|
- **Provider routing:** AI features look up
|
|
`fusion_accounting.provider.asset_useful_life`, falling back to
|
|
`fusion_accounting.provider.default`. When neither is set the
|
|
templated keyword fallback in `useful_life_predictor` keeps the
|
|
feature usable offline.
|
|
|
|
## Performance baseline (Tasks 23 + 41)
|
|
|
|
| Operation | P95 | Budget | Headroom |
|
|
|------------------------------------|-------|----------|----------|
|
|
| `engine.compute_schedule` (10yr SL)| 1ms | 500ms | 500x |
|
|
| `engine.post_depreciation_entry` | <1ms | 300ms | huge |
|
|
| `engine.dispose_asset` | 5ms | 300ms | 60x |
|
|
| `controller.list` (35 assets) | 42ms | 300ms | 7x |
|
|
| `controller.get_detail` | 40ms | 500ms | 12x |
|
|
|
|
All Phase 3 perf metrics are within 1x of budget; no optimization was
|
|
needed at ship (Task 42 skipped per the conditional rule).
|
|
|
|
## Test counts (Phase 3 ship)
|
|
|
|
- 140 logical tests total in fusion_accounting_assets
|
|
- 0 failures, 0 errors
|
|
- Coverage includes: 4 engine benchmarks + 1 controller benchmark
|
|
(tagged `benchmark`), 1 local LLM smoke (tagged `local_llm`, skips
|
|
when no LLM), 5 OWL tour tests (tagged `tour`, skip without
|
|
websocket-client), Hypothesis property tests on the engine,
|
|
integration tests on the public API, controller round-trip tests, MV
|
|
shape tests.
|
|
|
|
## Known concerns / Phase 3.5 backlog
|
|
|
|
- Sub-annual depreciation frequency (currently annual only)
|
|
- Units-of-production assumes even per-period units
|
|
- Disposal journal entry not yet created — `dispose_asset` writes the
|
|
`fusion.asset.disposal` record but not the cash / gain-loss move
|
|
- Multi-currency, allocation rules, and analytic tags for depreciation
|
|
moves are out of scope for Phase 3
|
|
- Partial-sale child asset is created with no own depreciation schedule
|
|
pre-disposal
|
|
- Migration wizard inheritance lives in `models/` rather than
|
|
`wizards/` (small inconsistency with the rest of the wizard layout —
|
|
intentional to keep ORM ordering simple)
|
|
- `useful_life_predictor` always returns a usable dict (templated
|
|
fallback when LLM absent), so callers can't distinguish "AI said so"
|
|
from "fallback fired"; the `confidence` key is the only signal
|