Phase 3 (fusion_accounting_assets) changed list_assets() to return
{count, total, assets} dict instead of a flat list — consistent with
bank_rec.list_unreconciled, reports.run_report, followup.list_overdue.
The pre-existing test in fusion_accounting_ai still asserted isinstance(rows, list)
and was failing on every run since Phase 3 merge. Updated to assert dict shape.
Made-with: Cursor
Ships the remaining items from the Sales UX Uplift plan:
D2 BOM Items kanban
New view_sale_order_line_bom_kanban grouped by x_fc_part_catalog_id.
Smart button 'BOM Items' on SO form opens it.
D5 Archive line
x_fc_archived Boolean on sale.order.line plus action_archive_line /
action_unarchive_line. Acknowledgement report filters out archived
lines.
D6 Add Quoted Lines sub-wizard
New fp.add.from.quote.wizard parallel to fp.add.from.so.wizard. Pick
quotes for this customer and clone them into direct-order lines
carrying part, coating, qty, unit price (from calculated or
override), and notes. Button '+ Add From Quotes' on wizard Lines tab.
D7 SO Acknowledgement PDF
New ir.actions.report + QWeb template in configurator/report/.
Header shows customer / contact / PO / Customer Job #, Bill-To,
Ship-To, planned start + customer deadline + ship-via. Line table
skips archived lines. Includes external notes, blanket-order
callout, and customer-signature + vendor-signature blocks.
Binding added to sale.order so it shows up under Print menu.
D9 Quick-nav chip bar
New smart buttons on SO form: Invoices / Pickings / NCRs / Files
with counts and icons. Each opens a filtered list. NCR button
appears only when fusion_plating_quality is installed.
D10 SO/WO perspective toggle
view_sale_order_line_wo_kanban grouped by x_fc_wo_group_tag. Smart
button 'By WO' on SO form.
D11 Assemblies minimal model
fp.sale.assembly + fp.sale.assembly.line with name, ship_to, count,
procured_count, completed_at. UX (forms / kanbans / integration
into receiving) deferred — model only for now.
D14 Uploaded Files
Files smart button on SO form opens ir.attachment kanban filtered
to this SO. Count appears in the chip bar.
F4 Signed tracking
x_fc_signed_at / x_fc_signed_by / x_fc_is_signed on sale.order +
action_mark_signed helper. Signed column on quotes list view.
F10 New Quote
Kept on existing action_fp_quotations (already surfaces the
default New button).
E5/F9 Action icons per row
Deferred — requires a custom widget; the native PDF action via the
Print menu covers 80% of the use case.
Bumped to 19.0.8.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two features from Phases B/C that were previously only data now do work:
1. WO GROUPING (x_fc_wo_group_tag)
_fp_auto_create_mo rewritten to iterate order_lines and group by
x_fc_wo_group_tag. Lines sharing a tag collapse into ONE MO with
product = first line's part.product_id, qty = Σ line qty,
recipe = first line's coating_config.recipe_id. Untagged lines
each get their own MO. Legacy path preserved for service-line SOs
with no plating data.
Idempotency is per (origin, tag): re-confirming an SO doesn't
create duplicate MOs for already-grouped lines.
New on mrp.production:
- x_fc_wo_group_tag (Char, tracking)
- x_fc_sale_order_line_ids (M2M back to sale.order.line)
- x_fc_start_at_node_id (Many2one fusion.plating.process.node)
2. START-AT-NODE (x_fc_start_at_node_id)
_generate_workorders_from_recipe pre-computes allowed_ids as the
set of {descendants of start_node} ∪ {ancestors of start_node}.
_is_node_included rejects any node outside that set. This skips
sibling branches earlier in the recipe while keeping the
container hierarchy so WO sequence numbers still make sense.
Smoke-tested S00070 (4 lines, 2 tagged groups + 1 untagged) -> 3 MOs:
WO#A qty=15 (2 lines batched), WO#B qty=50 (1 line), untagged qty=7
(1 line). Each got the ENP-ALUM-BASIC recipe.
Start-at-node smoke on the same recipe: full generation = 9 WOs,
partial with start_at='Ready for processing' = 1 WO.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- test_create_minimal/negative_delay used sequence=1, which now collides
with the seeded Friendly Reminder level. Use sequences 901/902.
- migration backfill: search by name (not raw seq) for idempotency,
allocate sequence as max(existing)+1 to avoid both seed clashes and
within-batch collisions when Enterprise has duplicate sequence values.
Made-with: Cursor
F1 follow-up: x_fc_follow_up_date + x_fc_follow_up_user_id fields on
sale.order, surfaced in the quotations list + a 'Needs Follow-Up'
preset filter.
F2 expires: native validity_date exposed as togglable column on the
quotes list + an 'Expired' preset filter.
F3 email status pills: x_fc_email_status computed (draft / sent /
opened / won). 'Opened' detects via mail.notification.is_read on any
email-type mail.message attached to this SO.
F5 part numbers summary: x_fc_part_numbers_summary ("PN1, PN2 (+3
more)") across order_line parts, togglable column.
F7 from-RFQ filter reuses existing x_fc_rfq_attachment_id.
Views:
- view_sale_order_list_fp_quotes (new list dedicated to quotes).
- view_sale_order_search_fp_quotes with filters Draft / Sent / Won /
From RFQ / Needs Follow-Up / Expired + group-bys.
- action_fp_quotations rewired to both of the above.
Bumped to 19.0.7.2.0. Closes all six phases originally planned.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E1/E2/E3/E4: list view gets new togglable columns for
- x_fc_wo_completion (e.g. '3/5'): count of completed vs total WOs
- x_fc_invoiced_amount (Monetary): sum of posted customer invoices
minus credit notes
- x_fc_margin_amount + x_fc_margin_percent: reuses Phase D8 computes
- x_fc_is_blanket_order toggle
New sale.order.search view (sale.order.search.fp) with preset
filters: My Orders / Open / Confirmed / Done / Blanket / Has Rush /
Overdue, plus group-bys for Customer / Status / Customer Deadline.
Bumped to 19.0.7.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
D3 first half: x_fc_workorder_count computes live count of active MRP
work orders linked to this SO (via mo.origin = so.name). Adds a
'Active WOs' smart button next to the existing PO / RFQ buttons on
the sale.order form. Clicking opens a filtered mrp.workorder list
grouped by MO.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase D first landing covers the quick-win Steelhead-parity fields on
the SO form / list:
- D1: x_fc_deadline_countdown ("in 2d 3h", "overdue 1d 4h") computed
from commitment_date. Surfaced in SO form scheduling group and as
togglable column on the SO list.
- D4: x_fc_internal_note + x_fc_external_note split (html). Existing
'note' field is left untouched for back-compat. External note is
intended for the SO acknowledgement + portal; internal note is
shop-floor only.
- D8: x_fc_margin_amount + x_fc_margin_percent, currently computed
against fp.coating.config.unit_cost if defined (else 0 -> 100%
margin). When cost rollup lands on fp.coating.config, margin will
reflect reality automatically.
- D12: x_fc_contact_phone related to partner.phone (readonly) on SO
header.
- D13: x_fc_ship_via Char on SO header (carrier name).
Smoke: S00066 shows 'in 9d 22h' countdown + \$3025 margin; S00069
shows 'in 24d 22h' + \$750. Contact phone pulls from partner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase C complete on odoo-entech. Smoke-tested S00069:
- C1 x_fc_start_at_node_id = Ready for De-Masking (resume-rework)
- C2 x_fc_part_wo_description = internal rework note
- C5 x_fc_is_one_off = False
- C3 x_fc_quote_id slot wired (no quote picked in this smoke)
- C4 push-to-defaults wrote EN High-Phos back onto part catalog
Phase D (SO detail view), Phase E (SO list view), and Phase F
(Quotes list) are independent tracks — outlined in the plan doc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds quote_id (Many2one fp.quote.configurator) on the wizard line
with a domain scoped to the wizard's customer + quote states (sent /
accepted / won). Onchange auto-fills part, coating, and unit price
(final = estimator_override_price or calculated_price, per-part).
Mirrors x_fc_quote_id on sale.order.line for the audit trail. Surfaced
as a togglable column on the SO line tree and under "Qty & Price" on
the wizard line drill-in form.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- fusion.followup.cron AbstractModel with two handlers
- cron_fusion_followup_daily_scan: walks every overdue partner and
delegates to engine.send_followup_email
- cron_fusion_followup_risk_refresh: weekly refresh of
fusion_followup_risk_score / risk_band on res.partner
- V19 ir.cron records (no numbercall field)
- 2 smoke tests added (80 total)
Made-with: Cursor
Adds x_fc_default_coating_config_id and x_fc_default_treatment_ids
fields on fp.part.catalog. Wizard line gets a push_to_defaults
toggle. After action_create_order confirms the SO, any line with
push_to_defaults=True writes its coating + treatments back onto the
part catalog entry as the new defaults.
Reverse direction too: onchange on part_catalog_id in the wizard
line seeds coating + treatments from the part's defaults (if set and
the line doesn't already have them).
Part catalog form gets a new "Defaults" tab showing the stored
defaults. Smoke-tested: pushing default on order 1 populates the
catalog entry; new wizard line for that part auto-seeds the coating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Switch FUSION_MODEL to fusion.followup.engine so adapter mode
selection matches the new module
- Add list_overdue() with fusion/enterprise/community variants
- Re-route send_followup_via_fusion to engine.send_followup_email
- 4 new TransactionCase tests (73 total)
Existing aging / overdue_invoices adapter methods continue to fall
back to the community implementation.
Made-with: Cursor
C1: start_at_node_id per wizard line, mirrors to x_fc_start_at_node_id
on sale.order.line. Domain filters to nodes descending from the
coating_config's recipe so the estimator only picks valid resume
points. bridge_mrp will use this in a follow-up to skip ancestor
steps in the generated work order.
C2: part_wo_description (separate from customer-facing line_description)
lets the planner add internal-only notes that appear on the travelling
sheet only. Mirrors to x_fc_part_wo_description on sale.order.line.
C5: is_one_off flag for prototype / non-catalog parts. Mirrors to
x_fc_is_one_off. Actual skip-catalog behaviour will be wired in a
later pass.
All three fields appear in the wizard line drill-in form (under a new
"Work Order (internal)" group) and as togglable columns on the
sale.order.line tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end flows over a real posted receivable line: aging discovery,
level resolution, send-with-cache reuse, pause+force override, and
audit history growth. Adds ignore_pause kwarg to compute_followup_level
so force=True in send_followup_email reaches level resolution.
Made-with: Cursor
The orchestrator AbstractModel for follow-up lifecycle.
get_overdue_for_partner, compute_followup_level, send_followup_email,
escalate_to_next_level, pause_followup, reset_followup, snapshot_followup_history.
All controllers, AI tools, wizards, cron must route through these
methods; no direct ORM writes to fusion.followup.run from anywhere else.
Made-with: Cursor
Phase B complete on odoo-entech:
- B1/B2: Blanket + Block Partial flags on wizard header + sale.order
- B3: x_fc_wo_group_tag per SO line (bridge_mrp will use this to
batch MOs in a follow-up)
- B4: 'Add From Prior SO' sub-wizard for repeat orders
- B5: Per-line is_missing_info compute + amber row decoration
- B6: Rush already on line (added in Phase A)
Smoke-tested: wizard accepts 4 lines (1 with missing price, 3 WO-tagged
across 2 groups), banner shows correctly, missing row highlighted in
amber, after fix SO creates cleanly with all flags + tags persisted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task B4. New fp.add.from.so.wizard transient model: given the current
direct-order wizard + customer, lists the customer's prior confirmed
sale orders, lets the estimator tick source lines, and clones them
into fp.direct.order.line rows (part, coating, treatments, qty,
price, deadline, rush, WO group, description).
Button "+ Add From Prior SO" lives on the Lines tab of the main
wizard, visible once the customer is picked. Sub-wizard rejects
source lines that predate the new plating fields (no x_fc_part_catalog_id).
Smoke-tested on odoo-entech: copying all 3 lines of S00066 onto a
fresh wizard reproduces part/coating/qty/price correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase B partial landing (B1, B2, B3, B5):
- B1/B2: x_fc_is_blanket_order and x_fc_block_partial_shipments on
sale.order; matching booleans on the wizard header.
- B3: x_fc_wo_group_tag Char on sale.order.line and wo_group_tag on
wizard line. Free-text tag; bridge_mrp will batch lines sharing a
tag into one MO in a follow-up.
- B5: is_missing_info computed Boolean on fp.direct.order.line;
tree uses decoration-warning to highlight incomplete rows in amber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task A8. Closes Phase A of the direct-order rewrite.
Smoke-tested on odoo-entech: wizard accepts 3 lines (qty 65, total
\$3,025 + tax -> \$3,418.25), creates SO S00066 in state=sale with all
header fields (customer job #, three deadlines, bill/ship addresses)
and per-line fields (part, coating, qty, price) populated correctly.
Phase A complete. Phase B (blanket flag, block partial, WO grouping,
add-from-SO, missing-info banner polish) and Phase C (to-node picker,
quote link, push-defaults) outlined in the plan doc; Phases D/E/F
(SO detail, SO list, quotes list) are separate tracks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>