Split 49 modules/suites into independent git repos; untrack from monorepo
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

Each top-level module/suite folder is now its own private repo on GitHub
(gsinghpal/<name>) and gitea (admin/<name>), with a fresh single initial
commit. The monorepo no longer tracks them (added to .gitignore + git rm
--cached); working-tree files are retained on disk and managed in their
own repos. The monorepo keeps shared root files (CLAUDE.md, docs/, scripts/,
tools/, AGENTS.md, WIP/obsolete dirs) and full history.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-07 01:54:34 -04:00
parent 2a7b315e98
commit a66cdefc01
6740 changed files with 51 additions and 1277207 deletions

View File

@@ -1,201 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- S19 - Surface Fischerscope thickness PDF on the cert form -->
<!-- ============================================================ -->
<!-- Without this extension the operator has no way to know, -->
<!-- before clicking Issue, whether the QC's Fischerscope PDF -->
<!-- will be appended to the CoC. After Issue, no indicator that -->
<!-- the merged PDF actually contains it. This extension fixes -->
<!-- both gaps with a banner + smart button + clickable file. -->
<record id="fp_certificate_view_form_jobs"
model="ir.ui.view">
<field name="name">fp.certificate.form.inherit.jobs</field>
<field name="model">fp.certificate</field>
<field name="inherit_id"
ref="fusion_plating_certificates.fp_certificate_view_form"/>
<field name="arch" type="xml">
<!-- 1. Smart button: linked Plating Job, and a separate -->
<!-- smart button for the Fischerscope-source QC. -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_open_job"
type="object"
class="oe_stat_button"
icon="fa-cogs"
invisible="not x_fc_job_id">
<field name="x_fc_job_id" widget="statinfo"
string="Work Order"/>
</button>
<button name="action_view_thickness_qc"
type="object"
class="oe_stat_button"
icon="fa-microscope"
invisible="not x_fc_thickness_qc_id">
<div class="o_field_widget o_stat_info">
<span class="o_stat_text">
<field name="x_fc_thickness_status" widget="badge"
decoration-info="x_fc_thickness_status == 'pending'"
decoration-success="x_fc_thickness_status == 'merged'"/>
</span>
<span class="o_stat_text">Fischerscope</span>
</div>
</button>
</xpath>
<!-- 2. Banner row above the title - explicit, can't miss. -->
<!-- Three states with distinct alert classes. -->
<xpath expr="//sheet/div[@class='oe_title']" position="before">
<div class="alert alert-info" role="alert"
invisible="x_fc_thickness_status != 'pending'">
<i class="fa fa-info-circle" title="Info"
aria-label="Info"/>
<strong> Fischerscope thickness PDF is on file.</strong>
It will be automatically appended as page&#160;2 of
the CoC when you click <strong>Issue</strong>.
</div>
<div class="alert alert-success" role="alert"
invisible="x_fc_thickness_status != 'merged'">
<i class="fa fa-check-circle" title="Merged"
aria-label="Merged"/>
<strong> Fischerscope thickness report merged.</strong>
The issued CoC PDF includes the Fischerscope report
as page&#160;2 - open the Certificate&#160;PDF tab to verify.
</div>
<div class="alert alert-warning" role="alert"
invisible="state != 'draft' or x_fc_thickness_status != 'none' or not partner_id"
style="margin-top:0;">
<i class="fa fa-exclamation-triangle" title="Warning"
aria-label="Warning"/>
<strong> No Fischerscope PDF available.</strong>
Drop the PDF into the <em>Thickness Report
(Fischerscope)</em> tab below, or upload it on the
linked QC check, before issuing. Thickness Report
certs cannot issue without thickness data.
</div>
</xpath>
<!-- 3. Thickness Report tab - single place to see/edit
every Fischerscope-related field on the cert.
Reorganized 2026-05-21:
* Status + linked QC at the top (read-only context)
* XDAL 600 metadata (operator/product/etc.) editable
so manager can correct OCR mistakes
* Microscope image preview (auto-extracted from RTF
or manually uploaded - either way editable here)
* Source files (PDF / non-PDF evidence / source name)
* Upload wizard button + help text -->
<xpath expr="//notebook/page[@name='pdf']" position="after">
<page string="Thickness Report (Fischerscope)"
name="thickness_pdf">
<!-- Status + QC link (read-only context) -->
<group>
<field name="x_fc_thickness_status" widget="badge"
readonly="1"
decoration-muted="x_fc_thickness_status == 'none'"
decoration-info="x_fc_thickness_status == 'pending'"
decoration-success="x_fc_thickness_status == 'merged'"/>
<field name="x_fc_thickness_qc_id" readonly="1"
invisible="not x_fc_thickness_qc_id"/>
</group>
<!-- Hints rotate by state -->
<div class="text-muted"
invisible="x_fc_thickness_status != 'none'">
<p>
No Fischerscope thickness data has been
uploaded yet. Click <strong>Upload Thickness
Report</strong> below to drop a `.doc` / `.docx`
/ `.rtf` / `.pdf` file straight from the
XDAL&#160;600. The wizard parses readings +
metadata and fills out the fields on this tab.
</p>
</div>
<div class="text-muted"
invisible="x_fc_thickness_status != 'pending'">
<p>
<i class="fa fa-arrow-up"/>
Click <strong>Issue</strong> in the header to
merge the Fischerscope PDF as page&#160;2 of
the CoC. Readings will render inline in the
body of the cert either way.
</p>
</div>
<!-- Upload wizard CTA -->
<div style="margin: 8px 0;">
<button name="%(fusion_plating_certificates.action_fp_thickness_upload_wizard)d"
type="action"
class="btn-primary"
string="Upload Thickness Report"
context="{'default_certificate_id': id}"
invisible="state != 'draft'"/>
</div>
<separator string="XDAL 600 Measurement Context"/>
<p class="text-muted small">
These values are pulled from the uploaded file
and printed on the CoC's thickness section. Edit
any field here to override what the parser saw.
</p>
<group>
<group>
<field name="x_fc_thickness_equipment"
placeholder="Fischerscope XDAL 600"/>
<field name="x_fc_thickness_operator"
placeholder="Operator initials / name"/>
<field name="x_fc_thickness_datetime"/>
<field name="x_fc_thickness_measuring_time_sec"/>
</group>
<group>
<field name="x_fc_thickness_product"
placeholder="e.g. 2805031 / NiP/Al-alloys 2805030"/>
<field name="x_fc_thickness_application"
placeholder="e.g. 16 / NiP/Al-alloys"/>
<field name="x_fc_thickness_directory"
placeholder="XDAL save directory"/>
<field name="x_fc_thickness_source_filename"
readonly="1"/>
</group>
</group>
<separator string="Microscope Image"/>
<p class="text-muted small">
Auto-extracted from RTF uploads (via libwmf) or
manually uploaded via the wizard. Drop a new
PNG/JPEG here to override.
</p>
<group>
<field name="x_fc_thickness_image_id"
options="{'no_create': True}"/>
</group>
<separator string="Source Files"/>
<group>
<group string="Fischerscope PDF"
invisible="not x_fc_local_thickness_pdf">
<field name="x_fc_local_thickness_pdf"
filename="x_fc_local_thickness_pdf_filename"/>
<field name="x_fc_local_thickness_pdf_filename"
invisible="1"/>
</group>
<group string="Non-PDF Evidence (RTF/DOCX)"
invisible="not x_fc_local_thickness_evidence_id">
<field name="x_fc_local_thickness_evidence_id"
options="{'no_create': True}"/>
</group>
<group string="QC-side Fischerscope PDF"
invisible="not x_fc_thickness_pdf_id">
<field name="x_fc_thickness_pdf_id" readonly="1"
widget="many2one_binary"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
One-shot backfill for closed jobs that never produced a CoC because
of the `coating` NameError regression (fixed 2026-05-18). Surfaced
as a Settings > Technical menu item so the user can click once after
deploying the fix.
-->
<odoo>
<record id="action_fp_job_backfill_missing_certs" model="ir.actions.server">
<field name="name">Generate Missing Certs for Closed Jobs</field>
<field name="model_id" ref="fusion_plating.model_fp_job"/>
<field name="binding_model_id" ref="fusion_plating.model_fp_job"/>
<field name="binding_view_types">list</field>
<field name="group_ids" eval="[(4, ref('base.group_system'))]"/>
<field name="state">code</field>
<field name="code">action = env['fp.job'].action_backfill_missing_certs()</field>
</record>
</odoo>

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_fp_job_consumption_list" model="ir.ui.view">
<field name="name">fp.job.consumption.list</field>
<field name="model">fp.job.consumption</field>
<field name="arch" type="xml">
<list editable="bottom" default_order="logged_date desc">
<field name="logged_date"/>
<field name="job_id"/>
<field name="step_id"/>
<field name="product_id"/>
<field name="quantity"/>
<field name="uom_id"/>
<field name="currency_id" column_invisible="1"/>
<field name="unit_cost" widget="monetary"
options="{'currency_field': 'currency_id'}"/>
<field name="total_cost" widget="monetary"
options="{'currency_field': 'currency_id'}" sum="Total"/>
<field name="source"/>
<field name="logged_by_id" optional="hide"/>
</list>
</field>
</record>
<record id="view_fp_job_consumption_form" model="ir.ui.view">
<field name="name">fp.job.consumption.form</field>
<field name="model">fp.job.consumption</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="job_id"/>
<field name="step_id"/>
<field name="product_id"/>
<field name="product_name"/>
<field name="source"/>
</group>
<group>
<field name="quantity"/>
<field name="uom_id"/>
<field name="currency_id"/>
<field name="unit_cost" widget="monetary"
options="{'currency_field': 'currency_id'}"/>
<field name="total_cost" widget="monetary"
options="{'currency_field': 'currency_id'}" readonly="1"/>
<field name="logged_date"/>
<field name="logged_by_id"/>
</group>
</group>
<separator string="Notes"/>
<field name="notes"/>
</sheet>
</form>
</field>
</record>
<record id="action_fp_job_consumption" model="ir.actions.act_window">
<field name="name">Job Consumables Log</field>
<field name="res_model">fp.job.consumption</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -1,343 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--
Adds a "Process Tree" header button + smart-button row to the
fp.job form. The fp.job form in core has no button_box yet, so
we inject one at the top of the sheet (xpath //sheet position
"inside" with a sibling reference at the start).
Smart buttons appear only when the underlying count is > 0
(except Steps, which always shows since every confirmed job
has steps). Pattern follows the existing oe_stat_button row
from sale.order / mrp.production.
Process Tree header button is hidden while the job is in draft
(no recipe-derived steps yet).
-->
<record id="view_fp_job_form_jobs_inherit" model="ir.ui.view">
<field name="name">fp.job.form.jobs.inherit</field>
<field name="model">fp.job</field>
<field name="inherit_id" ref="fusion_plating.view_fp_job_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<!-- Heal a WO that was created before the recipe was set
on the SO line: re-pull the recipe and build steps.
Hidden once steps exist or the job is terminal. -->
<button name="action_fp_resync_recipe_from_so" type="object"
string="Re-sync Recipe from SO"
class="btn-secondary"
icon="fa-refresh"
invisible="state in ('done', 'cancelled') or step_ids"/>
<!-- Phase 1 - Tablet redesign. Opens the JobWorkspace OWL
client action focused on this WO. Primary entry point
for techs before the Landing kanban (Phase 3) ships;
stays as a back-office shortcut after. -->
<button name="action_open_workspace" type="object"
string="Workspace"
class="btn-primary"
icon="fa-tablet"
invisible="state == 'draft'"/>
<button name="action_open_process_tree" type="object"
class="btn-secondary"
icon="fa-sitemap"
invisible="state == 'draft'"/>
<!-- Steelhead-style "Finish & Next": one click finishes
whatever's running and auto-starts the next pending
step. Falls back to starting the first step if
nothing is running yet. The classic Move wizard is
still available via the per-row Move button (used
for cross-station moves and rework / scrap). -->
<button name="action_finish_current_step" type="object"
string="Finish &amp; Next"
class="btn-primary"
icon="fa-arrow-right"
invisible="state not in ('confirmed', 'in_progress') or all_steps_terminal"/>
<!-- Milestone cascade (Phase 1). All four share the same
dispatcher; visibility is gated on next_milestone_action
so only one ever renders at a time. -->
<button name="action_advance_next_milestone" type="object"
string="Mark Job Done"
class="btn-success"
icon="fa-check-circle"
invisible="next_milestone_action != 'mark_done'"/>
<button name="action_advance_next_milestone" type="object"
string="Issue Certs"
class="btn-primary"
icon="fa-certificate"
invisible="next_milestone_action != 'issue_certs'"/>
<button name="action_advance_next_milestone" type="object"
string="Schedule Delivery"
class="btn-primary"
icon="fa-truck"
invisible="next_milestone_action != 'schedule_delivery'"/>
<button name="action_advance_next_milestone" type="object"
string="Mark Shipped"
class="btn-success"
icon="fa-paper-plane"
invisible="next_milestone_action != 'mark_shipped'"
groups="fusion_plating.group_fp_manager,fusion_plating.group_fp_owner"/>
<field name="all_steps_terminal" invisible="1"/>
<field name="next_milestone_action" invisible="1"/>
<button name="action_print_sticker" type="object"
class="btn-secondary"
icon="fa-qrcode"
invisible="state == 'draft'"
help="Print Sticker"/>
</xpath>
<!-- Sub 14 - Replace the generic Draft/Confirmed/In Progress/Done
statusbar with the configurable workflow_state_id bar.
Operators see meaningful plating milestones (Received,
Inspected, Shipped, etc.) instead of generic Odoo states. -->
<xpath expr="//header/field[@name='state']" position="replace">
<field name="workflow_state_id"
widget="statusbar"
options="{'clickable': '0'}"/>
</xpath>
<!-- Surface part / coating / recipe on the header so the
floor knows WHAT they're plating without diving into
Source. The "Reference Product" line in core is just
the FP-SERVICE stub from the SO - relabel it so it
doesn't compete with the real part identification. -->
<xpath expr="//field[@name='product_id']" position="attributes">
<attribute name="string">Service Product</attribute>
<attribute name="invisible">part_catalog_id</attribute>
</xpath>
<xpath expr="//field[@name='product_id']" position="after">
<field name="part_catalog_id" string="Part"/>
<field name="customer_spec_id" string="Specification" invisible="1"/>
<field name="recipe_id" string="Process Recipe" readonly="1"/>
</xpath>
<!-- Show qty completed alongside total so the partial-qty
picture is visible at a glance without opening Move Log. -->
<xpath expr="//field[@name='qty']" position="after">
<field name="qty_done" string="Qty Done"/>
<field name="qty_scrapped" string="Qty Scrapped"
invisible="not qty_scrapped"/>
</xpath>
<!-- Replace the bare-bones Steps list with the action-rich
manager view. Per-row buttons mirror what an operator
sees on the tablet; Running Min ticks on every refresh
for the active step. -->
<xpath expr="//page[@name='steps']/field[@name='step_ids']" position="replace">
<field name="step_ids" mode="list"
context="{'form_view_ref': 'fusion_plating_jobs.view_fp_job_step_quick_look_form'}">
<list editable="bottom" create="false" delete="false"
decoration-info="state in ('ready', 'in_progress')"
decoration-success="state == 'done'"
decoration-warning="state == 'paused'"
decoration-muted="state in ('skipped', 'cancelled')">
<field name="sequence" widget="handle"/>
<button name="action_open_quick_look" type="object"
title="View step details"
icon="fa-info-circle"
width="30"
class="btn-link o_fp_step_info_btn"/>
<field name="name"/>
<field name="work_centre_id" optional="show"/>
<field name="tank_id" optional="hide"/>
<field name="kind" optional="hide"/>
<field name="state" widget="badge"
decoration-info="state in ('ready', 'in_progress')"
decoration-success="state == 'done'"
decoration-warning="state == 'paused'"
decoration-muted="state in ('skipped', 'cancelled')"/>
<field name="assigned_user_id" optional="show"/>
<field name="duration_expected" optional="show"/>
<field name="duration_running_minutes" string="Running Min" optional="show"/>
<field name="duration_actual" optional="show"/>
<!-- Live qty currently parked at this step. Hits
zero once everything has moved on; >0 means
the floor still has parts to process here. -->
<field name="qty_at_step" string="Qty Here" optional="show"/>
<!-- Primary action: state-aware. Pending/ready → Start,
in_progress → Finish & Next (auto-advance like
Steelhead), paused → Resume. Done / skipped /
cancelled rows show no primary. -->
<button name="button_start" type="object"
string="Start" icon="fa-play"
class="btn-link text-success"
invisible="state not in ('ready', 'pending')"/>
<button name="button_resume" type="object"
title="Resume" icon="fa-play-circle"
class="btn-link text-success"
invisible="state != 'paused'"/>
<button name="action_finish_and_advance" type="object"
title="Finish &amp; Next" icon="fa-check-circle"
class="btn-link o_fp_finish_btn"
invisible="state != 'in_progress'"/>
<!-- Reset / redo - back to Ready so the step can be
run again (mistake, accidental skip, customer
redo). Clears finish + sign-off stamps; keeps the
start audit + moves. Hidden on ready/pending. -->
<button name="button_reset" type="object"
title="Reset step (redo)" icon="fa-undo"
class="btn-link text-warning"
invisible="state in ('ready', 'pending')"/>
<!-- Secondary actions - small icons only. Pause is
only relevant on a running step; Record Inputs
stays available so operators can capture
measurements without finishing the step;
Skip + Move (cross-station) tucked together. -->
<button name="button_pause" type="object"
title="Pause" icon="fa-pause"
class="btn-link text-warning"
invisible="state != 'in_progress'"/>
<!-- Streaming flow: complete 1 part at a time,
move to next step. Hidden when nothing is
parked or the step isn't actively running.
Auto-finishes when qty_at_step drains to 0. -->
<button name="action_complete_one_to_next" type="object"
title="Complete 1 → Next (streaming flow)" icon="fa-forward"
class="btn-link text-success"
invisible="state != 'in_progress' or qty_at_step &lt; 1"/>
<button name="action_open_input_wizard" type="object"
title="Record Inputs" icon="fa-pencil-square-o"
class="btn-link"
invisible="state in ('cancelled', 'skipped')"/>
<button name="button_skip" type="object"
title="Skip step" icon="fa-step-forward"
class="btn-link text-muted"
invisible="state not in ('pending', 'ready')"/>
<button name="action_open_move_wizard" type="object"
title="Move… (custom qty / destination)" icon="fa-exchange"
class="btn-link text-muted"
invisible="state in ('done', 'cancelled', 'skipped', 'pending')"/>
</list>
</field>
</xpath>
<!-- New tabs in the notebook: Move Log + Time Logs.
Both read-only - operators write via the wizards;
these tabs are the audit window. -->
<xpath expr="//page[@name='source']" position="before">
<page string="Move Log" name="move_log">
<field name="move_ids" readonly="1">
<list create="false" edit="false" delete="false"
decoration-info="transfer_type == 'step'"
decoration-warning="transfer_type in ('hold', 'rework')"
decoration-danger="transfer_type == 'scrap'">
<field name="move_datetime"/>
<field name="from_step_id"/>
<field name="to_step_id"/>
<field name="transfer_type" widget="badge"/>
<field name="qty_moved"/>
<field name="moved_by_user_id"/>
</list>
</field>
</page>
<page string="Time Logs" name="time_logs">
<field name="time_log_ids" readonly="1">
<list create="false" edit="false" delete="false">
<field name="step_id"/>
<field name="user_id"/>
<field name="date_started"/>
<field name="date_finished"/>
<field name="duration_minutes"/>
</list>
</field>
</page>
</xpath>
<!-- Inject a button_box at the top of the sheet, before the
oe_title block. Smart buttons drill into the matching
records the way sale.order does. -->
<xpath expr="//sheet/div[hasclass('oe_title')]" position="before">
<div class="oe_button_box" name="button_box">
<button name="action_view_sale_order" type="object"
class="oe_stat_button" icon="fa-shopping-cart"
invisible="sale_order_count == 0">
<field name="sale_order_count" widget="statinfo"
string="Sale Order"/>
</button>
<button name="action_view_steps" type="object"
class="oe_stat_button" icon="fa-list-ol">
<field name="step_count" widget="statinfo"
string="Steps"/>
</button>
<button name="action_view_deliveries" type="object"
class="oe_stat_button" icon="fa-truck"
invisible="delivery_count == 0">
<field name="delivery_count" widget="statinfo"
string="Delivery"/>
</button>
<button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o"
invisible="invoice_count == 0">
<field name="invoice_count" widget="statinfo"
string="Invoices"/>
</button>
<button name="action_view_payments" type="object"
class="oe_stat_button" icon="fa-money"
invisible="payment_count == 0">
<field name="payment_count" widget="statinfo"
string="Payments"/>
</button>
<button name="action_view_quality_holds" type="object"
class="oe_stat_button" icon="fa-pause-circle"
invisible="quality_hold_count == 0">
<field name="quality_hold_count" widget="statinfo"
string="Holds"/>
</button>
<button name="action_view_certificates" type="object"
class="oe_stat_button" icon="fa-certificate"
invisible="certificate_count == 0">
<field name="certificate_count" widget="statinfo"
string="Certificates"/>
</button>
<button name="action_view_timelogs" type="object"
class="oe_stat_button" icon="fa-clock-o"
invisible="timelog_count == 0">
<field name="timelog_count" widget="statinfo"
string="Time Logs"/>
</button>
<button name="action_view_portal_job" type="object"
class="oe_stat_button" icon="fa-globe"
invisible="portal_job_count == 0">
<field name="portal_job_count" widget="statinfo"
string="Portal Job"/>
</button>
</div>
</xpath>
<xpath expr="//group[@name='x_fc_customer_refs']" position="inside">
<field name="x_fc_delivery_method"/>
<field name="x_fc_ship_via"/>
<field name="x_fc_invoice_strategy"/>
</xpath>
<xpath expr="//group[@name='x_fc_customer_refs']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<!-- Notes group sits awkwardly above the main fields in core; relocate
to a notebook tab so the form opens on the operationally relevant
fields (customer / part / steps) instead of empty note placeholders. -->
<xpath expr="//group[@name='x_fc_notes']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//page[@name='costs']" position="before">
<page string="Masking Refs" name="masking_refs"
invisible="not x_fc_masking_attachment_ids">
<div class="text-muted mb-2">
Masking reference image(s)/PDF(s) attached at order entry (Express).
The operator sees these on the masking step in the workstation.
</div>
<field name="x_fc_masking_attachment_ids" widget="many2many_binary"
readonly="1" nolabel="1"/>
</page>
</xpath>
<xpath expr="//page[@name='costs']" position="before">
<page string="Notes" name="notes">
<group>
<field name="x_fc_internal_note" nolabel="1"
placeholder="Internal note (not shown to customer)…"/>
<field name="x_fc_external_note" nolabel="1"
placeholder="External note (printed on customer paperwork)…"/>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Sub 12 Phase D - quality smart-button row on fp.job. The quality
fields (fp_qc_hold_count, fp_qc_check_count, fp_qc_ncr_count,
fp_qc_capa_count, fp_qc_rma_count) are defined in fusion_plating_quality
via _inherit on fp.job. The view lives here because the button_box
container is added by fusion_plating_jobs (this module loads after
quality so we can safely reference quality fields).
-->
<odoo>
<record id="view_fp_job_form_quality_buttons" model="ir.ui.view">
<field name="name">fp.job.form.quality.buttons</field>
<field name="model">fp.job</field>
<field name="inherit_id" ref="view_fp_job_form_jobs_inherit"/>
<field name="arch" type="xml">
<!-- Hidden at a count of 0 so the row shows only quality work
that actually exists on this job (2026-06-04). -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_fp_holds" type="object"
class="oe_stat_button" icon="fa-hand-paper-o"
invisible="fp_qc_hold_count == 0">
<field name="fp_qc_hold_count" widget="statinfo" string="Holds"/>
</button>
<button name="action_view_fp_checks" type="object"
class="oe_stat_button" icon="fa-check-square-o"
invisible="fp_qc_check_count == 0">
<field name="fp_qc_check_count" widget="statinfo" string="Checks"/>
</button>
<button name="action_view_fp_ncrs" type="object"
class="oe_stat_button" icon="fa-exclamation-triangle"
invisible="fp_qc_ncr_count == 0">
<field name="fp_qc_ncr_count" widget="statinfo" string="NCRs"/>
</button>
<button name="action_view_fp_capas" type="object"
class="oe_stat_button" icon="fa-wrench"
invisible="fp_qc_capa_count == 0">
<field name="fp_qc_capa_count" widget="statinfo" string="CAPAs"/>
</button>
<button name="action_view_fp_rmas" type="object"
class="oe_stat_button" icon="fa-undo"
invisible="fp_qc_rma_count == 0">
<field name="fp_qc_rma_count" widget="statinfo" string="RMAs"/>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,169 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
Read-only "Step Details" quick-look modal. Triggered by clicking a
step's name in the embedded step list inside fp.job's form. Bound
via context="{'form_view_ref': '...'}" on the parent - see
fp_job_form_inherit.xml. The standalone editable form view stays
registered for the Job Steps menu / direct navigation.
-->
<odoo>
<record id="view_fp_job_step_quick_look_form" model="ir.ui.view">
<field name="name">fp.job.step.quick.look.form</field>
<field name="model">fp.job.step</field>
<field name="priority">50</field>
<field name="arch" type="xml">
<form string="Step Details" edit="false" create="false" delete="false">
<sheet>
<!-- Hidden helper fields used by section visibility
conditions below. Without these the empty-state
hides for Equipment / Schedule won't evaluate. -->
<field name="work_centre_id" invisible="1"/>
<field name="tank_id" invisible="1"/>
<field name="bath_id" invisible="1"/>
<field name="rack_id" invisible="1"/>
<field name="duration_expected" invisible="1"/>
<field name="duration_actual" invisible="1"/>
<field name="assigned_user_id" invisible="1"/>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
<div class="text-muted">
Step #<field name="sequence" readonly="1"/> ·
<field name="kind" readonly="1"/> ·
<field name="state" widget="badge"
decoration-info="state == 'in_progress'"
decoration-success="state == 'done'"
decoration-warning="state == 'paused'"
decoration-muted="state in ('skipped','cancelled')"/>
</div>
</div>
<!-- Job context - what job is this step part of, who's
the customer, what part, how many. The single most
useful thing to surface up top so the operator
orients themselves before drilling in. -->
<group string="Job Context">
<group>
<field name="job_id" readonly="1" options="{'no_open': True}"/>
<field name="quick_look_part_catalog_id" readonly="1"
options="{'no_open': True}"
invisible="not quick_look_part_catalog_id"/>
</group>
<group>
<field name="quick_look_partner_id" readonly="1"
options="{'no_open': True}"
invisible="not quick_look_partner_id"/>
<field name="quick_look_qty" readonly="1"
invisible="not quick_look_qty"/>
</group>
</group>
<!-- Equipment / Schedule - only render when there's
actually something to show. An Inspection step with
no tank / bath / time-budget shouldn't display
four empty rows of "-" - that's misleading. -->
<group invisible="not work_centre_id and not tank_id and not bath_id and not rack_id and not duration_expected and not duration_actual and not assigned_user_id">
<group string="Equipment"
invisible="not work_centre_id and not tank_id and not bath_id and not rack_id">
<field name="work_centre_id" readonly="1"
invisible="not work_centre_id"/>
<field name="tank_id" readonly="1"
invisible="not tank_id"/>
<field name="bath_id" readonly="1"
invisible="not bath_id"/>
<field name="rack_id" readonly="1"
invisible="not rack_id"/>
</group>
<group string="Schedule"
invisible="not duration_expected and not duration_actual and not assigned_user_id">
<field name="duration_expected" readonly="1"
invisible="not duration_expected"/>
<field name="duration_actual" readonly="1"
invisible="not duration_actual"/>
<field name="assigned_user_id" readonly="1"
invisible="not assigned_user_id"/>
</group>
</group>
<!-- Master switch banner -->
<field name="quick_look_collect_master" invisible="1"/>
<div class="alert alert-warning"
role="alert"
invisible="quick_look_collect_master or not recipe_node_id">
<i class="fa fa-exclamation-triangle"/>
<strong> Master switch off</strong> - no values will be collected at runtime for this step.
</div>
<!-- Operator Instructions - hide the whole section when
the recipe author didn't write any. -->
<separator string="Operator Instructions"
invisible="not quick_look_instructions"/>
<div class="o_fp_quick_look_instructions"
invisible="not quick_look_instructions">
<field name="quick_look_instructions" nolabel="1" readonly="1"/>
</div>
<!-- Instruction images - visual reference photos /
screenshots the recipe author attached to the
node. Hidden when none. -->
<separator string="Reference Images"
invisible="not quick_look_instruction_attachment_ids"/>
<field name="quick_look_instruction_attachment_ids"
nolabel="1" readonly="1"
widget="many2many_binary"
invisible="not quick_look_instruction_attachment_ids"/>
<separator string="Measurement Prompts"/>
<field name="quick_look_prompt_ids" nolabel="1" readonly="1">
<list create="false" delete="false" edit="false">
<field name="collect" widget="boolean_toggle" readonly="1" string="Collect"/>
<field name="name"/>
<field name="input_type"/>
<field name="target_min" optional="show"/>
<field name="target_max" optional="show"/>
<field name="target_unit" optional="show"/>
<field name="required" widget="boolean_toggle" readonly="1"/>
<field name="hint" optional="hide"/>
</list>
</field>
<p class="text-muted small"
invisible="quick_look_prompt_ids">
No measurement prompts authored for this step.
</p>
<separator string="Values Recorded So Far"/>
<field name="quick_look_recorded_value_ids" nolabel="1" readonly="1">
<list create="false" delete="false" edit="false" default_order="create_date desc">
<field name="node_input_id" string="Prompt"/>
<field name="value_text" optional="show"/>
<field name="value_number" optional="show"/>
<field name="value_boolean" optional="hide"/>
<field name="value_date" optional="hide"/>
<field name="value_attachment_id" optional="hide"/>
<field name="create_uid" string="Recorded By"/>
<field name="create_date" string="When"/>
</list>
</field>
<p class="text-muted small"
invisible="quick_look_recorded_value_ids">
No values recorded yet.
</p>
</sheet>
<footer>
<button name="action_open_full_form" type="object"
string="Open Full Form" class="btn-primary"/>
<button string="Close" class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

