Commit Graph

70 Commits

Author SHA1 Message Date
gsinghpal
b395600a1c changes 2026-05-21 05:18:32 -04:00
gsinghpal
d6d6249857 changes 2026-05-21 04:47:45 -04:00
gsinghpal
1314f4581d changes 2026-05-21 03:37:25 -04:00
gsinghpal
2730c455f5 fix(reports): remove Customer Acceptance/Authorized Representative signature block from FP sale report
The signature footer ('Customer Acceptance (Signature / Date)' +
'Authorized Representative') is not part of EN Plating's intended
customer-facing quote/SO PDF flow. Removed from both portrait and
landscape variants of report_fp_sale_portrait/landscape.

Invoice report (report_fp_invoice.xml) had no such block - nothing
to remove there. Verified by grep across fusion_plating_reports.

Version bump: fusion_plating_reports 19.0.11.14.0 -> 19.0.11.15.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 03:36:54 -04:00
gsinghpal
d3c5c25865 changes
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled
2026-05-17 03:20:33 -04:00
gsinghpal
152ed86c3a feat(thickness): single Char range field — drop fp.recipe.thickness picker
Per client direction: every order is a thickness RANGE (e.g.
"0.0005-0.0008 mils" or "5-10 mils"), never a single value. The
old picker model (fp.recipe.thickness with a single 'value' Float)
was modelling the wrong concept and overcrowding the order entry
UI. Replaced with one free-text Char field that auto-fills from
last-used or part default.

DELETED entirely:
- fp.recipe.thickness model (file + view + ACL + manifest entry)
- recipe.thickness_option_ids One2many (the picker source)
- "Thickness Options" inline list on the recipe form
- sale.order.line.x_fc_thickness_id (M2O picker)
- account.move.line.x_fc_thickness_id
- fp.delivery.x_fc_thickness_id
- fp.direct.order.line.thickness_id

ADDED:
- sale.order.line.x_fc_thickness_range (Char) — operator types range
- account.move.line.x_fc_thickness_range — for invoice rendering
- fp.delivery.x_fc_thickness_range — for packing slip
- fp.direct.order.line.thickness_range — for the wizard
- fp.part.catalog.x_fc_default_thickness_range — part default

AUTO-FILL CHAIN (sale.order.line + wizard line):
1. Operator already typed → keep
2. Most recent SO line for (this part, this customer) with a
   non-empty thickness_range → copy that
3. part.x_fc_default_thickness_range → copy
4. Blank — operator types

Implemented as both an @api.onchange (interactive) AND a
create() override (programmatic — wizard, sale_mrp bridge,
imports). Same logic in both paths.

WIZARD push-to-defaults: when "Save as Default" toggle is ticked
on a wizard line, persist the line's thickness_range to
part.x_fc_default_thickness_range so future first-customer orders
get a sensible starting point.

REPORTS: customer_line_header.xml + report_fp_wo_sticker.xml now
print the Char range as-typed (no display_name lookup needed).

