a2efc9f committed a hr_employee.py with unresolved <<<<<<<
HEAD / >>>>>>> Stashed changes markers — Python wouldn't have
imported the file. Restoring to f340c87's version. The intended
fix (Odoo 19 'in' operator handling) lives on main as 0f41eb1.
Two compounding bugs in _search_x_fc_is_clocked_in surfaced when
fusion_clock's auto-clock-out closed all demo open attendances:
1. Odoo 19 normalises ('=', True) to ('in', OrderedSet([True]))
before invoking the search method. The previous code only
handled '=' / '!=' and fell through to return [] for 'in' /
'not in' — which Odoo treats as 'no constraint' and matches
the entire table.
2. ('id', 'in', []) is also treated as no-constraint in some
Odoo versions; replaced with a [0] sentinel so the empty
case correctly matches nothing.
Rewrite reduces caller intent to a match_set of booleans, flips it
on negative operators, then emits id IN / NOT IN against the cached
open-attendance employee ids. Accepts a 3-arg signature too in case
Odoo's compute-field calling convention shifts again.
Verified on entech: clocked_in==True returns the 3 currently-on-shift
operators (Carlos, James, Marie); ==False returns the other 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the worker-access story. Handoffs now route themselves.
New model fp.work.role with 8 seeded defaults (noupdate so shops can
rename/prune):
masking · racking · plating_op · demask · oven · derack ·
inspection · rework
Each one has a code, icon, description, sequence, active flag.
Config menu: Configuration → Shop Roles (manager-only).
Field additions:
hr.employee.x_fc_work_role_ids (Many2many) — tag workers with the
roles they perform. One-person shop: one employee, every role.
Specialised shop: one role per employee. Cross-trained: multiple.
fusion.plating.process.node.x_fc_work_role_id (Many2one) — tag
each recipe operation with the role that performs it.
mrp.workorder.x_fc_work_role_id (Many2one) — copied from the recipe
operation on WO generation.
Auto-assignment on WO generation:
_generate_workorders_from_recipe() now copies the operation's role
onto the WO, then calls _fp_pick_worker_for_role() which picks the
least-loaded employee (active WO count) with that role. WO lands in
their Tablet "My Queue" the moment the MO is confirmed. No manual
routing needed for the common case.
Tablet Station — worker mode:
/fp/shopfloor/tablet_overview now filters to WOs where
x_fc_assigned_user_id == env.user when the field is populated.
KPIs (WOs Ready / In Progress) reflect the logged-in worker's load,
not shop-wide totals. "My Queue" rows carry wo_state + can_start +
can_finish so inline Start/Finish buttons appear.
New JS handlers onStartWo / onFinishWo call /fp/shopfloor/start_wo
and /fp/shopfloor/stop_wo (finish=true). One-tap progression.
Views:
hr.employee form gets a "Shop Roles" notebook page with many2many_tags.
Process node form gets x_fc_work_role_id inline after work_center_id.
Work Order form shows role + assigned worker.
Smoke-tested end-to-end on WH/MO/00010:
Masking → Administrator (masking role)
Racking → Administrator (racking role)
E-Nickel → Andrew (plating_op, least-loaded tiebreaker)
Demask → Administrator (masking)
Oven bake → Andrew (oven)
Derack → Administrator (racking fallback)
Post-plate QA → Administrator (inspection)
80 existing WOs backfilled with role + worker via name-match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New client action "Manager Desk" under Shop Floor menu (manager-only).
Three-column dashboard designed for the shop manager's daily job:
Column 1 — Needs a Worker
MOs with active WOs missing user assignment. Each card expands to
show per-WO rows with:
- Assign Worker dropdown (pulls from group_fusion_plating_operator)
- Tank swap dropdown (all tanks with current bath)
- Take Over (claims for the manager in one click)
- Open (jump to WO form)
Column 2 — In Progress
MOs with workers actively running WOs. Shows who's on each step,
lets manager reassign or take over if someone steps away.
Column 3 — Team
Avatar grid of operators with live queue + in-progress counts.
Click to drill into that operator's full WO list.
KPI strip on top: Unassigned WOs, In Progress, Ready to Ship, Awaiting
Assignment SOs.
Quick / Detailed view toggle — Detailed auto-expands every card body.
New field mrp.workorder.x_fc_assigned_user_id (indexed, tracked) —
the worker currently owning this step. Will be the pivot the Tablet
Station filters on in Chunk 4.
Three new endpoints:
/fp/manager/overview — dashboard snapshot (30s auto-refresh)
/fp/manager/assign_worker — set user on a WO
/fp/manager/assign_tank — swap tank on a WO
/fp/manager/take_over — manager claims the WO (no-show coverage)
Controller is graceful when mrp module isn't installed (empty overview,
no crash) and when the bridge_mrp assignment field isn't present (falls
back to showing all active WOs as "unassigned").
Verified: 4 WOs assigned across 2 users, overview queries return the
expected counts, res.groups.user_ids (Odoo 19 API, not deprecated .users).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sale Order form now guides the user through the next step without
making them navigate between screens.
New computed field sale.order.x_fc_workflow_stage with 12 stages:
draft → awaiting_parts → inspecting → accept_parts → assign_work
→ in_production → ready_to_ship → shipped → invoicing
→ paid → complete (+ cancelled)
Driven by SO.state + x_fc_receiving_status + MO state +
delivery.state + invoice payment state.
Five contextual header buttons (only 1-2 visible at any time,
fusion_claims pattern — invisible="x_fc_workflow_stage != 'foo'"):
Mark Inspecting → flips receiving to 'inspecting'
Accept Parts → flips receiving to 'accepted' + SO status
to 'inspected', unlocks manager assignment
Assign To Me & Release → manager claims the job, confirms all draft
MOs (which auto-generates WOs already)
Open Shop Floor → jumps to Plant Overview during production
Mark Shipped → closes open delivery records → triggers
auto-invoice per strategy
Info banner shows current stage + assigned manager on the sheet so
users always know where they are.
New fields:
sale.order.x_fc_assigned_manager_id (Many2one res.users, tracked)
mrp.production.x_fc_assigned_manager_id (Many2one, propagated on
MO confirm)
MO.action_confirm() now pulls the assigned manager from the SO (or
falls back to SO.user_id) and sets it on the MO — sets up the
Manager Dashboard (chunk 2) and role-based assignment (chunk 4) to
filter "my jobs" cleanly.
Smoke-tested across 10 demo SOs — stages compute correctly:
S00028 → ready_to_ship, S00027-25 → awaiting_parts,
S00023-20 → complete/shipped, S00029 → draft.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Customers can now pick which shipping-time documents they actually want
instead of the shop remembering it per account. Four booleans on
res.partner (only shown on companies, not contacts):
x_fc_send_coc (default True) Certificate of Conformance
x_fc_send_thickness_report (default True) Thickness readings
x_fc_send_packing_slip (default True) Packing slip PDF
x_fc_send_bol (default False) Bill of Lading
Surfaced in a "Plating Documents" page on the customer form.
Two downstream gates:
1. fp.notification.template._collect_attachments() now reads the flags
when attaching CoC / thickness / packing / BoL PDFs to the shipping
confirmation email. Flags missing on the partner (e.g. legacy
customers) fall back to the original defaults so nothing regresses.
2. mrp.production.button_mark_done() only auto-creates the quality
documents the customer wants. A customer that unchecks both CoC and
thickness gets zero certs auto-generated — shop can still create
them manually if needed.
Note: today a standalone thickness-only report template doesn't exist,
so when a customer asks for thickness only (CoC off, thickness on) the
dispatcher still attaches the CoC PDF (which carries thickness data)
but with CoC creation gated off. A dedicated thickness-only template
is a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: pricing.rule records had currency_id=NULL because the
default=lambda only applies on new records. Monetary fields without a
currency silently render as plain numbers — no $ symbol.
Fixes:
1. currency_id now required=True on fp.pricing.rule, fp.treatment,
fp.customer.price.list, fp.quote.configurator, fusion.plating.quote.request
— so it can never be missing going forward.
2. post_init_hook + matching backfill helper in
fusion_plating_configurator/__init__.py pins the company currency
on any existing records that were created before the required flag.
Ran on upgrade → all 4 pricing.rule rows now have CAD/$.
3. Flipped two remaining Float money fields to Monetary:
- fp.job.consumption.unit_cost and total_cost (were Float digits=4/2)
- (mrp.workorder.x_fc_workcenter_cost_hour stays Float — it is a
related field from core mrp.workcenter.costs_hour which is Float)
4. Every Monetary field reference in views now has explicit:
widget="monetary" options="{'currency_field': 'currency_id'}"
Previously Odoo's default rendering dropped the $ in some contexts.
Touched: fp_pricing_rule_views (list + form), fp_treatment_views,
fp_customer_price_list_views (already done), fp_quote_configurator_views
(list + form shipping/delivery/calculated/override), fp_quote_request_views
(list + form), fp_job_consumption_views, mrp_production_views job-costing
group, direct-order wizard (already done earlier).
5. Unit / % suffix polish as we went: rush_surcharge_percent shows "%",
default_duration_minutes shows "min" on treatment form, treatment list
labels duration column.
Verified: all 4 pricing rules now render "$0.45", "$0.85" etc; 62 records
across 6 models all have currency_id populated; zero remaining Float $
fields in the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sale Order form now hubs the full flow — Manufacturing, Work Orders,
Portal Jobs, Quality Holds, Certificates, Deliveries — hidden when
count == 0. Clicking each jumps to the filtered list/form so users
can drill in without leaving the SO.
Counts are computed on the fly from: mrp.production.origin == SO.name,
production.workorder_ids, production.x_fc_portal_job_id, quality.hold
production_id, fp.certificate.sale_order_id, fp.delivery.job_ref.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bridge_mrp._generate_workorders_from_recipe was writing 'description'
on mrp.workorder, which doesn't exist in Odoo 19 — instead the step
instructions now post to each WO's chatter after bulk create, which
is where the operator sees them anyway.
Demo seeder now creates the full WO chain:
- 9 MRP work centres paired with 9 FP work centres (FP-QUEUE, -RACK,
-MASK, -EN, -BAKE, -INSP, -DERACK, -DEMASK, -POSTBAKE) with
costs_hour set so actuals-vs-quoted margin can compute.
- Wires the existing ENP-ALUM-BASIC recipe's 9 operation nodes to
those FP work centres by matching names.
- Links every coating config to the recipe so the auto-assign hook
(mrp.production.action_confirm → _auto_assign_recipe_from_so) has
something to pull.
- Backfills work orders on all existing demo MOs: calls the generator
once recipe is set. For historical (done) MOs, marks all their WOs
done with backdated durations (25-90 min). For the Cyclone active
MO, sets a realistic progression: first WO done, second in progress
(priority: Hot), rest in 'ready'.
Verified: 90 WOs live, 10 per work centre. One MO shows the full
progression state mix. WO Traveller PDF renders (132KB) — both
portrait + landscape variants still work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug review fixes (found by code review + live QWeb error):
- report_fp_sale.xml: product_uom → product_uom_id (Odoo 19 renamed;
was raising KeyError during PDF render, blocking all sale-order prints)
- mrp_production.button_mark_done: add idempotency guard on delivery
auto-create (was duplicating on every re-close)
- fp.certificate._compute_batch_ids: use empty recordset instead of
False for Many2many computed fields
- fp_notification_template._collect_attachments: collapse attach_quotation
+ attach_sale_order into a single render so email doesn't double-attach
the same PDF
- fp.operator.certification: SQL unique on computed state was unreliable;
added explicit `revoked` boolean, made state pure-compute, replaced
SQL constraint with @api.constrains that checks active-only uniqueness;
has_active_cert now reads revoked + expires_date directly (no stale
stored state between nightly recomputes)
Two missing invoice strategies implemented + 1 pre-existing deposit bug fix:
- Progress Billing: new x_fc_progress_initial_percent field on sale.order;
_create_progress_initial_invoice bills the configured % on SO confirm
via down-payment wizard, _create_final_balance_invoice bills the
remainder on delivery
- Net Terms: no invoice on confirm; full invoice auto-created when
fusion.plating.delivery.action_mark_delivered fires
- Fix for deposit (pre-existing, silent): sale.advance.payment.inv
reads active_ids at wizard-create time, not on create_invoices();
context was being set on the wrong call, so every deposit attempt
raised "Expected singleton" and message-posted to chatter instead
of actually invoicing
- New fusion_plating_invoicing/models/fp_delivery.py hooks
action_mark_delivered to dispatch final invoice for progress/net_terms
- fp.direct.order.wizard + SO form surface the progress_initial_percent
field (conditional on strategy)
Report styling cleanup:
- Hide DISCOUNT column from sale + invoice landscape reports unless at
least one line has a non-zero discount; colspan auto-adjusts
- Replace hardcoded #0066a1 in all reports with company.primary_color
driven by doc.company_id → company → user.company_id fallback chain,
with #1d1f1e as ultimate fallback; new .fp-header-primary class
exposes the colour for inline section headers (CARGO DESCRIPTION,
PAYMENT DETAILS, OPERATOR SIGN-OFF, etc.) so they retint with the
company theme without template edits
Certificate of Conformance — formal ENTECH-style rebuild:
- New res.company fields: x_fc_owner_user_id (default signer, sig from
hr.employee.signature), x_fc_coc_signature_override (manual upload),
x_fc_{nadcap,as9100,cgp}_logo + _active toggles for accreditation
badges
- New res.config.settings section "Fusion Plating" exposing the above
as configurable blocks; manager-only menu under Configuration →
Fusion Plating Settings
- New fp.certificate fields: nc_quantity, customer_job_no,
contact_partner_id (child contact for Name / Email / Phone block)
- New report_coc_en + report_coc_fr templates (primary): custom header
(company contact | accreditations | company logo), bilingual labels
per variant, customer info block with customer logo, 3-column cert
info table, 6-column line-item table (Part # | Process | Customer
PO | Shipped | NC Qty | Customer Job No.), signature image + bordered
certification statement, footer "Fusion Plating by Nexa Systems"
- Legacy report_coc + report_coc_portrait kept for existing portal-job
bindings (no behaviour change)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 2 — Quality & audit readiness:
- T2.1 SPC on thickness readings (fp.certificate)
- spec_min_mils / spec_max_mils auto-pulled from coating config on create
- Computed: std_dev_mils, min/max, cpk, cpk_status (incapable/marginal/
capable/excellent/insufficient)
- Western Electric trend rules (rule 1: any point beyond 3σ; rule 4:
8 consecutive on one side of mean) → trend_alert + explanation
- New SPC group on certificate form with badge-coloured indicators
- T2.2 Operator certification enforcement (fp.operator.certification)
- Per (employee, process_type) records with issued/expires dates,
training record attachment, revocation workflow
- State auto-computed: active → expired when date passes
- MrpWorkorder.button_start() blocks with UserError if current user's
linked hr.employee lacks an active cert for the bath's process_type
- Managers bypass the check; expiring-soon filter in search view
- HR Employee form: "Plating Certifications" tab
- T2.3 Material traceability chain
- fusion.plating.batch.workorder_id (new Many2one) + production_id
(related through WO) for full chain
- fp.certificate gets computed batch_ids / bath_ids / batch_count
- "Batches" stat button → list of batches used for this cert's MO,
with their chemistry logs intact
- T2.4 Pre-treatment as first-class baths
- process_family selection on fusion.plating.process.type
(pre_treatment / plating / post_treatment / bake / strip / passivation /
masking / inspection)
- Bath search view: Pre-Treatments / Plating / Post-Treatments / Strip
quick filters
- Existing bath infra (logs, replenishment, SPC) now applies to pre-
treatment baths equally
Tier 3 — Business / revenue:
- T3.1 Customer-specific price lists (fp.customer.price.list)
- Per (customer, coating_config) with unit_price + basis (per_part /
sqin / sqft / lb)
- effective_from / effective_to for annual contract pricing
- min_quantity for volume breaks (cheapest price at requested qty wins)
- _find_price() helper resolves active entry by date + qty
- Direct Order wizard auto-fills unit_price on (partner, coating, qty)
change unless operator has typed an override
- Configurator menu → Customer Price Lists
- T3.2 Quote win/loss tracking (fp.quote.configurator)
- State values: draft → confirmed (won) / lost / expired / cancelled
- lost_reason selection (price / lead_time / tech / spec_mismatch /
no_bid / no_response / competitor / other) + lost_competitor_name
+ lost_details text
- Action buttons: Mark as Lost (requires reason), Mark as Expired
- won_date auto-set on SO creation; lost_date auto-set on mark_lost
- New "Win / Loss" tab on configurator form
- T3.3 Actuals vs. quoted margin (mrp.production)
- Computed monetary fields: x_fc_consumables_cost, x_fc_labour_cost,
x_fc_actual_cost, x_fc_quoted_revenue, x_fc_margin_actual,
x_fc_margin_pct
- Labour = sum(WO duration × workcentre cost_hour)
- Revenue = SO amount_untaxed via mo.origin lookup
- New "Job Costing" group on MO form with badge-coloured margin
- T3.4 Job consumables tracking (fp.job.consumption)
- One row per consumable event (bath replenisher, masking tape, PPE,
chemistry): product, qty, uom, unit_cost (snapshot), total_cost,
source, optional workorder link
- One2many x_fc_consumption_ids on mrp.production
- "Consumables" stat button on MO → filtered list
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>