Captures the current state of the system-wide fine-tuning initiative so a
fresh Claude Code session can resume without context loss.
CLAUDE.md additions (fusion_plating/CLAUDE.md):
* Sub-project roadmap (Sub 1 through 8 + two deferred items)
* Sub 2 locked decisions (Q1–Q6 answers)
* Sub 2 defensive measures that prevent rework when later subs land
* Sub 6 / 7 / 8 previews from the client transcript
* Client-confirmed operational thresholds (tank polling, active tanks)
* How to resume in a fresh session
Sub 2 design spec (docs/superpowers/specs/):
* Part Data Model Overhaul — covers gaps 2b, 2c, 2d, 4
* 12 sections: scope, data model, migration, UI, cert resolution,
reports, testing, defensive measures, files touched, rollout,
success criteria, open questions
* All clarifying questions answered; zero placeholders
* Ready for writing-plans skill to generate implementation plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a QC uploaded the XDAL 600 report, the CoC PDF render pipeline
now appends the Fischerscope PDF directly after the cert pages. This
matches what aerospace / Nadcap auditors expect (and how Steelhead
ships certs today) — a single PDF file carrying both the certificate
declaration and the raw equipment report.
Flow:
* _fp_generate_cert_pdf renders the CoC via QWeb as before
* _fp_merge_thickness_into_cert resolves the QC for the MO (preferring
the passed one) and extracts its thickness_report_pdf_id bytes
* PyPDF2.PdfMerger concatenates CoC then Fischerscope into a single PDF
* Merged bytes replace pdf_content before the ir.attachment is written
* Falls back to CoC-only (and logs a warning) if PyPDF2 is missing or
either PDF fails to parse — never blocks MO completion
Smoke test: synthetic Fischerscope + real QWeb CoC → 2-page merged PDF
with page 1 CoC text and page 2 Fischerscope text, verified via
PyPDF2 extract_text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Future sessions shouldn't silently re-install retired modules during
install/upgrade sweeps. Added an explicit "Retired / Do-Not-Install
Modules" section with guardrails:
- Don't include in -i / -u sequences
- Don't add as a depends target
- Don't re-sync to entech /mnt/extra-addons/custom/
- Don't recommend installing without user confirmation
Covers the two modules currently in this state:
- fusion_plating_culture (code in repo, uninstalled on entech)
- fusion_plating_sensors (fully removed, absorbed into fusion_iot)
Also struck-through the "| 80 | Culture | ..." menu row and added a
retired tag to the module-list tree so at-a-glance scans don't
suggest it's a live part of the menu hierarchy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Culture/values/recognitions framework was shipping zero data and zero
workflow integration for this client. It's a people-ops concern (peer
kudos, "Fundamental of the Week" rotations) with no overlap with the
technical plating pipeline — no interaction with process recipes,
quality holds, sensors, or compliance.
Verified zero data on entech before uninstalling:
fusion.plating.value 0 records
fusion.plating.value.set 0
fusion.plating.value.recognition 0
fusion.plating.value.rotation 0
Clean uninstall on entech, module dir removed from disk. The Culture
top-level menu disappears. If a future client wants it back, the
module is easy to re-author — nothing we built on top of it depends
on it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fusion_plating_sensors had broader scope (sensor_type taxonomy,
dashboards, location flexibility) but its core logic was ALL
scaffolding — alert rules stored thresholds with zero side effects,
measurement create just filled a name sequence, the HTTP endpoint
required user-auth session cookies. Meanwhile fusion_plating_iot had
the actual working alerting: in-spec checks, quality-hold auto-raise
with excursion dedupe, setpoint + deviation, token-auth ingest for
headless hardware. Plus 563 real readings from the pilot Pi.
Right unification: keep fusion_plating_iot (working) as the base,
port the valuable structural bits from fusion_plating_sensors, demolish
the latter entirely.
**Ported to fusion_plating_iot:**
- `fp.sensor.type` — taxonomy model with 8 seeded types (Temperature,
pH, Conductivity, Level, Pressure, Flow, Concentration, Switch).
Richer than the device_kind Selection; hardware-independent (one
"Temperature" type covers DS18B20 / PT100 / thermocouple).
- `fp.sensor.dashboard` — named grouping of sensors with
out-of-spec count. Simple but useful ("ENP Line 1 — all tanks")
without the broken alert-rule complexity.
- Extended `fp.tank.sensor`:
* `uuid` (stable logical ID, survives hardware swaps)
* `sensor_type_id` (link to the taxonomy above)
* `work_center_id`, `facility_id`, `location_name` — alternatives to
tank_id so probes can live on ovens, ambient air, effluent pipes
without faking a "tank"
* `effective_location` computed — picks the first non-empty of the
four location fields for display
**Post-install hook** backfills UUID + default sensor_type on existing
live sensors. Verified on the 2 pilot sensors: both got UUIDs, both
auto-assigned the Temperature type via device_kind=ds18b20 mapping.
**Deleted** (all of fusion_plating_sensors, 1205 LOC):
- fp.sensor (replaced by fp.tank.sensor with added fields)
- fp.sensor.measurement (replaced by fp.tank.reading)
- fp.sensor.alert.rule (replaced by inline alert_min/max + working hold)
- /fp/sensor/measure controller (replaced by /fp/iot/ingest)
- fp.sensor.measure.wizard (not needed — Odoo's normal create form works)
- The "Sensors" submenu hierarchy (Dashboards/All Sensors/Measurements/
Sensor Types) that created the dup menus the user reported
**Menu now**: Plating → Operations → Sensors
- Dashboards (fp.sensor.dashboard)
- Sensors (fp.tank.sensor — renamed from "Tank Sensors" since
it supports non-tank locations now)
- Readings (fp.tank.reading)
- Sensor Types (fp.sensor.type)
No data loss: all 591 Pi readings preserved (up from 563 earlier as
the live poller kept running throughout the refactor). Brief 503 on
the Pi during the Odoo module-update restart; poller auto-retried on
the next 30s tick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sensors previously only tracked alarm thresholds (alert_min/alert_max).
Missing the third piece of standard process control: the SETPOINT —
what the heater/chiller controls toward and what dashboards compare
against. Without it an operator can't tell whether 89°C is "on target"
or "barely still in spec".
Schema changes:
**fusion.plating.bath.parameter** (shop-wide default)
- New `target_value` field — the default setpoint for this parameter
across the shop (e.g. 87°C for ENP bath). Parallel to existing
target_min / target_max.
**fp.tank.sensor** (per-sensor override)
- New `target_value_override` — per-sensor override, zero = inherit
from parameter. Matches the existing override pattern for alert
thresholds so users can fine-tune per-tank without touching the
shop-wide spec.
- New `effective_target` / `effective_target_unit` computed — resolves
override → parameter default, converts to company-preferred unit.
- New `_get_setpoint()` helper for internal use.
**fp.tank.reading**
- New `deviation_from_target` — signed Δ from the sensor's effective
setpoint, in the company's preferred unit. Positive = above, negative
= below, zero if no setpoint defined.
- New `deviation_band` (selection: on/near/far/out/none) — coarse band
for fast visual scanning. `on` = within ±1° of target, `near` = ±3°,
`far` = beyond, `out` = actually out of the alarm band.
**Views**
- Sensor form: split the alerting panel into two groups — "Target
(setpoint)" on the left, "Alarm band" on the right. Makes the
distinction between "where we want to be" and "where we'd panic"
visually obvious.
- Reading list: new Δ + band columns, with decoration classes
(success/info/warning/danger) so the list reads at a glance.
- Tank form Sensors tab: inline setpoint + unit column.
Seeded: parameter "Bath Temperature (Hot Process)" now carries
target_value=87°C as a realistic ENP shop default. Sensors inherit
unless they set their own override.
Design decisions kept simple:
- Did NOT add a warning band (warn_min/warn_max). Two-tier model
(setpoint + alarm band) is enough for the pilot. Can add soft
warnings later as a separate commit if ops wants them.
- Did NOT auto-control heaters. Setpoint is stored as metadata only;
actual heater actuation via IoT is a future phase C project.
Verified: setpoint 87°C stored → displays 188.60°F on the live pilot
sensor (company pref = F). Each incoming reading correctly computes
signed deviation; bands colour the reading list appropriately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two user-reported bugs on S00056 (and visible on any SO/invoice):
Bug 1: Cancelling the Send dialog still sends the email.
action_quotation_send is a button handler that returns a
compose-dialog action synchronously. Our override was calling
_dispatch('quote_sent', ...) AFTER super(), which immediately
sends the email via template.send_mail() before the user ever
sees the dialog. Clicking Cancel at that point only dismisses
the already-sent email's compose window.
Fix: removed the _dispatch call entirely from action_quotation_send.
The Send button IS the manual-send path; Odoo's compose dialog
(pre-populated with our FP: Quotation Sent template thanks to
_find_mail_template) handles the send-or-cancel choice correctly.
If an auto-quote-sent notification is ever wanted, it should be
wired from a cron that scans SOs that just transitioned
draft -> sent, not from the button handler. Noted in a code
comment.
Bug 2: Two copies of the same PDF attached to every email.
The mail.template records now carry report_template_ids (set by
the post_init hook from the previous refactor) which Odoo uses to
auto-attach PDFs on send_mail(). But the fp.notification.template
records ALSO had attach_quotation / attach_sale_order / attach_invoice
flags set, which cause _collect_attachments() to render the same
PDF a second time and pass it in email_values.
Fix: turned those three flags off in the XML data file. Other
flags (attach_coc, attach_bol, attach_receipt, attach_thickness_report,
attach_packing_list, attach_pod) stay on — those are genuinely
different documents, not dupes.
Because the records are noupdate="1", updated the post_init_hook
to also backfill the flag changes onto existing DB rows so
production instances get cleaned up on -u, not just fresh installs.
Smoke:
attach_quotation=False attach_sale_order=False attach_invoice=False on all three records
1 report per template (no dupes)
action_quotation_send no longer contains _dispatch("quote_sent", ...)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier I built report_fp_so_acknowledgement.xml as a separate
customer-facing document. On review there was no good reason — our
existing report_fp_sale.xml already flips its title between
"Quotation" and "Sales Order" based on state, and carried ~90% of
the same content. Two documents would have meant the shop had to
remember which to send when, and the customer would get two
near-identical PDFs in their inbox.
Consolidation:
1. Merged the four unique blocks from the acknowledgement into
report_fp_sale.xml (both portrait AND landscape variants):
- CUSTOMER JOB # / PLANNED START / CUSTOMER DEADLINE / SHIP VIA
info row (shown only when any of those fields is populated)
- Blanket / block-partial highlight-box callout (shown only
when the flags are set)
- External notes (x_fc_external_note) block above Terms and
Conditions
2. Deleted fusion_plating_reports/report/report_fp_so_acknowledgement.xml
and removed it from the module manifest. Also purged the orphan
ir.actions.report and ir.ui.view DB rows + the stale
ir.model.data entries.
3. Re-pointed the fp_mail_template_so_confirmed mail template's
report_template_ids from the now-gone acknowledgement report to
action_report_fp_sale_portrait. Updated hooks.py accordingly; the
hook now uses "set" semantics (replace all) instead of "add" so
re-running it cleans up stale attachments from prior refactors.
4. UAT on S00071: the Send button pre-selects the FP: Order
Confirmation template with SalesOrder_S00071.pdf attached. The
PDF renders with the new plating rows populated — Customer Job #
AMPH-2026-0420-01, Customer Deadline 05/14/2026 08:00:00 PM,
"Partial shipments blocked" callout, all lines + totals.
One PDF, one Send button behaviour, matching what Odoo and most
ERP systems do.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five fixes from the end-to-end UAT debrief:
1. Menu discoverability (HIGH)
Added a prominent "+ New Direct Order" button in the Sale Orders
list header toolbar (class=btn-primary, display=always). The
existing menuitem at Plating > Sales > New Direct Order was
buried in a submenu that didn't always expand; the toolbar
button is a guaranteed entry point from the most common screen.
2. Escape/X destroys wizard state (HIGH)
Added a prominent info banner at the top of the wizard form:
"Changes are not saved until you click Create & Confirm Order.
Closing this window (Esc or X) discards your entries." The
Cancel button now has confirm="Discard this order? All header
data and line items will be lost." so the intentional-cancel
path also prompts.
3. Shell/cron crash in _fp_auto_create_mo (MEDIUM)
bridge_mrp/models/sale_order.py:232-264 used _() inside list
comprehensions to format the internal chatter summary of newly
created / adopted MOs. _() resolves language from env.context,
which is empty in odoo-shell and cron contexts — triggering a
translate.get_text_alias crash AFTER the MOs had been created.
These strings are internal audit log text, not user-facing UI;
dropped the _() wrappers so the message builds safely from any
context. Same for the per-group error-message on savepoint
rollback.
4. Misleading "100%" margin (MEDIUM)
x_fc_margin_percent displayed 100% on every SO because the cost
rollup from fp.coating.config.unit_cost isn't populated yet.
Added x_fc_margin_available Boolean (True only when at least
one line's coating has a non-zero unit_cost). The SO Plating
tab now hides the margin numbers when margin_available=False
and shows an inline muted note: "Margin n/a — coating cost
rollup not yet populated on any line's treatment."
5. Account Hold banner too loud (LOW)
fusion_plating_invoicing was injecting a full-height danger
alert above every SO header. Slimmed it to a one-line compact
alert with icon: "Account Hold — SO confirmation, invoicing
and shipping are blocked for non-managers." Half the vertical
footprint, less visual competition with the Plating chip bar.
Verified via UAT on S00071.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
D7 template was originally in fusion_plating_configurator with a
Bootstrap-only look-and-feel that didn't match the other Fusion
Plating reports. Re-styled and relocated:
- Moved to fusion_plating_reports/report/report_fp_so_acknowledgement.xml
alongside sale / work-order / job-traveller / invoice templates.
- Uses fp_portrait_styles (company primary colour for headers, .bordered
tables, .info-header row, .totals-table, .highlight-box, .sig-box /
.sig-line / .small-muted).
- Layout now mirrors report_fp_sale.xml: Billing / Shipping address
pair, references row (Customer PO / Customer Job / Order Date /
Salesperson), scheduling row (Planned Start / Internal / Customer
Deadline / Ship Via), blanket-order callout, order line table
(PART / DESCRIPTION / TREATMENT / QTY / UNIT PRICE / SUBTOTAL),
totals table with subtotal / taxes / grand total, and a two-column
signature block.
fusion_plating_configurator no longer ships report/ files — it
depends on fusion_plating_reports transitively via installed modules
order. Report XML ID changed from
'fusion_plating_configurator.report_fp_so_acknowledgement_doc' to
'fusion_plating_reports.report_fp_so_acknowledgement_doc'.
UAT on S00066: PDF renders cleanly with ENTECH branding, contact
footer, subtotal \$3,025 / taxes \$393.25 / grand total \$3,418.25,
signature lines — visually identical to the Quotation/Sales Order
report.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Acknowledgement PDF rendering failed with "QWeb widgets do not work
correctly on 'td' elements" — Odoo's qweb compiler rejects
t-field/t-options directly on <td>. Wrap the monetary / qty widgets
in an inner <span> for every cell that uses them (body rows + footer
total).
Caught during browser UAT on S00066 — shell _render_qweb_pdf smoke
test passed earlier because it bypasses the full compile path, but
the production /report/pdf/ endpoint fails the assertion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase D8 compute was returning x_fc_margin_percent already-multiplied
by 100, but the 'percentage' widget in the SO form multiplies again
for display. Result was 10000% instead of 100%.
Store as 0.0-1.0 fraction; widget handles the multiplier. Caught
during UAT on S00066.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two critical, one important, four polish fixes found by the
pr-review-toolkit code-reviewer.
C1 (CRITICAL) Start-at-node filter dropped later siblings
fusion_plating_bridge_mrp/models/mrp_production.py:448
The allowed_ids set was {descendants} ∪ {ancestors}, which wrongly
excluded nodes that should run AFTER the start node — including
later siblings of the start node and all operations in subsequent
sub-processes. Rewrote the upward walk to ALSO include each
ancestor's later-sequence siblings and their descendants. Smoke on
ENP-ALUM-BASIC: full=9 WOs, partial from mid-tree 'De-Masking'=5
WOs (previously was 1).
C2 (CRITICAL) Duplicate MO on re-confirm of pre-PR SOs
fusion_plating_bridge_mrp/models/sale_order.py:96
Legacy untagged MOs (created before this PR had line-linkage m2m)
were not recognized by the untagged idempotency check, so
re-confirming an already-processed SO would create one additional
MO per untagged plating line. Fix: pre-scan for a single legacy
untagged MO and adopt it by linking ALL untagged plating lines
onto it. Those lines are then treated as covered and no per-line
MOs are created on top. Smoke: S00066 before=1 MO, after
re-run=1 MO.
I5 (IMPORTANT) push_to_defaults wrote to pre-bump revision
fusion_plating_configurator/wizard/fp_direct_order_wizard.py:236
When create_new_revision=True, _get_or_bump_revision() returned a
new part record that got written to the SO line, but the
post-confirm push_to_defaults loop re-read line.part_catalog_id
(still the OLD rev) and wrote defaults there, defeating the whole
point of "save as default". Fix: cache resolved parts in a dict
keyed by wizard-line ID during the build loop, and use that cache
in the push_to_defaults pass.
I3/I4/I6 (PERF) Computes lacked @api.depends and did per-record
search_count / search queries
fusion_plating_configurator/models/sale_order.py
_compute_nav_counts, _compute_workorder_count, _compute_wo_completion
now:
- declare @api.depends
- batch via read_group across the whole self recordset
- rebuild {origin: counts} dicts and assign per record
M7 (MEDIUM) No savepoint around per-group MO creation
fusion_plating_bridge_mrp/models/sale_order.py:_fp_auto_create_mo
A mid-loop exception left group 1's MO persisted and aborted
groups 2..N. Wrapped each group's create in SAVEPOINT/RELEASE/
ROLLBACK TO SAVEPOINT so one bad group no longer corrupts state.
M8 (MEDIUM) Email 'opened' status false-positived on internal CC
fusion_plating_configurator/models/sale_order.py:_compute_email_status
Switched from 'any notification is_read' to 'customer partner has
a read email notification on this SO'.
M9 (LOW) start_at_node_id domain silently empty when coating unset
fusion_plating_configurator/wizard/fp_direct_order_line.py:94
Changed `('parent_id', 'child_of', ...)` to
`('id', 'child_of', ..., or 0)` and clarified the help text.
Regression smoke passed all checks on odoo-entech.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
Task A7. SO form Plating tab gets a new "Customer Reference /
Scheduling" block showing customer_job_number, planned_start_date,
internal_deadline, commitment_date (as Customer Deadline). Order line
tree in SO form now shows per-line part / coating / treatments /
deadline / rush. SO list view exposes customer job # and both
deadlines as togglable columns.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tasks A3 + A6. Wizard rewritten as header + lines architecture:
- Header carries customer/addresses/PO/deadlines/invoicing/notes.
- One SO line created per fp.direct.order.line, carrying part,
coating, treatments M2M, qty, price, per-line deadline, rush flag,
and description.
- action_create_order loops wizard lines, invokes revision-bump
helper, and builds order_line tuples with x_fc_* fields.
- Form view uses notebook (Lines tab with editable tree + drill-in
form, Notes tab), amber missing-info banner at top, running totals
at bottom. Customer deadline maps to Odoo commitment_date on SO.
Single-line fields and their computes/onchanges removed from wizard;
moved to fp.direct.order.line in task A4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task A4. Expands fp.direct.order.line with: part related fields,
optional new-revision block, additional treatment M2M, per-line
deadline + rush flag, description template + free-text, onchange
auto-price-lookup from customer price list, onchange template
suggestion (part > customer > coating), and _get_or_bump_revision
helper that will be called by the SO-creation loop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task A2 of the direct-order-wizard rewrite. Adds SO-header fields for
customer job #, three deadlines (planned start / internal / customer),
bill-to / ship-to address pickers, the line_ids O2M linking to
fp.direct.order.line, computed order totals, and a missing-info
warning banner. Partner onchange now also seeds default addresses.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task A1 of the direct-order-wizard rewrite. Adds the transient line
model that will hold per-part detail (part, coating, qty, price) when
the wizard moves from single-line to header+lines architecture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manager / operator opening an MO had no way to jump back to the
originating SO, see the WO list, or check the receiving record
without going through menus. Add three smart buttons in the MO
form's button-box:
• [📄 Sale Order] — opens the source SO (resolved via mo.origin)
• [⚙ Work Orders 9] — list view filtered by production_id
• [🚚 Receiving 1] — opens the fp.receiving record (or list when
multiple), filtered by mo.x_fc_sale_order_id
New computed fields on mrp.production (non-stored — recomputed on
view load, no migration cost):
• x_fc_sale_order_id — Many2one resolved from origin
• x_fc_workorder_count — len(workorder_ids)
• x_fc_receiving_count — search_count on fp.receiving
Each button hides itself when count is zero / link unresolvable, so
brand-new draft MOs without a source SO don't show stale buttons.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues from the wet-WO card screenshot:
**1. Tank picker bleeding past the card's right edge**
Native <select> defaults to `box-sizing: content-box`, so my
`width:100% + padding-right:2.25rem` rendered the picker wider than
its flex slot — the second picker (Tank, on wet WOs) overflowed the
card border at the typical card width.
Fix on `.o_fp_mgr_picker`:
• `box-sizing: border-box` — keep total width inside the slot
• `min-width: 0` — let flex actually shrink it past its content
• Custom SVG chevron via background-image so we control the
indicator's position exactly (Bootstrap's native chevron sits
almost flush with the right border, which the user flagged
earlier). 1rem of clearance from the right edge.
**2. Take Over button**
Earlier I'd collapsed it to icon-only because the wet card was too
wide; user pointed out the icon alone is confusing. Restored the
"Take Over" label (with icon prefix) so both buttons read cleanly:
[👤 Take Over] [↗ Open WO]
Asset cache cleared as part of the deploy so the recompiled SCSS
+ refreshed XML template ship together. A hard browser refresh
(DevTools → Empty Cache + Hard Reload) is needed to pick them up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Screenshot showed the new WO row was broken:
• Kind chip text clipped ("Mas" instead of "Mask", "Rac" instead of
"Racking")
• WO name truncated to first 4 chars
• The wet WO had no info column at all — kind chip + name pushed
off-screen by the tank picker
• "Needs:" chip showed as just an exclamation icon with "N" cut off
• Take Over and Open WO buttons unevenly sized
Root cause: `.o_fp_mgr_wo_info` carried `nowrap + ellipsis` from the
old single-line design, but the new template stacks kind chip + name +
meta + needs across multiple lines. Plus the rigid grid
(1fr auto auto auto auto) gave the info column whatever the dropdowns
left over — usually nothing.
**Layout rewrite** — flex with wrap instead of grid:
• `.o_fp_mgr_wo_row` — flex row, info on left, actions on right,
wraps to two rows on narrow viewports.
• `.o_fp_mgr_wo_info` — `flex: 1 1 280px` so it grows but never
narrower than 280px. Contains a vertical stack: title row
(badge + name) → meta row (workcenter / role / equipment chips)
→ needs row (yellow chip if anything missing).
• `.o_fp_mgr_wo_actions` — `flex: 0 0 auto` with its own gap, so
pickers + buttons align cleanly to the right.
• Kind chip can wrap to its full label; meta row uses `flex-wrap`
so equipment hints don't get clipped.
• Take Over collapses to icon-only with title tooltip — the row
was getting too wide on the wet kind (which adds the tank picker).
**Other tweaks**
• Added `tank_id` to the controller payload so the tank picker
pre-selects the current tank (was missing on the previous
"current tank" highlight).
@720px the action group stacks below the info — pickers go full-width,
buttons get `min-height: $fp-touch-min` for thumb tap.
Asset cache cleared as part of the deploy so the SCSS recompiles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>