KEPT (admin documentation only — doesn't affect order entry):
- recipe.thickness_min, thickness_max, thickness_uom on the recipe
  root: documents the recipe's CAPABILITY range. No UI gate; just
  for spec authors to record what the chemistry can produce.

JOB GROUPING: fp.job auto-create groups SO lines by (recipe, part,
spec, thickness, serial). Updated to key on the thickness_range
Char (stripped) instead of the deleted thickness_id integer.

DB cleanup: --update=base ran on the upgrade, dropping the
fp_recipe_thickness table + the four x_fc_thickness_id columns.
Existing data was already nulled in earlier dev work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:54:40 -04:00
gsinghpal
d891002c84 feat(promote-customer-spec): Phase E — final removal of coating + treatment
DELETED entirely (model + view + ACL + data file + menu):
- fp.coating.config (configurator)
- fp.treatment (configurator + seeded data)
- fp.coating.thickness (configurator) — replaced by fp.recipe.thickness in Phase A
- fp.customer.price.list (configurator) — coating-keyed, no replacement

Field deletions:
- sale.order.x_fc_coating_config_id
- sale.order.line.x_fc_coating_config_id + x_fc_treatment_ids
- account.move.line.x_fc_coating_config_id
- fp.part.catalog.x_fc_default_coating_config_id + x_fc_default_treatment_ids
- fp.job.coating_config_id
- fp.pricing.rule.coating_config_id
- fp.quality.point.coating_config_ids
- fp.direct.order.line.coating_config_id + treatment_ids
- fp.sale.description.template.coating_config_id

Refactored:
- fp.quote.configurator.coating_config_id → recipe_id (now points at
  fusion.plating.process.node, the actual recipe). All compute, onchange,
  and matcher logic updated to use recipe directly. Quality inherit
  extends matcher with spec-tier scoring.
- fp.job._fp_create_certificates now reads spec from job.customer_spec_id
  and formats spec_reference as "code Rev rev". Same for thickness
  source — bake fields read from recipe_root (Phase A).
- fp.job.step.button_finish bake-window auto-spawn reads bake settings
  from recipe_root instead of coating.
- fp.certificate auto-fill spec_min_mils/max_mils from recipe (Phase A
  thickness fields) instead of coating.
- jobs/sale_order.py: job creation reads x_fc_customer_spec_id from
  line, drops coating refs and the legacy header-coating fallback.
- Wizards drop coating + treatment fields and refs.
- Configurator views drop x_fc_coating_config_id + x_fc_treatment_ids
  fields entirely. Quality inherits re-anchor on stable fields
  (x_fc_part_catalog_id, x_fc_internal_description, default_process_id,
  process_variant_id, substrate_material) so they keep working.
- Reports drop coating fallback elifs; print recipe / spec.
- Tablet payload drops coating_config_id from job.read fields.

Skipped (deferred to backlog):
- fusion_plating_bridge_mrp — module is uninstalled per Sub 11; source
  files retain coating refs but no runtime impact.
- fusion_plating_portal — circular dep (portal → quality → certs →
  portal). Customer-facing portal coating picker stays for now;
  promote-spec polish is a separate sub-project.

Verification: grep for "coating_config_id|fp.coating.config|
fp.treatment|fp.coating.thickness" in live (non-bridge_mrp,
non-portal, non-script, non-test) Python/XML/CSV returns 3 hits,
all in module / class docstrings explaining Phase E history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:00:41 -04:00
gsinghpal
e0eacc2530 feat(promote-customer-spec): Phase D — reports + tablet payload include spec
Reports updated to print Specification (with revision via display_name):
- report_fp_sale.xml — header sections show "SPECIFICATION" instead
  of "COATING CONFIG", reads doc.x_fc_customer_spec_id (added on
  sale.order via quality inherit, computed from line.customer_spec_id)
- report_fp_wo_sticker.xml — propagates _spec alongside _coating
- fusion_plating_reports/report_fp_job_traveller.xml — header row
  now shows Specification (falls back to coating)
- fusion_plating_jobs/report_fp_job_traveller.xml — same fall-back
- fusion_plating_jobs/report_fp_job_sticker.xml — _spec added

sale.order.x_fc_customer_spec_id added as a stored compute on
sale.order (in quality) so reports can render order-level spec.
Mirrors the line's first spec; updates on line edit.

Tablet payload (shopfloor_controller.py):
- spec_label added to the job payload dict
- defensive 'customer_spec_id' in job._fields check (shopfloor doesn't
  depend on quality — circular if added)

Portal: deferred (same circular-dep issue, more substantial UI rewrite
needed; Phase E backlog item).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:30:05 -04:00
gsinghpal
e7c6960de9 feat(sticker): restore customer-name secrecy cover (ABC-MANU)
Body Customer row now prints a 3-and-4 short code instead of the
full company name. Operators see "ABC-MANU" on the floor; visiting
customers / unauthorised passers-by can't immediately tell whose
parts are on which rack.

