The wizard's dynamic fields were non-functional: the "Existing customer" box had
no search (no endpoint, no handler — the typed string was only sent on submit),
and address autocomplete never attached (google_address_autocomplete.js patches
FormController, which a client action is not).
Live client search:
- new jsonrpc endpoint /fusion_claims/service_booking/search_customers — searches
res.partner (name/phone/email) and resolves a typed SO number to its partner.
- JS: debounced (250ms) onCustSearch -> .sb-cust-results dropdown; pickCustomer()
sets state.partnerId + fills the contact, which action_book_from_wizard already
consumes for cust_mode='existing'.
- FIELD-SAFE domain: res.partner has NO `mobile` field in Odoo 19 — referencing it
raises ValueError and the swallowed exception made the search silently return
nothing. Build the OR domain only over fields in Partner._fields. Smoke-tested on
prod data ('25450'->1, '1 905-'->8).
Address autocomplete (wizard-local):
- component loads Google Places (key = ICP fusion_claims.google_maps_api_key, which
IS configured on westin), attaches via useRef('root')+onMounted/onPatched to every
input.sb-addr-input, writes street/city/lat/lng into reactive state. Fully guarded
(per-input _sbAc, _addrStarted/_addrNoKey gate, .catch on both hooks) so a missing
key degrades to manual entry and can never break render.
Verified: pyflakes clean, JS node --check, SCSS compiles, XML well-formed, dropdown
UI rendered against Bootstrap+compiled CSS. Documented in fusion_claims/CLAUDE.md §48.
Bump fusion_claims 19.0.9.6.0 -> 19.0.9.7.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 19.0.9.5.0 CSS pass (padding/scroll) did not fix "fields sitting on each
other" on small screens. Deep dive against the live web.assets_backend bundle
found two compounding root causes (both measured, not guessed):
1. Dead media query. The @media(max-width:560px) collapse for the inner field
grids (.two/.three) and .timepick was nested BEFORE the base .two/.three/
.timepick rules. Equal specificity → the later base rule wins → the media
query never applied. matchMedia matched at 320/390px yet grids stayed 2–3
columns and crammed/truncated. Moved all @media overrides to the END of the
.o_service_booking block so they win the cascade.
2. Bootstrap class collision. The wizard reused row/card/grid/btn; Odoo's
backend Bootstrap applies .row{display:flex;margin:0 -16px}, .card{display:flex},
etc. globally even under the scoped parent (they win for properties the scoped
rule doesn't set). Measured: every .row computed display:flex + margin-left/
right:-16px. Renamed all layout classes to sb-* (sb-row/sb-card/sb-grid/sb-btn)
in service_booking.xml + service_booking.scss.
Verified at 320/390/768/1280 against the real prod bundle (computed
grid-template-columns now 1fr at <=560px; .sb-row margin 0 / display block;
2-col desktop intact). Documented both gotchas in fusion_claims/CLAUDE.md §47.
Bump fusion_claims 19.0.9.5.0 -> 19.0.9.6.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reported on the live wizard: no scroll on small screens, not responsive, fields
look unpadded.
- .o_service_booking: min-height:100% -> height:100% so the root is capped to the
action area and overflow:auto scrolls INTERNALLY (min-height let it grow to
content height, so the clipping action container never scrolled).
- input/select/textarea.f: padding 10px 12px !important + line-height 1.4 so
Odoo's backend input normalisation can't strip the field padding.
- add a <=560px media query collapsing the .two/.three sub-grids, wrapping the
time picker, and tightening margins (the main .grid already collapses at 780px).
- bump version 19.0.9.4.0 -> 19.0.9.5.0 (asset cache-bust).
Also harden scripts/verify_service_booking.sh: force-compile web.assets_backend +
web.assets_web_dark on the clone after tests, so a broken SCSS fails the deploy
gate BEFORE prod (a bad stylesheet would break the whole backend bundle; -u does
not compile assets — Odoo compiles them lazily).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aggressive sheet override: flex-basis 100%%, !important on width and
max-width to beat parent flex/media-query constraints. Also overrides
the o_form_sheet_bg wrapper.
Layout at xl (>=1200px) now splits into 3 columns:
- Col 1 (3/12): Your Activities + Bottlenecks
- Col 2 (5/12): ADP Pre + ADP Post + MOD
- Col 3 (4/12): Aging + Other Funders + Recent ADP Exports
Falls back to 5/7 on lg (Col 3 wraps below as full row) and stacked
single column on md and below.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The .o_fc_dashboard .o_form_sheet override wasn't winning specificity
against Odoo's default form-sheet constraints. Added a dedicated class
o_fc_dashboard_sheet directly on the <sheet> element + !important
overrides on max-width, width, and flex to stretch the sheet to the
full container width.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 4 new sections:
- This Month rollup: submitted/approved/delivered/billed counts MTD
- Pipeline $ by stage: pre-submit / submitted / approved / ready-to-bill amounts
- Aging buckets: 30-59d, 60-89d, 90+ days
- Recent ADP Exports: last 5 with totals
Also overrides Odoo's form-sheet max-width on .o_fc_dashboard so the
dashboard uses the full browser width.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dashboard action existed but no menuitem ever pointed to it (latent
bug in the original module). Adding menu_fusion_claims_dashboard as the
first child of menu_adp_claims_root so the dashboard becomes the default
landing for the Fusion Claims app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps fusion_claims version to bust the asset bundle cache after the
Application Received wizard refactor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The long label 'Handed Off (Client/Authorizer Submitting)' was squeezing
the MOD statusbar on the sale order form — the parenthetical pushed half
the states off-screen behind a '...' overflow indicator. Context about
WHO is handling the submission is already captured in x_fc_mod_submitted_by
and visible in the MOD Documents tab, so the statusbar label does not need
to repeat it.
Deployed to odoo-westin and odoo-mobility, re-ran -u fusion_claims on
both so the ir_model_fields_selection.name row was resynced.
In v19.0.8.0.3 the new handoff_to_client MOD state was inserted between
project_complete and pod_submitted in the Selection list, so it rendered
near the bottom of status dropdowns even though it happens very early in
the workflow. Also forgot to add it to _expand_mod_statuses, so it would
not appear as a kanban column without holding records.
Fixes:
- Move handoff_to_client in the Selection tuple to position 6 (right after
quote_submitted, right before awaiting_funding) — parallel to
quote_submitted as the alternative entry into awaiting_funding
- Add handoff_to_client to the main list in _expand_mod_statuses so it
always appears as a kanban column
- Add handoff_to_client AND awaiting_funding to statusbar_visible on the
sale_order form view (awaiting_funding was also missing before)
Deployed to odoo-westin (westin-v19) and odoo-mobility (mobility).
Re-ran -u fusion_claims on both so Odoo resynced the Selection
sequence column. Verified: both databases now show sequence 0..15 with
handoff_to_client at sequence 5, identical between servers.
Reworks the March of Dimes workflow to match reality: the OT does their own
disability assessment and provides the VOD letter; our accessibility specialist
then visits to produce the proposal/drawings/quote; and the application can be
submitted by us (internal), the client, or the authorizer themselves. The old
workflow flattened all this into one assessment state with a dead-end
funding_denied and no document tracking.
Data model (13 new sale.order fields):
- 5 new document binaries + filenames: VOD letter, Application Form (filled),
Notice of Assessment, Property Tax, Proposal Document
- x_fc_mod_submitted_by Selection (internal/client/authorizer)
- x_fc_mod_handoff_date, x_fc_mod_vod_requested_date
- x_fc_mod_accessibility_specialist_id (m2o res.partner — internal or external)
- x_fc_mod_previous_status_before_hold (for proper resume)
- x_fc_mod_funding_denial_reason (captured via wizard)
Settings (4 res.company fields + res_config_settings mirrors):
- x_fc_mod_application_form (blank) + filename
- x_fc_mod_vod_form (blank) + filename
- x_fc_mod_followup_assignee_mode (office_contact / sales_rep)
- x_fc_mod_followup_office_contact_id
res.partner: added 'accessibility_specialist' to x_fc_contact_type.
State machine:
- New state handoff_to_client between quote_submitted and awaiting_funding,
used for paths B/C (client or authorizer submits themselves)
- Fixed action_mod_on_hold to save x_fc_mod_previous_status_before_hold
- Fixed action_mod_resume to restore previous status (was hardcoded to
in_production, losing context for cases held earlier)
4 new wizards:
- mod_submission_path_wizard — chooses submitted_by, auto-fires VOD request
email on first switch to 'internal'
- mod_funding_denied_wizard — captures denial category + reason
- mod_resubmit_wizard — revises + resubmits denied cases (with optional
doc clearing)
- mod_submission_confirmed_wizard — records client/authorizer confirmed
submission, advances to awaiting_funding
8 new action methods:
- action_mod_set_submission_path, action_mod_request_vod,
action_mod_handoff_to_client (validates docs, fires handoff email),
action_mod_confirmed_submission, action_mod_resubmit_from_denied,
action_mod_cancel_from_denied, action_mod_reopen_cancelled
- action_mod_funding_denied now opens the denial wizard
3 new email methods + 2 existing fixes:
- _send_mod_vod_request_email — auto-attaches blank VOD form from company
settings, sent to authorizer when we are handling submission
- _send_mod_handoff_email — two templates (client vs authorizer), attaches
proposal + drawing + blank MOD Application Form
- _mod_company_attachment helper for building attachments from company Binary
- Fixed _send_mod_assessment_completed_email to include authorizer
- Fixed _send_mod_pod_submitted_email to include client
New cron:
- _cron_mod_handoff_followup (daily 09:00) — creates mail.activity for office
to confirm MOD submission. Assignee via company setting (office contact or
sales rep). Uses existing rolling-window cap (2/month per order).
Views:
- sale_order form: new status-bar buttons (set path, request VOD, handoff,
confirm, resubmit, cancel, reopen), new document section in MOD Documents
tab with submission-path tracking, denial details, hold history
- res_config_settings: new MOD blank forms upload + assignee config
Deployed to odoo-westin (westin-v19) and odoo-mobility (mobility). Pre-deploy
FK cleanup from earlier session means mobility updated cleanly without
workaround. HTTP 200 on both, cron verified active, all new fields present.
Workflow (from the FigJam ADP board):
- 9 new ADP action methods to wire up the orphan states that the board
showed had no entry or no exit path: put_on_hold, withdraw, mark_denied,
mark_rejected, mark_needs_correction, cancel, reopen_cancelled,
reopen_expired, resubmit_from_denied.
- 12-month auto-expire cron (_cron_adp_expire_approved) configurable via
fusion_claims.adp_approval_expiry_months, runs daily at 03:00.
- 3 new recovery buttons in the ADP form view (Reopen cancelled, Reopen
expired, Resubmit from denied) in both the primary status bar and the
secondary details panel.
Email (from the 2026-04 email audit):
- 6 new ADP stage email methods via a shared _adp_send_stage_email helper:
assessment_scheduled, assessment_completed, application_received, accepted,
cancelled, expired. Each has a matching dispatch entry in write().
- _send_rejection_email now includes the client (was authorizer-only).
- _send_accepted_email excludes the authorizer per the new rule: "Accepted"
is a passive intermediate state with no authorizer action required.
- _send_ready_for_delivery_email excludes the authorizer: operational
scheduling, not delivery confirmation. Authorizers are notified at
case_closed when the product is actually delivered.
- action_adp_put_on_hold and action_adp_withdraw now fire their matching
email methods so direct action-method calls get the same notifications
as the status_change_reason_wizard path.
Authorizer notification rule (locked in for this update):
Send to authorizer ONLY for initial involvement (assessment/submit/
resubmit), delivery confirmation (case_closed), and problem states
(rejected, denied, needs_correction, withdrawn, on_hold, cancelled,
expired). Skip for billing, payment, ready_delivery scheduling, and
passive intermediates (accepted).
Scope: ADP + ADP/ODSP only. MOD workflow emails reverted and deferred
to a separate update.
Deployed to odoo-westin (westin-v19) and odoo-mobility (mobility).
Pre-existing stock_route_warehouse FK orphans on mobility worked around
by verifying fusion_claims transaction committed before container restart.
Two daily MOD crons were fighting each other. _cron_mod_schedule_followups
created a mail.activity on every MOD order in quote_submitted/awaiting_funding;
_cron_mod_escalate_followups unconditionally deleted the activity after
sending its one-time reminder email. The activity was recreated every day
in a tight loop with no per-period cap — a legitimate 2-4 month wait for
a MOD funding decision would generate dozens of activity churn events and
a bulk email burst the first time the escalate cron ran against a backlog.
Fix:
- New fields x_fc_mod_followup_month_count / _month_start / _cap_notified
(copy=False) track a rolling window per order.
- New config params mod_followup_max_per_month (default 2),
mod_followup_window_days (30), mod_followup_max_per_cron_run (10).
- _send_mod_followup_email resets the window after 30 days, refuses to
send past the cap, and posts a one-shot chatter note explaining why.
- _cron_mod_schedule_followups no longer recreates the activity when the
cap has been hit and stops daily-bumping x_fc_mod_next_followup_date.
- _cron_mod_escalate_followups processes oldest-deadline-first with a
per-run throttle, only unlinks the activity on a successful send so
humans can still action capped cases manually.
- write() resets the rolling counters on any real MOD status change.
Deployed to fusion_claims v19.0.8.0.1 on odoo-westin (westin-v19,
36 affected orders) and odoo-mobility (mobility, 2 affected orders).
- Add fusion_claims.adp.export.record model with filestore-backed Binary field
for tracking exported ADP claims files organized by Year > Month > Posting Period
- Add tree/form/search views with default group-by hierarchy, latest first
- Add "Export Files" menuitem under ADP menu section
- Add bulk ZIP download server action for multi-select export
- Replace Documents app storage with new model in export wizard
- Remove Documents-related methods (_save_to_documents, folder creation)
- Add migration button in Settings to move existing Documents files
- Fix Export ADP button visibility: only show on ADP portion invoices
- Remove redundant Sync All button from invoice form
- Add ACL entries for billing users (read/create) and managers (full CRUD)
- Bump version to 19.0.7.3.0
Made-with: Cursor