Tagged 'local_llm'. Auto-detects LM Studio (:1234) or Ollama (:11434)
via host.docker.internal or localhost. When running, configures the
provider params and runs engine.suggest_matches end-to-end. Skips
gracefully when no local LLM is present (CI / dev VM mode).
Made-with: Cursor
Tours: smoke (header loads), select_line, accept_suggestion (skipped
in CI without AI config), auto_reconcile_wizard, load_more. Each
tour scripts a typical user interaction; the Python wrappers run them
via HttpCase.start_tour. Tagged 'tour' so they can be excluded from
fast unit-test runs and selected when full browser infra is available.
Made-with: Cursor
Verifies that the coexistence group recompute method works as expected
in both Enterprise-present and Enterprise-absent scenarios, and that
the bank-rec menu is gated by the group while the engine itself is
always available.
Made-with: Cursor
Menu visible only when fusion_accounting_core.group_fusion_show_when_enterprise_absent
is set (Enterprise's account_accountant not installed). Opens the OWL
bank-rec kanban widget at the unreconciled-lines view.
Made-with: Cursor
Verifies the bank_rec_bootstrap migration step (a) creates precedents
from existing partial.reconcile rows, (b) is idempotent on re-run, and
(c) refreshes the MV without erroring.
Three TransactionCase tests:
- test_bootstrap_creates_precedents_from_existing_reconciles seeds two
reconciles via the engine, wipes the auto-recorded precedents, then
asserts the bootstrap produces source='backfill' precedents.
- test_bootstrap_step_idempotent runs the bootstrap twice and asserts
the second pass creates zero new precedents.
- test_bootstrap_refreshes_mv_without_error runs the bootstrap on a
clean partner and asserts no exception is raised and the result dict
reports MV + pattern refresh outcomes.
Implementation fixes uncovered by these tests:
- precedent_backfill.backfill_precedents now pre-filters
account.partial.reconcile to rows that touch a bank statement line on
either side. Previously it walked every partial in the DB; on the
westin-v19 dev DB that's 16k rows and the default limit=10000 missed
the newest test fixtures (highest IDs).
- backfill skips the periodic env.cr.commit() when running under a
TestCursor, since committing inside a test breaks the rollback.
Test count: 139 -> 142.
Made-with: Cursor
QWeb PDF showing per-company: backfilled precedent count, pattern count,
remaining unreconciled bank line count. Bound to fusion.migration.wizard
so it appears in the Print menu after migration runs.
- reports/migration_audit_report.py defines the AbstractModel
report.fusion_accounting_bank_rec.migration_audit_template, which
aggregates per-company counts from fusion.reconcile.precedent
(source='backfill'), fusion.reconcile.pattern, and
account.bank.statement.line (is_reconciled=False).
- reports/migration_audit_report_views.xml is the QWeb template.
- reports/migration_audit_report_action.xml registers the
ir.actions.report bound to fusion.migration.wizard.
Made-with: Cursor
Adds bank_rec_bootstrap step that backfills fusion.reconcile.precedent
from existing account.partial.reconcile rows during migration. This
gives the AI memory from past Enterprise reconciles. Also triggers
pattern refresh + MV refresh for immediate UI readiness.
- New service services/precedent_backfill.py walks
account.partial.reconcile rows, identifies the bank-statement-line
side, and creates a precedent per qualifying partial. Idempotent via
(statement_line, account, amount, source='backfill') signature.
- New model models/fusion_migration_wizard.py inherits
fusion.migration.wizard, exposes _bank_rec_bootstrap_step() (callable
from tests/audit), and overrides action_run_migration() to call
super() + the bootstrap.
- Adds 'backfill' to fusion.reconcile.precedent.source selection.
- Adds fusion_accounting_migration to depends.
Made-with: Cursor
TransientModel + view + binding action so users can select bank lines
from any list view and bulk-apply either engine.reconcile_batch or
a chosen reconcile model.
Made-with: Cursor
TransientModel that filters unreconciled bank lines by journal +
date range + strategy and runs engine.reconcile_batch. Shows
reconciled_count / skipped_count / error_summary in result view.
Made-with: Cursor
attachment_strip renders inline mimetype-aware chips linking to /web/content
downloads. partner_history_panel calls bank_reconciliation.getPartnerHistory
to surface the learned reconcile pattern (preferred strategy, typical cadence)
plus the most recent reconciles per partner — context Enterprise's bank-rec
widget cannot show because it has no behavioural-learning layer.
Made-with: Cursor
batch_action_bar exposes bulk Suggest-for-selected and Auto-reconcile-selected
toolbar driven by selectedIds prop and the bank_reconciliation service.
reconcile_model_picker is a quick-pick dropdown over account.reconcile.model
records (rule_type=writeoff_button) including the Fusion AI confidence
threshold; apply path is a state-only stub pending Task 38's dedicated endpoint.
Made-with: Cursor
ai_suggestion_strip (inline confidence badge + accept), ai_alternatives_panel
(expandable other-options), ai_reasoning_tooltip (score breakdown). These
go beyond Enterprise's bank_rec_widget which has no AI suggestions.
Made-with: Cursor
Mirrors 3 OWL components from account_accountant for Phase 1
structural parity:
- quick_create/ (BankRecQuickCreate + BankRecQuickCreateController
for inline missing-record creation)
- chatter/ (BankRecChatter — extends @mail Chatter with a
reloadParentView hook for the bound statement line)
- file_uploader/ (BankRecFileUploader — extends @account
DocumentFileUploader to inject statement_line_id into the
upload context, targeting account.bank.statement.line)
Renames applied per spec; CSS class
`o_bank_reconciliation_quick_create` ->
`o_fusion_bank_reconciliation_quick_create`.
Manifest version bumped to 19.0.1.0.15.
Module upgrade succeeds, 134 logical tests still pass — completing
the Phase 1 OWL component mirror (Tasks 30-33). All 14 components
across 4 batches are now bundled.
Made-with: Cursor
Mirrors 5 OWL components from account_accountant for Phase 1
structural parity:
- button/ (single action button)
- button_list/ (toolbar of buttons + dropdown + hotkeys)
- line_to_reconcile/ (editable matched-line editor)
- list_view/ (list view + many2one multi-edit field)
- apply_amount/ (amount application html field)
Renames applied per spec (template names, module IDs, CSS classes).
Notes / deferred to fusion-only Tasks 34-36:
- list_view extends @web ListController instead of Enterprise's
AttachmentPreviewListController; setSelectedRecord is a no-op
pending the previewer pane mirror.
- View/field registry IDs prefixed with `fusion_` to coexist with
Enterprise's account_accountant when both modules are installed
(`fusion_bank_rec_list`, `fusion_bank_rec_dialog_list`,
`fusion_apply_amount_html`, `fusion_bank_rec_list_many2one_multi_id`,
`fusion_bankrec_edit_line`).
- button_list still references Enterprise view_refs in dialog
contexts (`account_accountant.view_account_list_bank_rec_widget`
etc.) for parity; the `set_*` ORM methods on
account.bank.statement.line are Enterprise-only too. These call
sites only fire when the mirrored components are actually
rendered, which Phase 1 does not exercise.
Manifest version bumped to 19.0.1.0.13.
Module upgrade succeeds, 134 logical tests still pass.
Made-with: Cursor
Top-level OWL component (BankRecKanbanController) hosts the bank
reconciliation widget. Reads journal_id + company_id from action context,
initializes the fusion_bank_reconciliation service, and renders the
layout: header (stats), left column (line cards via BankRecLineCard
renderer), right column (detail panel with AI suggestions).
Custom view type 'fusion_bank_rec_kanban' registered so window actions
can use <field name="view_mode">fusion_bank_rec_kanban</field>.
Made-with: Cursor
Optimistic remove was decrementing unreconciledCount before assigning
the authoritative server count, leading to off-by-one. Order swapped:
remove first, then overwrite with server count.
Caught by Task 28 subagent self-review.
Made-with: Cursor
Central data layer + reactive state for the OWL widget. Wraps the 10
JSON-RPC endpoints from the bank_rec_controller (get_state,
list_unreconciled, get_line_detail, suggest_matches, accept_suggestion,
reconcile_manual, unreconcile, write_off, bulk_reconcile,
get_partner_history). Components inject via useService("fusion_bank_reconciliation").
State held in OWL's reactive() so components auto-rerender on
selection / pagination / reconcile-success changes.
Verified: web.assets_backend bundle includes
/fusion_accounting_bank_rec/static/src/services/bank_reconciliation_service.js;
134/134 module tests pass.
Made-with: Cursor
Provides design tokens (variables.scss), main bank-rec stylesheet,
AI suggestion strip + alternatives panel styling, and dark mode
overrides. CSS classes (.o_fusion_*) will be consumed by OWL components
in Tasks 28-36.
Verified: all 4 SCSS files compile via libsass; web.assets_backend
bundle picks up all 4 entries; 134/134 module tests pass.
Made-with: Cursor
All endpoints route through fusion.reconcile.engine via BankRecAdapter
(or directly for engine methods adapter doesn't expose). Uses V19's
type='jsonrpc' (replacement for deprecated type='json'). Auth=user.
Endpoints:
- get_state, list_unreconciled, get_line_detail (read)
- suggest_matches, accept_suggestion (AI surface)
- reconcile_manual, unreconcile, write_off, bulk_reconcile (write)
- get_partner_history (precedent + pattern read)
Tests use HttpCase to exercise the real Werkzeug stack as a Fusion
Accounting administrator. Includes a smoke test for the deferred
write-off path (Task 12) and a negative test confirming auth='user'
rejects anonymous requests. Helper _make_pair shares one bank journal
across pairs to avoid the (code, company) unique-constraint collision
that the default factory would hit on repeat calls.
Verified: 11/11 controller tests pass, 134/134 module tests pass.
Made-with: Cursor
- cron_suggest (every 30min): warm AI suggestions for unreconciled lines
that don't have a recent pending one
- cron_pattern_refresh (daily 02:00): recompute fusion.reconcile.pattern
for each (company, partner) pair with precedents
- cron_mv_refresh (every 5min): REFRESH MATERIALIZED VIEW CONCURRENTLY
using a dedicated autocommit cursor (REFRESH CONCURRENTLY can't run
inside a regular Odoo transaction)
V19 note: ir.cron dropped the numbercall field, so the data XML omits
it (cron now repeats indefinitely as long as active=True).
Tests: 5 new TestFusionBankRecCron tests pass; full module suite is
0 failed / 0 errors of 123 logical tests on westin-v19.
Made-with: Cursor
Three issues surfaced when running the MV smoke tests against westin-v19:
1. account_bank_statement_line has no `date` column in V19 — `date` is a
related field flowing through move_id -> account_move.date. The MV
now JOINs account_move and selects am.date.
2. is_reconciled is nullable; replace `= FALSE` with `IS NOT TRUE` so
nulls (genuinely unreconciled lines that haven't had the compute run
yet) are still included.
3. _refresh() now flushes the ORM cache (env.flush_all()) before the
REFRESH so computed-stored fields like is_reconciled are written to
the DB before the materialization snapshot reads them. Previously the
reconcile-then-refresh path saw the pre-reconcile column value.
4. _trigger_mv_refresh() (suggestion create/write hook) now uses
concurrently=False because Postgres forbids
REFRESH MATERIALIZED VIEW CONCURRENTLY inside a transaction block,
and Odoo's per-request cursor is always inside one. The cron path
(Task 25) will open an autocommit cursor for CONCURRENTLY refreshes.
5. Tests dropped the env.cr.commit() pattern: Postgres always shows a
transaction its own writes, so a non-CONCURRENTLY refresh in the
same txn picks up freshly-inserted rows. Cleaner + works inside
TransactionCase, which forbids cr.commit().
Verified: 4 new MV tests pass, 0 failures across 118 logical tests
(178 with parametrized property-based runs) of fusion_accounting_bank_rec
on westin-v19.
Made-with: Cursor
CREATE MATERIALIZED VIEW fusion_unreconciled_bank_line_mv pre-computes
the data the kanban widget needs (top suggestion, confidence band,
attachment count, partner reconcile hint) so that listing 50-100 lines
is one indexed query instead of N+1.
Refresh strategy:
- Triggered on fusion.reconcile.suggestion create/write (best-effort,
never poisons the originating transaction)
- Cron (every 5 min) — added in Task 25
The MV is created in the model's init() (Odoo calls this on
install/upgrade). The SQL DDL is idempotent
(CREATE MATERIALIZED VIEW IF NOT EXISTS / CREATE INDEX IF NOT EXISTS)
and includes a UNIQUE(id) index so REFRESH MATERIALIZED VIEW
CONCURRENTLY is supported. _refresh() falls back to a blocking refresh
on the first call after creation.
Made-with: Cursor
Per-step audit caught real enforcement bugs across all 9 WO kinds in
the recipe (Masking, Racking, Plating, De-Masking, Oven baking, etc.).
Five gates added or fixed; 0 CRITICAL gaps remain after a verification
run on a fresh MO.
**1. Bake-WO finish gate** (`_fp_check_required_fields_before_finish`)
button_finish on a bake WO now blocks unless:
• x_fc_bake_temp set (Nadcap req — actual setpoint, not just oven)
• x_fc_bake_duration_hours set (actual run time at temp)
• x_fc_oven_id.chart_recorder_ref set (so the chart for THIS run
can be retrieved by an auditor — required for AS9100/Nadcap)
Run-time data lives at FINISH, not START — operators don't know
temp/duration until the bake is done.
**2. Rack-WO start gate** added to the existing button_start gate.
Per-rack life tracking + which physical fixture handled the parts.
**3. Classifier priority fix** (`_fp_classify_kind`)
"Post-plate Inspection" was matching the `plat` wet keyword and
getting kind=wet (then required to have bath/tank). Reordered:
1. Explicit equipment links (bath_id/oven_id)
2. Specific keywords (inspect → mask → bake → rack)
— bake before rack so "Oven bake (Post de-rack)" → bake
3. Workcenter wet families
4. Wet name keywords as last fallback
**4. Auto-populate target_thickness + dwell_time** at recipe→WO
generation. Plating WOs inherit:
• thickness_target from coating_config.thickness_max
• thickness_uom from coating_config.thickness_uom
• dwell_time_minutes from recipe node's estimated_duration
So aerospace QC has the spec target on every WO without paper.
**5. Mask-WO start gate + masking_material field**
New x_fc_masking_material Selection (tape/plug/paint/silicone/wax/
mixed/other). Required to start a mask WO. Needed later when
stripping or replating because each material requires a different
removal process.
**View** (`mrp_workorder_views.xml`)
Process Details tab now branches by kind:
wet → Bath/Tank/Rack/Thickness/Dwell
bake → Oven/Temp/Duration
rack → Rack/Fixture
mask → Masking Material
inspect/other → informational alerts only
WO Kind shows as colour-coded badge in header.
**Backfill** (`scripts/fp_backfill.py`)
Idempotent script that catches up existing data:
• chart_recorder_ref on every oven
• rack_id on existing rack/de-rack WOs (91 backfilled)
• bake_temp + bake_duration_hours on existing bake WOs (33)
• masking_material on existing mask WOs (62)
• thickness/dwell on existing plating WOs (38)
• Cleared 7 legacy bath/tank from inspection WOs that had been
misclassified by the OLD wet-keyword classifier.
**Per-step audit** (`scripts/fp_per_step_audit.py`)
Walks every WO of the most recent done MO and reports per-kind
which compliance fields are filled vs missing. Re-runnable to
catch regressions.
**Final state on freshly-run MO 00049:**
• 0 CRITICAL gaps
• 2 IMPORTANT gaps (dwell_time + rack_id on E-Nickel Plating —
both inherited from recipe node data, not enforcement bugs)
Negative tests still passing (12 total).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When fusion_accounting_bank_rec is installed, match_bank_line_to_payments
and auto_reconcile_bank_lines now use fusion.reconcile.engine via the
BankRecAdapter, gaining precedent recording, AI suggestion superseding,
and shared validation. Legacy paths preserved for Enterprise/Community-
only installs (engine model absent -> fall back to set_line_bank_statement_line
and _try_auto_reconcile_statement_lines).
Also wraps engine.reconcile_batch's per-line loop in a savepoint so a
single bad line's DB error (e.g. check-constraint violation) no longer
poisons the whole batch transaction; the existing per-line try/except
now isolates failures as originally intended.
Made-with: Cursor
Adds fusion_suggest_matches, fusion_accept_suggestion,
fusion_reconcile_bank_line, fusion_unreconcile, and
fusion_get_pending_suggestions. All route through the BankRecAdapter
(or direct engine for ones the adapter doesn't expose), giving the AI
chat the same reconciliation surface a human operator gets in the OWL UI.
Made-with: Cursor
Enhances list_unreconciled_via_fusion to include fusion fields
(top_suggestion_id, confidence_band, attachment_count). Adds 3 new
adapter methods that proxy the engine: suggest_matches, accept_suggestion,
unreconcile. AI tools (Task 22+) and OWL controller (Task 26) will call
these adapter methods instead of touching the engine directly.
Made-with: Cursor
Provider-agnostic system + user prompt builder for the confidence
scoring pipeline's Pass 3 (AI re-rank). Output contract is JSON with
"ranked" array; works with OpenAI, Claude, and local OpenAI-compatible
servers (LM Studio, Ollama).
Made-with: Cursor
Two engine bugs caught by Task 19's integration tests:
1. Partial reconcile (bank_amount < invoice_residual) was creating an
unbalanced bank move. Counterpart balance now clamped to
min(remaining_bank_amount, abs(invoice_residual)) so the move stays
balanced; Odoo's reconcile() handles the resulting partial. The
counterpart's amount_currency is scaled proportionally so multi-
currency lines stay consistent.
2. Unreconcile only removed account.partial.reconcile rows but didn't
restore the suspense line on the bank move, leaving is_reconciled=True
after unreconcile. Now delegates to V19's standard
account.bank.statement.line.action_undo_reconciliation for any
affected bank line, which both deletes partials and restores the
suspense state in one shot.
Made-with: Cursor
Tests engine behavior using factories (Task 18) instead of SQL fixtures.
Covers simple match, partial chain, multi-invoice batch, suggest-then-
accept flow, unreconcile reversal, and edge cases.
Two tests are intentionally failing — they expose real engine bugs
that should be fixed in a follow-up:
- TestReconcilePartialChain.test_partial_reconcile_leaves_residual:
reconcile_one() builds counterpart vals using the full invoice
residual, which leaves the bank move unbalanced when bank amount
is smaller than the invoice (UserError: entry not balanced).
- TestUnreconcile.test_unreconcile_removes_partial: unreconcile()
unlinks partial.reconcile rows but does not restore the suspense
line on the bank move, so account.bank.statement.line.is_reconciled
remains True after reversal.
Made-with: Cursor
Provides make_bank_journal, make_bank_statement, make_bank_line,
make_invoice, make_vendor_bill, make_suggestion, make_pattern,
make_precedent, make_reconcileable_pair helpers used across the
bank-rec test suite. Replaces the original plan's SQL-fixture capture
with programmatic factories — same testing intent, simpler maintenance,
no real Westin data baked into the repo.
Note: the original plan called for 5 SQL fixtures captured from the local
DB (westin_simple_match.sql, westin_partial_chain.sql, etc.). Those are
replaced by factory-driven test creation in Task 19 — eliminates fragile
hand-curated SQL while testing the same code paths.
Made-with: Cursor
Adds fusion.reconcile.engine — the AbstractModel orchestrator for all
bank-line reconciliations. Six public methods (reconcile_one,
reconcile_batch, suggest_matches, accept_suggestion, write_off,
unreconcile) form the only sanctioned write path to
account.partial.reconcile from the rest of the module (controllers, AI
tools, wizards).
Implementation follows V19's bank_rec_widget pattern: rewrite the bank
move's suspense line into one counterpart per matched invoice (or a
write-off line) on the appropriate receivable / payable / write-off
account, then call account.move.line.reconcile() on each pair. Records
a precedent row per reconcile for downstream pattern learning.
16 new unit tests cover all six methods across happy paths, the
precedent side effect, suggestion lifecycle, batch auto-strategy, and
write-off line clearance. 67 total tests, 0 failed.
Made-with: Cursor
Task 11 of Phase 1 Bank Reconciliation. Adds the brain that ranks
candidate journal-item matches for a bank statement line.
Pass 1 — SQL filter (done by caller's _fetch_candidates).
Pass 2 — Statistical scoring: weighted blend of amount-delta,
partner pattern fit, and precedent similarity.
Pass 3 — Optional AI re-rank when an LLM provider is configured;
gracefully no-ops when provider missing, prompt module not
yet present (Task 20), or the JSON response is malformed.
Pass 4 — Persistence (handled by engine.suggest_matches).
Returns top-K ScoredCandidate dataclasses with per-feature scores
exposed for transparency and future learning.
7 new tests added; full module suite green (51 tests, 0 failures).
Made-with: Cursor
Adds the foundation for AI confidence scoring:
- fusion.reconcile.pattern: per-(company, partner) aggregate profile
(volume, cadence, preferred matching strategy, memo signature,
write-off habits) — recomputed nightly from precedents.
- fusion.reconcile.precedent: per-historical-decision memory holding
full feature vector + outcome, used by precedent_lookup for KNN
scoring of new bank lines.
Includes ACL rows for fusion accounting user (read) and admin (CRUD)
groups. Manifest bumped to 19.0.1.0.1.
Note: switched the pattern uniqueness rule from the deprecated
_sql_constraints attribute to models.Constraint (Odoo 19 native API)
so the unique(company_id, partner_id) is actually enforced at the
PG level — _sql_constraints is silently ignored in 19.
Made-with: Cursor
Phase 1 prerequisite for local LLM support. Adapters now declare
capability flags (supports_tool_calling, max_context_tokens, etc.) so
the engine can reason about what backend is available.
OpenAI adapter accepts fusion_accounting.openai_base_url config -- point
it at LM Studio (http://host.docker.internal:1234/v1) or Ollama
(http://host.docker.internal:11434/v1) and the existing OpenAI adapter
works unchanged.
Implementation note: existing Odoo AbstractModel adapters
(fusion.accounting.adapter.openai/claude) are preserved untouched to
avoid breaking the chat panel; the new plain-Python OpenAIAdapter and
ClaudeAdapter classes (LLMProvider subclasses) are added alongside them.
Made-with: Cursor
group_fusion_show_when_enterprise_absent has membership = all internal
users iff no Enterprise accounting module is installed. Membership is
recomputed on module install/uninstall via overrides on ir.module.module.
Used by Phase 1 fusion_bank_rec menus to auto-hide when Enterprise is
active and auto-appear after Enterprise uninstall.
Made-with: Cursor
Declare account.bank.statement.line.cron_last_check on
fusion_accounting_core so the column survives Enterprise
account_accountant uninstall. Mirrors the existing pattern used
for account.move and account.reconcile.model shared fields.
- Add models/account_bank_statement_line.py declaring cron_last_check
as fields.Datetime(copy=False)
- Wire model into models/__init__.py
- Add post_install regression test verifying field presence and type
- Bump manifest 19.0.1.0.0 -> 19.0.1.0.1
Made-with: Cursor