Rule (per user's reference design):
  - First 3 chars of first word + "-" + first 4 chars of second word
  - Single-word names → just first 3 chars
  - All uppercase
  - Strips non-alphanumeric per word so "St. John's Mfg." doesn't
    leak punctuation into the slice

Logic lives in the shared inner template, so all 4 variants pick
it up automatically:
  sale.order     External + Internal Sticker
  fp.job         External + Internal Job Sticker

Verified on fp.job 2635: Customer row now reads "ABC-MANU" (was
"ABC Manufactoring").

Doesn't use the orphaned x_fc_short_code field on res.partner
(that field has no column or compute — broken Studio remnant).
A future spec can replace this inline computation with a proper
stored+inverse field if customers want per-partner overrides.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:24:07 -04:00
gsinghpal
cd763fa1d7 chore(sticker): rename External action labels for the variant split
Print menu now shows External + Internal as paired entries:

  sale.order:  External Sticker      / Internal Sticker
  fp.job:      External Job Sticker  / Internal Job Sticker

XML IDs unchanged (action_report_fp_so_sticker /
action_report_fp_job_sticker) so existing bookmarks and
binding_model_id records keep working. print_report_name strings
also updated so the downloaded filename matches the new label.

DB verification:
  fp.job      | External Job Sticker
  fp.job      | Internal Job Sticker
  sale.order  | External Sticker
  sale.order  | Internal Sticker

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:07:50 -04:00
gsinghpal
63bf271725 feat(sticker): add Internal Sticker variant on sale.order Print menu
Same 3-cell + body layout as External; Notes column reads
x_fc_internal_description (Sub 2 internal-description field on the
SO line) instead of line.name. Shop floor gets ops-facing notes
without leaking them to the customer-facing variant.

New action record action_report_fp_so_sticker_internal — binds to
sale.order, appears in the Print menu next to the existing External
sticker. New template report_fp_so_sticker_internal that pre-sets
_notes_content before t-calling the shared inner.

Verified on SO-30019 with a seeded internal_description: Notes
column reads "INTERNAL: rework if any dings on flange. Buff per
WI-104." — confirms the override path is wired through the
defaults-block initialiser, the inner's fallback chain, and the
new outer template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:04:29 -04:00
gsinghpal
974b8a5152 feat(sticker): wire _qty_total in SO + Job External outers
Activates the per-box loop landed in the prior commit. SO External
reads line.product_uom_qty; Job External reads job.qty. Inner
template now renders one sticker per physical box, marking each
with "X / N" in the Qty row.

Verified on fp.job 2635 (qty temporarily set to 3): 3-page PDF
with Qty rows "1 / 3", "2 / 3", "3 / 3" — each page identical
otherwise (same WO#, same QR, same body fields).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:02:32 -04:00
gsinghpal
0a32ed2da7 feat(sticker): per-box render loop + Notes override hook
Inner sticker template gains two parameters that outer templates
pre-set:

  _qty_total — total qty for the line/job. Inner wraps the body
    in t-foreach="range(int(_qty_total or 1))" so a qty=5 line
    produces 5 consecutive single-box stickers. Qty row in the
    body switches from "5" to "1 / 5", "2 / 5", ... "5 / 5".
    When _qty_total is missing/0/1, the Qty row keeps showing
    the plain integer (regression-free).

  _notes_content — Notes column source. Existing inner code
    hard-read _line.name; new code accepts an outer override
    and falls back to _line.name. External outers don't set it
    (unchanged behaviour); the new Internal outers (Task 4+5)
    pre-set it to x_fc_internal_description.

Defaults template initialises both new vars to False so the
inner's "outer-supplied OR fallback" pattern doesn't NameError
when called from existing outers that haven't been updated yet.

Verified regression-free: fp.job 2635 (qty=1) renders identically
to its pre-Task baseline — Qty row shows plain "1", Notes from
line.name as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:00:22 -04:00
gsinghpal
677e460438 fix(sticker): wire SN # + Thickness to the correct Sub 5 fields
The values were structurally blank because the variable
resolution was reading the wrong field names:

  Was:  _line.x_fc_serial_number    (doesn't exist)
        _line.x_fc_thickness        (doesn't exist)
  Now:  _line.x_fc_serial_id.name           (M2O fp.serial)
        _line.x_fc_thickness_id.display_name (M2O fp.coating.thickness)

Sub 5 shipped these as Many2one registries (fp.serial,
fp.coating.thickness) — the sticker was guessing at flat
Char-field equivalents that were never created.

Verified on SO-30019: SN # now prints "65767", Thickness now
prints "0.3-0.5 mils" (the en-dash in display_name mojibakes
to "â€"" through wkhtmltopdf's font path on entech, so we
replace en-dash + em-dash with ASCII hyphen-minus before
render — ASCII-only is what label printers want anyway).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 07:25:42 -04:00
gsinghpal
c7b794f604 fix(sticker): drop SO-line sequence suffix + bump Notes type
SO sticker (report_fp_so_sticker):
  Was: "SO-30019 / 10"  (the "/ 10" was line.sequence — Odoo's
       default increment-by-10 — meaningless to the operator)
  Now: "SO-30019"
Multi-line SOs are disambiguated by the body fields (Part #,
Customer, etc.) which already differ per sticker, so the
suffix wasn't earning its keep.

Notes column size bumps:
- Label 44pt -> 48pt
- Content 30pt -> 36pt (+20%) — easier to read from across
  the line. Line-height tightened 1.15 -> 1.1 to keep the
  multi-paragraph wrap inside the body band.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 07:21:09 -04:00
gsinghpal
64c61dcca8 feat(sticker): much bigger text + QR +30%
wkhtmltopdf renders CSS font-size at a smaller physical scale
than the em-square math predicts (a "30pt" cell text was only
~4mm tall visually). Pushing all type up significantly so it
actually reads at scan/print distance:

Text bumps:
- Body field text 30pt -> 50pt (+67%, label + value)
- WO# 56pt -> 72pt (+29%)
- Notes label 30pt -> 44pt
- Notes content 22pt -> 30pt (+36%)
- Muted rev tag 22pt -> 30pt
- Body cell padding 0 10px -> 0 8px (a touch more horizontal
  room for long values now that the font is bigger)

QR + 30% as asked:
- Wrapper 280 -> 365px (+30.4%). Image 368 -> 480px, offset
  -44 -> -58px (recomputed for the new quiet-zone crop).

Header re-balanced for the bigger content:
- Height 25% -> 32% (fits the +30% QR + bigger WO# + bigger
  logo at 135px)
- Body band: 75% -> 68% (rows now ~9.6mm tall; line-height
  1.0 keeps the 50pt body text snug inside)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:48:40 -04:00
gsinghpal
649b75d4a1 feat(sticker): bigger field labels + values + notes text
Trimmed the header from 30% to 25% of page height to free up
vertical room for the body band's 7 rows. Each row is now
~10.45mm tall (was 9.88mm), so the field font fits comfortably
at the bigger size.

Size bumps:
- Body field text 26pt -> 30pt (label + value, +15%)
- Muted rev tag 18pt -> 22pt
- Notes label 26pt -> 30pt
- Notes content 19pt -> 22pt (+16%, wraps cleanly to 2 lines
  when the customer description runs long)

Header re-fit (smaller cells, same content):
- Header height 30% -> 25%
- WO# font 62pt -> 56pt
- Logo max-height 135 -> 105px
- QR wrapper 340 -> 280px (image 447 -> 368px, offset -53 ->
  -44px to keep the quiet-zone crop math right)
- High-def 600x600 QR source unchanged — still prints crisp
  at the smaller wrapper size

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:41:17 -04:00
gsinghpal
8aa817b1a0 feat(sticker): bigger text, bigger high-def QR, drop "WO #" prefix
WO# cell now just renders the number (e.g. "WO-30019") since the
"WO" is already baked into the doc index format — the redundant
prefix was eating cell width without adding information.

Size bumps:
- WO# 44pt -> 62pt (text is shorter so the cell can carry the
  extra weight)
- Body field text 22pt -> 26pt, line-height 1.1 -> 1.0 so the
  bigger font still fits 7 rows in the body band
- Notes label 22pt -> 26pt, content 16pt -> 19pt
- Logo max-height 120 -> 135px
- Muted rev tag 16pt -> 18pt

QR upgrades (both "bigger" and "high def" as asked):
- Source resolution 300x300 -> 600x600. At 300dpi print across
  a 28.8mm wrapper, effective output is ~515ppi vs the prior
  ~256ppi. Scanners on the floor will read it cleanly even at
  steeper angles / scuffed labels.
- Wrapper 290 -> 340px (+17%). Image 390 -> 447px, offset -50
  -> -53px (recomputed quiet-zone crop: 600 * 0.12 = 72px
  margin -> 456px effective QR data -> 340 * 600/456 = 447
  scaled image -> (447-340)/2 = 53px offset).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:38:05 -04:00
gsinghpal
80d1cc5639 feat(sticker): 3-cell header + right-side Notes column + new field list
Restores the original ENTECH sticker layout from the operator's
screenshot reference:

Header (3 horizontal cells, divided by vertical rules):
  [Logo]  |  WO #WO-30019  |  [QR]

Body (left side = field table, right side = Notes column):
  PO #:        587854         | Notes:
  SN #:        -              | <customer-facing description>
  Customer:    ABC Manufact.  |
  Part #:      9876... Rev A  |
  Due Date:    May 17, 2026   |
  Thickness:   -              |
  Qty:         1              |

Changes from previous (stacked-left) layout:
- Header: 1-row 3-cell (Logo 28% | WO# 44% | QR 28%) replaces
  the 2-cell w/ logo+WO# stacked on left.
- Body: 2-region (66% / 34%) replaces single 7-row table.
  Notes column now spans full body height on the right.
- Fields: SN # and Thickness added; Process row removed.
- Labels: "PO (RO)" -> "PO #", "Part Number" -> "Part #".
- Notes content: switched from SO.x_fc_internal_note to the SO
  line's `name` (= customer-facing description per Sub 2 Q6).
- SN # reads _line.x_fc_serial_number (Sub 5 field).
- Thickness reads _line.x_fc_thickness with coating.thickness
  fallback (Sub 5 field, defensive 'in _fields' check).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:33:18 -04:00
gsinghpal
2db789d7dd feat(sticker): bigger QR + double-height Notes row
Both changes the operator asked for, applied to the original
ENTECH stacked-left layout (no other structural changes):

- QR wrapper 380px → 460px (image 510px → 620px, offset -65 → -80
  to keep the white quiet-zone cropped). Roughly +21% surface area.
- Notes row height 14.28% → 24% (~2x). Other 6 rows shrink
  proportionally from 14.28% to 12.67% each so the band still
  totals 100%. Notes value also gets white-space: normal +
  vertical-align: top so the operator's handwriting room sits at
  the top of the cell and a long internal note can wrap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:25:37 -04:00
gsinghpal
6b7b44264a changes 2026-05-10 10:25:12 -04:00
gsinghpal
ec0a07fbe9 fix(audit-trail): 3 production bugs found via end-to-end Anodize battle test
Battle-tested complete workflow on entech: ABC Manufacturing + Anodize
recipe (id=136) cloned to part-variant (id=1775) → SO S00276 confirmed →
fp.job 1234 with 17 steps → recorded 56 measurement values exercising all
13 input types (incl. all 4 new types) → CoC chronological report renders
69KB with all values incl. photo thumbnails.

Bugs found and fixed:

1. fp.process.node.input_ids missing copy=True — when a master recipe
   was cloned per-part (the standard variant pattern), the operator
   prompts on each step did NOT get copied to the variant. Result: jobs
   built from variants ran with zero prompts even though the master had
   them. Fixed: input_ids now copy=True so cloning auto-duplicates.

2. CoC chronological template read dest.input_ids where dest is
   fp.job.step. Steps don't carry input_ids — that field lives on the
   recipe node. Result: AttributeError aborted the entire CoC render.
   Fixed: walk via dest.recipe_node_id.input_ids; preserves the existing
   collect=True filter.

3. CoC chronological template used hasattr() in a t-value expression.
   QWeb's expression engine doesn't expose Python builtins, raised
   KeyError: 'hasattr'. Fixed: use 'collect' in i._fields instead.

Also enhanced photo rendering in CoC: was just "[Attachment]" placeholder;
now renders an actual <img> thumbnail (max 80px tall) plus the filename.

Battle-test script saved to fusion_plating/scripts/bt_e2e_anodize_v2.py
for re-runs / regression testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:53:59 -04:00
gsinghpal
b187192c58 feat(step-library): full plating workflow coverage + per-recipe configurability + audit
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>
2026-04-29 22:13:54 -04:00
gsinghpal
a2fe1fcbcc changes 2026-04-29 03:35:33 -04:00
gsinghpal
7d3b8f132a fix(sub12c+): close 3 known gaps — rack travel ticket, cert statement, CoC actuals
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>
2026-04-27 21:55:48 -04:00
gsinghpal
9d88c25136 feat(sub12c): chronological CoC body + body_style opt-in router (Task 3)
New template: fusion_plating_reports.coc_body_chronological.
Walks fp.job.step.move records in time order (chain-of-custody).
Per-move heading 'Step Name (Tank Code)' with 'Moved By / Time / Qty'
meta line + a 5-column measurement sub-table (Name / Description /
Target / Actual / Recorded By) when the destination step has captured
inputs. Heading-only when there are no inputs (gating moves).

New router template: coc_body_router. Picks chronological vs classic
based on fp.certificate.body_style. Existing certs default to 'classic'
so no regressions. Both English + French CoC templates rerouted.

fp.certificate.body_style ('classic' | 'chronological') exposed on
the cert form alongside certified_by_id. Operator picks per cert.

Sign-off block reuses the existing owner_user_id signature pattern +
x_fc_coc_signature_override fallback. Cert statement boilerplate is
inline (Sub 12d will move it to a configurable per-customer field).

The Actual column in the measurement sub-table is rendered blank
because Sub 12a/12b runtime captures step_input values via the
operator's per-step input form which lives in a model not yet wired
into this template — Sub 12d follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:42:03 -04:00
gsinghpal
f55193fb1b feat(sub12c): bump versions + manifest scaffolding (Task 1)
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>
2026-04-27 21:37:24 -04:00
gsinghpal
f08f328688 changes 2026-04-27 00:11:18 -04:00
gsinghpal
d9f58b9851 changes 2026-04-26 15:05:17 -04:00
gsinghpal
160198edb1 chore(reports): drop duplicate Print menu entries on legacy MRP models
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>
2026-04-26 10:53:19 -04:00
gsinghpal
9fe7855fc3 fix(shopfloor,reports): scanner status line + sticker rev cleanup
- 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>
2026-04-25 13:30:40 -04:00
gsinghpal
b93633d728 fix(shopfloor,reports): make QR scan actually navigate after decode
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>
2026-04-25 13:14:06 -04:00
gsinghpal
ecac43eef4 fix(reports): restore the original ENTECH box-sticker layout for fp.job + sale.order
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>
2026-04-25 13:03:29 -04:00
gsinghpal
41d0908ade changes 2026-04-24 21:04:38 -04:00
gsinghpal
0eab4b4efb fix(reports): sticker PDF KeyError: 'quote' on fusion_pdf_preview path
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>
2026-04-23 10:58:24 -04:00
gsinghpal
e32ff4b056 feat(reports): MO-bound WO sticker + polished professional layout
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>
2026-04-23 10:52:22 -04:00
gsinghpal
be33a76ad2 feat(reports): WO box sticker + QR-scan-to-WO endpoint
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>
2026-04-23 10:39:35 -04:00
gsinghpal
eddf803d4c feat(reports): split customer-facing line header into Part Number + Description columns
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>
2026-04-23 10:17:56 -04:00
gsinghpal
25c3f6f8d1 feat(plating): Sub 5 — order-line fields (serial, job#, thickness, revision)
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>
2026-04-22 23:04:44 -04:00
gsinghpal
20d547bb4f feat(reports): traveler PDF surfaces part_number + internal description (Sub 2 Task 23) 2026-04-21 23:19:33 -04:00
gsinghpal
84c0745ab5 feat(reports): WO PDF surfaces part_number + internal description (Sub 2 Task 22) 2026-04-21 23:18:10 -04:00
gsinghpal
79d9e6b3b0 feat(reports): BoL PDF uses customer_line_header macro (Sub 2 Task 21)
Rewired portrait + landscape variants of report_fp_bol. The BoL had no
line collection of its own (fusion.plating.delivery only has a soft
`job_ref` Char), so the previous cargo-description block was a single
hardcoded row. Restructured to look up the job's mrp.production via
`job_ref`, iterate its `move_finished_ids` (excluding cancelled), and
render each finished-goods move through the shared
customer_line_header macro using the `move.sale_line_id or move`
adapter pattern.

When no MO is found or there are no finished moves, the template falls
back to the previous single-row "Plated parts — Job X" behavior so
legacy records without a backing MO still print correctly. Per-row QTY
now reflects the individual move's `product_uom_qty` instead of the
MO's aggregate `product_qty`.

Both variants render successfully on entech against a delivery whose
job_ref matches a real MO with one finished move.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 23:06:07 -04:00
gsinghpal
ac824c2cfb feat(reports): packing slip PDF uses customer_line_header macro (Sub 2 Task 20)
Rewired portrait + landscape variants of report_fp_packing_slip to use the
shared customer_line_header QWeb macro. The packing slip iterates
stock.move records (doc.move_ids_without_package); the adapter
`<t t-set="line" t-value="move.sale_line_id or move"/>` bridges the macro's
`line.x_fc_part_catalog_id` lookup to the sale line when the move is tied
to a sale (preferred path), falling back to rendering the stock.move's
product_id for stray moves with no sale line.

SKU + PRODUCT columns collapsed into a single PART column (width
adjusted to absorb the removed SKU column). Both variants render
successfully on entech with a real picking whose move has a sale_line_id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 23:05:58 -04:00
gsinghpal
ea2cfc37c3 feat(reports+configurator): invoice PDF uses macro; x_fc_part_catalog_id on account.move.line (Sub 2 Task 19)
Invoice PDF (portrait + landscape) now collapses SKU + Description into
a single Part column rendered via fusion_plating_reports.customer_line_header,
so customer-facing invoices print the customer's part number (with
revision) instead of the internal service SKU.

To feed the macro on invoice lines, add x_fc_part_catalog_id to
account.move.line and override sale.order.line._prepare_invoice_line so
the part reference propagates automatically when an SO is invoiced.
2026-04-21 22:59:40 -04:00
gsinghpal
6cbea9d2f3 feat(reports): SO PDF uses customer_line_header macro (Sub 2 Task 18)
Collapse the SKU and Description columns in both the portrait and
landscape sale-order PDFs into a single Part column rendered through
the shared customer_line_header macro, so customer-facing quotes and
confirmed orders print the customer's part number (with revision)
instead of the internal service SKU.

Updates column widths, section/note colspans, and the conditional
col_count used for the landscape template's optional discount column
to reflect the collapsed header.
2026-04-21 22:57:33 -04:00
gsinghpal
d959775648 feat(reports): customer_line_header QWeb macro + version bump (Sub 2 Task 17) 2026-04-21 22:54:17 -04:00
gsinghpal
f09bef9083 refactor(reports): consolidate SO Acknowledgement back into the Sales Order PDF
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>
2026-04-20 01:30:06 -04:00
gsinghpal
c8ca37099b refactor(reports): move SO Acknowledgement into fusion_plating_reports with house style
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>
2026-04-20 00:10:33 -04:00
gsinghpal
fa82ce17dd feat(reports): sequence-sort the Print dropdown so FP reports are #1
Odoo 19's `ir.actions.actions._get_bindings` returns the print-menu
bindings via `ORDER BY a.id` (insertion order) and only sequence-sorts
the `action`-type bindings — `report`-type bindings are returned in
raw SQL order. Result: FP reports installed after Odoo's stock ones
appear at the BOTTOM of the dropdown, even when they're the
customer-facing primary report (e.g. Timesheets above Quotation on
sale.order).

Two changes in fusion_plating_reports/models/ir_actions_report.py:

1. **Add `sequence` (Integer, default 100) to ir.actions.report** —
   gives every report a sortable knob.

2. **Override `ir.actions.actions._get_bindings`** to also sort the
   `report` slice by `(sequence, name.lower())`. super() returns the
   cached frozendict; we rebuild with the sorted reports.

Then set sequences in fp_hide_default_reports.xml (lower = top):

| Model           | seq 10 (#1)              | seq 15 (#2)              | seq 20+               |
|-----------------|--------------------------|--------------------------|-----------------------|
| sale.order      | FP Quotation Portrait    | FP Quotation Landscape   | FP Job Traveller (20) |
| account.move    | FP Invoice Portrait      | FP Invoice Landscape     |                       |
| stock.picking   | FP Packing Slip Portrait | FP Packing Slip Landscape|                       |
| mrp.production  | FP Job Traveller Portrait| FP Job Traveller Landscape| FP WO Margin (20)   |
| account.payment | FP Receipt Portrait      | FP Receipt Landscape     |                       |
| fp.delivery     | FP BoL Portrait          | FP BoL Landscape         |                       |
| portal.job      | FP CoC Portrait          | FP CoC Landscape         |                       |
| fp.certificate  | FP CoC English           | FP CoC Français          |                       |

Odoo defaults stay at sequence 100 (default) → always at bottom.

Verified on entech: sale.order print menu now shows
Quotation Portrait → Quotation Landscape → Job Traveller × 2 →
PRO-FORMA → Timesheets. Same pattern across all touched models.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 09:05:29 -04:00
gsinghpal
9a1ee4b369 feat(reports): hide Odoo's default PDFs where FP ships a branded one
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>
2026-04-19 08:57:38 -04:00