View File

@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Adds the Work Order smart button + header button to fp.receiving so
the receiving form mirrors the SO's WO entry point. Header button
appears once state == 'closed' and at least one linked fp.job is
still open. Smart button is always visible when WOs exist.
-->
<odoo>
<record id="view_fp_receiving_form_fp_jobs" model="ir.ui.view">
<field name="name">fp.receiving.form.fp.jobs</field>
<field name="model">fp.receiving</field>
<field name="inherit_id" ref="fusion_plating_receiving.view_fp_receiving_form"/>
<field name="arch" type="xml">
<!-- Work Order header button - only after receiving is
closed and while at least one job is still open. -->
<xpath expr="//header" position="inside">
<field name="x_fc_show_work_order_btn" invisible="1"/>
<button name="action_view_fp_jobs"
string="Work Order" type="object"
class="btn-primary" icon="fa-cogs"
invisible="not x_fc_show_work_order_btn"
help="Open the Work Order(s) for this receiving. Hidden automatically once every linked WO is marked Done."/>
<!-- Print box stickers for this receiving's work order - one
label per tracked box (external = customer copy, internal
= shop copy with internal notes). Shown once a WO exists. -->
<button name="action_print_external_sticker"
string="External Sticker" type="object" icon="fa-print"
invisible="x_fc_fp_job_count == 0"
help="Print the customer (external) box sticker(s) - one per box."/>
<button name="action_print_internal_sticker"
string="Internal Sticker" type="object" icon="fa-print"
invisible="x_fc_fp_job_count == 0"
help="Print the shop (internal) box sticker(s) - same layout, internal notes."/>
</xpath>
<!-- Work Order smart button on the button_box (mirrors the
one on the SO form). Always visible when count > 0. -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_fp_jobs" type="object"
class="oe_stat_button" icon="fa-cogs"
invisible="x_fc_fp_job_count == 0">
<field name="x_fc_fp_job_count" widget="statinfo" string="WO"/>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Phase 3 (Sub 11) - native Production Priorities kanban / list on
fp.job.step. Replaces the bridge_mrp version that bound to
mrp.workorder. Same UX (drag-drop ordering across work centres,
list with handle, badges by state).
-->
<odoo>
<record id="view_fp_step_priority_kanban" model="ir.ui.view">
<field name="name">fp.job.step.priority.kanban</field>
<field name="model">fp.job.step</field>
<field name="arch" type="xml">
<kanban default_group_by="work_centre_id" default_order="sequence, id">
<field name="name"/>
<field name="work_centre_id"/>
<field name="job_id"/>
<field name="state"/>
<field name="sequence"/>
<field name="duration_actual"/>
<field name="duration_expected"/>
<field name="assigned_user_id"/>
<templates>
<t t-name="card">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="o_kanban_record_top mb-0">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<field name="job_id"/>
</strong>
</div>
</div>
<div class="o_kanban_record_body">
<div><strong><field name="name"/></strong></div>
<div class="text-muted">
<t t-if="record.assigned_user_id.raw_value">
<field name="assigned_user_id"/>
</t>
<t t-if="record.duration_actual.raw_value">
- <field name="duration_actual" widget="float_time"/> elapsed
</t>
</div>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<field name="state" widget="badge"
decoration-info="state == 'ready'"
decoration-warning="state == 'in_progress'"
decoration-muted="state == 'paused'"
decoration-success="state == 'done'"/>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="view_fp_step_priority_list" model="ir.ui.view">
<field name="name">fp.job.step.priority.list</field>
<field name="model">fp.job.step</field>
<field name="arch" type="xml">
<list default_order="sequence, id">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="job_id"/>
<field name="work_centre_id"/>
<field name="assigned_user_id"/>
<field name="duration_expected" widget="float_time"/>
<field name="duration_actual" widget="float_time"/>
<field name="state" widget="badge"
decoration-info="state == 'ready'"
decoration-warning="state == 'in_progress'"
decoration-muted="state == 'paused'"
decoration-success="state == 'done'"/>
</list>
</field>
</record>
<record id="action_fp_step_priority" model="ir.actions.act_window">
<field name="name">Production Priorities</field>
<field name="res_model">fp.job.step</field>
<field name="view_mode">kanban,list,form</field>
<field name="domain">[('state', 'in', ['pending', 'ready', 'in_progress', 'paused'])]</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('view_fp_step_priority_kanban')}),
(0, 0, {'view_mode': 'list', 'view_id': ref('view_fp_step_priority_list')})]"/>
</record>
<menuitem id="menu_fp_step_priority"
name="Production Priorities"
parent="fusion_plating.menu_fp_operations"
action="action_fp_step_priority"
sequence="10"
groups="fusion_plating.group_fusion_plating_supervisor"/>
</odoo>

