feat(configurator): Express form layout rebuild — match mockup
Restructures the Express form to align with the brainstorming mockup: Header (4-column grid via <group col='4'>): - Row 1: Customer (colspan=2) + Shipping Address (colspan=2) - Row 2: Consolidated PO Block (colspan=2 with PO#/PDF/Pending toggle/ Expected date stacked + chase warning inline) + Customer Job # + Job Sorting - Row 3: Material/Process Tag + Lead Time (X to Y inline) + Payment Terms + Delivery Method - Row 4: Blanket SO + Currency/Pricelist + Quote Validity + Invoice Strategy Lines: 13 inline columns including the Express-specific Line Job #, masking toggle, bake text, plus per-line action buttons (DWG, OPEN, + bulk) wired to the Phase B helpers. Footer: side-by-side cards — Notes + Terms stacked in the left card, Totals card on the right with Total Lines / Total Qty / Grand Total + currency pill. SCSS adds: - PO block: accent-bordered card-within-card - Lines: tight spreadsheet borders, hover row highlight - Bake column: amber pill style, italic 'no bake' for empty - Customer Line Job #: bold, uppercase, narrow column - Inline action buttons: small uppercase bordered chips - Footer cards with prominent Grand Total + currency pill OWL multi-row Part cell (FpExpressPartCell) and click-to-edit Bake pill (FpExpressBakePill) are still deferred — they need real OWL components, separate pass.
This commit is contained in:
@@ -1,19 +1,58 @@
|
||||
// Express Orders — base styles (C3 v1 / 2026-05-26)
|
||||
// Express Orders — main stylesheet (v1.5 / 2026-05-26)
|
||||
//
|
||||
// Tokens load FIRST via the manifest's web.assets_backend ordering;
|
||||
// $xpr-* are CSS custom-property wrappers from _express_tokens.scss.
|
||||
//
|
||||
// Goals (matching the .claude/mockups/express_orders.html mockup):
|
||||
// - Header reads as a single 4-col grid (not stacked groups)
|
||||
// - PO block reads as a single consolidated card-within-card
|
||||
// - Lines feel like a spreadsheet (tight borders, bordered cells)
|
||||
// - Bake column reads as a coloured pill, "no bake" reads italic muted
|
||||
// - Customer Line Job # reads bold + uppercase
|
||||
// - Footer is side-by-side Notes/Terms left + Totals right
|
||||
// - Grand Total shows a prominent currency pill
|
||||
|
||||
.o_fp_express_order {
|
||||
|
||||
// ---- Header banners — slightly tighter than stock alerts ----
|
||||
.alert {
|
||||
margin-bottom: 8px;
|
||||
// ============================================================
|
||||
// Header — 4-col grid (.o_fp_express_header)
|
||||
// ============================================================
|
||||
.o_fp_express_header {
|
||||
// Group renders as a table by default. Tighten spacing and let
|
||||
// the colspan-2 spans on customer + shipping breathe.
|
||||
td.o_td_label > label { font-weight: 600; font-size: 12px; }
|
||||
}
|
||||
|
||||
// ---- Table — spreadsheet feel ----
|
||||
.o_list_view {
|
||||
// ============================================================
|
||||
// PO Block — consolidated inside the header (.o_fp_po_block)
|
||||
// ============================================================
|
||||
.o_fp_po_block {
|
||||
background: $xpr-accent-bg;
|
||||
border: 1px solid $xpr-accent;
|
||||
border-left: 4px solid $xpr-accent;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0;
|
||||
|
||||
.o_horizontal_separator {
|
||||
color: $xpr-accent;
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
border-top: none;
|
||||
padding-top: 2px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Lines list — spreadsheet feel (.o_fp_express_lines)
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid $xpr-border-table;
|
||||
}
|
||||
thead th {
|
||||
background: $xpr-table-head;
|
||||
@@ -22,31 +61,31 @@
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid $xpr-border-table;
|
||||
border-bottom: 2px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 6px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
tbody td {
|
||||
border-bottom: 1px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
tbody tr:hover {
|
||||
background: $xpr-row-hover;
|
||||
}
|
||||
tbody tr.o_data_row:focus-within {
|
||||
background: $xpr-cell-focus;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Mask toggle — make it pop a bit more in the list ----
|
||||
.o_list_view .o_field_boolean_toggle {
|
||||
// Slightly larger so it reads at a glance in the column
|
||||
.form-check-input { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
// ---- Bake column (text input) — give the cell an inset pill feel ----
|
||||
// (Until C5 OWL widget lands, this is the simplest visual nudge.)
|
||||
.o_list_view td[name="bake_instructions"] input[type="text"] {
|
||||
// ============================================================
|
||||
// Bake column — coloured pill (text-input visual)
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="bake_instructions"] input[type="text"] {
|
||||
background: $xpr-bake-bg;
|
||||
color: $xpr-bake-text;
|
||||
border: 1px solid $xpr-bake-border;
|
||||
@@ -54,27 +93,142 @@
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
min-width: 100px;
|
||||
|
||||
&::placeholder {
|
||||
color: $xpr-text-dim;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
background: transparent;
|
||||
}
|
||||
&:focus {
|
||||
background: $xpr-cell-focus;
|
||||
border-color: $xpr-accent;
|
||||
outline: none;
|
||||
}
|
||||
// Empty cell → render as muted italic "no bake"
|
||||
&:not(:focus):placeholder-shown {
|
||||
background: $xpr-card-soft;
|
||||
border-color: $xpr-border-strong;
|
||||
color: $xpr-text-muted;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Customer Line Job # — narrow, bold, accent colour ----
|
||||
.o_list_view td[name="customer_line_ref"] input {
|
||||
// ============================================================
|
||||
// Customer Line Job # — bold, uppercase, narrow
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="customer_line_ref"] input {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
// ---- Bullet section markers ----
|
||||
.o_group > .o_horizontal_separator {
|
||||
// ============================================================
|
||||
// Masking toggle — bigger so it reads at a glance
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="masking_enabled"] {
|
||||
.form-check-input,
|
||||
.o_field_boolean_toggle .form-check-input { transform: scale(1.15); }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Inline action buttons (DWG / OPEN / + bulk)
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_fp_inline_btn {
|
||||
font-size: 10px !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
padding: 1px 6px !important;
|
||||
border: 1px solid $xpr-border-strong;
|
||||
border-radius: 3px;
|
||||
color: $xpr-text-muted !important;
|
||||
background: transparent;
|
||||
margin: 0 2px;
|
||||
|
||||
&:hover {
|
||||
color: $xpr-accent !important;
|
||||
border-color: $xpr-accent;
|
||||
background: $xpr-accent-bg;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Footer — Notes / Terms (left card) + Totals (right card)
|
||||
// ============================================================
|
||||
.o_fp_express_footer {
|
||||
|
||||
> .o_group { gap: 16px; }
|
||||
|
||||
.o_fp_footer_left,
|
||||
.o_fp_footer_totals {
|
||||
background: $xpr-card;
|
||||
border: 1px solid $xpr-border;
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.o_fp_footer_left {
|
||||
.o_horizontal_separator {
|
||||
color: $xpr-text-muted;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
font-weight: 700;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.o_horizontal_separator:first-child { margin-top: 0; }
|
||||
textarea { min-height: 80px; }
|
||||
}
|
||||
|
||||
.o_fp_footer_totals {
|
||||
.o_horizontal_separator {
|
||||
color: $xpr-text-muted;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Grand Total — bigger + currency pill
|
||||
// ============================================================
|
||||
.o_fp_grand_total_label {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: $xpr-text;
|
||||
padding-top: 6px;
|
||||
border-top: 2px solid $xpr-text;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.o_fp_grand_total {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: $xpr-text;
|
||||
padding-top: 6px;
|
||||
border-top: 2px solid $xpr-text;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.o_fp_currency_pill {
|
||||
background: $xpr-accent-bg;
|
||||
color: $xpr-accent;
|
||||
padding: 2px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Section separators inside the sheet
|
||||
// ============================================================
|
||||
> .o_form_sheet .o_horizontal_separator {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: $xpr-accent;
|
||||
@@ -84,10 +238,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ---- view_source badge column on drafts list ----
|
||||
// The Drafts list view shows a small EXPRESS/LEGACY chip. Stock Odoo
|
||||
// badge widget styling is fine; this just bumps the Express variant
|
||||
// a touch more visible via the accent colour.
|
||||
// ============================================================
|
||||
// view_source badge column — Express vs Legacy in drafts list
|
||||
// ============================================================
|
||||
.o_list_view .badge.text-bg-info {
|
||||
background-color: $xpr-accent !important;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,21 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ============================================================
|
||||
Express Orders form view (2026-05-26 — Phase C v1)
|
||||
Express Orders form view (2026-05-26 — Phase C v1.5 rebuild)
|
||||
|
||||
Spreadsheet-style flat entry for repeat customers. Reuses the
|
||||
existing fp.direct.order.wizard model end-to-end (Q1=D from the
|
||||
design spec). This view is shipped alongside the legacy form;
|
||||
both write to the same DB rows.
|
||||
Spreadsheet-style flat entry — header as a 4-column grid,
|
||||
PO block as a consolidated multi-row mini-card, lines with
|
||||
inline DWG/OPEN/+bulk action buttons, footer as side-by-side
|
||||
Notes/Terms cards + Totals card with the currency pill.
|
||||
|
||||
v1 deferments (will be added in later iterations):
|
||||
- OWL widget for multi-row Part cell (FpExpressPartCell)
|
||||
- OWL widget for click-to-edit Bake pill (FpExpressBakePill)
|
||||
- Inline DWG / OPEN / + bulk buttons per line
|
||||
- Custom SCSS (uses stock Odoo styles)
|
||||
Matches docs/superpowers/2026-05-25-express-orders-brainstorm-handoff
|
||||
+ .claude/mockups/express_orders.html as closely as Odoo's
|
||||
form-view constraints allow.
|
||||
|
||||
Still deferred (separate OWL widget pass):
|
||||
- Multi-row Part cell (FpExpressPartCell — part# stacked
|
||||
over description over serial list + bulk button)
|
||||
- Click-to-edit Bake pill (FpExpressBakePill)
|
||||
============================================================ -->
|
||||
|
||||
<record id="view_fp_express_order_form" model="ir.ui.view">
|
||||
@@ -43,13 +46,6 @@
|
||||
statusbar_visible="draft,confirmed"/>
|
||||
</header>
|
||||
|
||||
<div class="alert alert-info py-2 mb-0 small"
|
||||
role="alert"
|
||||
invisible="state != 'draft'">
|
||||
<i class="fa fa-bolt me-1"/>
|
||||
<strong>Express Orders.</strong> Spreadsheet-style flat entry — every column on the line, every field inline.
|
||||
Drafts auto-save. Switch to the Legacy view any time via the header button.
|
||||
</div>
|
||||
<div class="alert alert-warning mb-0"
|
||||
role="alert"
|
||||
invisible="not missing_info_msg">
|
||||
@@ -70,174 +66,209 @@
|
||||
|
||||
<div class="oe_title">
|
||||
<label for="name" class="o_form_label"/>
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
<h1 class="d-flex align-items-center gap-2">
|
||||
<field name="name" readonly="1"/>
|
||||
<span class="badge text-bg-info" style="font-size: 10px;">EXPRESS</span>
|
||||
</h1>
|
||||
<field name="user_id" readonly="state != 'draft'"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="view_source" invisible="1"/>
|
||||
</div>
|
||||
|
||||
<!-- ====== Row 1 — Customer + Shipping (both prominent) ====== -->
|
||||
<group>
|
||||
<group string="Customer">
|
||||
<field name="partner_id" options="{'no_create_edit': True}"/>
|
||||
<field name="partner_invoice_id"
|
||||
options="{'no_create_edit': True}"
|
||||
invisible="not partner_id"/>
|
||||
<field name="partner_shipping_id"
|
||||
options="{'no_create_edit': False}"
|
||||
invisible="not partner_id"/>
|
||||
<field name="customer_job_number"/>
|
||||
<field name="job_sort_id"
|
||||
options="{'no_create_edit': False, 'no_open': True}"
|
||||
placeholder="Type to create a new bucket..."/>
|
||||
<field name="material_process"
|
||||
placeholder="e.g. ENP-STEEL-HP-ADVANCED"/>
|
||||
</group>
|
||||
<!-- =========================================================
|
||||
ROW 1 — Customer (span 2) + Shipping (span 2) — 4-col grid
|
||||
========================================================= -->
|
||||
<group col="4" class="o_fp_express_header">
|
||||
<field name="partner_id"
|
||||
colspan="2"
|
||||
options="{'no_create_edit': True}"/>
|
||||
<field name="partner_shipping_id"
|
||||
colspan="2"
|
||||
options="{'no_create_edit': False}"
|
||||
invisible="not partner_id"/>
|
||||
|
||||
<!-- ====== PO Block ====== -->
|
||||
<group string="Purchase Order">
|
||||
<!-- ============================================
|
||||
ROW 2 — PO Block (span 2) + Job# + Job Sorting
|
||||
============================================ -->
|
||||
<group colspan="2" class="o_fp_po_block">
|
||||
<separator string="Customer PO" colspan="2"/>
|
||||
<field name="po_number"
|
||||
placeholder="Enter the customer PO number"/>
|
||||
<field name="po_attachment_file"
|
||||
filename="po_attachment_filename"/>
|
||||
filename="po_attachment_filename"
|
||||
string="PO Document (PDF)"/>
|
||||
<field name="po_attachment_filename" invisible="1"/>
|
||||
<field name="po_pending" widget="boolean_toggle"/>
|
||||
<field name="po_pending" widget="boolean_toggle"
|
||||
string="PO Pending"/>
|
||||
<field name="po_expected_date"
|
||||
string="PO Expected By"
|
||||
invisible="not po_pending"/>
|
||||
<div colspan="2" class="text-muted small"
|
||||
<div colspan="2" class="alert alert-warning py-1 my-1 small"
|
||||
role="alert"
|
||||
invisible="not po_pending">
|
||||
<i class="fa fa-info-circle me-1"/>
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
Order will confirm without a PO. A chase activity
|
||||
will be scheduled for the expected date so sales
|
||||
follows up.
|
||||
will be scheduled for the expected date.
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field name="customer_job_number"
|
||||
string="Customer Job #"/>
|
||||
<field name="job_sort_id"
|
||||
string="Job Sorting"
|
||||
options="{'no_create_edit': False, 'no_open': True}"
|
||||
placeholder="Type to create a new bucket..."/>
|
||||
|
||||
<!-- ====== Row 2 — Scheduling + Pricing ====== -->
|
||||
<group>
|
||||
<group string="Scheduling & Lead Time">
|
||||
<label for="lead_time_min_days" string="Lead Time (days)"/>
|
||||
<div class="o_row">
|
||||
<field name="lead_time_min_days" class="oe_inline" style="width: 4em;"/>
|
||||
<span> to </span>
|
||||
<field name="lead_time_max_days" class="oe_inline" style="width: 4em;"/>
|
||||
</div>
|
||||
<field name="planned_start_date"/>
|
||||
<field name="customer_deadline" string="Delivery Date"/>
|
||||
<field name="validity_date" string="Quote Validity"/>
|
||||
<field name="is_blanket_order"/>
|
||||
</group>
|
||||
<group string="Pricing & Fulfilment">
|
||||
<field name="pricelist_id"
|
||||
string="Currency / Pricelist"
|
||||
context="{'fp_express_currency_picker': True}"
|
||||
options="{'no_create_edit': True}"/>
|
||||
<field name="payment_term_id"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="delivery_method"/>
|
||||
<field name="invoice_strategy"/>
|
||||
<label for="deposit_percent"
|
||||
invisible="invoice_strategy != 'deposit'"/>
|
||||
<div class="o_row"
|
||||
invisible="invoice_strategy != 'deposit'">
|
||||
<field name="deposit_percent" nolabel="1" class="oe_inline"/>
|
||||
<span class="ms-1">%</span>
|
||||
</div>
|
||||
</group>
|
||||
<!-- ============================================
|
||||
ROW 3 — Material + Lead Time + Payment + Delivery
|
||||
============================================ -->
|
||||
<field name="material_process"
|
||||
string="Material / Process Tag"
|
||||
placeholder="e.g. ENP-STEEL-HP-ADVANCED"/>
|
||||
<label for="lead_time_min_days" string="Lead Time (days)"/>
|
||||
<div class="o_row">
|
||||
<field name="lead_time_min_days" class="oe_inline" style="width: 3em;"/>
|
||||
<span> to </span>
|
||||
<field name="lead_time_max_days" class="oe_inline" style="width: 3em;"/>
|
||||
</div>
|
||||
<field name="payment_term_id"
|
||||
string="Payment Terms"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="delivery_method"
|
||||
string="Delivery Method"/>
|
||||
|
||||
<!-- ============================================
|
||||
ROW 4 — Blanket + Currency + Quote Valid + Invoice
|
||||
============================================ -->
|
||||
<field name="is_blanket_order"
|
||||
string="Blanket SO"
|
||||
widget="boolean_toggle"/>
|
||||
<field name="pricelist_id"
|
||||
string="Currency / Pricelist"
|
||||
context="{'fp_express_currency_picker': True}"
|
||||
options="{'no_create_edit': True}"/>
|
||||
<field name="validity_date"
|
||||
string="Quote Validity"/>
|
||||
<field name="invoice_strategy"
|
||||
string="Invoice Strategy"/>
|
||||
</group>
|
||||
|
||||
<!-- ====== Order Lines — the spreadsheet ====== -->
|
||||
<notebook>
|
||||
<page string="Lines" name="lines">
|
||||
<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">
|
||||
<list editable="bottom"
|
||||
decoration-warning="is_missing_info">
|
||||
<field name="is_missing_info" column_invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="part_catalog_id"
|
||||
string="Part Number"
|
||||
context="{'default_partner_id': parent.partner_id, 'default_revision': 'A'}"
|
||||
domain="[('partner_id', '=', parent.partner_id), ('is_latest_revision', '=', True)]"
|
||||
options="{'no_quick_create': True}"/>
|
||||
<field name="line_description"
|
||||
string="Specification (Customer-Facing)"/>
|
||||
<field name="customer_line_ref"
|
||||
string="Line Job #"
|
||||
placeholder="ABC"/>
|
||||
<field name="thickness_range"
|
||||
placeholder="e.g. .0005-.0010"/>
|
||||
<field name="masking_enabled"
|
||||
string="Mask"
|
||||
widget="boolean_toggle"/>
|
||||
<field name="bake_instructions"
|
||||
string="Bake"
|
||||
placeholder="empty = no bake"/>
|
||||
<field name="internal_description"
|
||||
string="Internal Notes"
|
||||
optional="show"/>
|
||||
<field name="serial_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_quick_create': False, 'color_field': 'state_color'}"
|
||||
domain="[('part_id', '=', part_catalog_id)]"
|
||||
optional="show"/>
|
||||
<field name="quantity"/>
|
||||
<field name="unit_price"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="line_subtotal"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
sum="Total"/>
|
||||
<field name="process_variant_id"
|
||||
string="Process / Recipe"
|
||||
options="{'no_quick_create': True}"
|
||||
invisible="not part_catalog_id"
|
||||
optional="hide"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
<group class="mt-3">
|
||||
<group>
|
||||
<field name="total_line_count" readonly="1"/>
|
||||
<field name="total_qty" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<separator string="Order Lines"/>
|
||||
<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_express_lines">
|
||||
<list editable="bottom"
|
||||
decoration-warning="is_missing_info">
|
||||
<field name="is_missing_info" column_invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="part_catalog_id"
|
||||
string="Part Number"
|
||||
context="{'default_partner_id': parent.partner_id, 'default_revision': 'A'}"
|
||||
domain="[('partner_id', '=', parent.partner_id), ('is_latest_revision', '=', True)]"
|
||||
options="{'no_quick_create': True}"/>
|
||||
<field name="line_description"
|
||||
string="Specification"/>
|
||||
<field name="customer_line_ref"
|
||||
string="Line Job #"
|
||||
placeholder="ABC"/>
|
||||
<field name="thickness_range"
|
||||
string="Thickness"
|
||||
placeholder=".0005-.0010"/>
|
||||
<field name="masking_enabled"
|
||||
string="Mask"
|
||||
widget="boolean_toggle"/>
|
||||
<field name="bake_instructions"
|
||||
string="Bake"
|
||||
placeholder="no bake"/>
|
||||
<field name="internal_description"
|
||||
string="Internal Notes"
|
||||
optional="show"/>
|
||||
<field name="serial_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_quick_create': False, 'color_field': 'state_color'}"
|
||||
domain="[('part_id', '=', part_catalog_id)]"
|
||||
optional="show"/>
|
||||
<button name="action_open_serial_bulk_add" type="object"
|
||||
string="+ bulk"
|
||||
class="btn-link btn-sm o_fp_inline_btn"
|
||||
title="Bulk-add serial numbers (paste list or range fill)"
|
||||
invisible="not part_catalog_id"/>
|
||||
<field name="quantity"/>
|
||||
<field name="unit_price"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="line_subtotal"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
sum="Total"/>
|
||||
<button name="action_upload_drawing" type="object"
|
||||
string="DWG"
|
||||
class="btn-link btn-sm o_fp_inline_btn"
|
||||
title="Upload a drawing for this part (saves to the part record)"
|
||||
invisible="not part_catalog_id"/>
|
||||
<button name="action_open_part" type="object"
|
||||
string="OPEN"
|
||||
class="btn-link btn-sm o_fp_inline_btn"
|
||||
title="Open the part record in a modal"
|
||||
invisible="not part_catalog_id"/>
|
||||
<field name="process_variant_id"
|
||||
string="Process / Recipe"
|
||||
options="{'no_quick_create': True}"
|
||||
invisible="not part_catalog_id"
|
||||
optional="hide"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
|
||||
<!-- ====== Footer — Notes/Terms (left card) + Totals (right card) ====== -->
|
||||
<div class="o_fp_express_footer mt-3">
|
||||
<group col="2">
|
||||
<!-- LEFT — Notes + Terms stacked -->
|
||||
<group class="o_fp_footer_left">
|
||||
<separator string="Order-Level Internal Notes"/>
|
||||
<field name="internal_notes" nolabel="1"
|
||||
placeholder="Visible only to estimator / planner / shop. Never prints."/>
|
||||
<separator string="Terms & Conditions"/>
|
||||
<field name="terms_and_conditions" nolabel="1"
|
||||
placeholder="Customer-facing terms — prints on quote / SO / invoice."/>
|
||||
</group>
|
||||
|
||||
<!-- RIGHT — Totals card with currency pill -->
|
||||
<group class="o_fp_footer_totals">
|
||||
<separator string="Totals"/>
|
||||
<field name="total_line_count"
|
||||
string="Total Lines"
|
||||
readonly="1"/>
|
||||
<field name="total_qty"
|
||||
string="Total Quantity"
|
||||
readonly="1"/>
|
||||
<label for="total_amount" string="Grand Total"
|
||||
class="o_fp_grand_total_label"/>
|
||||
<div class="d-flex align-items-center gap-2 o_fp_grand_total">
|
||||
<field name="total_amount"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
readonly="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
readonly="1"
|
||||
nolabel="1"
|
||||
class="oe_inline"/>
|
||||
<field name="currency_id"
|
||||
readonly="1"
|
||||
nolabel="1"
|
||||
class="o_fp_currency_pill"/>
|
||||
</div>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
<!-- ====== Notes + Terms ====== -->
|
||||
<page string="Notes & Terms" name="notes">
|
||||
<group>
|
||||
<group string="Order-Level Internal Notes (never prints)">
|
||||
<field name="internal_notes" nolabel="1"
|
||||
placeholder="Visible only to estimator / planner / shop."/>
|
||||
</group>
|
||||
<group string="Terms & Conditions (prints on customer docs)">
|
||||
<field name="terms_and_conditions" nolabel="1"
|
||||
placeholder="Customer-facing terms — seeded from company default."/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</group>
|
||||
</div>
|
||||
|
||||
</sheet>
|
||||
<chatter/>
|
||||
|
||||
Reference in New Issue
Block a user