Split 49 modules/suites into independent git repos; untrack from monorepo
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:
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_additional_charge_type_list" model="ir.ui.view">
|
||||
<field name="name">fp.additional.charge.type.list</field>
|
||||
<field name="model">fp.additional.charge.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="default_amount" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_additional_charge_type" model="ir.actions.act_window">
|
||||
<field name="name">Additional Charge Types</field>
|
||||
<field name="res_model">fp.additional.charge.type</field>
|
||||
<field name="view_mode">list</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_additional_charge_type"
|
||||
name="Additional Charge Types"
|
||||
parent="fusion_plating.menu_fp_config_pricing_billing"
|
||||
action="action_fp_additional_charge_type"
|
||||
sequence="30"/>
|
||||
</odoo>
|
||||
@@ -1,106 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Window actions (must be defined before menus reference them) ===== -->
|
||||
<record id="action_fp_customers" model="ir.actions.act_window">
|
||||
<field name="name">Customers</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_mode">list,form,kanban</field>
|
||||
<field name="domain">[('customer_rank', '>', 0)]</field>
|
||||
<field name="context">{'default_customer_rank': 1}</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
The Plating app's landing screen is now resolved by a server
|
||||
action defined in fusion_plating core (Phase 1):
|
||||
user.x_fc_plating_landing_action_id →
|
||||
company.x_fc_default_landing_action_id →
|
||||
action_fp_sale_orders fallback
|
||||
See fusion_plating/data/fp_landing_data.xml.
|
||||
-->
|
||||
|
||||
<!-- ===== SALES submenu under Fusion Plating root ===== -->
|
||||
<menuitem id="menu_fp_sales"
|
||||
name="Sales"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="5"
|
||||
groups="fusion_plating.group_fp_sales_rep"/>
|
||||
|
||||
<!-- === New Quote - top-of-menu entry point for a fresh quote === -->
|
||||
<menuitem id="menu_fp_new_quote"
|
||||
name="New Quote"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_quote_configurator"
|
||||
sequence="1"/>
|
||||
|
||||
<menuitem id="menu_fp_direct_order"
|
||||
name="New Direct Order"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_direct_order_wizard"
|
||||
sequence="5"/>
|
||||
|
||||
<menuitem id="menu_fp_direct_order_drafts"
|
||||
name="Direct Order Drafts"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_direct_order_drafts"
|
||||
sequence="6"/>
|
||||
|
||||
<menuitem id="menu_fp_quotations"
|
||||
name="Quotations"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_quotations"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem id="menu_fp_sale_orders"
|
||||
name="Sale Orders"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_sale_orders"
|
||||
sequence="20"/>
|
||||
|
||||
<menuitem id="menu_fp_customers"
|
||||
name="Customers"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_customers"
|
||||
sequence="30"/>
|
||||
|
||||
<menuitem id="menu_fp_part_catalog"
|
||||
name="Part Catalog"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_part_catalog"
|
||||
sequence="40"/>
|
||||
|
||||
<menuitem id="menu_fp_part_catalog_import"
|
||||
name="Import Parts (CSV)"
|
||||
parent="menu_fp_sales"
|
||||
action="action_fp_part_catalog_import_wizard"
|
||||
sequence="45"/>
|
||||
|
||||
<!-- The Configurator top-level menu was retired in Phase F (2026-05-15)
|
||||
after the Promote Customer Spec refactor left only 3 admin items
|
||||
under it. They've been re-homed into the Configuration hub's
|
||||
themed folders, where managers expect to find admin records:
|
||||
Pricing Rules → Configuration → Pricing & Billing
|
||||
Materials → Configuration → Materials & Tanks
|
||||
Line Desc Tpl → Configuration → Quality & Documents (in
|
||||
fp_sale_description_template_views.xml)
|
||||
-->
|
||||
<menuitem id="menu_fp_pricing_rules"
|
||||
name="Pricing Rules"
|
||||
parent="fusion_plating.menu_fp_config_pricing_billing"
|
||||
action="action_fp_pricing_rule"
|
||||
sequence="40"
|
||||
groups="group_fp_estimator,fusion_plating.group_fusion_plating_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_part_materials"
|
||||
name="Materials"
|
||||
parent="fusion_plating.menu_fp_config_materials_tanks"
|
||||
action="action_fp_part_material"
|
||||
sequence="40"
|
||||
groups="group_fp_estimator,fusion_plating.group_fusion_plating_manager"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,413 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- ============================================================
|
||||
Express Orders form view (2026-05-26 - rebuild #2)
|
||||
|
||||
Uses raw <div> + CSS Grid for the header to match the mockup's
|
||||
4-column flat grid layout. Odoo's <group col="4"> renders as
|
||||
an HTML table with broken cells when colspan + nested groups
|
||||
are involved - switching to manual divs.
|
||||
|
||||
Same model (fp.direct.order.wizard) as the legacy view.
|
||||
============================================================ -->
|
||||
|
||||
<record id="view_fp_express_order_form" model="ir.ui.view">
|
||||
<field name="name">fp.express.order.form</field>
|
||||
<field name="model">fp.direct.order.wizard</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Express Order Entry" class="o_fp_xpr">
|
||||
<header>
|
||||
<button name="action_create_order" type="object"
|
||||
string="Confirm Order"
|
||||
class="btn-primary"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="action_view_sale_order" type="object"
|
||||
string="Open Sale Order"
|
||||
class="btn-primary"
|
||||
invisible="state != 'confirmed' or not sale_order_id"/>
|
||||
<button name="action_switch_to_legacy" type="object"
|
||||
string="Switch to Legacy View"
|
||||
class="btn-secondary"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="action_cancel" type="object"
|
||||
string="Discard Draft"
|
||||
confirm="Mark this draft as cancelled? The data is preserved for audit."
|
||||
invisible="state != 'draft'"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,confirmed"/>
|
||||
</header>
|
||||
|
||||
<div class="alert alert-warning mb-0"
|
||||
role="alert"
|
||||
invisible="not missing_info_msg">
|
||||
<i class="fa fa-exclamation-triangle me-2"/>
|
||||
<field name="missing_info_msg" readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
<sheet>
|
||||
<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="not sale_order_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Sale Order</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="oe_title">
|
||||
<label for="name" class="o_form_label"/>
|
||||
<h1 class="d-flex align-items-center gap-2">
|
||||
<field name="name" readonly="1"/>
|
||||
<span class="o_fp_xpr_pill">EXPRESS</span>
|
||||
</h1>
|
||||
<field name="view_source" invisible="1"/>
|
||||
</div>
|
||||
|
||||
<!-- =========================================================
|
||||
HEADER GRID - pure CSS Grid (4 cols × 4 rows)
|
||||
========================================================= -->
|
||||
<div class="o_fp_xpr_grid">
|
||||
|
||||
<!-- ROW 1 -->
|
||||
<div class="o_fp_xpr_cell span-2 required">
|
||||
<label for="partner_id">Customer</label>
|
||||
<field name="partner_id" nolabel="1"
|
||||
options="{'no_create_edit': True}"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell span-2">
|
||||
<label for="partner_shipping_id">Shipping Address</label>
|
||||
<field name="partner_shipping_id" nolabel="1"
|
||||
options="{'no_create_edit': False}"
|
||||
invisible="not partner_id"/>
|
||||
</div>
|
||||
|
||||
<!-- ============================================================
|
||||
PO Block fills LEFT half (cols 1-2) across rows 2-7.
|
||||
RIGHT half (cols 3-4) flows 6 pairs of fields
|
||||
alongside it - Customer Job #/Job Sorting, Material
|
||||
Process/Lead Time, Payment Terms/Delivery Method,
|
||||
Pricelist/Quote Validity, Blanket SO/Invoice Strategy,
|
||||
Sales Rep/conditional Deposit-or-Progress %.
|
||||
|
||||
Net: PO block height matches 6 × ~60px right stack -
|
||||
no dead air on either side.
|
||||
============================================================ -->
|
||||
<div class="o_fp_xpr_cell span-2 row-span-6 o_fp_xpr_po_block">
|
||||
<div class="o_fp_xpr_po_head">
|
||||
<span>CUSTOMER PO</span>
|
||||
<field name="po_status" widget="badge"
|
||||
decoration-success="po_status == 'received'"
|
||||
decoration-warning="po_status == 'pending'"
|
||||
decoration-danger="po_status == 'missing'"
|
||||
nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_po_row">
|
||||
<label for="po_number">PO #</label>
|
||||
<field name="po_number" nolabel="1"
|
||||
placeholder="Enter the customer PO number"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_po_row">
|
||||
<label for="po_attachment_file">PDF</label>
|
||||
<field name="po_attachment_file" nolabel="1"
|
||||
filename="po_attachment_filename"/>
|
||||
<field name="po_attachment_filename" invisible="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_po_row">
|
||||
<label for="po_pending">PO Pending</label>
|
||||
<field name="po_pending" nolabel="1"
|
||||
widget="boolean_toggle"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_po_row" invisible="not po_pending">
|
||||
<label for="po_expected_date">Expected By</label>
|
||||
<field name="po_expected_date" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_po_chase" invisible="not po_pending">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
Order will confirm without a PO. A chase activity
|
||||
will fire on the expected date.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 2: Customer Job # + Job Sorting -->
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="customer_job_number">Customer Job #</label>
|
||||
<field name="customer_job_number" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="job_sort_id">Job Sorting</label>
|
||||
<field name="job_sort_id" nolabel="1"
|
||||
options="{'no_create_edit': False, 'no_open': True}"
|
||||
placeholder="Type to create a new bucket..."/>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 3: Material/Process + Lead Time -->
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="material_process">Material / Process</label>
|
||||
<field name="material_process" nolabel="1"
|
||||
options="{'no_create_edit': True}"
|
||||
placeholder="Pick a recipe..."/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="lead_time_min_days">Lead Time (days)</label>
|
||||
<div class="o_fp_xpr_range">
|
||||
<field name="lead_time_min_days" nolabel="1"
|
||||
class="o_fp_xpr_range_input"/>
|
||||
<span class="o_fp_xpr_range_sep">to</span>
|
||||
<field name="lead_time_max_days" nolabel="1"
|
||||
class="o_fp_xpr_range_input"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 4: Payment Terms + Delivery Method -->
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="payment_term_id">Payment Terms</label>
|
||||
<field name="payment_term_id" nolabel="1"
|
||||
options="{'no_create': True}"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="delivery_method">Delivery Method</label>
|
||||
<field name="delivery_method" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 5: Pricelist + Quote Validity -->
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="pricelist_id">Currency / Pricelist</label>
|
||||
<field name="pricelist_id" nolabel="1"
|
||||
context="{'fp_express_currency_picker': True}"
|
||||
options="{'no_create_edit': True}"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="validity_date">Quote Validity</label>
|
||||
<field name="validity_date" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 6: Blanket Sales Order + Invoice Strategy -->
|
||||
<div class="o_fp_xpr_cell o_fp_xpr_inline_label">
|
||||
<label for="is_blanket_order">Blanket Sales Order</label>
|
||||
<div class="o_fp_xpr_inline_pair">
|
||||
<field name="is_blanket_order" nolabel="1"
|
||||
widget="boolean_toggle"/>
|
||||
<field name="block_partial_shipments" nolabel="1"
|
||||
widget="boolean_toggle"
|
||||
invisible="not is_blanket_order"/>
|
||||
<span class="o_fp_xpr_inline_help"
|
||||
invisible="not is_blanket_order">Block partial</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="invoice_strategy">Invoice Strategy</label>
|
||||
<field name="invoice_strategy" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
<!-- Right side row 7: Sales Rep + conditional Deposit/Progress % -->
|
||||
<div class="o_fp_xpr_cell">
|
||||
<label for="user_id">Sales Rep</label>
|
||||
<field name="user_id" nolabel="1"
|
||||
readonly="state != 'draft'"
|
||||
options="{'no_create': True}"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell" invisible="invoice_strategy != 'deposit'">
|
||||
<label for="deposit_percent">Deposit %</label>
|
||||
<field name="deposit_percent" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_cell" invisible="invoice_strategy != 'progress'">
|
||||
<label for="progress_initial_percent">Progress Initial %</label>
|
||||
<field name="progress_initial_percent" nolabel="1"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- =========================================================
|
||||
ORDER LINES - spreadsheet
|
||||
========================================================= -->
|
||||
<div class="o_fp_xpr_section_title">Order Lines</div>
|
||||
|
||||
<!-- Legend bar - like the mockup -->
|
||||
<div class="o_fp_xpr_legend">
|
||||
<span><strong>Mask ✓</strong> include all masking + de-masking recipe steps</span>
|
||||
<span><strong>Bake pill</strong> click to type bake instruction (empty = skip bake)</span>
|
||||
<span><strong>DWG</strong> upload drawing to part</span>
|
||||
<span><strong>OPEN</strong> open the part record</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-flex gap-2">
|
||||
<button name="action_add_from_prior_so"
|
||||
type="object"
|
||||
string="+ Add From Prior SO"
|
||||
class="btn-secondary btn-sm"
|
||||
invisible="not partner_id"/>
|
||||
<button name="action_add_from_quotes"
|
||||
type="object"
|
||||
string="+ Add From Quotes"
|
||||
class="btn-secondary btn-sm"
|
||||
invisible="not partner_id"/>
|
||||
</div>
|
||||
<field name="line_ids" class="o_fp_xpr_lines">
|
||||
<list editable="bottom"
|
||||
decoration-warning="is_missing_info">
|
||||
<field name="is_missing_info" column_invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<!-- The multi-row Part cell. Owns part_catalog_id picker
|
||||
PLUS displays part_revision_display, part_name_display,
|
||||
serial_ids + the inline + bulk button. -->
|
||||
<field name="part_catalog_id"
|
||||
string="Part Number"
|
||||
widget="fp_express_part_cell"
|
||||
width="230px"
|
||||
context="{'default_partner_id': parent.partner_id, 'default_revision': 'A', 'fp_express_part_picker': True}"
|
||||
domain="[('partner_id', '=', parent.partner_id), ('is_latest_revision', '=', True)]"
|
||||
options="{'no_quick_create': True}"/>
|
||||
<!-- Hidden related fields the widget reads. Must be on the
|
||||
list so they're prefetched per row. -->
|
||||
<field name="part_number_display" column_invisible="1"/>
|
||||
<field name="part_revision_display" column_invisible="1"/>
|
||||
<field name="part_name_display" column_invisible="1"/>
|
||||
<!-- Writable bridges used by the Part cell widget for
|
||||
editable rows 2 (description) and 3 (serials). -->
|
||||
<field name="part_name_editable" column_invisible="1"/>
|
||||
<field name="serials_text" column_invisible="1"/>
|
||||
<field name="serial_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_quick_create': False, 'color_field': 'state_color'}"
|
||||
domain="[('part_id', '=', part_catalog_id)]"
|
||||
column_invisible="1"/>
|
||||
<!-- Specification + Internal Notes: NO width attr, let them grow with available space -->
|
||||
<field name="line_description" string="Specification (Customer-Facing)"/>
|
||||
<field name="customer_line_ref" string="Line Job #" placeholder="ABC" width="80px"/>
|
||||
<field name="thickness_range" string="Thickness" placeholder=".0005-.0010" width="100px"/>
|
||||
<field name="masking_enabled" string="Mask" widget="boolean_toggle" width="55px"/>
|
||||
<field name="masking_attachment_ids" column_invisible="1"/>
|
||||
<!-- Bake pill - click to edit -->
|
||||
<field name="bake_instructions"
|
||||
string="Bake"
|
||||
widget="fp_express_bake_pill"
|
||||
width="120px"/>
|
||||
<field name="internal_description" string="Internal Notes" optional="show"/>
|
||||
<field name="quantity" string="Qty" width="55px"/>
|
||||
<field name="unit_price"
|
||||
string="Price"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
width="80px"/>
|
||||
<field name="line_subtotal"
|
||||
string="Subtotal"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
sum="Total"
|
||||
width="90px"/>
|
||||
<!-- Stacked DWG / OPEN buttons in ONE column -->
|
||||
<field name="action_btns_anchor"
|
||||
string=" "
|
||||
widget="fp_express_action_btns"
|
||||
width="60px"/>
|
||||
<field name="process_variant_id"
|
||||
string="Process / Recipe"
|
||||
options="{'no_quick_create': True}"
|
||||
invisible="not part_catalog_id"
|
||||
optional="hide"/>
|
||||
<field name="tax_ids"
|
||||
string="Tax"
|
||||
widget="many2many_tags"
|
||||
options="{'no_create': True}"
|
||||
optional="hide"
|
||||
width="110px"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
|
||||
<!-- =========================================================
|
||||
FOOTER GRID - Notes/Terms left + Totals right
|
||||
========================================================= -->
|
||||
<div class="o_fp_xpr_footer">
|
||||
|
||||
<div class="o_fp_xpr_footer_left">
|
||||
<div class="o_fp_xpr_card">
|
||||
<div class="o_fp_xpr_card_title">Order-Level Internal Notes</div>
|
||||
<div class="o_fp_xpr_card_sub">Visible only to estimator / planner / shop. Never prints.</div>
|
||||
<field name="internal_notes" nolabel="1"
|
||||
placeholder="Type internal notes..."/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_card">
|
||||
<div class="o_fp_xpr_card_title">Terms & Conditions
|
||||
<span class="o_fp_xpr_chip">PRINTS</span>
|
||||
</div>
|
||||
<div class="o_fp_xpr_card_sub">Customer-facing - prints on quote / SO / invoice.</div>
|
||||
<field name="terms_and_conditions" nolabel="1"
|
||||
placeholder="Customer-facing terms..."/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="o_fp_xpr_footer_right">
|
||||
<div class="o_fp_xpr_card o_fp_xpr_totals">
|
||||
<div class="o_fp_xpr_total_row">
|
||||
<span class="o_fp_xpr_total_label">Subtotal</span>
|
||||
<field name="total_subtotal"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_total_row">
|
||||
<span class="o_fp_xpr_total_label">Tax</span>
|
||||
<field name="total_tax"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_total_row">
|
||||
<span class="o_fp_xpr_total_label">Tooling Charge</span>
|
||||
<field name="tooling_charge"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_total_row">
|
||||
<span class="o_fp_xpr_total_label">Total Lines</span>
|
||||
<field name="total_line_count" readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_total_row">
|
||||
<span class="o_fp_xpr_total_label">Total Quantity</span>
|
||||
<field name="total_qty" readonly="1" nolabel="1"/>
|
||||
</div>
|
||||
<div class="o_fp_xpr_total_row o_fp_xpr_grand">
|
||||
<span class="o_fp_xpr_total_label">Grand Total</span>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<field name="total_amount"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
readonly="1" nolabel="1"/>
|
||||
<field name="currency_id"
|
||||
readonly="1" nolabel="1"
|
||||
class="o_fp_xpr_currency_pill"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ====== Action + Menu ====== -->
|
||||
<record id="action_fp_express_order" model="ir.actions.act_window">
|
||||
<field name="name">+ New Express Order</field>
|
||||
<field name="res_model">fp.direct.order.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_fp_express_order_form"/>
|
||||
<field name="target">current</field>
|
||||
<field name="context">{'default_view_source': 'express'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_express_order"
|
||||
name="+ New Express Order"
|
||||
parent="fusion_plating_configurator.menu_fp_sales"
|
||||
action="action_fp_express_order"
|
||||
sequence="3"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,386 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Part Catalog List View ===== -->
|
||||
<record id="view_fp_part_catalog_list" model="ir.ui.view">
|
||||
<field name="name">fp.part.catalog.list</field>
|
||||
<field name="model">fp.part.catalog</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Part Catalog" decoration-muted="not active">
|
||||
<field name="part_number"/>
|
||||
<field name="name" string="Part Name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="revision"/>
|
||||
<field name="material_id" string="Material"/>
|
||||
<field name="substrate_material" optional="hide"/>
|
||||
<field name="surface_area"/>
|
||||
<field name="complexity"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Part Catalog Form View ===== -->
|
||||
<record id="view_fp_part_catalog_form" model="ir.ui.view">
|
||||
<field name="name">fp.part.catalog.form</field>
|
||||
<field name="model">fp.part.catalog</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Part Catalog">
|
||||
<header>
|
||||
<button name="action_open_revision_wizard"
|
||||
string="Create New Revision"
|
||||
type="object"
|
||||
class="btn-secondary"
|
||||
icon="fa-code-fork"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_customer"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-user"
|
||||
invisible="not partner_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Customer</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_view_sale_orders"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-text-o"
|
||||
invisible="sale_order_count == 0">
|
||||
<field name="sale_order_count" widget="statinfo" string="Sale Orders"/>
|
||||
</button>
|
||||
<button name="action_view_workorders"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-cogs"
|
||||
invisible="workorder_count == 0">
|
||||
<field name="workorder_count" widget="statinfo" string="Work Orders"/>
|
||||
</button>
|
||||
<button name="action_view_configurators"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-sliders"
|
||||
invisible="configurator_count == 0">
|
||||
<field name="configurator_count" widget="statinfo" string="Quotes"/>
|
||||
</button>
|
||||
<button name="action_view_revisions"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-code-fork"
|
||||
invisible="revision_count < 2">
|
||||
<field name="revision_count" widget="statinfo" string="Revisions"/>
|
||||
</button>
|
||||
<button name="%(action_fp_sale_description_template)d"
|
||||
type="action"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-text-o"
|
||||
context="{'search_default_part_catalog_id': id, 'default_part_catalog_id': id}">
|
||||
<field name="description_template_count" widget="statinfo" string="Descriptions"/>
|
||||
</button>
|
||||
<!-- Sub 3 follow-up: Process smart button. Click
|
||||
opens the part-scoped Process Composer so
|
||||
users have a one-tap path from any part
|
||||
form to its process tree. -->
|
||||
<button name="action_open_part_composer"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-sitemap">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value"
|
||||
invisible="default_process_id">
|
||||
None
|
||||
</span>
|
||||
<span class="o_stat_value text-success"
|
||||
invisible="not default_process_id">
|
||||
<i class="fa fa-check-circle"/>
|
||||
</span>
|
||||
<span class="o_stat_text">Process</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<widget name="web_ribbon" title="Superseded" bg_color="text-bg-warning" invisible="is_latest_revision"/>
|
||||
<div class="oe_title">
|
||||
<label for="part_number" string="Part Number"/>
|
||||
<h1><field name="part_number" placeholder="e.g. VS-R392007E01"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Identity">
|
||||
<field name="name" string="Part Name"
|
||||
placeholder="Descriptive part name (e.g. Valve Body Housing)"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="revision"/>
|
||||
<field name="is_latest_revision" invisible="1"/>
|
||||
<field name="parent_part_id" invisible="not parent_part_id"/>
|
||||
</group>
|
||||
<group string="Manufacturing Defaults">
|
||||
<field name="material_id"
|
||||
options="{'no_quick_create': True}"/>
|
||||
<field name="substrate_material" invisible="1"/>
|
||||
<field name="x_fc_default_lead_time_days"/>
|
||||
<field name="certificate_requirement"/>
|
||||
</group>
|
||||
</group>
|
||||
<!-- Quality & Delivery moved into its own notebook tab below
|
||||
(was a top-level group above the notebook). -->
|
||||
<!-- Auto-extracted geometry from 3D model -->
|
||||
<group string="3D Model Analysis"
|
||||
invisible="not volume_mm3 and not bbox_summary_in and hole_count == 0">
|
||||
<group>
|
||||
<field name="bbox_summary_in" readonly="1"/>
|
||||
<field name="volume_mm3" readonly="1"/>
|
||||
<field name="bbox_length_mm" invisible="1"/>
|
||||
<field name="bbox_width_mm" invisible="1"/>
|
||||
<field name="bbox_height_mm" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="hole_count" readonly="1"/>
|
||||
<field name="hole_summary" readonly="1" invisible="not hole_summary"/>
|
||||
<field name="is_manifold" widget="boolean_toggle" readonly="1"/>
|
||||
</group>
|
||||
<div class="alert alert-warning mb-0"
|
||||
colspan="2"
|
||||
invisible="is_manifold or not model_attachment_id">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>
|
||||
<strong>Geometry warning:</strong> 3D model is not watertight (manifold).
|
||||
This often indicates open shells or invalid surfaces. Review before quoting.
|
||||
</div>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Process" name="process">
|
||||
<group>
|
||||
<field name="default_process_id" readonly="1"
|
||||
help="The variant used by default when an order line does not pick another."/>
|
||||
<field name="process_variant_count" readonly="1"/>
|
||||
</group>
|
||||
<div class="mt-2">
|
||||
<button name="action_open_part_composer" type="object"
|
||||
string="Compose"
|
||||
icon="fa-wrench"
|
||||
class="btn-primary"
|
||||
help="Open the Process Composer to manage this part's process variants."/>
|
||||
<button name="action_open_default_simple_editor" type="object"
|
||||
string="Edit Default (Simple)"
|
||||
icon="fa-list-alt"
|
||||
class="btn-info ms-1"
|
||||
invisible="not default_process_id"
|
||||
help="Jump straight to the Simple Recipe Editor for the default variant - flat 2-pane drag-drop layout."/>
|
||||
<button name="action_open_default_tree_editor" type="object"
|
||||
string="Edit Default (Tree)"
|
||||
icon="fa-sitemap"
|
||||
class="btn-secondary ms-1"
|
||||
invisible="not default_process_id"
|
||||
help="Jump straight to the Tree Editor for the default variant."/>
|
||||
</div>
|
||||
<p class="text-muted mt-3">
|
||||
The <strong>Compose</strong> button opens the Process Composer where you can add
|
||||
multiple process <em>variants</em> for this part - for example "Standard ENP",
|
||||
"Selective Masking", "Rework". One variant is flagged as default; estimators
|
||||
may pick a different variant on a per-order basis. Each variant can be edited
|
||||
in either the <strong>Tree</strong> or <strong>Simple</strong> view - same data,
|
||||
two layouts.
|
||||
</p>
|
||||
<field name="process_variant_ids" readonly="1">
|
||||
<list>
|
||||
<field name="is_default_variant" widget="boolean_toggle" readonly="1"/>
|
||||
<field name="variant_label"/>
|
||||
<field name="name"/>
|
||||
<field name="estimated_duration" optional="hide"/>
|
||||
<button name="action_open_simple_editor" type="object"
|
||||
string="Simple" icon="fa-list-alt"
|
||||
class="btn-link"/>
|
||||
<button name="action_open_tree_editor" type="object"
|
||||
string="Tree" icon="fa-sitemap"
|
||||
class="btn-link"/>
|
||||
</list>
|
||||
</field>
|
||||
<!-- Default Specification picker added by
|
||||
fusion_plating_quality view inherit. -->
|
||||
<separator string="Default Thickness" class="mt-4"/>
|
||||
<group>
|
||||
<field name="x_fc_default_thickness_range"
|
||||
placeholder="e.g. 0.0005-0.0008 mils"/>
|
||||
</group>
|
||||
<p class="text-muted">
|
||||
Defaults pre-fill new direct-order lines
|
||||
for this part. Thickness also auto-fills
|
||||
from the most recent order for the same
|
||||
(part, customer) pair when one exists.
|
||||
</p>
|
||||
</page>
|
||||
<page string="Dimensions & Complexity" name="dimensions">
|
||||
<group>
|
||||
<field name="geometry_source"/>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Surface & Weight">
|
||||
<label for="surface_area"/>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<field name="surface_area" class="oe_inline"/>
|
||||
<button name="action_calculate_surface_area" type="object"
|
||||
string="Calculate from 3D Model"
|
||||
class="btn-link" icon="fa-calculator"
|
||||
invisible="not model_attachment_id"/>
|
||||
</div>
|
||||
<field name="surface_area_uom"/>
|
||||
<field name="masking_area_sqin"/>
|
||||
<field name="effective_area_sqin" readonly="1"/>
|
||||
<field name="weight"/>
|
||||
<field name="material_weight_kg" readonly="1"/>
|
||||
</group>
|
||||
<group string="Bounding Box">
|
||||
<field name="dimensions_length"/>
|
||||
<field name="dimensions_width"/>
|
||||
<field name="dimensions_height"/>
|
||||
</group>
|
||||
<group string="Complexity">
|
||||
<field name="complexity"/>
|
||||
<field name="masking_zones"/>
|
||||
<field name="has_blind_holes"/>
|
||||
<field name="has_recesses"/>
|
||||
<field name="has_threads"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="masking_description" placeholder="e.g. Mask threaded holes, mask bore ID"/>
|
||||
</page>
|
||||
<page string="Attachments" name="attachments">
|
||||
<group>
|
||||
<!-- Upload slot: Binary field that wraps the file
|
||||
in an ir.attachment on change. Hidden once a
|
||||
3D model is already attached. -->
|
||||
<label for="model_upload" string="Upload 3D Model"
|
||||
invisible="model_attachment_id"/>
|
||||
<div class="o_row" invisible="model_attachment_id">
|
||||
<field name="model_upload" nolabel="1"
|
||||
filename="model_upload_filename"
|
||||
class="oe_inline"/>
|
||||
<field name="model_upload_filename" invisible="1"/>
|
||||
<span class="text-muted ms-2 small">
|
||||
STEP / STP / STL / IGES / IGS / BREP
|
||||
</span>
|
||||
</div>
|
||||
<!-- Current attachment + remove affordance -->
|
||||
<label for="model_attachment_id" string="3D Model File"
|
||||
invisible="not model_attachment_id"/>
|
||||
<div class="o_row" invisible="not model_attachment_id">
|
||||
<field name="model_attachment_id" nolabel="1" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="drawing_attachment_ids" widget="fp_pdf_preview_binary"/>
|
||||
</group>
|
||||
<div invisible="not model_attachment_id" class="mt-3">
|
||||
<field name="model_attachment_id" widget="fp_3d_preview" nolabel="1"/>
|
||||
</div>
|
||||
</page>
|
||||
<page string="Descriptions" name="descriptions">
|
||||
<div class="alert alert-info" role="alert">
|
||||
<strong>Canned descriptions for this part.</strong>
|
||||
Internal = what the shop floor sees on the WO / traveler.
|
||||
Customer-Facing = what prints on SO, invoice, packing slip.
|
||||
Whichever row the estimator picks on the order wizard lands both values on the SO line.
|
||||
</div>
|
||||
<field name="description_template_ids"
|
||||
context="{'default_part_catalog_id': id, 'default_partner_id': partner_id}">
|
||||
<list editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name" placeholder="e.g. Standard, With threaded holes masked"/>
|
||||
<field name="tag" optional="show"/>
|
||||
<field name="internal_description" placeholder="What the shop floor sees on the WO / traveler"/>
|
||||
<field name="customer_facing_description" placeholder="What prints on SO, invoice, packing slip"/>
|
||||
<field name="usage_count" string="Used" optional="hide"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
<separator string="Description History (auto-saved per order)"/>
|
||||
<field name="description_version_ids" readonly="1">
|
||||
<list>
|
||||
<field name="version_no" string="#"/>
|
||||
<field name="name" string="Reference"/>
|
||||
<field name="customer_facing_description" string="Customer-Facing"/>
|
||||
<field name="sale_order_id" string="Order"/>
|
||||
<field name="create_uid" string="By"/>
|
||||
<field name="create_date" string="When"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Revision History" name="revisions"
|
||||
invisible="not parent_part_id and not revision_ids">
|
||||
<field name="revision_ids" mode="list">
|
||||
<list default_order="revision_date desc">
|
||||
<field name="revision"/>
|
||||
<field name="revision_note"/>
|
||||
<field name="revision_date"/>
|
||||
<field name="surface_area"/>
|
||||
<field name="model_attachment_id" string="3D Model"/>
|
||||
<field name="is_latest_revision" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Notes" name="notes">
|
||||
<field name="notes" placeholder="Additional notes about this part..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Part Catalog Search View ===== -->
|
||||
<record id="view_fp_part_catalog_search" model="ir.ui.view">
|
||||
<field name="name">fp.part.catalog.search</field>
|
||||
<field name="model">fp.part.catalog</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="part_number"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="material_id" string="Material"/>
|
||||
<separator/>
|
||||
<filter string="Aluminium" name="material_aluminium" domain="[('substrate_material','=','aluminium')]"/>
|
||||
<filter string="Steel" name="material_steel" domain="[('substrate_material','=','steel')]"/>
|
||||
<filter string="Stainless Steel" name="material_stainless" domain="[('substrate_material','=','stainless')]"/>
|
||||
<filter string="Copper" name="material_copper" domain="[('substrate_material','=','copper')]"/>
|
||||
<filter string="Titanium" name="material_titanium" domain="[('substrate_material','=','titanium')]"/>
|
||||
<filter string="Other" name="material_other" domain="[('substrate_material','=','other')]"/>
|
||||
<separator/>
|
||||
<filter string="Simple" name="complexity_simple" domain="[('complexity','=','simple')]"/>
|
||||
<filter string="Moderate" name="complexity_moderate" domain="[('complexity','=','moderate')]"/>
|
||||
<filter string="Complex" name="complexity_complex" domain="[('complexity','=','complex')]"/>
|
||||
<filter string="Very Complex" name="complexity_very_complex" domain="[('complexity','=','very_complex')]"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_partner" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Material" name="group_material" context="{'group_by':'material_id'}"/>
|
||||
<filter string="Material Category" name="group_material_category" context="{'group_by':'substrate_material'}"/>
|
||||
<filter string="Complexity" name="group_complexity" context="{'group_by':'complexity'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Window Action ===== -->
|
||||
<record id="action_fp_part_catalog" model="ir.actions.act_window">
|
||||
<field name="name">Part Catalog</field>
|
||||
<field name="res_model">fp.part.catalog</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_part_catalog_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No parts in the catalog yet
|
||||
</p>
|
||||
<p>
|
||||
Add customer parts with geometry, material, and complexity data
|
||||
for instant re-quoting on repeat orders.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,92 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="view_fp_part_material_list" model="ir.ui.view">
|
||||
<field name="name">fp.part.material.list</field>
|
||||
<field name="model">fp.part.material</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Materials" editable="bottom" decoration-muted="not active">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="category"/>
|
||||
<field name="density" string="Density (g/cm³)"/>
|
||||
<field name="notes" optional="show"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_part_material_form" model="ir.ui.view">
|
||||
<field name="name">fp.part.material.form</field>
|
||||
<field name="model">fp.part.material</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Material">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Material"/>
|
||||
<h1><field name="name" placeholder="e.g. Aluminium 6061"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="category"/>
|
||||
<field name="density"/>
|
||||
<field name="sequence"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="notes" placeholder="Alloy spec, source, supplier note..."/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="text-muted">
|
||||
Leave Density at 0 to use the category default
|
||||
(Aluminium 2.70, Steel 7.85, Stainless 8.00,
|
||||
Copper 8.96, Titanium 4.51 g/cm³).
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_part_material_search" model="ir.ui.view">
|
||||
<field name="name">fp.part.material.search</field>
|
||||
<field name="model">fp.part.material</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="category"/>
|
||||
<separator/>
|
||||
<filter string="Aluminium" name="cat_aluminium" domain="[('category','=','aluminium')]"/>
|
||||
<filter string="Steel" name="cat_steel" domain="[('category','=','steel')]"/>
|
||||
<filter string="Stainless" name="cat_stainless" domain="[('category','=','stainless')]"/>
|
||||
<filter string="Copper" name="cat_copper" domain="[('category','=','copper')]"/>
|
||||
<filter string="Titanium" name="cat_titanium" domain="[('category','=','titanium')]"/>
|
||||
<filter string="Other" name="cat_other" domain="[('category','=','other')]"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<group>
|
||||
<filter string="Category" name="group_category" context="{'group_by':'category'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_part_material" model="ir.actions.act_window">
|
||||
<field name="name">Materials</field>
|
||||
<field name="res_model">fp.part.material</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_part_material_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">No materials yet</p>
|
||||
<p>Define the materials your shop processes. Each material
|
||||
picks a category (Aluminium, Steel, etc.) used for pricing
|
||||
rules and density-based weight calculations.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,137 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Pricing Rule List View ===== -->
|
||||
<record id="view_fp_pricing_rule_list" model="ir.ui.view">
|
||||
<field name="name">fp.pricing.rule.list</field>
|
||||
<field name="model">fp.pricing.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Pricing Rules" decoration-muted="not active">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="substrate_material"/>
|
||||
<field name="certification_level"/>
|
||||
<field name="pricing_method"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="base_rate" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}" sum="Total"/>
|
||||
<field name="minimum_charge" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Pricing Rule Form View ===== -->
|
||||
<record id="view_fp_pricing_rule_form" model="ir.ui.view">
|
||||
<field name="name">fp.pricing.rule.form</field>
|
||||
<field name="model">fp.pricing.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pricing Rule">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. EN Mid-Phos Aluminium - Commercial"/></h1>
|
||||
</div>
|
||||
<group string="Filters">
|
||||
<group>
|
||||
<field name="substrate_material"/>
|
||||
<field name="certification_level"/>
|
||||
</group>
|
||||
<group>
|
||||
<div class="text-muted" colspan="2">
|
||||
Leave filter fields blank to create a global rule
|
||||
that matches any configuration.
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Pricing">
|
||||
<group>
|
||||
<field name="pricing_method"/>
|
||||
<field name="currency_id"/>
|
||||
<field name="base_rate" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="thickness_factor"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="masking_rate_per_zone" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="setup_fee" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="minimum_charge" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<label for="rush_surcharge_percent"/>
|
||||
<div class="o_row">
|
||||
<field name="rush_surcharge_percent" nolabel="1" class="oe_inline"/>
|
||||
<span class="ms-1">%</span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Complexity Surcharges" name="surcharges">
|
||||
<field name="complexity_surcharge_ids">
|
||||
<list editable="bottom">
|
||||
<field name="complexity"/>
|
||||
<field name="surcharge_percent" string="Surcharge %"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Notes" name="notes">
|
||||
<field name="notes" placeholder="Internal notes about this pricing rule..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Pricing Rule Search View ===== -->
|
||||
<record id="view_fp_pricing_rule_search" model="ir.ui.view">
|
||||
<field name="name">fp.pricing.rule.search</field>
|
||||
<field name="model">fp.pricing.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<separator/>
|
||||
<filter string="Per Square Inch" name="per_sqin" domain="[('pricing_method','=','per_sqin')]"/>
|
||||
<filter string="Per Square Foot" name="per_sqft" domain="[('pricing_method','=','per_sqft')]"/>
|
||||
<filter string="Per Piece" name="per_piece" domain="[('pricing_method','=','per_piece')]"/>
|
||||
<filter string="Flat Rate" name="flat_rate" domain="[('pricing_method','=','flat_rate')]"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<group>
|
||||
<filter string="Pricing Method" name="group_pricing_method" context="{'group_by':'pricing_method'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Window Action ===== -->
|
||||
<record id="action_fp_pricing_rule" model="ir.actions.act_window">
|
||||
<field name="name">Pricing Rules</field>
|
||||
<field name="res_model">fp.pricing.rule</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_pricing_rule_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No pricing rules defined yet
|
||||
</p>
|
||||
<p>
|
||||
Define formula-based pricing rules matched by coating
|
||||
configuration, substrate material, and certification level.
|
||||
The first matching rule (by sequence) wins.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,118 +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.
|
||||
|
||||
Separates shared recipe templates from part-scoped clones in the
|
||||
backend so thousands of part clones don't bury the 5-10 real
|
||||
templates in the main Recipes list.
|
||||
|
||||
* Narrow the existing Process Recipes action to templates only
|
||||
(part_catalog_id = False).
|
||||
* Add a sibling "Part Processes" menu + action for audit /
|
||||
administration of every clone, grouped by part by default.
|
||||
* Extend the search view with template / part-scoped toggles
|
||||
so admins can cross between the two lists without switching
|
||||
menus.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ========== Extend search view: add part_catalog_id filters ========== -->
|
||||
<record id="view_fp_process_node_search_part_scoped"
|
||||
model="ir.ui.view">
|
||||
<field name="name">fusion.plating.process.node.search.part.scoped</field>
|
||||
<field name="model">fusion.plating.process.node</field>
|
||||
<field name="inherit_id"
|
||||
ref="fusion_plating.view_fp_process_node_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='operations']" position="after">
|
||||
<separator/>
|
||||
<filter name="templates_only"
|
||||
string="Shared Templates"
|
||||
domain="[('part_catalog_id', '=', False)]"/>
|
||||
<filter name="part_scoped"
|
||||
string="Part-Scoped"
|
||||
domain="[('part_catalog_id', '!=', False)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='group_wc']" position="after">
|
||||
<filter name="group_part" string="Part"
|
||||
context="{'group_by': 'part_catalog_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Extend form view: Linked Parts smart button ========== -->
|
||||
<!-- Smart button visible only on shared template recipes (no
|
||||
part_catalog_id). Opens the list of part-cloned recipe roots
|
||||
that were copied from this template. -->
|
||||
<record id="view_fp_process_node_form_linked_parts"
|
||||
model="ir.ui.view">
|
||||
<field name="name">fusion.plating.process.node.form.linked.parts</field>
|
||||
<field name="model">fusion.plating.process.node</field>
|
||||
<field name="inherit_id"
|
||||
ref="fusion_plating.view_fp_process_node_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_open_cloned_recipes" type="object"
|
||||
class="oe_stat_button" icon="fa-link"
|
||||
invisible="node_type != 'recipe' or part_catalog_id">
|
||||
<field name="cloned_recipe_count" widget="statinfo"
|
||||
string="Linked Parts"/>
|
||||
</button>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Extend list view: surface part column ========== -->
|
||||
<record id="view_fp_process_node_tree_part_scoped"
|
||||
model="ir.ui.view">
|
||||
<field name="name">fusion.plating.process.node.tree.part.scoped</field>
|
||||
<field name="model">fusion.plating.process.node</field>
|
||||
<field name="inherit_id"
|
||||
ref="fusion_plating.view_fp_process_node_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="part_catalog_id" string="Part" optional="show"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Narrow main Recipes action to templates only ========== -->
|
||||
<record id="fusion_plating.action_fp_process_recipe"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('node_type', '=', 'recipe'), ('part_catalog_id', '=', False)]</field>
|
||||
<field name="context">{'default_node_type': 'recipe', 'search_default_recipes_only': 1, 'search_default_templates_only': 1}</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== NEW action - Part Processes ========== -->
|
||||
<record id="action_fp_process_recipe_part_scoped"
|
||||
model="ir.actions.act_window">
|
||||
<field name="name">Part Processes</field>
|
||||
<field name="res_model">fusion.plating.process.node</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('node_type', '=', 'recipe'), ('part_catalog_id', '!=', False)]</field>
|
||||
<field name="context">{'search_default_part_scoped': 1, 'search_default_group_part': 1}</field>
|
||||
<field name="search_view_id"
|
||||
ref="fusion_plating.view_fp_process_node_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No part-customised processes yet
|
||||
</p>
|
||||
<p>
|
||||
This view lists every process tree that has been
|
||||
composed for a specific part via the part form's
|
||||
<b>Compose</b> button. Each row is a clone of a shared
|
||||
template with per-part tweaks. To browse the shared
|
||||
templates themselves, use <b>Process Recipes</b>.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_process_part_scoped"
|
||||
name="Part Processes"
|
||||
parent="fusion_plating.menu_fp_operations"
|
||||
action="action_fp_process_recipe_part_scoped"
|
||||
sequence="6"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,386 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Configurator Form View ===== -->
|
||||
<record id="view_fp_quote_configurator_form" model="ir.ui.view">
|
||||
<field name="name">fp.quote.configurator.form</field>
|
||||
<field name="model">fp.quote.configurator</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Quote Configurator">
|
||||
<header>
|
||||
<button name="action_promote_to_direct_order"
|
||||
string="Add to Direct Order"
|
||||
type="object"
|
||||
class="btn-primary"
|
||||
invisible="state != 'draft'"
|
||||
help="Add this quote as a line on a Direct Order draft. Multiple quotes can land on the same draft so one PO covers them all."/>
|
||||
<button name="action_recalculate_price"
|
||||
string="Recalculate"
|
||||
type="object"
|
||||
class="btn-secondary"/>
|
||||
<button name="action_save_to_catalog"
|
||||
string="Save to Catalog"
|
||||
type="object"
|
||||
class="btn-secondary"
|
||||
confirm="This will overwrite the part catalog's geometry, substrate, masking area, and complexity with values from this quote. Continue?"
|
||||
invisible="not part_catalog_id"/>
|
||||
<button name="action_mark_lost"
|
||||
string="Mark as Lost"
|
||||
type="object"
|
||||
class="btn-warning"
|
||||
invisible="state != 'draft'"
|
||||
confirm="Mark this quote as Lost? Set the Lost Reason first."/>
|
||||
<button name="action_mark_expired"
|
||||
string="Mark as Expired"
|
||||
type="object"
|
||||
invisible="state != 'draft'"/>
|
||||
<button name="action_cancel"
|
||||
string="Cancel"
|
||||
type="object"
|
||||
invisible="state == 'cancelled'"/>
|
||||
<button name="action_reset_draft"
|
||||
string="Reset to Draft"
|
||||
type="object"
|
||||
invisible="state == 'draft'"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,confirmed,lost,expired,cancelled"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_sale_order"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-text-o"
|
||||
invisible="not sale_order_id">
|
||||
<field name="sale_order_id" widget="statinfo" string="Sale Order"/>
|
||||
</button>
|
||||
<button name="action_view_part_catalog"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-cube"
|
||||
invisible="not part_catalog_id">
|
||||
<field name="part_catalog_id" widget="statinfo" string="Part"/>
|
||||
</button>
|
||||
<!--
|
||||
3D Model + Drawings smart buttons.
|
||||
Both open a modal preview (action_open_3d_fullscreen
|
||||
and action_view_drawings) that replaces what used
|
||||
to be the right-column inline previews.
|
||||
-->
|
||||
<button name="action_open_3d_fullscreen"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-cube"
|
||||
invisible="not model_attachment_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">1</span>
|
||||
<span class="o_stat_text">3D Model</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_view_drawings"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-pdf-o"
|
||||
invisible="drawing_count == 0">
|
||||
<field name="drawing_count" widget="statinfo" string="Drawings"/>
|
||||
</button>
|
||||
<button name="action_view_rfq"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-envelope-o"
|
||||
invisible="not rfq_attachment_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">1</span>
|
||||
<span class="o_stat_text">RFQ</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_view_po"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-o"
|
||||
invisible="not po_attachment_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">1</span>
|
||||
<span class="o_stat_text">PO</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" readonly="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Single-column layout. The right-side 3D viewer +
|
||||
Drawing preview were removed (commit pending) - both
|
||||
live behind the 3D Model / Drawings smart buttons at
|
||||
the top of the form, plus inline "Preview" links
|
||||
next to each respective field.
|
||||
-->
|
||||
<div class="o_fp_cfg_layout">
|
||||
<div class="o_fp_cfg_fields">
|
||||
<group>
|
||||
<group string="Customer & Part">
|
||||
<field name="partner_id"/>
|
||||
<field name="part_catalog_id"/>
|
||||
<field name="recipe_id"/>
|
||||
<!-- 3D File: upload before, filename + clear button after -->
|
||||
<field name="upload_3d_file" filename="upload_3d_filename"
|
||||
invisible="state != 'draft' or model_attachment_id"
|
||||
string="Attach 3D File"/>
|
||||
<field name="upload_3d_filename" invisible="1"/>
|
||||
<!--
|
||||
3D Model + inline Preview link. Field shows
|
||||
the attachment name, the small Preview link
|
||||
opens the same fullscreen wizard as the
|
||||
smart button at the top of the form.
|
||||
-->
|
||||
<label for="model_attachment_id" string="3D Model"
|
||||
invisible="not model_attachment_id"/>
|
||||
<div class="o_row" invisible="not model_attachment_id">
|
||||
<field name="model_attachment_id" nolabel="1"
|
||||
readonly="state != 'draft'"/>
|
||||
<button name="action_open_3d_fullscreen"
|
||||
type="object"
|
||||
string="Preview"
|
||||
icon="fa-eye"
|
||||
class="btn btn-link btn-sm ms-2 p-0"
|
||||
title="Open 3D model preview"/>
|
||||
</div>
|
||||
<!-- Drawing: upload before, filename + Preview link after -->
|
||||
<field name="upload_drawing" filename="upload_drawing_filename"
|
||||
invisible="state != 'draft' or drawing_count > 0"
|
||||
string="Attach Drawing"/>
|
||||
<field name="upload_drawing_filename" invisible="1"/>
|
||||
<label for="first_drawing_id" string="Drawing"
|
||||
invisible="drawing_count == 0"/>
|
||||
<div class="o_row" invisible="drawing_count == 0">
|
||||
<field name="first_drawing_id" nolabel="1"
|
||||
readonly="state != 'draft'"/>
|
||||
<button name="action_view_drawings"
|
||||
type="object"
|
||||
string="Preview"
|
||||
icon="fa-eye"
|
||||
class="btn btn-link btn-sm ms-2 p-0"
|
||||
title="Open drawing preview"/>
|
||||
</div>
|
||||
<field name="drawing_count" invisible="1"/>
|
||||
</group>
|
||||
<group string="RFQ / PO Documents">
|
||||
<field name="upload_rfq_file"
|
||||
filename="upload_rfq_filename"
|
||||
invisible="state != 'draft' or rfq_attachment_id"
|
||||
string="Attach RFQ"/>
|
||||
<field name="upload_rfq_filename" invisible="1"/>
|
||||
<field name="rfq_attachment_id"
|
||||
invisible="not rfq_attachment_id"
|
||||
readonly="state != 'draft'"/>
|
||||
<field name="upload_po_file"
|
||||
filename="upload_po_filename"
|
||||
invisible="state != 'draft' or po_attachment_id"
|
||||
string="Attach PO"/>
|
||||
<field name="upload_po_filename" invisible="1"/>
|
||||
<field name="po_attachment_id"
|
||||
invisible="not po_attachment_id"
|
||||
readonly="state != 'draft'"/>
|
||||
<field name="po_number_preliminary"
|
||||
string="PO Number"
|
||||
readonly="state != 'draft'"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!--
|
||||
Row 2 - Quantity / Options on the LEFT, Auto-from-3D on
|
||||
the RIGHT (visible only when a part catalog is linked).
|
||||
Quantity moved out of the RFQ/PO group so the right
|
||||
column has a peer instead of stretching alone.
|
||||
-->
|
||||
<group>
|
||||
<group string="Quantity & Options">
|
||||
<field name="quantity"/>
|
||||
<field name="batch_size"/>
|
||||
<field name="complexity"/>
|
||||
<field name="rush_order"/>
|
||||
</group>
|
||||
<group string="Auto from 3D"
|
||||
invisible="not part_catalog_id">
|
||||
<field name="bbox_summary_in"
|
||||
string="Dimensions"
|
||||
readonly="1"/>
|
||||
<field name="material_weight_kg"
|
||||
string="Weight (kg)"
|
||||
readonly="1"/>
|
||||
<field name="hole_count"
|
||||
string="Holes"
|
||||
readonly="1"/>
|
||||
<field name="hole_summary"
|
||||
readonly="1"
|
||||
invisible="not hole_summary"/>
|
||||
<field name="is_manifold"
|
||||
widget="boolean_toggle"
|
||||
readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<div class="alert alert-warning"
|
||||
invisible="is_manifold or not part_catalog_id or not hole_count">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>
|
||||
<strong>Warning:</strong> 3D model is not watertight.
|
||||
Surface area calculation may be inaccurate. Review the file before quoting.
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Row 3 - Geometry on the LEFT, Delivery & Fees on the
|
||||
RIGHT. Delivery/Fees used to live in its own row with
|
||||
an empty right side; pairing it with Geometry keeps
|
||||
both columns balanced.
|
||||
-->
|
||||
<group>
|
||||
<group string="Geometry">
|
||||
<field name="surface_area"/>
|
||||
<field name="surface_area_uom"/>
|
||||
<field name="masking_area_sqin"
|
||||
string="Masking Area (sq in)"/>
|
||||
<field name="effective_area_sqin"
|
||||
string="Effective Plating Area"
|
||||
readonly="1"/>
|
||||
<field name="thickness_requested"/>
|
||||
<field name="material_id"
|
||||
options="{'no_quick_create': True}"/>
|
||||
<field name="substrate_material" invisible="1"/>
|
||||
<field name="masking_zones"/>
|
||||
<field name="turnaround_days"/>
|
||||
</group>
|
||||
<group string="Delivery & Fees">
|
||||
<field name="delivery_method"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="shipping_fee" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="delivery_fee" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Pricing"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="calculated_price" widget="monetary" readonly="1"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
class="fw-bold fs-4"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="estimator_override_price" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="price_breakdown_html" readonly="1" colspan="2"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<notebook>
|
||||
<page string="Sale Order" name="sale_order">
|
||||
<group>
|
||||
<field name="sale_order_id" readonly="1"/>
|
||||
<field name="won_date" readonly="1"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Win / Loss" name="win_loss">
|
||||
<group>
|
||||
<group>
|
||||
<field name="lost_reason"/>
|
||||
<field name="lost_competitor_name"
|
||||
invisible="lost_reason != 'competitor'"/>
|
||||
<field name="lost_date" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Notes"/>
|
||||
<field name="lost_details" colspan="2"
|
||||
placeholder="What did we learn? (Price point competitor beat, spec we didn't meet, etc.)"/>
|
||||
</page>
|
||||
<page string="Notes" name="notes">
|
||||
<field name="notes" placeholder="Internal notes about this quote..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Configurator List View ===== -->
|
||||
<record id="view_fp_quote_configurator_list" model="ir.ui.view">
|
||||
<field name="name">fp.quote.configurator.list</field>
|
||||
<field name="model">fp.quote.configurator</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Quote Configurators"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-muted="state == 'cancelled'"
|
||||
default_order="create_date desc">
|
||||
<field name="create_date" string="Date"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="recipe_id"/>
|
||||
<field name="surface_area"/>
|
||||
<field name="quantity"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="calculated_price" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}" sum="Total"/>
|
||||
<field name="estimator_override_price" string="Final Price"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}" sum="Total"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-success="state == 'confirmed'"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-danger="state == 'cancelled'"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Configurator Search View ===== -->
|
||||
<record id="view_fp_quote_configurator_search" model="ir.ui.view">
|
||||
<field name="name">fp.quote.configurator.search</field>
|
||||
<field name="model">fp.quote.configurator</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="recipe_id"/>
|
||||
<separator/>
|
||||
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter string="Confirmed" name="confirmed" domain="[('state', '=', 'confirmed')]"/>
|
||||
<filter string="Cancelled" name="cancelled" domain="[('state', '=', 'cancelled')]"/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_customer" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Recipe" name="group_recipe" context="{'group_by': 'recipe_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Window Action ===== -->
|
||||
<record id="action_fp_quote_configurator" model="ir.actions.act_window">
|
||||
<field name="name">Quote Configurator</field>
|
||||
<field name="res_model">fp.quote.configurator</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_quote_configurator_search"/>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new quote configurator session
|
||||
</p>
|
||||
<p>
|
||||
Select a customer and coating configuration, enter part geometry,
|
||||
and the pricing engine will calculate a quote. The estimator can
|
||||
override the calculated price before creating a sale order.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,118 +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.
|
||||
|
||||
Manage reusable descriptions that estimators can drop onto sale order lines.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="view_fp_sale_description_template_list" model="ir.ui.view">
|
||||
<field name="name">fp.sale.description.template.list</field>
|
||||
<field name="model">fp.sale.description.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<list multi_edit="1">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="part_catalog_id" optional="show"/>
|
||||
<field name="name"/>
|
||||
<field name="tag" widget="badge"
|
||||
decoration-info="tag == 'standard'"
|
||||
decoration-warning="tag == 'masking'"
|
||||
decoration-danger="tag == 'rework'"
|
||||
decoration-success="tag in ('aerospace','nuclear')"/>
|
||||
<field name="partner_id" optional="show"/>
|
||||
<field name="usage_count" string="Used"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_sale_description_template_form" model="ir.ui.view">
|
||||
<field name="name">fp.sale.description.template.form</field>
|
||||
<field name="model">fp.sale.description.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. ENP - Standard Aluminium"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="part_catalog_id"/>
|
||||
<field name="partner_id" readonly="part_catalog_id"/>
|
||||
<field name="tag"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="usage_count" readonly="1"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Internal Description (Shop Floor Only)">
|
||||
<field name="internal_description" nolabel="1" colspan="2"
|
||||
placeholder="What the shop floor sees on the WO / traveler…"/>
|
||||
</group>
|
||||
<separator string="Customer-Facing Description"/>
|
||||
<field name="customer_facing_description" colspan="2"
|
||||
placeholder="Electroless nickel plating per AMS 2404, Class I, Type II…"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_sale_description_template_search" model="ir.ui.view">
|
||||
<field name="name">fp.sale.description.template.search</field>
|
||||
<field name="model">fp.sale.description.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="internal_description"/>
|
||||
<field name="customer_facing_description"/>
|
||||
<field name="part_catalog_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="tag"/>
|
||||
<filter name="active" string="Active" domain="[('active','=',True)]"/>
|
||||
<filter name="with_part" string="Part-Specific"
|
||||
domain="[('part_catalog_id','!=',False)]"/>
|
||||
<filter name="no_part" string="Generic (No Part)"
|
||||
domain="[('part_catalog_id','=',False)]"/>
|
||||
<group>
|
||||
<filter name="group_part" string="Part"
|
||||
context="{'group_by': 'part_catalog_id'}"/>
|
||||
<filter name="group_customer" string="Customer"
|
||||
context="{'group_by': 'partner_id'}"/>
|
||||
<filter name="group_tag" string="Category"
|
||||
context="{'group_by': 'tag'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_sale_description_template" model="ir.actions.act_window">
|
||||
<field name="name">Line Description Templates</field>
|
||||
<field name="res_model">fp.sale.description.template</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_sale_description_template_search"/>
|
||||
<field name="context">{'search_default_active': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create your first line description template
|
||||
</p>
|
||||
<p>
|
||||
Save the language you use on repeat orders - masking rules,
|
||||
spec callouts, packaging notes. The estimator picks one,
|
||||
tweaks it, and it lands on the order line.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_sale_description_templates"
|
||||
name="Line Description Templates"
|
||||
parent="fusion_plating.menu_fp_config_quality_docs"
|
||||
action="action_fp_sale_description_template"
|
||||
sequence="90"
|
||||
groups="fusion_plating_configurator.group_fp_estimator,fusion_plating.group_fusion_plating_manager"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,191 +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.
|
||||
|
||||
Phase 2 (2026-04-28) - relocates the fp.serial views from
|
||||
fusion_plating_bridge_mrp (uninstalled in Sub 11) into configurator
|
||||
where the model lives. Adds the new state machine to the form +
|
||||
list with workflow buttons + status badge.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="view_fp_serial_form" model="ir.ui.view">
|
||||
<field name="name">fp.serial.form</field>
|
||||
<field name="model">fp.serial</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Serial Number">
|
||||
<header>
|
||||
<button name="action_mark_racked" type="object"
|
||||
string="Mark Racked" class="btn-primary"
|
||||
invisible="state != 'received'"/>
|
||||
<button name="action_mark_in_process" type="object"
|
||||
string="Start Processing" class="btn-primary"
|
||||
invisible="state != 'racked'"/>
|
||||
<button name="action_mark_inspected" type="object"
|
||||
string="Pass Inspection" class="btn-success"
|
||||
invisible="state != 'in_process'"/>
|
||||
<button name="action_mark_packed" type="object"
|
||||
string="Mark Packed" class="btn-primary"
|
||||
invisible="state != 'inspected'"/>
|
||||
<button name="action_mark_shipped" type="object"
|
||||
string="Mark Shipped" class="btn-success"
|
||||
invisible="state != 'packed'"/>
|
||||
<button name="action_mark_on_hold" type="object"
|
||||
string="Hold" class="btn-warning"
|
||||
invisible="state in ('on_hold', 'shipped', 'scrapped')"/>
|
||||
<button name="action_release_hold" type="object"
|
||||
string="Release Hold" class="btn-secondary"
|
||||
invisible="state != 'on_hold'"/>
|
||||
<button name="action_mark_scrapped" type="object"
|
||||
string="Scrap" class="btn-danger"
|
||||
invisible="state in ('shipped', 'scrapped')"
|
||||
confirm="Mark this serial as scrapped? This is reversible only via Reopen."/>
|
||||
<button name="action_mark_returned" type="object"
|
||||
string="Returned by Customer" class="btn-secondary"
|
||||
invisible="state != 'shipped'"/>
|
||||
<button name="action_reopen" type="object"
|
||||
string="Reopen" class="btn-secondary"
|
||||
groups="fusion_plating.group_fusion_plating_manager"
|
||||
invisible="state not in ('shipped', 'scrapped')"
|
||||
confirm="Reopen a terminal-state serial? Manager-only override; the chatter audit will record who, when, why."/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="received,racked,in_process,inspected,packed,shipped"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Scrapped" bg_color="text-bg-danger"
|
||||
invisible="state != 'scrapped'"/>
|
||||
<widget name="web_ribbon" title="On Hold" bg_color="text-bg-warning"
|
||||
invisible="state != 'on_hold'"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_sale_order" type="object"
|
||||
class="oe_stat_button" icon="fa-file-text-o"
|
||||
invisible="not sale_order_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Sale Order</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_view_invoices" type="object"
|
||||
class="oe_stat_button" icon="fa-money"
|
||||
invisible="invoice_count == 0">
|
||||
<field name="invoice_count" widget="statinfo" string="Invoices"/>
|
||||
</button>
|
||||
<button name="action_view_part" type="object"
|
||||
class="oe_stat_button" icon="fa-cube"
|
||||
invisible="not part_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Part</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Serial #"/>
|
||||
<h1><field name="name" placeholder="e.g. SN-12345"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="customer_id" readonly="1"/>
|
||||
<field name="part_id" readonly="1"/>
|
||||
<field name="sale_order_id" readonly="1"/>
|
||||
<field name="sale_order_line_id" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="last_state_change" readonly="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Scrap / Return Reason"
|
||||
invisible="state not in ('scrapped', 'returned', 'on_hold')"/>
|
||||
<field name="scrap_reason"
|
||||
invisible="state not in ('scrapped', 'returned', 'on_hold')"
|
||||
placeholder="What happened? Cause / disposition / who decided..."/>
|
||||
<separator string="Notes"/>
|
||||
<field name="notes"/>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_serial_list" model="ir.ui.view">
|
||||
<field name="name">fp.serial.list</field>
|
||||
<field name="model">fp.serial</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Serial Numbers"
|
||||
decoration-success="state in ('inspected', 'packed', 'shipped')"
|
||||
decoration-info="state in ('received', 'racked', 'in_process')"
|
||||
decoration-warning="state in ('on_hold', 'returned')"
|
||||
decoration-danger="state == 'scrapped'">
|
||||
<field name="name"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-success="state in ('inspected', 'packed', 'shipped')"
|
||||
decoration-info="state in ('received', 'racked', 'in_process')"
|
||||
decoration-warning="state in ('on_hold', 'returned')"
|
||||
decoration-danger="state == 'scrapped'"/>
|
||||
<field name="customer_id"/>
|
||||
<field name="part_id"/>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="last_state_change" optional="show"/>
|
||||
<field name="invoice_count" string="Invoices" optional="hide"/>
|
||||
<field name="create_date" optional="hide"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_serial_search" model="ir.ui.view">
|
||||
<field name="name">fp.serial.search</field>
|
||||
<field name="model">fp.serial</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="customer_id"/>
|
||||
<field name="part_id"/>
|
||||
<field name="sale_order_id"/>
|
||||
<separator/>
|
||||
<filter name="state_received" string="Received"
|
||||
domain="[('state','=','received')]"/>
|
||||
<filter name="state_in_process" string="In Process"
|
||||
domain="[('state','=','in_process')]"/>
|
||||
<filter name="state_inspected" string="Inspected"
|
||||
domain="[('state','=','inspected')]"/>
|
||||
<filter name="state_packed" string="Packed"
|
||||
domain="[('state','=','packed')]"/>
|
||||
<filter name="state_shipped" string="Shipped"
|
||||
domain="[('state','=','shipped')]"/>
|
||||
<separator/>
|
||||
<filter name="state_on_hold" string="On Hold"
|
||||
domain="[('state','=','on_hold')]"/>
|
||||
<filter name="state_scrapped" string="Scrapped"
|
||||
domain="[('state','=','scrapped')]"/>
|
||||
<filter name="state_returned" string="Returned"
|
||||
domain="[('state','=','returned')]"/>
|
||||
<separator/>
|
||||
<filter name="active_only" string="In Flight (not shipped/scrapped)"
|
||||
domain="[('state','not in',('shipped','scrapped'))]"/>
|
||||
<group>
|
||||
<filter name="group_state" string="Status"
|
||||
context="{'group_by': 'state'}"/>
|
||||
<filter name="group_customer" string="Customer"
|
||||
context="{'group_by': 'customer_id'}"/>
|
||||
<filter name="group_part" string="Part"
|
||||
context="{'group_by': 'part_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_serial" model="ir.actions.act_window">
|
||||
<field name="name">Serial Numbers</field>
|
||||
<field name="res_model">fp.serial</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_serial_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_serial"
|
||||
name="Serial Numbers"
|
||||
parent="fusion_plating_configurator.menu_fp_sales"
|
||||
action="action_fp_serial"
|
||||
sequence="60"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,261 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
Job Sorting:
|
||||
- Section model views (list/form) under Configuration → Sales.
|
||||
- Alternate SO list ("Sale Orders by Sorting") grouped by job sort
|
||||
with foldable sections and create-from-here support.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Section management (Configuration) ===== -->
|
||||
<record id="view_fp_so_job_sort_list" model="ir.ui.view">
|
||||
<field name="name">fp.so.job.sort.list</field>
|
||||
<field name="model">fp.so.job.sort</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Job Sorting" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="color" widget="color_picker"/>
|
||||
<field name="fold" widget="boolean_toggle"/>
|
||||
<field name="sale_order_count"/>
|
||||
<field name="active" widget="boolean_toggle" optional="hide"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_fp_so_job_sort_form" model="ir.ui.view">
|
||||
<field name="name">fp.so.job.sort.form</field>
|
||||
<field name="model">fp.so.job.sort</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Job Sorting">
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_sale_orders" type="object"
|
||||
class="oe_stat_button" icon="fa-shopping-cart">
|
||||
<field name="sale_order_count" widget="statinfo"
|
||||
string="Sale Orders"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Rush Orders"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="color" widget="color_picker"/>
|
||||
<field name="fold"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="description"
|
||||
placeholder="What kinds of orders belong in this section?"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_so_job_sort" model="ir.actions.act_window">
|
||||
<field name="name">Job Sorting</field>
|
||||
<field name="res_model">fp.so.job.sort</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_so_job_sort"
|
||||
name="Job Sorting"
|
||||
parent="fusion_plating.menu_fp_config_pricing_billing"
|
||||
action="action_fp_so_job_sort"
|
||||
sequence="25"/>
|
||||
|
||||
<!-- ===== Kanban grouped by Job Sorting =====
|
||||
Groups SOs into foldable columns by x_fc_job_sort_id.
|
||||
Drag-drop between columns rewrites the bucket; quick-create on
|
||||
the column header creates a new fp.so.job.sort row. Wired into
|
||||
the existing Sale Orders action below so it shows up in the
|
||||
view-switcher next to the flat list. -->
|
||||
<record id="view_sale_order_kanban_fp_by_sorting" model="ir.ui.view">
|
||||
<field name="name">sale.order.kanban.fp.by_sorting</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban default_group_by="x_fc_job_sort_id"
|
||||
group_create="true"
|
||||
group_edit="true"
|
||||
group_delete="true"
|
||||
quick_create="false"
|
||||
sample="1">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="amount_total"/>
|
||||
<field name="currency_id"/>
|
||||
<field name="x_fc_part_numbers_summary"/>
|
||||
<field name="x_fc_customer_job_number"/>
|
||||
<field name="x_fc_deadline_countdown"/>
|
||||
<field name="x_fc_deadline_urgency"/>
|
||||
<field name="x_fc_fp_job_status"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_kanban_card_content p-2">
|
||||
<div class="d-flex justify-content-between align-items-start mb-1">
|
||||
<strong><field name="name"/></strong>
|
||||
<span t-att-class="'badge ' + (
|
||||
record.x_fc_deadline_urgency.raw_value == 'overdue' and 'text-bg-danger' or
|
||||
record.x_fc_deadline_urgency.raw_value == 'urgent' and 'text-bg-warning' or
|
||||
record.x_fc_deadline_urgency.raw_value == 'safe' and 'text-bg-success' or
|
||||
'text-bg-light')"
|
||||
t-if="record.x_fc_deadline_countdown.raw_value">
|
||||
<field name="x_fc_deadline_countdown"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-muted small mb-1">
|
||||
<field name="partner_id"/>
|
||||
</div>
|
||||
<div class="small mb-1" t-if="record.x_fc_part_numbers_summary.raw_value">
|
||||
<i class="fa fa-cube me-1"/>
|
||||
<field name="x_fc_part_numbers_summary"/>
|
||||
</div>
|
||||
<div class="small mb-2" t-if="record.x_fc_customer_job_number.raw_value">
|
||||
<i class="fa fa-hashtag me-1"/>
|
||||
<field name="x_fc_customer_job_number"/>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<field name="x_fc_fp_job_status" widget="html"/>
|
||||
<strong>
|
||||
<field name="amount_total" widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Sale Orders by Sorting (alternate SO list) ===== -->
|
||||
<!-- Duplicate of view_sale_order_list_fp but renamed and intended
|
||||
to be opened with group_by=x_fc_job_sort_id by default so the
|
||||
user sees foldable sections per Job Sorting bucket. -->
|
||||
<record id="view_sale_order_list_fp_by_sorting" model="ir.ui.view">
|
||||
<field name="name">sale.order.list.fp.by_sorting</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Sale Orders by Sorting" create="0"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-muted="state == 'cancel'"
|
||||
decoration-danger="x_fc_is_late_forecast">
|
||||
<header>
|
||||
<button name="%(action_fp_direct_order_wizard)d"
|
||||
type="action"
|
||||
string="New Order"
|
||||
class="btn-primary"
|
||||
display="always"/>
|
||||
</header>
|
||||
<field name="name" optional="show"/>
|
||||
<field name="partner_id" optional="show"/>
|
||||
<field name="x_fc_po_number" optional="show"/>
|
||||
<field name="x_fc_customer_job_number" optional="show"/>
|
||||
<field name="x_fc_job_sort_id" optional="show"
|
||||
options="{'no_create_edit': False, 'no_open': True}"/>
|
||||
<field name="x_fc_internal_deadline" optional="show"/>
|
||||
<field name="commitment_date" string="Customer Deadline"
|
||||
optional="show"/>
|
||||
<field name="x_fc_order_completion_date" string="Completion"
|
||||
optional="show"/>
|
||||
<field name="x_fc_is_late_forecast" optional="hide"
|
||||
widget="boolean_toggle"/>
|
||||
<field name="x_fc_deadline_urgency" column_invisible="1"/>
|
||||
<field name="x_fc_deadline_countdown" optional="show"
|
||||
decoration-danger="x_fc_deadline_urgency == 'overdue'"
|
||||
decoration-warning="x_fc_deadline_urgency == 'urgent'"
|
||||
decoration-success="x_fc_deadline_urgency == 'safe'"/>
|
||||
<field name="x_fc_wo_completion" optional="show"/>
|
||||
<field name="x_fc_planned_start_date" optional="hide"/>
|
||||
<field name="x_fc_part_numbers_summary" string="Part"
|
||||
optional="show"/>
|
||||
<field name="amount_total" sum="Total" optional="show"/>
|
||||
<field name="x_fc_invoiced_amount" sum="Invoiced"
|
||||
optional="hide"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="x_fc_fp_job_status" widget="html"
|
||||
string="Job Status" optional="show" readonly="1"/>
|
||||
<field name="x_fc_receiving_status" widget="badge"
|
||||
optional="hide"
|
||||
decoration-warning="x_fc_receiving_status == 'not_received'"
|
||||
decoration-info="x_fc_receiving_status == 'partial'"
|
||||
decoration-success="x_fc_receiving_status == 'received'"/>
|
||||
<field name="x_fc_delivery_method" optional="hide"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="state" widget="badge" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search view for the alternate list: surface "Group by Job
|
||||
Sorting" as a search-default filter. -->
|
||||
<record id="view_sale_order_search_fp_by_sorting" model="ir.ui.view">
|
||||
<field name="name">sale.order.search.fp.by_sorting</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Sale Orders by Sorting">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="x_fc_part_numbers_summary" string="Part"/>
|
||||
<field name="x_fc_customer_job_number"/>
|
||||
<field name="x_fc_po_number"/>
|
||||
<field name="x_fc_job_sort_id"/>
|
||||
<filter name="late_forecast" string="Late Forecast"
|
||||
domain="[('x_fc_is_late_forecast','=',True)]"/>
|
||||
<filter name="cancelled" string="Cancelled"
|
||||
domain="[('state','=','cancel')]"/>
|
||||
<separator/>
|
||||
<group>
|
||||
<filter name="group_by_job_sort"
|
||||
string="Job Sorting"
|
||||
context="{'group_by': 'x_fc_job_sort_id'}"/>
|
||||
<filter name="group_by_customer"
|
||||
string="Customer"
|
||||
context="{'group_by': 'partner_id'}"/>
|
||||
<filter name="group_by_state"
|
||||
string="Status"
|
||||
context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Append the kanban view to the existing Sale Orders action so
|
||||
users can switch from the flat list to the grouped-by-sorting
|
||||
kanban (foldable columns, drag-drop bucket reassignment) via
|
||||
the view-switcher icon in the top-right of the SO list. -->
|
||||
<record id="action_fp_sale_orders" model="ir.actions.act_window">
|
||||
<field name="view_mode">list,kanban,form</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('view_sale_order_list_fp')}),
|
||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('view_sale_order_kanban_fp_by_sorting')})]"/>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_sale_orders_by_sorting" model="ir.actions.act_window">
|
||||
<field name="name">Sale Orders (by Sorting)</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="view_id" ref="view_sale_order_list_fp_by_sorting"/>
|
||||
<field name="search_view_id" ref="view_sale_order_search_fp_by_sorting"/>
|
||||
<field name="domain">[('state', 'not in', ('draft', 'sent'))]</field>
|
||||
<field name="context">{'search_default_group_by_job_sort': 1}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fp_sale_orders_by_sorting"
|
||||
name="Sale Orders (by Sorting)"
|
||||
parent="fusion_plating_configurator.menu_fp_sales"
|
||||
action="action_fp_sale_orders_by_sorting"
|
||||
sequence="12"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_partner_form_fp_parts" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.fp.parts</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_view_parts"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-cube"
|
||||
invisible="x_fc_part_count == 0">
|
||||
<field name="x_fc_part_count" widget="statinfo" string="Parts"/>
|
||||
</button>
|
||||
<button name="action_fp_new_direct_order"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-shopping-cart"
|
||||
invisible="customer_rank == 0">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Direct Order</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_fp_import_parts"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-upload"
|
||||
invisible="customer_rank == 0">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Import Parts</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//page[@name='sales_purchases']" position="after">
|
||||
<page string="Part Library" name="part_library">
|
||||
<field name="x_fc_part_catalog_ids" mode="list"
|
||||
domain="[('is_latest_revision', '=', True)]">
|
||||
<list default_order="name" >
|
||||
<field name="part_number"/>
|
||||
<field name="name"/>
|
||||
<field name="revision"/>
|
||||
<field name="substrate_material"/>
|
||||
<field name="surface_area"/>
|
||||
<field name="surface_area_uom" string="UoM"/>
|
||||
<field name="complexity"/>
|
||||
<field name="model_attachment_id" string="3D Model"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,670 +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.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- ===== Inherit SO Form - add Plating tab ===== -->
|
||||
<record id="view_sale_order_form_fp" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.fp.configurator</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Header buttons: make draft Confirm the primary CTA, demote/rename
|
||||
Send to "Send Email" (red), and reorder so Confirm sits first.
|
||||
Phase D5 - gate Confirm button to Sales Manager + higher; matches
|
||||
the model-level gate from Phase G so Sales Rep sees the SO in
|
||||
draft but no Confirm button. -->
|
||||
<xpath expr="//header/button[@name='action_confirm' and not(@id)]" position="attributes">
|
||||
<attribute name="class">btn-primary</attribute>
|
||||
<attribute name="groups">fusion_plating.group_fp_sales_manager</attribute>
|
||||
</xpath>
|
||||
<!-- Also gate the state=sent Confirm button (id="action_confirm") so
|
||||
Sales Reps don't see EITHER variant. Matches the model-level gate. -->
|
||||
<xpath expr="//header/button[@id='action_confirm']" position="attributes">
|
||||
<attribute name="groups">fusion_plating.group_fp_sales_manager</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//header/button[@id='quotation_send_primary']" position="attributes">
|
||||
<attribute name="string">Send Email</attribute>
|
||||
<attribute name="class">btn-danger</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//header/button[@id='quotation_send_primary']" position="before">
|
||||
<xpath expr="//header/button[@name='action_confirm' and not(@id)]" position="move"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Hide standard Delivery button: our Transfers button (below) shows
|
||||
all stock.picking records - inbound receipts AND outbound deliveries -
|
||||
which matches the plating workflow better than outbound-only. -->
|
||||
<xpath expr="//button[@name='action_view_delivery']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
|
||||
<!-- Insert before standard Invoices: PO, then RFQ (multiple position="before"
|
||||
stack so last-inserted ends up closest to anchor; order here is PO, RFQ). -->
|
||||
<xpath expr="//button[@name='action_view_invoice']" position="before">
|
||||
<button name="action_view_po"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-file-o"
|
||||
invisible="not x_fc_po_attachment_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">1</span>
|
||||
<span class="o_stat_text">PO</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_view_rfq"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-envelope-o"
|
||||
invisible="not x_fc_rfq_attachment_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_value">1</span>
|
||||
<span class="o_stat_text">RFQ</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<!-- After standard Invoices: Transfers (all stock.picking for this SO). -->
|
||||
<xpath expr="//button[@name='action_view_invoice']" position="after">
|
||||
<button name="action_view_pickings"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-truck"
|
||||
invisible="x_fc_picking_count == 0">
|
||||
<field name="x_fc_picking_count" widget="statinfo"
|
||||
string="Transfers"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<!-- Sub 11 - MRP gone. The "Work Orders" button used to count
|
||||
mrp.workorder; removed because Plating Jobs (added by
|
||||
fusion_plating_jobs) now counts the canonical fp.job.step
|
||||
rows. NCRs surfaces only when there's at least one open;
|
||||
BOM Items and By Job Group only when the SO is actually
|
||||
multi-part / tagged (otherwise both render one column with
|
||||
one card - pure noise). Anchored after Transfers; the two
|
||||
conditional ones go last so the typical clean SO shows
|
||||
just the meaningful buttons up front. -->
|
||||
<xpath expr="//button[@name='action_view_pickings']" position="after">
|
||||
<button name="action_view_ncrs"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-exclamation-triangle"
|
||||
invisible="x_fc_ncr_count == 0">
|
||||
<field name="x_fc_ncr_count" widget="statinfo"
|
||||
string="NCRs"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<!-- Push BOM Items / By Job Group to the end of the button
|
||||
box (after the Plating Jobs / Holds row added by jobs +
|
||||
quality). They sit hidden by default and only surface
|
||||
when the SO actually has multi-part lines or job-group
|
||||
tags. -->
|
||||
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
|
||||
<button name="action_view_bom_items"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-list-alt"
|
||||
invisible="x_fc_distinct_part_count < 2">
|
||||
<field name="x_fc_distinct_part_count" widget="statinfo"
|
||||
string="BOM Items"/>
|
||||
</button>
|
||||
<button name="action_view_wo_perspective"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-th-large"
|
||||
invisible="not x_fc_has_wo_group_tag">
|
||||
<field name="x_fc_wo_group_count" widget="statinfo"
|
||||
string="Job Groups"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<!-- Surface Delivery Date (commitment_date) right after Order
|
||||
Date in the header info group. The standard view only
|
||||
shows it buried under the Delivery section in the Other
|
||||
Info tab - having it at the top keeps it visible without
|
||||
scrolling, since most operators are setting it on every
|
||||
order. The same field is also surfaced lower in our
|
||||
Plating tab Scheduling group as "Customer Deadline"; both
|
||||
reference the same field so edits sync. -->
|
||||
<xpath expr="//group[@name='order_details']/field[@name='payment_term_id']" position="before">
|
||||
<field name="commitment_date" string="Delivery Date"
|
||||
readonly="state in ('cancel',)"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Job Sorting sits right under Payment Terms - a free-form
|
||||
bucket that groups the SO in the "Sale Orders by Sorting"
|
||||
list. Quick-create from the dropdown. -->
|
||||
<xpath expr="//group[@name='order_details']/field[@name='payment_term_id']" position="after">
|
||||
<field name="x_fc_job_sort_id"
|
||||
options="{'no_create_edit': False, 'no_open': True}"
|
||||
placeholder="Type to create a new bucket..."/>
|
||||
</xpath>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Plating" name="plating_tab">
|
||||
<!-- Multi-part summary: read-only list of every order line
|
||||
showing part / coating / process. The Order Lines tab
|
||||
is the editable surface; this is the at-a-glance view
|
||||
so you can confirm an order has the right parts/coatings
|
||||
without scrolling pricing columns. The pre-Sub-12 SO-
|
||||
header singletons (x_fc_part_catalog_id /
|
||||
x_fc_customer_spec_id) only ever populated when the
|
||||
order was built via the quote configurator - they're
|
||||
silent on direct orders, which is why they appeared
|
||||
empty after confirm. They still exist on the model
|
||||
(used by configurator/portal) but are no longer the
|
||||
primary display. -->
|
||||
<separator string="Parts on this order"/>
|
||||
<field name="order_line" nolabel="1"
|
||||
context="{'tree_view_ref': 'fusion_plating_configurator.view_sale_order_line_plating_summary'}"
|
||||
readonly="1">
|
||||
<list create="false" delete="false" edit="false">
|
||||
<field name="x_fc_part_catalog_id"/>
|
||||
<field name="x_fc_thickness_range" optional="show"/>
|
||||
<field name="x_fc_process_variant_id" optional="show"
|
||||
string="Process"/>
|
||||
<field name="product_uom_qty" string="Qty"/>
|
||||
<field name="x_fc_effective_part_deadline" optional="show"
|
||||
string="Effective Deadline"/>
|
||||
<field name="x_fc_part_deadline" optional="hide"
|
||||
string="Part Deadline Override"/>
|
||||
<field name="x_fc_rush_order" optional="hide"/>
|
||||
<field name="x_fc_job_number" optional="show"
|
||||
string="Job #"/>
|
||||
</list>
|
||||
</field>
|
||||
<!-- Row 1: RFQ/PO (left) + Scheduling (right) - pairs the two
|
||||
tallest groups so neither column dangles empty. -->
|
||||
<group>
|
||||
<group string="RFQ / PO">
|
||||
<field name="x_fc_po_number"/>
|
||||
<field name="upload_rfq_file"
|
||||
filename="upload_rfq_filename"
|
||||
invisible="x_fc_rfq_attachment_id"
|
||||
string="Attach RFQ"/>
|
||||
<field name="upload_rfq_filename" invisible="1"/>
|
||||
<field name="x_fc_rfq_attachment_id"
|
||||
invisible="not x_fc_rfq_attachment_id"/>
|
||||
<field name="upload_po_file"
|
||||
filename="upload_po_filename"
|
||||
invisible="x_fc_po_attachment_id"
|
||||
string="Attach PO"/>
|
||||
<field name="upload_po_filename" invisible="1"/>
|
||||
<field name="x_fc_po_attachment_id"
|
||||
invisible="not x_fc_po_attachment_id"/>
|
||||
<field name="x_fc_po_received"/>
|
||||
<field name="x_fc_po_pending" widget="boolean_toggle"/>
|
||||
<field name="x_fc_po_expected_date"
|
||||
invisible="not x_fc_po_pending"/>
|
||||
<field name="x_fc_po_override"
|
||||
groups="fusion_plating.group_fusion_plating_manager"/>
|
||||
<field name="x_fc_po_override_reason"
|
||||
invisible="not x_fc_po_override"/>
|
||||
</group>
|
||||
<group string="Scheduling">
|
||||
<field name="x_fc_planned_start_date"/>
|
||||
<field name="x_fc_internal_deadline"/>
|
||||
<field name="commitment_date" string="Customer Deadline"/>
|
||||
<field name="x_fc_deadline_countdown" readonly="1"/>
|
||||
<label for="x_fc_order_completion_date"/>
|
||||
<div class="o_row">
|
||||
<field name="x_fc_order_completion_date"
|
||||
readonly="1" class="oe_inline"/>
|
||||
<span class="badge text-bg-danger ms-2"
|
||||
invisible="not x_fc_is_late_forecast">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>Late
|
||||
</span>
|
||||
<field name="x_fc_is_late_forecast" invisible="1"/>
|
||||
</div>
|
||||
<field name="x_fc_is_blanket_order"/>
|
||||
<field name="x_fc_block_partial_shipments"/>
|
||||
<!-- Lead Time range. Both 0 = "Standard" on
|
||||
the PDF; otherwise renders "X-Y days"
|
||||
(or "X days" if min==max or one is 0). -->
|
||||
<label for="x_fc_lead_time_min_days" string="Lead Time (days)"/>
|
||||
<div class="o_row">
|
||||
<field name="x_fc_lead_time_min_days" class="oe_inline" style="width: 4em;"/>
|
||||
<span> to </span>
|
||||
<field name="x_fc_lead_time_max_days" class="oe_inline" style="width: 4em;"/>
|
||||
</div>
|
||||
<field name="x_fc_lead_time_display" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Row 2: Invoicing + Delivery (unchanged pairing). -->
|
||||
<group>
|
||||
<group string="Invoicing">
|
||||
<field name="x_fc_invoice_strategy"/>
|
||||
<field name="x_fc_deposit_percent"
|
||||
invisible="x_fc_invoice_strategy != 'deposit'"/>
|
||||
<field name="x_fc_progress_initial_percent"
|
||||
invisible="x_fc_invoice_strategy != 'progress'"/>
|
||||
<field name="x_fc_final_invoice_id" readonly="1"
|
||||
invisible="not x_fc_final_invoice_id"/>
|
||||
</group>
|
||||
<group string="Delivery">
|
||||
<field name="x_fc_rush_order"/>
|
||||
<field name="x_fc_delivery_method"/>
|
||||
<field name="x_fc_receiving_status"/><!-- Will become computed when fusion_plating_receiving is installed -->
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Row 3: Customer Reference + Margin - both short groups, so
|
||||
pairing them keeps the right column from going blank. -->
|
||||
<group>
|
||||
<group string="Customer Reference">
|
||||
<field name="x_fc_customer_job_number"/>
|
||||
<field name="x_fc_contact_phone"/>
|
||||
<field name="x_fc_ship_via"/>
|
||||
</group>
|
||||
<group string="Margin">
|
||||
<div colspan="2"
|
||||
invisible="x_fc_margin_available"
|
||||
class="text-muted">
|
||||
<i class="fa fa-info-circle me-1"/>
|
||||
Margin n/a - coating cost rollup not yet
|
||||
populated on any line's treatment.
|
||||
</div>
|
||||
<field name="x_fc_margin_amount"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
invisible="not x_fc_margin_available"/>
|
||||
<field name="x_fc_margin_percent"
|
||||
widget="percentage"
|
||||
invisible="not x_fc_margin_available"/>
|
||||
<field name="x_fc_margin_available" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Row 4: Notes - two side-by-side textareas instead of the
|
||||
previous broken separator-in-group layout. -->
|
||||
<group>
|
||||
<group string="Internal Notes">
|
||||
<field name="x_fc_internal_note" nolabel="1"
|
||||
placeholder="Internal notes for estimator / planner / shop floor..."/>
|
||||
</group>
|
||||
<group string="External Notes (customer-visible)">
|
||||
<field name="x_fc_external_note" nolabel="1"
|
||||
placeholder="Notes that appear on the acknowledgement and portal..."/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Legacy configurator block - invisible on new SOs (only
|
||||
the handful that came through the old quote configurator
|
||||
flow have x_fc_configurator_id set). Kept at the bottom
|
||||
so it doesn't waste vertical space on the common case. -->
|
||||
<group invisible="not x_fc_configurator_id">
|
||||
<group string="Configurator (legacy)">
|
||||
<field name="x_fc_configurator_id" readonly="1"/>
|
||||
<field name="x_fc_process_summary" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<!-- Make the standard customer-facing description column togglable.
|
||||
The base sale.order line view shows `name` always; flipping it
|
||||
to optional lets estimators hide/show it like the other columns. -->
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='name']" position="attributes">
|
||||
<attribute name="string">Customer-Facing</attribute>
|
||||
<attribute name="optional">show</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='product_uom_qty']" position="before">
|
||||
<field name="x_fc_part_catalog_id" optional="show"/>
|
||||
<field name="x_fc_description_template_id"
|
||||
domain="[('part_catalog_id', '=', x_fc_part_catalog_id)]"
|
||||
context="{'default_part_catalog_id': x_fc_part_catalog_id}"
|
||||
invisible="not x_fc_part_catalog_id"
|
||||
optional="hide"/>
|
||||
<field name="x_fc_internal_description"
|
||||
placeholder="Shop-floor workflow instructions (prints on WO / traveler)"
|
||||
optional="hide"/>
|
||||
<field name="x_fc_process_variant_id"
|
||||
string="Process / Recipe"
|
||||
options="{'no_quick_create': True}"
|
||||
invisible="not x_fc_part_catalog_id"
|
||||
optional="show"/>
|
||||
<field name="x_fc_save_as_default_process"
|
||||
string="Set as Part Default"
|
||||
widget="boolean_toggle"
|
||||
invisible="not x_fc_process_variant_id"
|
||||
optional="hide"/>
|
||||
<field name="x_fc_thickness_range"
|
||||
placeholder="e.g. 0.0005-0.0008 mils"
|
||||
optional="show"/>
|
||||
<field name="x_fc_serial_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_quick_create': False, 'color_field': 'state_color'}"
|
||||
domain="[('part_id', '=', x_fc_part_catalog_id)]"
|
||||
optional="show"/>
|
||||
<field name="x_fc_serial_count"
|
||||
string="Serial Count"
|
||||
optional="hide"/>
|
||||
<button name="action_open_serial_bulk_add" type="object"
|
||||
title="Bulk add serials"
|
||||
icon="fa-list-ol"
|
||||
class="btn-link"
|
||||
invisible="not x_fc_part_catalog_id or x_fc_serial_count > 0"/>
|
||||
<field name="x_fc_job_number" optional="show"/>
|
||||
<field name="x_fc_revision_pick_id"
|
||||
string="Revision"
|
||||
options="{'no_create': True}"
|
||||
invisible="not x_fc_part_catalog_id"
|
||||
optional="hide"/>
|
||||
<field name="x_fc_revision_snapshot"
|
||||
readonly="1"
|
||||
optional="hide"/>
|
||||
<field name="x_fc_part_deadline" string="Part Deadline Override" optional="hide"/>
|
||||
<field name="x_fc_part_deadline_offset_days" string="Days Offset" optional="hide"/>
|
||||
<field name="x_fc_effective_part_deadline" string="Effective Deadline"
|
||||
optional="show"
|
||||
readonly="1"/>
|
||||
<field name="x_fc_effective_internal_deadline" string="Shop Target"
|
||||
optional="hide"
|
||||
readonly="1"/>
|
||||
<field name="x_fc_wo_group_tag" optional="hide"/>
|
||||
<field name="x_fc_start_at_node_id" optional="hide"/>
|
||||
<field name="x_fc_is_one_off" optional="hide"/>
|
||||
<field name="x_fc_quote_id" optional="hide"/>
|
||||
<field name="x_fc_rush_order" optional="hide"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Phase D5 - gate pricing columns/totals to Sales Rep + higher
|
||||
(defense in depth - Technician/Shop Manager don't see pricing
|
||||
even if they navigate to an SO). -->
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='price_unit']" position="attributes">
|
||||
<attribute name="groups">fusion_plating.group_fp_sales_rep</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='price_subtotal']" position="attributes">
|
||||
<attribute name="groups">fusion_plating.group_fp_sales_rep</attribute>
|
||||
</xpath>
|
||||
<!-- Default-hidden columns (still toggleable via the optional-
|
||||
columns menu). Keeps the order-line grid focused on the
|
||||
plating fields; the generic FP-SERVICE product, delivered/
|
||||
invoiced quantities, and the per-line tax column are noise
|
||||
on a plating order. Estimators can switch any back on. -->
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='product_template_id']" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='qty_delivered']" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='qty_invoiced']" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/list/field[@name='tax_ids']" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</xpath>
|
||||
<!-- Odoo 19: amount_total / amount_untaxed / amount_tax are rendered
|
||||
by the single tax_totals widget; no separate fields. Gate the
|
||||
widget itself to hide the entire totals block from non-Sales-Rep. -->
|
||||
<xpath expr="//field[@name='tax_totals']" position="attributes">
|
||||
<attribute name="groups">fusion_plating.group_fp_sales_rep</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Custom SO List View for Fusion Plating ===== -->
|
||||
<record id="view_sale_order_list_fp" model="ir.ui.view">
|
||||
<field name="name">sale.order.list.fp</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Sale Orders" create="0" decoration-info="state == 'draft'"
|
||||
decoration-muted="state == 'cancel'"
|
||||
decoration-danger="x_fc_is_late_forecast">
|
||||
<header>
|
||||
<button name="%(action_fp_direct_order_wizard)d"
|
||||
type="action"
|
||||
string="New Order"
|
||||
class="btn-primary"
|
||||
display="always"/>
|
||||
</header>
|
||||
<field name="name" optional="show"/>
|
||||
<field name="partner_id" optional="show"/>
|
||||
<field name="x_fc_po_number" optional="show"/>
|
||||
<field name="x_fc_customer_job_number" optional="show"/>
|
||||
<field name="x_fc_internal_deadline" optional="show"/>
|
||||
<field name="commitment_date" string="Customer Deadline" optional="show"/>
|
||||
<field name="x_fc_order_completion_date" string="Completion" optional="show"/>
|
||||
<field name="x_fc_is_late_forecast" optional="hide" widget="boolean_toggle"/>
|
||||
<field name="x_fc_deadline_urgency" column_invisible="1"/>
|
||||
<field name="x_fc_deadline_countdown" optional="show"
|
||||
decoration-danger="x_fc_deadline_urgency == 'overdue'"
|
||||
decoration-warning="x_fc_deadline_urgency == 'urgent'"
|
||||
decoration-success="x_fc_deadline_urgency == 'safe'"/>
|
||||
<field name="x_fc_wo_completion" optional="show"/>
|
||||
<field name="x_fc_planned_start_date" optional="hide"/>
|
||||
<!-- "Part" column - walks order_line.x_fc_part_catalog_id
|
||||
and shows a compact summary (e.g. "M1234, M5678
|
||||
(+3 more)"). The header x_fc_part_catalog_id field
|
||||
is rarely populated in the configurator flow; the
|
||||
line carries the authoritative part link. -->
|
||||
<field name="x_fc_part_numbers_summary" string="Part"
|
||||
optional="show"/>
|
||||
<field name="amount_total" sum="Total" optional="show"/>
|
||||
<field name="x_fc_invoiced_amount" sum="Invoiced" optional="hide"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="x_fc_margin_amount" sum="Margin" optional="hide"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="x_fc_margin_percent" optional="hide"
|
||||
widget="percentage"/>
|
||||
<field name="x_fc_is_blanket_order" optional="hide"/>
|
||||
<!-- Single Job Status pill. Renders as HTML with a
|
||||
per-kind class (.fp-kind-*) so every phase carries
|
||||
its own distinct tint - see
|
||||
static/src/scss/fp_job_status_pill.scss. -->
|
||||
<field name="x_fc_fp_job_status" widget="html"
|
||||
string="Job Status" optional="show"
|
||||
readonly="1"/>
|
||||
<field name="x_fc_receiving_status" widget="badge"
|
||||
optional="hide"
|
||||
decoration-warning="x_fc_receiving_status == 'not_received'"
|
||||
decoration-info="x_fc_receiving_status == 'partial'"
|
||||
decoration-success="x_fc_receiving_status == 'received'"/>
|
||||
<field name="x_fc_delivery_method" optional="hide"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="state" widget="badge" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== BOM Items view (lines grouped by part) - Phase D2 ===== -->
|
||||
<record id="view_sale_order_line_bom_kanban" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.bom.kanban</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban default_group_by="x_fc_part_catalog_id" records_draggable="0">
|
||||
<field name="x_fc_part_catalog_id"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="qty_delivered"/>
|
||||
<field name="x_fc_wo_group_tag"/>
|
||||
<field name="x_fc_archived"/>
|
||||
<field name="currency_id"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_kanban_card_content">
|
||||
<div class="o_kanban_record_title">
|
||||
<strong><field name="x_fc_part_catalog_id"/></strong>
|
||||
</div>
|
||||
<div class="text-muted">
|
||||
Qty: <field name="product_uom_qty"/>
|
||||
/ Delivered: <field name="qty_delivered"/>
|
||||
</div>
|
||||
<div t-if="record.x_fc_wo_group_tag.raw_value">
|
||||
<span class="badge bg-info">
|
||||
<field name="x_fc_wo_group_tag"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== WO-perspective view: lines grouped by WO tag - Phase D10 ===== -->
|
||||
<record id="view_sale_order_line_wo_kanban" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.wo.kanban</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban default_group_by="x_fc_wo_group_tag" records_draggable="0">
|
||||
<field name="x_fc_wo_group_tag"/>
|
||||
<field name="x_fc_part_catalog_id"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_kanban_card_content">
|
||||
<div>
|
||||
<strong><field name="x_fc_part_catalog_id"/></strong>
|
||||
</div>
|
||||
<div>
|
||||
Qty: <field name="product_uom_qty"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Quotes list view (state in draft/sent) ===== -->
|
||||
<record id="view_sale_order_list_fp_quotes" model="ir.ui.view">
|
||||
<field name="name">sale.order.list.fp.quotes</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Quotations" decoration-muted="state == 'cancel'">
|
||||
<field name="name" optional="show"/>
|
||||
<field name="partner_id" optional="show"/>
|
||||
<field name="x_fc_part_numbers_summary" optional="show"/>
|
||||
<field name="x_fc_po_number" optional="hide"/>
|
||||
<field name="x_fc_customer_job_number" optional="hide"/>
|
||||
<field name="create_date" string="Created" optional="show"/>
|
||||
<field name="validity_date" string="Expires" optional="show"/>
|
||||
<field name="x_fc_follow_up_date" optional="show"/>
|
||||
<field name="x_fc_follow_up_user_id" optional="show"/>
|
||||
<field name="amount_total" sum="Total" optional="show"/>
|
||||
<field name="x_fc_is_signed" widget="boolean_toggle"
|
||||
string="Signed" optional="show"/>
|
||||
<field name="x_fc_email_status" widget="badge"
|
||||
optional="show"
|
||||
decoration-info="x_fc_email_status == 'sent'"
|
||||
decoration-warning="x_fc_email_status == 'opened'"
|
||||
decoration-success="x_fc_email_status == 'won'"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
<field name="state" widget="badge" optional="show"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Quotes search view ===== -->
|
||||
<record id="view_sale_order_search_fp_quotes" model="ir.ui.view">
|
||||
<field name="name">sale.order.search.fp.quotes</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Quotations">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="x_fc_part_numbers_summary" string="Part Number"/>
|
||||
<filter name="my_quotes" string="My Quotes"
|
||||
domain="[('user_id', '=', uid)]"/>
|
||||
<separator/>
|
||||
<filter name="draft" string="Draft"
|
||||
domain="[('state', '=', 'draft')]"/>
|
||||
<filter name="sent" string="Sent"
|
||||
domain="[('state', '=', 'sent')]"/>
|
||||
<filter name="won" string="Won"
|
||||
domain="[('state', 'in', ('sale', 'done'))]"/>
|
||||
<separator/>
|
||||
<filter name="from_rfq" string="From RFQ"
|
||||
domain="[('x_fc_rfq_attachment_id', '!=', False)]"/>
|
||||
<filter name="needs_followup" string="Needs Follow-Up"
|
||||
domain="[('x_fc_follow_up_date', '<=', context_today()), ('state', 'in', ('draft', 'sent'))]"/>
|
||||
<filter name="expired" string="Expired"
|
||||
domain="[('validity_date', '<', context_today()), ('state', 'in', ('draft', 'sent'))]"/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_partner"
|
||||
context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Status" name="group_state"
|
||||
context="{'group_by': 'state'}"/>
|
||||
<filter string="Follow-Up Owner" name="group_followup"
|
||||
context="{'group_by': 'x_fc_follow_up_user_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Search view for Fusion Plating SO list ===== -->
|
||||
<record id="view_sale_order_search_fp" model="ir.ui.view">
|
||||
<field name="name">sale.order.search.fp</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Sales Orders">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="x_fc_po_number" string="Customer PO #"/>
|
||||
<field name="x_fc_customer_job_number" string="Customer Job #"/>
|
||||
<filter name="my_orders" string="My Orders"
|
||||
domain="[('user_id', '=', uid)]"/>
|
||||
<separator/>
|
||||
<filter name="open_orders" string="Open"
|
||||
domain="[('state', 'in', ('draft', 'sent', 'sale'))]"/>
|
||||
<filter name="confirmed" string="Confirmed"
|
||||
domain="[('state', '=', 'sale')]"/>
|
||||
<filter name="done" string="Done"
|
||||
domain="[('state', '=', 'done')]"/>
|
||||
<separator/>
|
||||
<filter name="blanket_orders" string="Blanket Orders"
|
||||
domain="[('x_fc_is_blanket_order', '=', True)]"/>
|
||||
<filter name="late_forecast" string="Will Be Late"
|
||||
domain="[('x_fc_is_late_forecast', '=', True)]"/>
|
||||
<filter name="rush_lines" string="Has Rush Line"
|
||||
domain="[('order_line.x_fc_rush_order', '=', True)]"/>
|
||||
<filter name="overdue" string="Overdue"
|
||||
domain="[('commitment_date', '<', context_today()), ('state', 'in', ('sale',))]"/>
|
||||
<group>
|
||||
<filter string="Customer" name="group_partner"
|
||||
context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Status" name="group_state"
|
||||
context="{'group_by': 'state'}"/>
|
||||
<filter string="Customer Deadline" name="group_deadline"
|
||||
context="{'group_by': 'commitment_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Window Action - Quotations (for Fusion Plating menu) ===== -->
|
||||
<record id="action_fp_quotations" model="ir.actions.act_window">
|
||||
<field name="name">Quotations</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">list,form,kanban</field>
|
||||
<field name="domain">[('state', 'in', ('draft', 'sent'))]</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('view_sale_order_list_fp_quotes')})]"/>
|
||||
<field name="search_view_id" ref="view_sale_order_search_fp_quotes"/>
|
||||
<field name="context">{'default_x_fc_delivery_method': 'shipping_partner'}</field>
|
||||
<field name="x_fc_pickable_landing" eval="True"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new quotation
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== Window Action - Confirmed Sale Orders =====
|
||||
The kanban view_mode + kanban view_id are appended in
|
||||
fp_so_job_sort_views.xml after the kanban view is defined, so
|
||||
we don't hit a missing-ref at module load. -->
|
||||
<record id="action_fp_sale_orders" model="ir.actions.act_window">
|
||||
<field name="name">Sale Orders</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">list,form,kanban</field>
|
||||
<field name="domain">[('state', 'in', ('sale', 'done'))]</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('view_sale_order_list_fp')})]"/>
|
||||
<field name="x_fc_pickable_landing" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user