Drafts the design for fusion_accounting_bank_rec — a native bank
reconciliation widget that replaces Odoo Enterprise account_accountant
in V19 OWL architecture, with a clean-room reconcile engine reading and
writing Community account.partial.reconcile rows.
Key design decisions captured:
- CORE scope (~5.5-6 weeks): manual + auto reconcile, write-offs,
partial, multi-currency, chatter, model picker
- Strict mirror of all 18 Enterprise OWL units (zero functional loss)
plus 5 fusion-only additions for AI/history visibility
- Hybrid AI badge layout: inline strip with one-click Accept plus
expandable ranked-alternatives panel
- Behavioural learning via fusion.reconcile.pattern (per-partner) and
fusion.reconcile.precedent (per-decision memory) with bootstrap from
the 16,500 historical reconciliations
- Local LLM ready via OpenAI-compatible adapter base_url config and
per-feature provider routing — works against LM Studio, Ollama, vLLM
- Statistical-mode-without-API-key as a first-class path
- Coexistence with Enterprise: Enterprise wins by default, fusion
menu hides until uninstall, then auto-appears
- Migration wizard step bootstraps pattern memory and produces an
audit report PDF proving every reconciliation preserved
- TDD on engine algorithms with Hypothesis property-based tests for
amount invariants; migration round-trip integration test
Builds on Phase 0 (commit c450bb2, range pre-phase-0..phase-0-complete).
Made-with: Cursor
Users were seeing both Odoo's stock PDFs and FP's branded equivalents
in the Print dropdown side-by-side, and accidentally sending the wrong
(unbranded, missing PO# / job ref / plating fields) PDF to customers.
Add fp_hide_default_reports.xml that drops the Print-menu binding on:
| Model | Hidden | FP replacement |
|-----------------|-------------------------------------------------------------|---------------------------------|
| sale.order | sale.action_report_saleorder | action_report_fp_sale_* |
| sale.order | sale_pdf_quote_builder.action_report_saleorder_raw | action_report_fp_sale_* |
| account.move | account.account_invoices | action_report_fp_invoice_* |
| account.move | account.account_invoices_without_payment | action_report_fp_invoice_* |
| stock.picking | stock.action_report_delivery | action_report_fp_packing_slip_* |
| mrp.production | mrp.action_report_production_order | action_report_fp_job_traveller_*|
| account.payment | account.action_report_payment_receipt | action_report_fp_receipt_* |
Mechanism: set binding_model_id=False + binding_type=action — removes
from the Print dropdown but leaves the report record + template intact.
Fully reversible from Settings → Technical → Reports if anyone needs
the stock PDF back.
Intentionally NOT touched:
- sale.action_report_pro_forma_invoice (no FP pro-forma yet)
- account.action_account_original_vendor_bill (vendor bills, internal)
- stock.action_report_picking / picking_packages / return_label_report
(internal warehouse ops, not customer-facing)
- mrp.action_report_finished_product / mrp.label_manufacture_template
(production labels — ZPL, not customer-facing)
- sale_timesheet.* (timesheet integration)
Added sale_pdf_quote_builder to depends so the data file always finds
that record when applied (it ships in entech's repackaged enterprise
bundle and was already installed there).
Verified on entech: re-running the print-menu audit shows zero stock
Odoo customer-facing PDFs left where FP has an equivalent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The floating message-action toolbar (reaction / reply / star / link
icons) appearing on hover renders white-icons-on-white-background in
dark mode — Odoo's own dark.scss sets the icon hover color to white
but never gives the toolbar itself a dark background. Result: the
icons vanish entirely in dark mode.
Add fp_chatter_dark.scss that branches at compile time on
$o-webclient-color-scheme == dark (Odoo 19 compiles every SCSS file
into both web.assets_backend with `bright` AND web.assets_web_dark
with `dark`) and gives the toolbar:
- Solid dark background (#2b2f33 fallback, var(--o-component-bgcolor))
- Subtle 1px white-alpha border + drop shadow so it floats nicely
- Icon color rgba(255,255,255,.78) at full opacity (not 35%)
- Brighter hover state with a subtle bg highlight
Light bundle output is empty (the @if branch doesn't fire), so the
light theme is untouched.
Verified: dark bundle includes our rule with #2b2f33 marker present.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes from a single SO walkthrough screenshot:
**1. "Current stage" banner**
- Was placed `inside sheet` so it rendered at the BOTTOM of the form
where users miss it. Moved to `before form/header` (same xpath
pattern as the Account Hold banner) — now it's the first thing
visible above the SO header.
- Was still showing "Shipped — awaiting invoice" after the invoice
was posted because `_compute_workflow_stage` only advanced to
`complete` when shipped + ALL paid; an unpaid posted invoice left
the SO stuck on `shipped`. Added an `invoicing` branch: shipped +
has_posted_invoice → invoicing. Banner invisible-list now also
includes `invoicing` and `paid`, so the banner only shows for
in-progress steps.
**2. Chatter messages rendering raw HTML tags as text**
Odoo 19 escapes any string passed to `message_post(body=...)`
unless wrapped in `markupsafe.Markup`. We had ~10 places posting
HTML (`<a href>`, `<b>`, `<br/>`, `<code>`, `<pre>`) that all
showed up as `<a href=...>` literal text in the chatter.
Wrapped each one with `Markup(_(...))` so the tags render. Files
touched:
- fusion_plating_bridge_mrp/models/sale_order.py
(auto-MO failure code block, "Draft MO created" link,
"Job assigned to <b>" message)
- fusion_plating_bridge_mrp/models/mrp_production.py
("Recipe steps" pre/br block on each WO)
- fusion_plating_bridge_mrp/models/fp_proficiency.py
(operator promotion announcement)
- fusion_plating_configurator/models/fp_quote_configurator.py
(SO link, 3D model attached, drawing attached, save to catalog)
- fusion_plating_configurator/models/fp_part_catalog.py
(3D/drawing change tracking + propagation to linked quotes)
- fusion_plating_portal/models/fp_quote_request.py
(RFQ → SO link)
- fusion_plating_quality/models/fp_quality_hold.py
(hold status change)
- fusion_plating_shopfloor/controllers/manager_controller.py
(worker / tank / manager-takeover assignments)
Verified on entech: SO S00038 stage now reads `invoicing` (banner
hidden), and a freshly posted message shows `<a href>` and `<b>`
as actual link + bold instead of escaped text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 'Fusion Accounting' top-level menu was missing the web_icon attribute,
so the app switcher grid showed a placeholder instead of the branded icon.
ir.ui.menu.web_icon is separate from ir.module.module.icon (Apps page) —
both need to be set for full icon coverage.
Made-with: Cursor
User reported "multiple unwanted vertical lines in the boxes" on the
portrait BoL. Pixel analysis confirmed it: previous design had 3
separate `<div class="sig-box">` each with its own 1px border, with a
4-8px gap between adjacent boxes — visually those adjacent borders
read as a doubled / "duplicate" line between cells.
Fix: replace 3-box layout with a single `<table class="bordered
sig-table">` containing 3 td cells. With border-collapse: collapse,
adjacent cells share their border — so the row now shows 4 vertical
lines (1 outer left + 2 internal dividers + 1 outer right) instead
of 6 close-together border lines.
- Dropped `.sig-box` class entirely (no per-box border anymore)
- Added `.sig-table` + `.sig-cell` with explicit 1px borders so the
layout works without depending on `.bordered` class inheritance
- Applied to both portrait + landscape variants
- Landscape sig-row was still using the OLD Bootstrap row+col-4
layout (never got replaced earlier) — also migrated to the new
table layout
Verified: page count unchanged (portrait 1, landscape 1), all
labels and content present, structure clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the 73KB icon.png into each of the four sub-modules
(fusion_accounting meta, _core, _ai, _migration) so Odoo's Apps page
renders the branded icon for each. Meta-module manifest 'icon' path
now points to its own icon instead of the AI sub-module's.
Made-with: Cursor
Last fix kept signatures intact but the landscape BoL still overflowed
to a second page (with the signature row pushed entirely to page 2).
The real ask was for the landscape variant to fit on one page since
landscape has plenty of vertical room.
Aggressive landscape compaction:
- Body font 11pt → 10pt, td font 10 → 9.5pt, th font 10 → 9pt
- Cell padding 8/10px → 4/8px
- Table margin-bottom 12px → 6px
- h2 title 26pt → 18pt with tighter top/bottom margins
- BoL # subtitle 14pt → 11pt
- Shipper/consignee row height 120 → 70px
- highlight-box (cert) padding 10px → 6/10, font 10 → 9pt
- sig-box padding 12 → 8/10px
- sig-line height 70 → 45px
Verified with pypdf: landscape BoL now renders as exactly 1 page
with cert + all 3 signature labels + company info all present.
137 KB clean PDF.
Portrait variant left untouched (it already fit on one page and
the bigger title is appropriate for portrait).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Never again touch production without explicit confirmation. This rule
codifies the hard-won lesson from 2026-04-19: ssh odoo-westin goes to
PRODUCTION (192.168.1.40, erp.westinhealthcare.ca), not dev, despite
the container being named odoo-dev-app.
alwaysApply: true.
Made-with: Cursor
Last fix added page-break-inside: avoid but the boxes still split
because wkhtmltopdf 0.12 ignores that rule inside flex containers,
and BOTH the .sig-box (display: flex) AND the Bootstrap .row
wrapper were flex.
Replace both with non-flex equivalents:
- .sig-box: dropped `display: flex` + `flex-direction: column` +
`justify-content: flex-end`. Layout now uses padding + a fixed-
height .sig-line block + the muted label below. Same visual
result, but a plain block element so wkhtmltopdf honors the
page-break rule.
- Replaced `<div class="row">` + 3 `<div class="col-4">` (Bootstrap
flex grid) with a `<table class="sig-table">` containing one row
of three 33% tds. wkhtmltopdf treats table rows as atomic for
page-breaking, so the whole signature row now stays on a single
page.
Verified with pypdf: page 1 has the cert statement, page 2 has
all three signature labels together — no more sliced boxes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Landscape BoL was splitting the signature row down the middle —
boxes half on page 1, half on page 2. Two complementary fixes:
1. **Per-element rule**: added `page-break-inside: avoid` +
`break-inside: avoid` to `.sig-box` (both portrait + landscape
styles) so an individual signature box can never split across
pages.
2. **Wrapper rule**: introduced `.fp-keep-together` utility +
wrapped the BoL's certification statement + signature row in
it, so the whole "sign here" block moves to the next page as
one unit if it doesn't fit. Also applied
`page-break-inside: avoid` to `table tr` so cargo lines don't
split mid-row either.
Lives in shared `report_base_styles.xml` so any FP template that
opts into `.fp-keep-together` benefits automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five fixes applied to the Bill of Lading and (where relevant) all
report templates:
1. **Bigger title + BoL #** — portrait now uses h2 24pt (was h4 16pt),
landscape h2 26pt; BoL # ticker is 13/14pt instead of body size.
2. **Shipper info missing** — root cause: `_fp_build_delivery_vals`
was creating deliveries without `company_id`, so the BoL's
`<span t-field="doc.company_id.name"/>` rendered empty. Two fixes:
- Hook now sets `company_id = mo.company_id.id or env.company.id`.
- Template falls back defensively to `env.company` when
`doc.company_id` is empty (covers any legacy delivery that
somehow slips through without it).
- Backfilled 14 existing deliveries via SQL on entech.
3. **Uniform header backgrounds** — replaced mixed `info-header`
(gray) + default-th (brand black) headers with a single
`fp-header-primary` (brand black) across all sub-tables for a
consistent look.
4. **Cargo description alignment + missing column** — added a QTY
column (matches landscape variant), pulled from the linked MO
via job_ref → mrp.production.product_qty. Added `.fp-cell-mid`
utility class with `vertical-align: middle !important;` and
applied it to every cargo + info cell so values sit centred
instead of jammed against the top border.
5. **Signature box too short** — bumped `.sig-box` from 70 → 110 px
(portrait) / 130 px (landscape), `.sig-line` from 28 → 60/70 px,
added flex layout so the label sits at the bottom and signers
have a real space to write in. Lives in the shared
`report_base_styles.xml` so EVERY FP template benefits, not just
the BoL.
Verified: BoL portrait renders cleanly at 140 KB with full shipper
block + uniform headers + middle-aligned cargo cells.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Odoo 19 renamed the m2m-to-groups fields on res.users:
- groups_id (Odoo <=18) was split into group_ids (direct) +
all_group_ids (direct + implied)
The /book-assessment route was raising KeyError: 'groups_id' on every hit,
returning HTTP 500. Switched to all_group_ids so any user with the sales
salesman group access (direct OR via implied manager/admin groups) is
matched when resolving available sales reps.
Verified by curl: /book-assessment now returns HTTP 200.
Made-with: Cursor
Task 18 — empirical verification of the data-preservation claims in
Section 3 of the Enterprise Takeover Roadmap.
Key empirical findings (verified on westin-v19 live DB + clone):
1. Safety guard blocks Enterprise uninstall (Scenario A, verified on
throwaway clone) — UserError fires with the correct migration-wizard
guidance message.
2. Bank reconciliation tables (account.partial.reconcile,
account.full.reconcile) are owned exclusively by Community account
module. 30,874 reconciliation rows (16,500 partial + 14,374 full)
confirmed immune to any Enterprise uninstall.
3. All 5 Enterprise extension fields on account.move (deferred_move_ids,
deferred_original_move_ids, deferred_entry_type, signing_user,
payment_state_before_switch) are dual-owned by account_accountant
AND fusion_accounting_core. Odoo's module-ownership ledger will
preserve columns/relations when Enterprise uninstalls.
4. account.reconcile.model is triple-owned (account + account_accountant
+ fusion_accounting_core). Reconciliation rules survive.
5. account.move has 36 module owners; table cannot be dropped by any
realistic uninstall scenario.
A full destructive uninstall cycle on a clone was attempted but blocked
by pre-existing data-integrity issues in westin-v19 (orphan FK references
in payslip_tags_table + account_account_res_company_rel — outside fusion
scope). The schema-ownership verification approach provides stronger
evidence than a point-in-time count comparison — it proves the invariants
hold for any real-world data shape, not just a single fixture.
Test clone westin-v19-phase0-empirical dropped after testing. No live
data was modified.
Phase 0 data-preservation design is empirically validated. Phase 1 can
proceed.
Made-with: Cursor
The Bill of Lading template assigned a temp variable
`<t t-set="dest" t-value="doc.delivery_address_id or doc.partner_id"/>`
and then tried `<div t-field="dest" .../>`. Odoo 19 QWeb asserts
t-field must be `record.field_name` (have a dot) — the temp variable
form fails compilation and the report renders as a multi-page
"Oops! Something went wrong" PDF stuffed with the traceback.
Fix: branch with `t-if`/`t-else` and call `t-field="doc.delivery_address_id"`
or `t-field="doc.partner_id"` directly. Same pattern in both header
and second-page-header sections (lines 49/235).
Verified: BoL render goes from 39 KB error page to 138 KB clean PDF.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 splits the fusion_accounting module into a multi-sub-module
architecture (fusion_accounting_core, fusion_accounting_ai,
fusion_accounting_migration) as the foundation for the Enterprise
Takeover Roadmap (docs/superpowers/specs/2026-04-18-fusion-accounting-
enterprise-takeover-roadmap-design.md).
What landed:
- 3 sub-modules + fusion_accounting as meta-module
- Data-adapter pattern (base + bank_rec + reports + followup + assets)
routing AI tool lookups across fusion / Enterprise / Community
- All AI tools refactored through adapters (13 tool files)
- Zero hard deps on Enterprise modules; runtime detection only
- Shared-field-ownership for deferred_move_ids, signing_user, etc.
(survives Enterprise uninstall)
- Enterprise uninstall safety guard blocks destructive uninstalls
- Migration wizard skeleton (per-feature migrations come in later phases)
- check_odoo_diff.sh tool for annual Odoo version upgrades
- Per-sub-module CLAUDE.md, UPGRADE_NOTES.md, README.md
- Gitea CI workflow scaffold (install-Odoo step is TODO for Phase 1)
- 23/23 tests pass on odoo-westin with westin-v19
Deferred:
- Task 18 (empirical Enterprise-uninstall test on throwaway instance)
pending env provisioning decision
- Manual browser smoke test (subagents can't drive browsers)
See tags fusion_accounting/pre-phase-0 and fusion_accounting/phase-0-complete
for range markers.
Made-with: Cursor
# Conflicts:
# fusion_plating/fusion_plating_receiving/models/fp_receiving.py
# fusion_plating/fusion_plating_shopfloor/__manifest__.py
# fusion_plating/scripts/fp_demo_stage_filler.py
The auto-prefill logic that fills received_qty from expected_qty on
fp.receiving create was committed to the entech LXC but never made it
back to main. Verified by a full quote→delivery→invoice walkthrough
(scripts/fp_e2e_human.py) — receiving step now passes.
Also adds the human-walkthrough E2E script that exercises every step:
RFQ → quote → SO confirm → MO + portal job auto-create → receiving
prefill → recipe → WO execution → MO done → CoC cert (rich PDF, no
thickness duplicate) → delivery prefill + lifecycle → invoice (posted,
not auto-paid) → notification log audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The auto-prefill logic that fills received_qty from expected_qty on
fp.receiving create was committed to the entech LXC but never made it
back to main. Verified by a full quote→delivery→invoice walkthrough
(scripts/fp_e2e_human.py) — receiving step now passes.
Also adds the human-walkthrough E2E script that exercises every step:
RFQ → quote → SO confirm → MO + portal job auto-create → receiving
prefill → recipe → WO execution → MO done → CoC cert (rich PDF, no
thickness duplicate) → delivery prefill + lifecycle → invoice (posted,
not auto-paid) → notification log audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workflow structure is complete (path filters, matrix, services).
The 'Install Odoo 19' step is a TODO placeholder — the reproducible
Odoo-19 build environment is deferred to Phase 1 CI hardening.
Current Phase 0 test workflow is manual via ssh odoo-westin.
Made-with: Cursor
Three reported PDF bugs from the customer-facing email package:
1. Invoice body was empty — Odoo 19 sets display_type='product' on
regular invoice/SO lines (was empty string in 18.0). Both
report_fp_invoice.xml and report_fp_sale.xml only matched
`not line.display_type`, so every product line was skipped.
Fixed both portrait + landscape variants to also match
display_type == 'product'.
2. CoC PDF was a bare 30 KB header — _fp_generate_cert_pdf was
rendering action_report_coc, which is bound to portal_job and
has minimal content. Rewrote to use the rich fp.certificate-bound
report (action_report_coc_en / action_report_coc_fr based on
cert.partner_id.lang) and slugged the filename to
CoC-<Customer>-<CertName>.pdf so the email attachment reads
nicely instead of CERT-00123.pdf.
3. Thickness cert was an exact duplicate of the CoC — the CoC
template already embeds thickness readings. Skip thickness cert
creation entirely when the customer also wants CoC; only create
a standalone thickness cert when the customer opted out of CoC.
Also: dispatcher in fp_notification_template now prefers
portal_job.coc_attachment_id (the rich one we just generated) and
falls back to rendering action_report_coc_en against fp.certificate
by partner.lang — never the bare portal-job report.
Versions bumped: bridge_mrp 19.0.6.0.0, notifications 19.0.4.0.0,
reports 19.0.4.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 20 of Phase 0: document the sub-module split.
- fusion_accounting_core: foundation doc covering security groups, shared-field
schema preservation, and the Enterprise-detection helper.
- fusion_accounting_ai: preserves the original module's AI-specific design
decisions, Odoo 19 gotchas, deployment commands, controllers, models, theme
rules, and known issues. Adds a new Data-adapter pattern section documenting
tri-mode routing (fusion / enterprise / community).
- fusion_accounting_migration: doc for the Enterprise uninstall safety guard
and the wizard shell that future feature sub-modules will extend.
- fusion_accounting (meta): rewritten CLAUDE.md as a pure overview pointing at
sub-modules, plus a new README.md covering one-click install/uninstall.
Each sub-module now has CLAUDE.md (Cursor/Claude context), UPGRADE_NOTES.md
(version-by-version deltas / reference sources), and README.md (user-facing
install/usage docs). 11 files total.
Made-with: Cursor
Addresses code review feedback on Task 17:
- Add menuitem so 'Fusion Accounting -> Migrate from Enterprise' is reachable
(the UserError guidance now actually works). Placed at top level since
parenting under fusion_accounting_ai.menu_fusion_accounting_root would
require adding that module as a hard dep, which is wrong semantically
(migration should not require AI). Both menuitems carry the admin group
so the menu stays hidden from users who can't open the wizard anyway.
- Update the UserError wording to "Fusion Accounting -> Migrate from
Enterprise" (no longer "Settings -> ...") to match the actual menu
location; 'migration' is preserved per the test's assertIn check.
- Add skipTest guard to test_uninstall_not_blocked_when_migration_completed
so it doesn't pass vacuously on Community-only CI (the guard's
`if not installed: continue` would otherwise return True regardless of
the flag value, giving a false green).
- Move GUARDED_MODULES import to top of wizards/migration_wizard.py
(no circular-import risk -- models/ir_module_module.py doesn't import
from wizards/).
- Expand docstrings on button_immediate_uninstall and module_uninstall
overrides to note they may both fire in a single UI uninstall call
and that the guard is idempotent (pure read + raise).
Made-with: Cursor
E2E test (quote → SO → MO → WOs → ship → invoice → payment) ran clean
but flagged five gaps where the operator was filling in data the
system already knew. Closes all five.
#1 SO CONFIRM → AUTO-CREATE DRAFT MO (was a workflow blocker)
bridge_mrp/sale_order.py: action_confirm() override + new
_fp_auto_create_mo helper. Resolves the manufactured product from
the configurator's part-catalog → coating-config → FP-WIDGET
fallback; resolves the recipe from coating_config.recipe_id →
part_catalog.recipe_id → first installed recipe. Idempotent:
skips if any MO already exists for the SO. Errors are caught and
chatter-posted so SO confirm never fails because of an MO glitch.
#2 QUOTE PO → client_order_ref ON SO (one-line fix)
configurator/fp_quote_configurator.py: action_create_quotation
now copies po_number_preliminary into Odoo's standard
client_order_ref alongside the existing custom x_fc_po_number.
Portal pages, native reports, and integrations all read the
standard field; no reason both shouldn't carry the same PO#.
#3 MO DONE → AUTO-RENDER CoC + THICKNESS PDFs
bridge_mrp/mrp_production.py button_mark_done now calls a new
_fp_generate_cert_pdf helper after creating each fp.certificate.
Renders fusion_plating_reports.action_report_coc to PDF, stores
as ir.attachment, links to cert.attachment_id, AND cross-links
to portal_job.coc_attachment_id + delivery.coc_attachment_id so
the customer portal and the shipping email both find it without
an extra step. Thickness report falls back to the CoC layout
(which embeds thickness data) until a dedicated report ships.
Errors are logged but never block MO completion.
#4 RECEIVING received_qty PREFILL
receiving/fp_receiving.py: create() prefills received_qty from
expected_qty on draft. Operator only types when the count is
wrong (the rare case). Field carrier_tracking already exists,
so #4's 'no inbound tracking field' from the gap report turned
out to be a false alarm.
#5 DELIVERY scheduled_date + driver PREFILL
bridge_mrp/mrp_production.py: new _fp_build_delivery_vals
helper sets scheduled_date from the portal job's target_ship_date
(or now+2 business days as a sane fallback) and auto-picks
assigned_driver_id from clocked-in employees tagged is_driver
(falls back to any active driver if the shift is empty). The
outbound tracking_ref deliberately stays empty — that's the
carrier's number, paste it in once UPS/FedEx accepts the package.
Module bumps: configurator 19.0.5.0.0, bridge_mrp 19.0.5.0.0,
receiving 19.0.2.0.0.
Verified on entech: re-ran the E2E test against a fresh quote.
Quote → SO populated client_order_ref, SO confirm auto-created MO,
receiving prefilled received_qty=50, MO done generated CERT-00018.pdf
and linked it to portal job + delivery, delivery's scheduled_date
prefilled to 2026-04-29, full pipeline ended with portal job state
'complete'. The remaining 'gaps' in the static report are script
artefacts (e.g. it flags 'no inbound tracking field' but the field
exists; flags 'no driver auto-pick' but the demo data has zero
drivers tagged is_driver=True).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 Task 17. Installs a safety guard on ir.module.module that blocks
uninstall of Odoo Enterprise accounting modules (account_accountant,
account_reports, accountant, account_followup, account_asset,
account_budget, account_loans) until the per-module migration flag
fusion_accounting.migration.<name>.completed is set to True. Guard
covers both button_immediate_uninstall (UI) and module_uninstall
(CLI/API) paths, raising UserError with a pointer to the migration
wizard and an escape hatch config parameter.
Also ships a TransientModel fusion.migration.wizard as a shell: it
detects installed Enterprise modules via GUARDED_MODULES and exposes
action_run_migration for sub-modules to extend in later phases. No
per-feature migrations are registered yet -- Phase 1+ sub-modules will
hook in their own steps.
Tests: TestSafetyGuard x2 pass (blocked-when-pending verified with
account_accountant installed; not-blocked-when-completed verified by
setting the flag).
Made-with: Cursor
Task 16's security group rehoming (fusion_accounting → fusion_accounting_core)
only existed in post-migration. That flow fails on fresh pre-Phase-0 upgrades:
data-load runs before post-migration and looks up group xml-ids by
(module, name); if the row still has module='fusion_accounting', Odoo
creates a duplicate res.groups record under
module='fusion_accounting_core'. The subsequent post-migration
UPDATE...SET module='fusion_accounting_core' then trips the (module, name)
unique constraint on ir_model_data, rolling back the whole transaction.
Pre-migration runs BEFORE data-load, renames the five security xml-ids
(module_category, privilege, three groups) to the new module, so data-load
finds the existing rows and UPDATEs them in place. Existing user-group
links via res_groups_users_rel are preserved.
The post-migration is kept as an idempotent safety net (docstring
updated to reflect the new division of labour).
Verified on westin-v19 by simulating the pre-Phase-0 state (UPDATE
ir_model_data SET module='fusion_accounting' ...) and re-running the
upgrade: 5 rows renamed cleanly, zero duplicates, no errors.
Made-with: Cursor
After the right-side preview panel was retired, the left column had
Customer & Part / RFQ-PO / Geometry / Delivery & Fees stacked while
the right side ran out of content after Rush Order — almost half the
form was dead air. Reshuffled the groups so every row has peers.
Old layout (4 rows, mostly half-empty):
Customer & Part | RFQ / PO / Quantity & Options
Geometry | Auto from 3D (often empty)
Delivery & Fees | (empty)
Calculated Price | Final Price
New layout (every row balanced):
Customer & Part | RFQ / PO Documents
Quantity & Options | Auto from 3D (visible only with part catalog)
Geometry | Delivery & Fees
Calculated Price | Final Price
Quantity & Options moved out of the RFQ/PO group (where it was
shoehorned in via a <separator>) into its own group on the left of
row 2. Auto from 3D becomes its right-side peer when present, or
shrinks gracefully when absent.
Delivery & Fees moves up one row to pair with Geometry instead of
sitting alone. Net effect: form fits more above the fold and the
estimator's eye doesn't have to chase fields across uneven columns.
Bumped fusion_plating_configurator to 19.0.4.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Quote Configurator form devoted nearly half its width to a sticky
3D viewer + drawing PDF preview. That panel meant the actual fields
(geometry, dimensions, pricing) had to fight for real estate. Replaced
the inline previews with two affordances that take zero layout space:
1. New '3D Model' smart button at the top of the form, next to the
existing 'Drawings' button. Click to open the existing
fp_3d_viewer_open client action — same fullscreen modal the
'Full Screen' button used to launch from the side panel.
2. Inline 'Preview' link (eye icon) sits next to the 3D Model and
Drawing fields in the Customer & Part group. Click to open the
same modal preview as the smart button. Two paths to the same
content — power users grab the field-adjacent link mid-edit;
visual-thinkers grab the smart button up top.
Layout collapses to a single full-width column. The .o_fp_cfg_layout
wrapper is kept (display:block) so we have a stable hook in case a
side panel returns later for a different purpose. Old SCSS dance with
:has() selectors to fake-collapse the grid is gone.
Bumped fusion_plating_configurator to 19.0.3.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The search bar required Enter to fire, which felt clunky on a shop
floor where managers expect cards to filter as they type. Switched
to a 200ms-debounced live search — fast enough to feel instant on
keystrokes, slow enough to skip the network call when someone is
mid-word.
Search bar visual weight bumped:
- Width 260px → 380px (320px on iPad, full width on phones)
- Height 48px → 52px
- Font-size base → md, weight medium
- Search icon nudged 14px → 16px from the edge with a 1.05rem size
- Placeholder uses the lighter $fp-ink-faint so the input feels
inviting rather than already-filled
Behaviour:
- Type → cards filter after 200ms of no input
- Enter → fires immediately (skips debounce) for power users
- Escape → clears the search (new shortcut)
- Clear button → unchanged
Bumped shopfloor to 19.0.14.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Bake Window + First-Piece Gate cards looked rounded on their
own, but Odoo's default .o_kanban_record wrapper painted its own
background + border + box-shadow with sharper corners than our
inner .o_fp_kcard — visible as a faint square ghost behind every
card, especially obvious on the missed_window state where the red
wash on the inner card didn't extend to the wrapper edges.
Added a .o_fp_bw_kanban / .o_fp_fpg_kanban scoped override that
zeroes the wrapper's background, border, box-shadow and padding,
letting only our card surface render. Also drops the kanban group
container's tinted bg for the same reason.
Bumped shopfloor to 19.0.13.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Bake Window + First-Piece Gate cards looked rounded on their
own, but Odoo's default .o_kanban_record wrapper painted its own
background + border + box-shadow with sharper corners than our
inner .o_fp_kcard — visible as a faint square ghost behind every
card, especially obvious on the missed_window state where the red
wash on the inner card didn't extend to the wrapper edges.
Added a .o_fp_bw_kanban / .o_fp_fpg_kanban scoped override that
zeroes the wrapper's background, border, box-shadow and padding,
letting only our card surface render. Also drops the kanban group
container's tinted bg for the same reason.
Bumped shopfloor to 19.0.13.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to commit 4843146 / f7f500f which added the shared
SCSS. This commit wires the views to use it: the manifest now
loads fp_kanbans.scss and the two kanban templates render with
the new .o_fp_kcard structure (state stripe, title, subtitle,
big metric, meta line, chip footer).
Companion to commit 4843146 / f7f500f which added the shared
SCSS. This commit wires the views to use it: the manifest now
loads fp_kanbans.scss and the two kanban templates render with
the new .o_fp_kcard structure (state stripe, title, subtitle,
big metric, meta line, chip footer).
Task 13 Step 10 of phase-0 plan.
- month_end.get_period_summary → ReportsAdapter.run_report(...) with
Community fallback to the trial_balance() aggregator.
- hst_management.get_tax_report → ReportsAdapter.run_report(...).
Other tools in these files (get_unreconciled_counts, find_entries_in_locked_period,
get_accrual_status, run_hash_integrity_check, calculate_hst_balance,
find_missing_tax_invoices, find_missing_itc_bills, create_expense_entry) touch
pure-Community models (account.move, account.move.line, account.account,
account.payment) directly and are tri-mode safe.
account.return tools in hst_management (get_tax_return_status, generate_tax_return,
validate_tax_return) and account.audit.account.status tools in audit.py already
handle the missing-model case gracefully. They fall outside this task's target
set of {account.report, account.followup.line, account.asset} and are left
as-is per plan.
All 12 data-adapter tests pass on westin-v19.
Made-with: Cursor
The two standalone menu pages (Bake Windows, First-Piece Gates) were
still on the older o_fp_card design from a pre-Plant-Overview pass —
visually drifted from the polished kanban-pattern cards we settled on
for Plant Overview. Pulling them onto the same design language without
rewriting them as OWL client actions (the 'Option A' from chat).
What changed
============
New shared SCSS — fp_kanbans.scss
---------------------------------
Defines .o_fp_kcard as the base kanban card surface. Mirrors the
Plant Overview .o_fp_po_card recipe: white $fp-card surface, 1px
$fp-border, $fp-radius-md corners, soft $fp-elev-1 shadow, hover
lift, 4px state stripe via ::before clipped by overflow:hidden.
Sub-elements (title, sub, metric, meta line, footer chip) get
their own classes so per-page tweaks stay surgical.
Page-scoped wrappers (.o_fp_bw_kanban, .o_fp_fpg_kanban) carry the
state/result → stripe colour mapping plus exception-state tints
(missed_window + fail get a soft danger wash so the card stands
out in a sea of normal ones).
Bake Window kanban
------------------
Rebuilt template — title (window name), part_ref subtitle, big
time-remaining metric (the operator's primary cue), meta line for
lot/customer/qty, footer with oven badge + state chip.
data-state attribute drives the stripe colour:
awaiting_bake → warning
bake_in_progress → info
baked → success
missed_window → danger + soft red wash
scrapped → muted + dimmed
First-Piece Gate kanban
-----------------------
Rebuilt template — title (gate name), part_ref subtitle, bath +
customer meta, inspector + first_piece_produced timestamp,
footer with result chip and an optional 'Released' badge when
the lot has been signed off.
data-result attribute drives the stripe colour:
pending → warning
pass → success
fail → danger + soft red wash
Shopfloor manifest bumped to 19.0.12.0.0 and the new SCSS is
registered in web.assets_backend after manager_dashboard.scss so
the design tokens it references are already in scope.
Plant Overview's existing .o_fp_po_card classes are deliberately
untouched — the OWL client action and the new kanbans share the
visual language but stay loosely coupled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two standalone menu pages (Bake Windows, First-Piece Gates) were
still on the older o_fp_card design from a pre-Plant-Overview pass —
visually drifted from the polished kanban-pattern cards we settled on
for Plant Overview. Pulling them onto the same design language without
rewriting them as OWL client actions (the 'Option A' from chat).
What changed
============
New shared SCSS — fp_kanbans.scss
---------------------------------
Defines .o_fp_kcard as the base kanban card surface. Mirrors the
Plant Overview .o_fp_po_card recipe: white $fp-card surface, 1px
$fp-border, $fp-radius-md corners, soft $fp-elev-1 shadow, hover
lift, 4px state stripe via ::before clipped by overflow:hidden.
Sub-elements (title, sub, metric, meta line, footer chip) get
their own classes so per-page tweaks stay surgical.
Page-scoped wrappers (.o_fp_bw_kanban, .o_fp_fpg_kanban) carry the
state/result → stripe colour mapping plus exception-state tints
(missed_window + fail get a soft danger wash so the card stands
out in a sea of normal ones).
Bake Window kanban
------------------
Rebuilt template — title (window name), part_ref subtitle, big
time-remaining metric (the operator's primary cue), meta line for
lot/customer/qty, footer with oven badge + state chip.
data-state attribute drives the stripe colour:
awaiting_bake → warning
bake_in_progress → info
baked → success
missed_window → danger + soft red wash
scrapped → muted + dimmed
First-Piece Gate kanban
-----------------------
Rebuilt template — title (gate name), part_ref subtitle, bath +
customer meta, inspector + first_piece_produced timestamp,
footer with result chip and an optional 'Released' badge when
the lot has been signed off.
data-result attribute drives the stripe colour:
pending → warning
pass → success
fail → danger + soft red wash
Shopfloor manifest bumped to 19.0.12.0.0 and the new SCSS is
registered in web.assets_backend after manager_dashboard.scss so
the design tokens it references are already in scope.
Plant Overview's existing .o_fp_po_card classes are deliberately
untouched — the OWL client action and the new kanbans share the
visual language but stay loosely coupled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 13 Step 8 of phase-0 plan.
get_ap_aging → FollowupAdapter.aged_payables().
The adapter method was added alongside aged_receivables() in the previous
commit, so this is a pure tool-wrapper change. Other AP tools
(find_duplicate_bills, get_unpaid_bills, get_payment_schedule, etc.) touch
account.move / account.move.line with pure-Community filters (move_type in
(in_invoice, in_refund)) which are tri-mode safe and do not need adapter
routing.
All 9 data-adapter tests pass on westin-v19.
Made-with: Cursor
Task 13 Step 7 of phase-0 plan.
Routes the AR tools through the FollowupAdapter so they work identically on
fusion-native, Enterprise, and pure Community installs:
- get_ar_aging → FollowupAdapter.aged_receivables()
- get_overdue_invoices → FollowupAdapter.overdue_invoices()
- send_followup → FollowupAdapter.send_followup()
- get_followup_report → FollowupAdapter.followup_report_html()
FollowupAdapter extended:
- overdue_invoices() now includes partner_email, partner_phone and
amount_total so the tool wrapper can render its richer response.
- aged_receivables() and aged_payables() new shared-implementation method
_aged_buckets() produces the 5-bucket aging shape the AR/AP tools emit.
- followup_report_html() and send_followup() isolate the Enterprise
account.followup.report / partner.execute_followup calls; Community mode
returns a graceful error dict.
Pure-Community tools in accounts_receivable.py (get_partner_balance,
reconcile_payment_to_invoice, get_unmatched_payments) unchanged — they touch
account.move / account.move.line directly which is tri-mode safe.
3 new data-adapter tests added (total: 9; all passing on westin-v19).
Made-with: Cursor
Pilot refactor per Task 13 Step 2 of phase-0 plan: route the bank-rec AI tool
function through the data adapter so it works identically whether the install
profile is fusion-native, Enterprise, or pure Community.
Extends BankRecAdapter.list_unreconciled() with optional filter params
(date_from, date_to, min_amount, company_id, and optional journal_id) and adds
partner_name / journal_id / journal_name to the returned shape so the tool
wrapper can preserve its existing outward return dict.
All 6 data-adapter tests pass against westin-v19 (TestDataAdapterBase,
TestBankRecAdapter, TestReportsAdapter, TestFollowupAdapter, TestAssetsAdapter).
Made-with: Cursor
The old label was easy to confuse with the customer-facing
Certificate of Conformance (fp.certificate). Operators kept asking
why a customer cert appeared on their employee profile. The tab is
actually the operator's process-level training record (EN, chrome,
anodize, etc.) that gates WO start in mrp_workorder.button_start —
nothing to do with customer documents.
Renamed the page string and added a one-line muted description
so anyone landing on the tab understands what it's for. Also
distinguishes it from the new 'Shop Roles' tab (coarser task tags
used by Manager Desk auto-routing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 'Tasks This Operator Can Do' many2many_tags widget declared
options="{'no_create_edit': True, 'color_field': 'color'}" but
fp.work.role doesn't have a color field — Odoo then tried to
fetch it on every employee form load and crashed with:
ValueError: Invalid field 'color' on 'fp.work.role'
Dropped the color_field option. Roles still render as tags, just
without the coloured chip background. (If we want coloured chips
later, add a Color integer field to fp.work.role and restore the
option — but the feature wasn't wired up anyway.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>