The FP sale report template (report_fp_sale_portrait) walks into
fp.part.catalog records, which portal users don't have ACL on -
they'd hit 'You are not allowed to access Fusion Plating - Part
Catalog' when rendering. Standard /report/pdf/ route runs as the
authed user, so the template traversal fails.
Mirror the portal_download_coc pattern: gate on _document_check_access
for the portal job (customer can only ever reach their own data),
then render the report via ir.actions.report.sudo()._render_qweb_pdf
so the QWeb template traversal bypasses ACL. Return the PDF as an
attachment with a friendly filename.
Updates _fp_group_documents to point the From-You SO Confirmation
link at this new route instead of /report/pdf/ directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Standard sale.report_saleorder hit the sale_pdf_quote_builder
header/footer merge bug (CLAUDE.md MEMORY.md gotcha) and produced
garbled PDFs on FP-customised sale orders. Switching to
fusion_plating_reports.report_fp_sale_portrait which is the
customer-facing FP template and bypasses the merge gate. Added
?download=true so the browser saves the PDF instead of trying to
embed it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. /my now serves the FP dashboard (stock Odoo home was leaking
through because parent route declared ['/my', '/my/home'] but my
override only listed /my/home).
2. Button padding bumped to .5rem 1rem + font 1rem so o_fp_btn matches
Odoo's standard Bootstrap button rhythm. Ghost button drops its
custom padding override.
3. .o_fp_job_card on /my/home + /my/jobs is now an <a> wrapping the
whole card area — full row is the click target, not just the WO
number. Inner <a> on job.name dropped to avoid nested anchors;
focus-visible outline added for keyboard nav.
4. fp.job.write() now mirrors state -> fp.portal.job.state via new
_FP_JOB_STATE_TO_PORTAL_STATE map (confirmed->received,
in_progress->in_progress, done->ready_to_ship). Fixes the bug where
completed backend jobs left the portal stuck on 'in_progress'.
'on_hold' and 'cancelled' intentionally not mirrored — manager
choice what to surface.
5. Sales Order Confirmation now surfaces in the 'From You' group on
the job detail page, pulled via job.x_fc_job_id.sale_order_id ->
/report/pdf/sale.report_saleorder/<id>. Falls back to the upload
placeholder when no SO is linked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-column grid: vertical timeline (5 stages with per-stage timestamps)
on the left, grouped document panel (4 categories) on the right. Hero
header carries WO ref + part / qty / ETA / tracking facts.
Controller adds stage_timeline, doc_groups, and timeline_spine_pct
to the render context. Spine fill = done + half-credit for the
active stage (so the spine visually leads the eye to where the work
is happening).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
V1 surfaces only the fields directly on fp.portal.job (CoC + packing
list). Other 2 groups (From You, Specifications) render placeholder
rows. V2 will wire in sale.order linking for full doc surfacing.
Also adds _fp_size_label helper for friendly file-size strings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds a 5-entry list (label, status, started_at, time_label, notes)
ordered by stage. Labels match the dashboard stepper exactly
(Received/Inspected/Plating/QC/Shipped) so the two surfaces tell
the same story. Inspected and Plating share in_progress_started_at
since state in_progress means both transitions happened.
Time labels use lowercase am/pm matching the mockup typography.
'complete' state correctly shows all 5 stages as done (caught by
new test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds active_job_count, awaiting_review_count, ready_to_ship_count
to the dashboard context. Tests verify partition is correct across
the fp.portal.job and fp.quote.request state machines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The custom dashboard at fusion_plating_portal was rendering a 6-card
view at /my/home, but a method-name mismatch left the parent
portal.CustomerPortal.home() route active instead. Rename the
override to home() so Python MRO does the override naturally, and
add CLAUDE.md Critical Rule 16 documenting the gotcha so future
controller-override work doesn't trip on it.
Version bump: 19.0.2.2.0 -> 19.0.2.3.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>