Implements 2026-04-29-step-library-audit-design.md. Bumps fusion_plating
to 19.0.18.7.0, fusion_plating_jobs to 19.0.8.12.0, fusion_plating_reports
to 19.0.10.2.0.
LIBRARY EXPANSION
- 8 new Step Kinds: Receiving, Electroclean, Strike, Salt Spray,
Adhesion Test, Hardness Test, Packaging, Tank Replenishment
- 4 new input types: photo, multi_point_thickness, bath_chemistry_panel, ph
- DEFAULT_INPUTS_BY_KIND rewritten to seed audit-grade prompts on every
kind (bath IDs, photos, multi-point thickness, signatures, etc.)
- + Common Audit Fields one-click button on the library template form
- Default Operator Instructions relabel + alert callout
PER-RECIPE CONFIGURABILITY
- collect (Boolean) per recipe-step input prompt — opt out without delete
- collect_measurements (Boolean) master switch on recipe step — when off,
wizard skips entirely
- template_input_id (Many2one) traceability link from recipe to library
- Recipe-step backend form view exposes the new fields with handle drag,
toggle, target range, and library-source column
RUNTIME WIRING
- Step input wizard filters node.input_ids to step_input AND collect=True;
short-circuits on collect_measurements=False
- New input types: photo (image widget + ir.attachment), multi-point
thickness (5 readings + auto avg, skips empty cells), bath chemistry
panel (pH/conc/temp/bath bundle), pH (0-14 numeric)
- Composite values JSON-serialized into value_text; photo via attachment
CoC REPORT
- Filters captured prompts to collect=True only
- Renders new input types with appropriate format
MIGRATION (post-migrate.py for 19.0.18.7.0)
- Backfills collect=True on recipe-step inputs
- Backfills collect_measurements=True on recipe steps
- Re-runs action_seed_default_inputs on every existing template
(idempotent, preserves user edits)
- Backfills template_input_id by name-matching against source library
template (handles JSONB vs varchar name columns)
SEED DATA
- 8 example templates (one per new kind) in fp_step_template_data.xml
with noupdate=1
BATTLE TEST
- bt_step_library_audit.py: 29 assertions all PASS on entech
OWL EDITOR EXTENSION DEFERRED
- The simple recipe editor's per-step Instructions/Measurements
expansions were not implemented in this pass; users configure via the
backend recipe-step form. Track follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gap 1 — Rack Travel Ticket PDF (Sub 12b's Save+Print 404):
+ report_fp_rack_travel.xml in fusion_plating_reports — A5 landscape
single page, big rack name, Code 128 of FP-RACK:<name>, tag chips,
contained part-batches table.
+ ir.actions.report bound to fusion.plating.rack so it appears in
the rack form's Print menu too.
+ Sub 12b's rack_parts_dialog.js Save+Print URL fixed to use the
standard /report/pdf/<xmlid>/<id> route.
Gap 2 — Per-customer cert statement:
+ res.company.x_fc_default_cert_statement (company-level fallback).
+ res.partner.x_fc_cert_statement (per-customer override).
+ Surfaced on the partner form under the existing Cert + Document
Routing block.
+ Chronological CoC body resolves: customer override → company
default → hardcoded AS9100/ISO 9001 boilerplate. Three-tier
fallback so existing certs without overrides keep working.
Gap 3 — Chronological CoC 'Actual' column:
+ Build a captured_values_by_input dict from the move's
transition_input_value_ids (Sub 12b captures these on every
Move Parts commit).
+ Render typed Actual: text → as-is, number → with target unit,
boolean → PASS/FAIL, date → formatted, attachment → '[Attachment]'
placeholder.
+ Falls back to prompts from the destination step's step_input list
when no values were captured (still useful as audit-of-what-was-
asked even if blank).
Version bumps:
fusion_plating → 19.0.10.3.0
fusion_plating_reports → 19.0.10.1.0
fusion_plating_certificates → 19.0.5.3.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fusion_plating → 19.0.10.2.0 (Labor History views)
fusion_plating_jobs → 19.0.7.0.0 (Operator Traveller v2)
fusion_plating_reports → 19.0.10.0.0 (Chronological CoC body)
Adds data entries for the 2 new XML files (timelog views + coc
chronological).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the fp.job migration, every MRP-bound Print action in
fusion_plating_reports has a fp.job-bound canonical version in
fusion_plating_jobs. Having both registered means clicking Print on a
record shows two identical entries.
Removed 7 ir.actions.report records (templates kept for backwards
compat — only the menu bindings are gone):
action_report_wo_margin (mrp.production)
action_report_fp_work_order_portrait (mrp.workorder)
action_report_fp_work_order_landscape (mrp.workorder)
action_report_fp_wo_sticker (mrp.workorder)
action_report_fp_mo_sticker (mrp.production)
action_report_fp_job_traveller_mo_landscape (mrp.production)
action_report_fp_job_traveller_mo_portrait (mrp.production)
Kept:
action_report_fp_job_traveller_so_* (sale.order)
action_report_fp_so_sticker (sale.order)
The shared inner sticker templates (report_fp_wo_sticker_inner /
_defaults) stay registered because fp.job + sale.order stickers
both t-call them.
Version: reports 19.0.7.17 -> 19.0.7.18.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Re-detect BarcodeDetector / window.jsQR at every modal open instead
of only at component setup. Avoids the trap where a stale cached
bundle reports "no decoder" even after a redeploy.
- Add a one-line status indicator at the top of the scan modal showing
exactly which decoder is active ("Decoder: native" / "Decoder: jsqr"
/ "Decoder: none — paste URL below"). Lets the operator see at a
glance whether scanning is even possible without round-tripping
through Safari Web Inspector.
- Sticker: strip a leading "Rev " (case-insensitive) from
fp.part.catalog.revision before printing so values like "Rev 1"
don't render as "Rev Rev 1".
Versions: shopfloor 19.0.18 -> 19.0.19, reports 19.0.7.16 -> 19.0.7.17.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs were colluding to make iPhone scans look like "nothing
happens":
1. The in-app scanner was calling action.doAction({res_model: 'fp.job',
res_id: <decoded-id>}). Old physical stickers (still on every box)
encode /fp/wo/<mrp.production.id> — that id space doesn't match
fp.job, so the form opened on a non-existent record and silently
showed nothing. New /fp/job/<id> stickers happened to work because
the IDs lined up by coincidence.
2. The /fp/wo/<id> controller redirected to mrp.production / mrp.workorder
forms, both of which still exist as legacy records but aren't the
canonical source of truth post-migration.
Fix:
- qr_scanner._handleCode now navigates via window.location.href instead
of action.doAction. It hands /fp/job/<n> and /fp/wo/<n> URLs straight
to the existing server-side controllers, which know how to resolve
the right record. Bare numeric ids pasted manually -> /fp/job/<n>.
Anything else surfaces the decoded text as an error so the operator
can see decode worked but the value isn't a sticker.
- Modal now shows "Detected: <value>" the moment a code is decoded
(before navigation), so even on slow phones the operator sees
immediate feedback that the camera read the QR.
- wo_scan.py now resolves in this order:
1. fp.job by legacy_mrp_production_id (migration-aware — old
stickers route to the new model)
2. mrp.production direct browse
3. mrp.workorder direct browse
4. fall back to /odoo/plating-jobs (or work-orders list)
Versions: shopfloor 19.0.17.0.0 -> 19.0.18.0.0,
reports 19.0.7.15.0 -> 19.0.7.16.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original mrp.production / mrp.workorder sticker (logo + WO# stack
on the left, big QR on the right, 7-row body with PO/Customer/Process/
Part Number/Due/Qty/Notes — the design ENTECH has been printing for
months) lives in fusion_plating_reports.report_fp_wo_sticker_inner.
The new fp.job sticker had been rebuilt from scratch with a different
look. This wires fp.job into the existing canonical template instead.
What changed:
- report_fp_wo_sticker_inner — every t-set now uses the
"_var or fallback-from-_mo" pattern so callers can pre-resolve
values; mrp.production/mrp.workorder callers still work via the
fallback path.
- report_fp_wo_sticker_defaults — new shared template that initialises
every overridable name to False so the inner's `or` chain doesn't
NameError when an outer hasn't set it.
- report_fp_job_sticker_template — replaces the parallel layout with
a t-call to report_fp_wo_sticker_inner, feeding it from fp.job
fields (name, partner_id, qty, date_deadline, sale_order_id,
sale_order_line_ids, recipe_id, part_catalog_id, coating_config_id).
- report_fp_so_sticker — new outer that iterates sale.order.order_line
and emits one sticker per line that has a part_catalog_id. Bound to
sale.order's print menu via action_report_fp_so_sticker.
Versions: reports 19.0.7.14.0 -> 19.0.7.15.0,
jobs 19.0.5.0.0 -> 19.0.5.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The QR <img> src used `% quote(_scan_url)` to URL-encode the value,
but `quote` isn't always in the QWeb render context — particularly
on the fusion_pdf_preview / account render chains. Result:
KeyError: 'quote' when printing from the browser.
_scan_url is always base_url + '/fp/wo/<int>' — no characters that
need encoding. Replaced the % + quote() formatting with simple
string concatenation, dropping the quote dependency entirely.
Smoke verified on entech: MO + WO stickers render cleanly at 27KB
each (with QR image included).
fusion_plating_reports → 19.0.7.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reported two issues with the sticker:
1. "Print → WO Box Sticker" didn't appear on the MO form
(WH/MO/00067). The operator workflow lives on the MO form, not
the WO — binding only to mrp.workorder meant they couldn't see
the option. Now bound to BOTH:
* mrp.workorder (per-WO sticker)
* mrp.production (per-MO sticker — prints the MO friendly
name after "WO #" so it reads naturally in shop-floor
vocabulary)
Internal refactor: factored the layout into a shared inner
template report_fp_wo_sticker_inner; the two outer templates
normalise their input to the same _order_id / _scan_id / _mo
variables and t-call the inner.
2. Design polish. The previous layout was a plain label/value
table that looked rough. Redesigned with:
* Proper sticker chrome: 0.5mm black border, 1.5mm rounded
corners, edge padding.
* Header row with bottom border rule separating logo+WO-# on
the left from QR+caption on the right.
* Grid rows now alternate white / #f4f5f7 zebra-striping with
a right-aligned vertical rule between label and value.
* ALL-CAPS, letter-spaced, gray-333 labels at 7.5pt; values
at 8.5pt with strong (9.5pt, 700) emphasis on the key data
(PO, Part Number, Qty) so it reads at a glance from across
the warehouse.
* Helvetica Neue font stack.
* "SCAN TO OPEN" caption under the QR.
Scan endpoint updated: /fp/wo/<id> now tries mrp.production first
(operator home form) then falls back to mrp.workorder. Numeric
collisions between the two id spaces are possible; MO wins because
the MO view carries the full context.
fusion_plating_reports → 19.0.7.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Client is migrating from Steelhead and needs to keep the small
parts-box sticker format the warehouse crew already knows. Two
pieces shipped together so scanning is seamless from day one:
1. report_fp_wo_sticker — 4x3" QWeb label bound to mrp.workorder.
Layout mirrors the Steelhead sticker:
* ENTECH logo top-left (via env.company.logo)
* QR code top-right encoding /fp/wo/<id>
* Grid: PO (RO) / Customer / Process / Part Number / Due
Date / Qty / Notes
Dedicated paperformat_fp_wo_sticker at 102x76mm, 300 DPI,
landscape, 3mm margins — sized for thermal / inkjet label
printers without shrink-to-fit.
Binding added so "Print → WO Box Sticker" appears on every
mrp.workorder record.
2. FpWoScanController — GET /fp/wo/<int:wo_id> redirects the
scanner straight to the work-order form
(/odoo/action-mrp.action_mrp_workorder/<id>). auth='user' so
logged-in scanners land on the WO immediately; others bounce
through Odoo's login and return to the same URL. No custom
client work needed — any phone camera, handheld barcode
scanner, or tablet browser opens the URL on scan.
Process row resolution chain: part.default_process_id →
coating.recipe_id → fallback. So the sticker prints whichever
process is actually going to drive WO generation for this line,
matching the direct-order wizard's Effective Process column.
fusion_plating_reports → 19.0.7.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User request: on customer-facing PDFs the single "Part" column was
stacking part number + revision + description together. Split into
two distinct columns so customers see Part Number in its own field
and the Description column carries only the prose.
Macro split:
* customer_line_part_number — strong part number + "(Rev X)"
* customer_line_description — line.name + any populated line
metadata (serial, job#, thickness)
* customer_line_header (legacy) kept as a thin wrapper that
t-calls both macros stacked so older reports still render.
Reports updated — each gains a new first "PART NUMBER" column and
the old "PART" column renamed to "DESCRIPTION":
* report_fp_sale (both portrait variants)
* report_fp_invoice (both portrait variants)
* report_fp_packing_slip (both variants — delivery + MO-origin)
Column widths rebalanced per report. Section / note colspans bumped
to account for the extra column.
report_fp_bol left as-is — its "Description of Goods" td is a
freight-convention combined field, intentionally keeps the stacked
layout via the legacy customer_line_header macro.
fusion_plating_reports → 19.0.6.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four new fields on every sale.order.line, propagated through to MO,
Delivery, and Invoice for end-to-end traceability:
- fp.serial registry (new model in configurator) with smart-button
traceability to Sale Order, MO, Delivery, Invoice, Part. M2O on SO
line; optional; user types a customer serial or clicks Generate
Serial for a sequence-backed one. Reverse O2M links split across
configurator (invoice) / bridge_mrp (MO) / logistics (delivery) so
module load order is respected.
- x_fc_job_number on SO line, auto-sequenced FP-JOB-NNNNN on SO
confirm. Editable — shops can override for customer/legacy schemes.
- fp.coating.thickness (new child of fp.coating.config) with per-
config discrete thickness options; x_fc_thickness_id on SO line
domain-filtered to the line's coating. Auto-clears when coating
changes.
- x_fc_revision_snapshot Char on SO line, frozen from
x_fc_part_catalog_id.revision at save. Protects historical SOs from
later catalog edits. Secondary "Revision" picker on the tree view
lets users switch between prior revisions of the same part number;
the Part M2O still surfaces only is_latest_revision rows.
Reports (CoC, packing slip, invoice, BoL) pick up all four via the
Sub 2 customer_line_header macro — one macro edit, four reports.
Smoke on entech: 11 assertions pass including revision snapshot,
generate-serial button, typed-serial create-on-fly, coating→thickness
domain reset, SO confirm auto job#, and MO traceability carry.
Module version bumps:
fusion_plating_configurator → 19.0.12.0.0
fusion_plating_bridge_mrp → 19.0.11.0.0
fusion_plating_logistics → 19.0.2.0.0 (+depends configurator)
fusion_plating_reports → 19.0.5.1.0
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>
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>
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>
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>
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>
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>
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>
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>
Database stores datetimes naive-UTC, but the dashboards and emails were
showing UTC strings to users in EST/EDT — making 9pm Toronto look like 1am
the next day. Adds a single helper module + auto-detection on install.
Core changes (fusion_plating):
- New fp_tz.py helper: fp_user_tz, fp_format, fp_isoformat_utc, fp_time_ago
Resolves user.tz → company.x_fc_default_tz → UTC.
- res.company.x_fc_default_tz Selection (full pytz IANA list)
- res.config.settings exposes the company tz under a new "Regional
Settings" block in Settings > Fusion Plating
- post_init_hook auto-populates the tz on first install: tries admin
user → server /etc/timezone → America/Toronto fallback
- fp_process_node._to_dict now sends create_date/write_date as ISO with
explicit +00:00 marker so JS new Date() parses it as UTC and the
recipe tree editor's "time ago" math works correctly
Shop-floor controllers:
- shopfloor_controller.py: every fields.Datetime.to_string() and naive
.strftime() swapped for fp_format(env, ...) — due_at, bake times,
last_log_date, gates, server_time all now in user's tz
- _time_ago() removed; replaced with fp_time_ago helper which compares
tz-aware datetimes (the local one was naive-vs-naive and could be
off by hours)
- manager_controller.py date_planned: str(...)[:10] slice replaced
with fp_format MM/DD in user's tz
Notifications + reports:
- mail_template_data.xml: 5 .strftime() calls in body_html → babel
format_datetime / format_date with tz=(user.tz or company tz)
- report_fp_job_traveller.xml: rec.received_date (Datetime) gets
t-options="{'widget':'datetime'}" so Odoo's QWeb renders in user tz
Settings view layout:
- fusion_plating now owns the Settings page "Fusion Plating" app shell
- fusion_plating_certificates xpaths into it instead of redefining
(prevents app-name collision)
Verified on odoo-entech (LXC 111): post_init_hook detects
America/Toronto from /etc/timezone, MO date_start 2026-04-17 05:28 UTC
correctly displays as 2026-04-17 01:28 EDT.
Module versions bumped: fusion_plating 19.0.3.0.0,
fusion_plating_shopfloor 19.0.9.0.0, plus certificates / notifications /
reports → 19.0.3.0.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
One PDF that follows a job through the shop — prints from either the
Sale Order or the Manufacturing Order. Matches existing design language
(fp_landscape_styles, .fp-header-primary banners, bordered tables,
.sig-line for sign-off, .highlight-box for callouts).
Sections per traveller:
1. Title bar with REWORK / RUSH ORDER badges
2. Job header — customer, PO #, part #, coating, recipe, facility,
qty, dates, current parts location
3. Receiving summary — received qty, state, damage flag
4. Process Routing table — one row per WO with step #, operation,
work centre, bath, tank, target thickness, dwell, expected
duration, + sign-off columns (operator, date/time, initials,
qty pass/reject)
5. Bath chemistry targets snapshot per bath used
6. Quality holds — red callout only when present
7. Certificates issued + Delivery info (side-by-side)
8. Rework reason block (only on rework MOs)
9. Ruled notes / exceptions area
10. Final supervisor + QA sign-off
Four ir.actions.report entries registered:
- Job Traveller (Landscape) on mrp.production [default print]
- Job Traveller (Portrait) on mrp.production
- Job Traveller (Landscape) on sale.order [iterates MOs]
- Job Traveller (Portrait) on sale.order
Regression-tested all 15 existing reports (SO, WO, MO margin, invoice,
BoL, CoC EN, receipt) — every one still renders.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>