View File

@@ -1,141 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Sub 14 - Workflow state catalog UI (admin / Settings).
-->
<odoo>
<!-- ====================== List view ====================== -->
<record id="view_fp_workflow_state_list" model="ir.ui.view">
<field name="name">fp.job.workflow.state.list</field>
<field name="model">fp.job.workflow.state</field>
<field name="arch" type="xml">
<list string="Workflow States" default_order="sequence, id">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="code"/>
<field name="color" widget="badge"
decoration-info="color == 'blue'"
decoration-success="color == 'success' or color == 'green'"
decoration-warning="color == 'yellow' or color == 'orange'"
decoration-danger="color == 'danger'"
decoration-muted="color == 'grey'"/>
<field name="trigger_default_kinds"/>
<field name="trigger_first_step_started"/>
<field name="trigger_all_steps_done"/>
<field name="block_when_quality_hold"/>
<field name="is_initial"/>
<field name="is_terminal"/>
<field name="active" widget="boolean_toggle"/>
</list>
</field>
</record>
<!-- ====================== Form view ====================== -->
<record id="view_fp_workflow_state_form" model="ir.ui.view">
<field name="name">fp.job.workflow.state.form</field>
<field name="model">fp.job.workflow.state</field>
<field name="arch" type="xml">
<form string="Workflow State">
<sheet>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" placeholder="Received"/></h1>
</div>
<group>
<group string="Identity">
<field name="code" placeholder="received"/>
<field name="sequence"/>
<field name="color" widget="badge"/>
<field name="active"/>
</group>
<group string="Lifecycle role">
<field name="is_initial"/>
<field name="is_terminal"/>
<field name="block_when_quality_hold"/>
</group>
</group>
<separator string="Trigger Conditions"/>
<group>
<field name="trigger_default_kinds"
placeholder="receiving, inspect"/>
<field name="trigger_first_step_started"/>
<field name="trigger_all_steps_done"/>
</group>
<!-- Help block - full sheet width, alert-info card so
the explanation is readable instead of squeezed
into a 2-column form layout. -->
<div class="alert alert-info mt-3" role="alert">
<h6 class="alert-heading mb-2">
<i class="fa fa-info-circle me-2"/>
How triggers combine
</h6>
<p class="mb-2">
A state is <strong>"passed"</strong> when
<strong>either</strong>:
</p>
<ul class="mb-2">
<li>
The special trigger is true
(<code>trigger_first_step_started</code> or
<code>trigger_all_steps_done</code>),
<strong>OR</strong>
</li>
<li>
Every recipe step matching the listed
<code>trigger_default_kinds</code> (or tagged
via the per-node override on the recipe) is
in <code>done</code> / <code>skipped</code> /
<code>cancelled</code> state.
</li>
</ul>
<p class="mb-0">
<strong>Blocked by Quality Hold:</strong> holds
back the advance even if the trigger conditions
are met, until all open quality holds on the job
are closed.
</p>
</div>
<separator string="Notes"/>
<field name="description" nolabel="1"
placeholder="What this milestone represents and when it should fire..."/>
</sheet>
<!-- Chatter - adds activity log + message thread + followers
so admins can audit who changed which trigger and when. -->
<chatter/>
</form>
</field>
</record>
<!-- ====================== Action + Menu ====================== -->
<record id="action_fp_workflow_state" model="ir.actions.act_window">
<field name="name">Workflow States</field>
<field name="res_model">fp.job.workflow.state</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first workflow state.
</p>
<p>
Workflow states are the milestones that appear on the
status bar of every plating job. Each state passes
automatically when its trigger conditions are met
(recipe step kind finishes, all steps done, etc.).
</p>
</field>
</record>
<!-- Workflow milestones live under "Recipes & Steps" because each
state is triggered by a recipe-step kind / per-step override. -->
<menuitem id="menu_fp_workflow_state"
name="Job Workflow Stages"
parent="fusion_plating.menu_fp_config_recipes_steps"
action="action_fp_workflow_state"
sequence="50"/>
</odoo>

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--
Add "Work Orders" and "Steps" as children of fusion_plating_shopfloor's
Shop Floor menu. We can reference shopfloor's xmlid here because
fusion_plating_jobs declares it as a depend.
Sequences fit between Tablet Station (10) and Bake Windows (20)
in shopfloor's existing fp_menu.xml.
Renamed 2026-05-25 - "All Jobs" → "Work Orders" for consistency
with the rest of the UI (SO smart button is "WO", tablet cards
show "WO # 00001", KPI tile says "Work Orders"). xmlid kept
as menu_fp_jobs_all_jobs so bookmarks and inherits don't break.
-->
<menuitem id="menu_fp_jobs_all_jobs"
name="Work Orders"
parent="fusion_plating_shopfloor.menu_fp_shopfloor"
action="fusion_plating.action_fp_job"
sequence="15"/>
<menuitem id="menu_fp_jobs_steps"
name="Steps"
parent="fusion_plating_shopfloor.menu_fp_shopfloor"
action="fusion_plating.action_fp_job_step"
sequence="17"
groups="fusion_plating.group_fusion_plating_supervisor"/>
</odoo>

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo noupdate="0">
<!-- After the shopfloor consolidation (2026-04-24) the shopfloor
operator UIs are the canonical native fp.job / fp.job.step
consoles. Only bridge_mrp's Production Priorities menu (still
bound to mrp.workorder) remains legacy.
The group_fusion_plating_legacy_menus group is preserved so a
site that needs to bring legacy menus back can simply add a
user to the group. -->
<!-- Reset group_ids on the shopfloor menus that used to be
hidden - they are now the canonical UIs and should be visible
to all users (subject to the original groups= attribute on
each menuitem in fusion_plating_shopfloor/views/fp_menu.xml). -->
<record id="fusion_plating_shopfloor.menu_fp_shopfloor_manager" model="ir.ui.menu">
<field name="group_ids" eval="[(6, 0, [ref('fusion_plating.group_fusion_plating_manager')])]"/>
</record>
<record id="fusion_plating_shopfloor.menu_fp_shopfloor_tablet" model="ir.ui.menu">
<field name="group_ids" eval="[(6, 0, [])]"/>
</record>
<!-- Phase 3 tablet redesign (2026-05-22) - the standalone Plant
Overview menu was superseded by the single Workstation entry.
The original <delete> directive lived here; it was retired
2026-05-25 because the menu row had been gone for weeks and the
re-run of `<delete model="ir.ui.menu" ref="...">` against a
missing xmlid raises ValueError on every -u. The action record
(action_fp_plant_overview) is kept and retargeted to
fp_plant_kanban for bookmark back-compat. -->
<!-- bridge_mrp Production Priorities reference removed post-Sub 11
(the bridge module is uninstalled and its menu xmlid no longer
resolves). fp.job has its own priority field on the header. -->
</odoo>

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
Add a plating-signature pad to the user preferences dialog.
Anchors on the existing HTML 'signature' field (email signature)
and adds our binary image-signature right after it. The
widget="signature" gives finger / mouse drawing + image upload.
-->
<record id="view_users_preferences_form_fp_signature" model="ir.ui.view">
<field name="name">res.users.preferences.form.fp.signature</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='signature']" position="after">
<field name="x_fc_signature_image"
widget="signature"
string="Plating Signature"
options="{'full_name': 'name'}"/>
</xpath>
</field>
</record>
<!-- Same field on the full user form (Settings > Users) so admins
can review or seed signatures for operators who aren't tech-
savvy enough to do it themselves. -->
<record id="view_users_form_fp_signature" model="ir.ui.view">
<field name="name">res.users.form.fp.signature</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='signature']" position="after">
<field name="x_fc_signature_image"
widget="signature"
string="Plating Signature"
options="{'full_name': 'name'}"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Adds the SO → fp.job smart button so the SO form is a hub for the
native job lifecycle (replaces the legacy MO smart button when the
use_native_jobs flag is on).
-->
<odoo>
<record id="view_sale_order_form_fp_jobs" model="ir.ui.view">
<field name="name">sale.order.form.fp.jobs</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<!-- After the legacy Manufacturing button (added by bridge_mrp).
Hidden at a count of 0 (2026-06-04) so the SO button row only
shows what has data. A confirmed plating SO always has >=1 WO,
so this only hides the button on drafts/quotations with no job
yet; the header "Work Order" button below still reaches jobs
when one is open. -->
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
<button name="action_view_fp_jobs" type="object"
class="oe_stat_button" icon="fa-cogs"
invisible="x_fc_fp_job_count == 0">
<field name="x_fc_fp_job_count" widget="statinfo"
string="WO"/>
</button>
<!-- Sarah/Tom path: SO → Certificates (one click instead -->
<!-- of two via the job). Hidden until a cert exists. -->
<button name="action_view_fp_certificates" type="object"
class="oe_stat_button" icon="fa-certificate"
invisible="x_fc_fp_certificate_count == 0">
<field name="x_fc_fp_certificate_count" widget="statinfo"
string="Certificates"/>
</button>
</xpath>
<!-- Work Order header action - appears once any linked
receiving is closed AND at least one WO is still open.
Reuses the existing action_view_fp_jobs smart-button
target so single-job SOs land on the form directly. -->
<xpath expr="//header" position="inside">
<field name="x_fc_show_work_order_btn" invisible="1"/>
<button name="action_view_fp_jobs"
string="Work Order" type="object"
class="btn-primary" icon="fa-cogs"
invisible="not x_fc_show_work_order_btn"
help="Open the Work Order(s) for this order. Hidden automatically once every linked WO is marked Done."/>
</xpath>
<!-- Quote ref: small grey "Originally quoted as Q202605-200"
line under the SO name (the big SO-30000 heading). Only
renders once the SO has been confirmed (quote_ref is set
on create, parent_number is set on confirm - both
needed for the line to make sense).
NB: Odoo 19 forbids t-if in standard form views - using
`invisible` attribute on the wrapper div instead. -->
<xpath expr="//div[hasclass('oe_title')]" position="inside">
<field name="x_fc_parent_number" invisible="1"/>
<div class="text-muted"
style="font-size: 0.9em; margin-top: 4px;"
invisible="not x_fc_quote_ref or not x_fc_parent_number">
Originally quoted as
<field name="x_fc_quote_ref"
readonly="1" nolabel="1"
class="d-inline"/>
</div>
</xpath>
</field>
</record>
</odoo>