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).
Pre-fill image field only for NEW variants. Already-synced variants
get an empty image field — existing WC image is kept unless the user
actively uploads a new one in the wizard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added 'Default' toggle column in variant push wizard. First variant is
pre-selected as default. User can change it. The default variant's
attribute values are set as WC product's default_attributes using
WC attribute IDs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Image endpoint was returning base64 text instead of decoded binary.
Now properly decodes base64 from Odoo Binary field and detects actual
content type from magic bytes (JPEG, PNG, GIF, WebP).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Product image URL was serving placeholder because writing to image_1920
doesn't work for variants (computed field). New approach:
1. Store image in wizard line table (attachment=False, bytea column)
2. Serve directly via /woo/image/{line_id}/{filename} endpoint
3. WC downloads real image data from this URL
4. Also saves to image_variant_1920 for Odoo reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Binary field with attachment=True (default) stores in ir.attachment
which doesn't work reliably for transient model inline list records.
Set attachment=False to store in the woo_variant_push_line table directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Image wasn't persisting because transient model write was in the same
transaction. Added cr.commit() after saving image to ensure it's
available when WC downloads it. Added size/type logging to trace
image data flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The wizard's binary image field wasn't being saved to the product.product
record, so the public URL returned the default placeholder. Now saves
line.image → variant.image_1920 first, then generates the URL with a
cache-busting timestamp. Only includes image in WC data if the wizard
line has an actual image uploaded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WooCommerce requires a file extension in image src URLs to determine
file type. Added filename.png to all Odoo image URLs. Also fixed
variable name ordering bugs where img_name was used before defined.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WC consumer key cannot auth against /wp/v2/media (401). Instead, pass
Odoo's public image URL in the product/variation data and let WC
download it directly from {odoo_base}/web/image/product.product/{id}/image_1920.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WooCommerce silently ignores attribute 'name' on variation updates —
requires 'id' (the WC attribute ID). Built a lookup map from the
parent product's wc_attributes and use 'id' in all variation payloads.
Fixed in all 3 files: variant push wizard, product creation wizard,
and product map direct push.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Odoo web client strips readonly field values during record creation,
so already_synced/wc_variation_id/map_id were always NULL. Removed
readonly from Python model, added force_save="1" in XML view to ensure
these tracking fields are persisted through the wizard lifecycle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Archived variants (active=False) and variants with no attribute values
were being included, causing WC 400 errors. Now only active variants
with attribute assignments are shown and pushed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Onchange wasn't firing when wizard opened via context defaults. Moved all
variant line population to default_get so lines are pre-filled immediately.
Added debug logging to trace new vs update classification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update path sends attributes per variation (was missing, causing
"Any COLOR..." on WC)
- Sets default_attributes on parent product (first variant's values)
- Better API error logging with response body
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Already synced variants are editable — change price, SKU, image and click
Save & Sync to update them on WooCommerce. New variants are created,
existing ones updated in a single action. Button shows on all products
with variants (purple for new, grey for already synced).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced direct push with a wizard that shows all variants in an editable
table. User can review/edit standard price, sale price, SKU, and image
per variant before pushing. Include/exclude toggle per variant. Already
synced variants shown for reference. Geo-tags images if configured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed separate Variants column — button now appears right next to the
Odoo product name so it's visible without scrolling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a mapped product is simple on WC but has variants in Odoo, a purple
"N variants" button appears in the Variants column. Clicking it:
1. Converts the WC product from simple to variable
2. Creates WC attributes and terms from Odoo attribute lines
3. Creates a WC variation for each Odoo variant with price/SKU/stock/tax/image
4. Creates woo.product.map records for each variation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Search endpoint now queries product.template instead of product.product,
so a product with 20 variants shows as 1 row with a "20 variants" badge
instead of 20 duplicate rows. Category name shown in sub-text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detects Odoo product variants (product.template.attribute_line) and creates
WC variable products with attributes and variations. Each variation gets
its own price, SKU, image, stock, and tax class. Variant lines shown in
wizard with include/exclude toggle. WC attributes and terms auto-created.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tax mapping and pricelist mapping now live directly on the instance
form under Sync Settings. Added Fetch WC Tax Classes button that pulls
tax classes from WC API and auto-matches. Removed standalone menu items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replaced hardcoded badge step indicator with Odoo's native statusbar widget
- Removed all hardcoded Bootstrap colour classes (bg-primary, text-muted, etc.)
- Using oe_highlight and oe_link for buttons (theme-aware)
- Cleaner 2-column layout for basic info and review steps
- Full-width separators and fields for content step
- Images in 2-column grid
- SEO meta fields use proper group layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Used doAction onClose callback instead of await — the wizard dialog
close now triggers reload of excluded count and product list. Same
fix applied to product creation wizard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Categories to hide are stored on woo.instance and persist across sessions.
Click 'Hidden (N)' button to open wizard where you can add/remove categories
using a tag picker. Eye/eye-slash toggle to quickly apply or unapply the
filter without losing the saved list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Filter by Odoo product category or clear filter. Backend supports
both include and exclude category filtering. Loads all categories
on init for the dropdown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed group wrappers around prompt fields. Using label + field pattern
directly inside the page so prompts span the full available width.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced free-text model field with Selection showing standard models
from both providers: Claude Opus/Sonnet/Haiku and OpenAI GPT-4o/4.1/o3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4-step wizard: Basic Info → Images → Content & SEO → Review & Create.
AI generates product titles, descriptions, meta data using Claude or OpenAI.
Image geo-tagging with company EXIF data. SEO meta pushed to Rank Math,
Yoast, AIOSEO, and SEOPress simultaneously. Products created with CAPS
name in Odoo, Title Case in WooCommerce.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AIService wraps both Anthropic Claude and OpenAI APIs for product content
generation. ImageProcessor handles EXIF geo-tagging with company info and
GPS coordinates using piexif.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Category mapping between Odoo product categories and WC categories with
auto-match by name and manual mapping UI. AI settings for Claude/OpenAI
with customizable prompts for product content generation. GPS coordinates
for image geo-tagging pulled from company settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both SKU fields are inline-editable. Arrow buttons sync individual SKUs.
Bulk buttons sync all SKUs Odoo→WC or WC→Odoo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>