chore(plating): de-dash shipped code + intake-neutral customer emails

Replace em-dashes and en-dashes with hyphens across 789 shipped source
files (py/xml/js/scss) so the delivered module reads as human-written;
em-dashes had become a recognizable AI-generated tell. Internal .md dev
notes are excluded. The WO-sticker mojibake strippers keep their dash
search targets (now written — / –). No logic changes: comments
and display strings only; validated with py_compile + lxml parse.

Rewrite the 7 customer notification emails to be intake-neutral
(ship-in / drop-off / pickup) and repair-aware, and fix the Shipped
email documents line (packing slip vs bill of lading; certificate only
when issued). Subjects use a hyphen separator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-05 00:16:19 -04:00
parent c9eb61ee0c
commit 8c76a16366
789 changed files with 4692 additions and 4692 deletions

View File

@@ -1,10 +1,10 @@
/** @odoo-module **/
// Express Orders stacked DWG / OPEN action buttons widget (2026-05-26)
// Express Orders - stacked DWG / OPEN action buttons widget (2026-05-26)
//
// Renders BOTH the upload-drawing and open-part buttons stacked
// vertically in one list cell, saving horizontal width. The widget
// binds to part_catalog_id (read-only picker is owned by the
// binds to part_catalog_id (read-only - picker is owned by the
// FpExpressPartCell widget on the same field; this widget is
// declared on a separate dummy column).

View File

@@ -1,6 +1,6 @@
/** @odoo-module **/
// Express Orders Bake pill widget (2026-05-26)
// Express Orders - Bake pill widget (2026-05-26)
//
// Renders the `bake_instructions` Text field as a coloured pill:
// - Non-empty → amber pill showing the text ("350°F × 4 hr")

View File

@@ -1,13 +1,13 @@
/** @odoo-module **/
// Express Orders multi-row Part cell widget (2026-05-26, revised 2026-05-27)
// Express Orders - multi-row Part cell widget (2026-05-26, revised 2026-05-27)
//
// Row 1: Many2OneField picker (shows part_catalog_id.display_name
// Row 1: Many2OneField picker (shows part_catalog_id.display_name -
// which is just the part_number when fp_express_part_picker
// context flag is set).
// Row 2: editable input bound to part_name_editable (writable compute
// with inverse that writes part.name on the linked catalog
// record see fp_direct_order_line.py).
// record - see fp_direct_order_line.py).
// Row 3: editable input bound to serials_text (parses comma-separated
// names, finds-or-creates fp.serial records, updates the line's
// serial_ids M2M) + small "+ bulk" button that opens the existing

View File

@@ -101,7 +101,7 @@ export class Fp3dViewerDialog extends Component {
registry.category("dialog").add("Fp3dViewerDialog", Fp3dViewerDialog);
// Client action handler opens the 3D viewer in a dialog within the same window.
// Client action handler - opens the 3D viewer in a dialog within the same window.
// Triggered by Python returning:
// { type: 'ir.actions.client', tag: 'fp_3d_viewer_open',
// params: { attachment_id: N, name: "..." } }

View File

@@ -1,10 +1,10 @@
/** @odoo-module **/
// =============================================================================
// Fusion Plating Part-Scoped Process Composer (OWL client action)
// Fusion Plating - Part-Scoped Process Composer (OWL client action)
// Copyright 2026 Nexa Systems Inc.
// License OPL-1 (Odoo Proprietary License v1.0)
//
// Sub 9 multi-variant Composer. Each part can carry several recipe trees
// Sub 9 - multi-variant Composer. Each part can carry several recipe trees
// (e.g. "Standard ENP", "Selective Masking", "Rework"). One is the default;
// estimators may pick a non-default variant on a per-order basis.
//
@@ -188,20 +188,20 @@ export class FpPartProcessComposer extends Component {
}
openRecipeEditor(rootId) {
// Tree editor the original drag-and-drop hierarchy view.
// Tree editor - the original drag-and-drop hierarchy view.
const id = rootId || this.state.rootId;
if (!id) return;
this.action.doAction({
type: "ir.actions.client",
tag: "fp_recipe_tree_editor",
name: `Process Editor ${(this.state.part && this.state.part.display) || ""}`,
name: `Process Editor - ${(this.state.part && this.state.part.display) || ""}`,
context: { recipe_id: id, part_id: this.partId },
target: "current",
});
}
openRecipeSimpleEditor(rootId) {
// Simple Recipe Editor (Sub 12a) flat 2-pane drag-drop layout.
// Simple Recipe Editor (Sub 12a) - flat 2-pane drag-drop layout.
// Lives alongside the tree editor; the user picks per-variant
// which one to open. Both edit the same underlying tree, so
// changes flow back-and-forth without conflict.
@@ -210,7 +210,7 @@ export class FpPartProcessComposer extends Component {
this.action.doAction({
type: "ir.actions.client",
tag: "fp_simple_recipe_editor",
name: `Process Editor (Simple) ${(this.state.part && this.state.part.display) || ""}`,
name: `Process Editor (Simple) - ${(this.state.part && this.state.part.display) || ""}`,
context: { recipe_id: id, part_id: this.partId },
target: "current",
});
@@ -219,7 +219,7 @@ export class FpPartProcessComposer extends Component {
backToPart() {
// Pop this composer off the action stack and restore the
// previous controller (the part form the user came from).
// Preserves the full breadcrumb trail clearBreadcrumbs: true
// Preserves the full breadcrumb trail - clearBreadcrumbs: true
// would wipe parent crumbs (e.g. "Parts > 2144A6201-105").
// Falls back to the part form only when restore() throws (e.g.
// composer opened directly via URL with no prior crumb).
@@ -227,7 +227,7 @@ export class FpPartProcessComposer extends Component {
this.action.restore();
return;
} catch (e) {
// No prior controller fall through to the part form.
// No prior controller - fall through to the part form.
}
this.action.doAction({
type: "ir.actions.act_window",

View File

@@ -1,4 +1,4 @@
// Express Orders colour tokens (C3 / 2026-05-26)
// Express Orders - colour tokens (C3 / 2026-05-26)
//
// Per the Odoo-Modules CLAUDE.md "Dark Mode" rule: branch on
// $o-webclient-color-scheme at SCSS compile time. Odoo compiles this
@@ -6,7 +6,7 @@
// (dark); the @if below makes each bundle pick the right hex.
//
// Tokens are wrapped in CSS custom properties so a downstream module
// can override per-shop branding without recompiling e.g.
// can override per-shop branding without recompiling - e.g.
// --xpr-accent: #d4af37; /* gold for premium plating shops */
// on a global :root rule.

View File

@@ -1,4 +1,4 @@
// Express Orders styles for the CSS Grid rebuild
// Express Orders - styles for the CSS Grid rebuild
// (matches .claude/mockups/express_orders.html line-for-line where Odoo allows)
.o_fp_xpr {
@@ -18,17 +18,17 @@
}
// ============================================================
// HEADER GRID 4-column CSS Grid (mirrors mockup .header-grid)
// HEADER GRID - 4-column CSS Grid (mirrors mockup .header-grid)
// ============================================================
.o_fp_xpr_grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 6px 20px; // tighter row gap (was 14px) denser vertical packing
gap: 6px 20px; // tighter row gap (was 14px) - denser vertical packing
align-items: start;
margin: 12px 0;
}
// Each grid cell label on top, field below
// Each grid cell - label on top, field below
.o_fp_xpr_cell {
display: flex;
flex-direction: column;
@@ -47,7 +47,7 @@
cursor: pointer;
}
// The actual field input kill Odoo's default block-level chrome.
// The actual field input - kill Odoo's default block-level chrome.
// Aggressive width 100% on all known wrappers so the click target
// matches the cell's visible width.
> .o_field_widget,
@@ -68,7 +68,7 @@
display: block;
}
// Field input visual underline style like the mockup.
// Field input visual - underline style like the mockup.
// EXCLUDES checkboxes / radios / file inputs (they have their own
// visual treatment and would disappear under this style).
.o_input,
@@ -96,7 +96,7 @@
}
.o_field_widget select { cursor: pointer; }
// Native (non-switch) checkboxes inside Express cells keep
// Native (non-switch) checkboxes inside Express cells - keep
// them visible at a comfortable size. EXCLUDES the boolean_toggle
// widget (.o_field_boolean_toggle) which uses Bootstrap's
// .form-switch slider styling that breaks if we set a fixed
@@ -110,7 +110,7 @@
accent-color: $xpr-accent;
}
// Boolean toggle widget preserve Bootstrap's slider proportions
// Boolean toggle widget - preserve Bootstrap's slider proportions
// (2em × 1em). Just centre vertically with the row height.
.o_field_boolean_toggle {
padding: 5px 0;
@@ -118,7 +118,7 @@
display: inline-flex;
align-items: center;
// Bootstrap form-switch styling accent the slider
// Bootstrap form-switch styling - accent the slider
.form-check-input {
width: 2em !important;
height: 1.2em !important;
@@ -168,7 +168,7 @@
.o_fp_xpr_cell.row-span-4 { grid-row: span 4; }
.o_fp_xpr_cell.row-span-6 { grid-row: span 6; }
// Cells with a toggle-style field label sits inline with the
// Cells with a toggle-style field - label sits inline with the
// toggle instead of stacked above. Used by Blanket Sales Order
// (matches the mockup's compact toggle row). Toggle anchored
// next to the label so layout doesn't shift when the secondary
@@ -196,7 +196,7 @@
color: $xpr-bad;
}
// Lead Time range inline X to Y
// Lead Time range - inline X to Y
.o_fp_xpr_range {
display: flex;
align-items: baseline;
@@ -213,7 +213,7 @@
}
// ============================================================
// PO BLOCK consolidated card-within-card
// PO BLOCK - consolidated card-within-card
// ============================================================
.o_fp_xpr_po_block {
background: $xpr-grad-surface;
@@ -303,7 +303,7 @@
}
// ============================================================
// SECTION TITLE between header and lines
// SECTION TITLE - between header and lines
// ============================================================
.o_fp_xpr_section_title {
font-size: 13px;
@@ -318,7 +318,7 @@
}
// ============================================================
// LINES TABLE tight bordered spreadsheet
// LINES TABLE - tight bordered spreadsheet
// ============================================================
.o_fp_xpr_lines .o_list_view table {
border-collapse: collapse;
@@ -359,7 +359,7 @@
.o_fp_xpr_lines .o_list_view tbody tr:hover { background: $xpr-row-hover; }
.o_fp_xpr_lines .o_list_view tbody tr.o_data_row:focus-within { background: $xpr-cell-focus; }
// Bake column coloured pill input
// Bake column - coloured pill input
.o_fp_xpr_lines td[name="bake_instructions"] input[type="text"] {
background: $xpr-bake-bg;
color: $xpr-bake-text;
@@ -381,14 +381,14 @@
font-style: italic;
}
}
// Line Job # bold uppercase narrow
// Line Job # - bold uppercase narrow
.o_fp_xpr_lines td[name="customer_line_ref"] input {
font-weight: 600;
letter-spacing: 0.5px;
text-transform: uppercase;
max-width: 70px;
}
// Masking toggle bigger
// Masking toggle - bigger
.o_fp_xpr_lines td[name="masking_enabled"] .form-check-input { transform: scale(1.15); }
// Inline buttons
.o_fp_xpr_lines .o_fp_xpr_inline_btn {
@@ -442,7 +442,7 @@
}
}
// MASK upload amber so order-entry notices the "attach reference"
// MASK upload - amber so order-entry notices the "attach reference"
// affordance the moment masking is toggled on. Solid amber works on
// both the light and dark backend bundles (dark text on amber fill).
.o_fp_xpr_mask_btn {
@@ -459,7 +459,7 @@
}
// ============================================================
// FOOTER Notes/Terms left + Totals right (CSS Grid)
// FOOTER - Notes/Terms left + Totals right (CSS Grid)
// ============================================================
.o_fp_xpr_footer {
display: grid;
@@ -527,7 +527,7 @@
}
}
// Totals card rendered as a clean bordered "summary table"
// Totals card - rendered as a clean bordered "summary table"
.o_fp_xpr_card.o_fp_xpr_totals {
padding: 0; // rows carry their own padding
overflow: hidden; // clip header/footer fills to the radius
@@ -548,13 +548,13 @@
.o_fp_xpr_total_row {
display: grid;
grid-template-columns: 1.2fr 1fr; // label | value fixed split keeps the divider aligned
grid-template-columns: 1.2fr 1fr; // label | value - fixed split keeps the divider aligned
align-items: stretch;
font-size: 13px;
color: $xpr-text;
border-bottom: 1px solid $xpr-border-table; // horizontal divider per row
// col 1 label (+ optional inline picker), carrying the
// col 1 - label (+ optional inline picker), carrying the
// vertical column divider on its right edge
.o_fp_xpr_total_label {
display: flex;
@@ -574,7 +574,7 @@
}
}
// col 2 value amount, top-aligned with the label text and
// col 2 - value amount, top-aligned with the label text and
// right-aligned so every amount lines up in one column
> :not(.o_fp_xpr_total_label) {
display: flex;
@@ -587,7 +587,7 @@
}
}
// Footer row emphasised + tinted, closes the table
// Footer row - emphasised + tinted, closes the table
.o_fp_xpr_total_row.o_fp_xpr_grand {
border-bottom: 0;
border-top: 2px solid $xpr-accent;
@@ -637,7 +637,7 @@
}
// ============================================================
// PO Block status pill (received / pending / missing)
// PO Block - status pill (received / pending / missing)
// ============================================================
.o_fp_xpr_po_block .o_fp_xpr_po_head {
display: flex;
@@ -659,7 +659,7 @@
}
// ============================================================
// PART CELL multi-row content (FpExpressPartCell widget)
// PART CELL - multi-row content (FpExpressPartCell widget)
// ============================================================
.o_fp_xpr_part_cell {
display: flex;
@@ -710,7 +710,7 @@
outline: none;
}
// OVERLAY shown when picker not focused; hides display_name
// OVERLAY - shown when picker not focused; hides display_name
// and shows just part_number_display
.o_fp_xpr_part_num_overlay {
position: absolute;
@@ -835,7 +835,7 @@
}
// ============================================================
// BAKE PILL click-to-edit (FpExpressBakePill widget)
// BAKE PILL - click-to-edit (FpExpressBakePill widget)
// ============================================================
.o_fp_xpr_bake_wrap {
display: inline-block;
@@ -898,7 +898,7 @@
}
}
// view_source badge column on drafts list accent-coloured for Express
// view_source badge column on drafts list - accent-coloured for Express
.o_list_view .badge.text-bg-info {
background-color: $xpr-accent !important;
}

View File

@@ -1,10 +1,10 @@
// =============================================================================
// Fusion Plating Job Status pill on the SO list
// Fusion Plating - Job Status pill on the SO list
// Copyright 2026 Nexa Systems Inc. · License OPL-1
//
// One pill per row, one colour per phase, vibrant + saturated so phases
// pop at a glance against both the light and dark Odoo bundles. Same
// hue map for both modes saturated 500-level Tailwind hues with white
// hue map for both modes - saturated 500-level Tailwind hues with white
// text give consistent contrast against either page background.
// =============================================================================
@@ -19,7 +19,7 @@ $_fp-invoiced-bg : #84cc16; // lime
$_fp-paid-bg : #16a34a; // green
$_fp-danger-bg : #ef4444; // red
// Matching glow shadows darker tone of the same hue for a subtle
// Matching glow shadows - darker tone of the same hue for a subtle
// drop-shadow that gives the pill a "lifted" feel without being noisy.
$_fp-muted-glow : rgba(31, 41, 55, 0.35);
$_fp-warning-glow : rgba(180, 83, 9, 0.45);
@@ -50,7 +50,7 @@ $_fp-danger-glow : rgba(185, 28, 28, 0.45);
}
// =============================================================================
// Per-kind tints same map applies to light + dark bundles. White text
// Per-kind tints - same map applies to light + dark bundles. White text
// gives consistent contrast against any saturated mid-tone hue.
// =============================================================================
.fp-kind-muted { background-color: $_fp-muted-bg; box-shadow: 0 1px 3px $_fp-muted-glow; }

View File

@@ -2,13 +2,13 @@
// License OPL-1 (Odoo Proprietary License v1.0)
// Part of the Fusion Plating product family.
//
// Sub 3 Process Composer styles.
// Sub 3 - Process Composer styles.
//
// Theme handling: Odoo 19 compiles this SCSS into BOTH web.assets_backend
// (bright) and web.assets_web_dark (dark). We branch at compile time on
// $o-webclient-color-scheme so the dark bundle gets distinct colours.
// Bootstrap CSS variables (--bs-body-bg etc.) don't flip reliably in
// Odoo 19's backend hardcoded hex via CSS custom properties is the
// Odoo 19's backend - hardcoded hex via CSS custom properties is the
// pattern documented in CLAUDE.md (shopfloor tokens).
$o-webclient-color-scheme: bright !default;
@@ -45,7 +45,7 @@ $fp-composer-muted: var(--fp-composer-muted, $_fp-composer-muted-hex);
margin: 0 auto;
color: $fp-composer-text;
// Variants table keep the 5 action buttons (Tree / Simple /
// Variants table - keep the 5 action buttons (Tree / Simple /
// Duplicate / Rename / Delete) on a single row. Without this the
// Delete button wraps even on wide screens because Bootstrap's
// `.table` lets cells shrink to content+wrap.
@@ -135,7 +135,7 @@ $fp-composer-muted: var(--fp-composer-muted, $_fp-composer-muted-hex);
}
}
// "Open Process Editor" button icon + label vertically centred,
// "Open Process Editor" button - icon + label vertically centred,
// icon forced to a dark tone for high contrast against the primary
// button's green fill (the default inherited colour was washed out).
&_editor_btn {
@@ -147,7 +147,7 @@ $fp-composer-muted: var(--fp-composer-muted, $_fp-composer-muted-hex);
line-height: 1;
.fa {
color: #ffffff; // white matches the button label for a clean read
color: #ffffff; // white - matches the button label for a clean read
font-size: 1.05em;
margin: 0; // override the _hint/_empty 16px bottom margin
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<!-- Express Orders stacked DWG / OPEN action buttons -->
<!-- Express Orders - stacked DWG / OPEN action buttons -->
<t t-name="fusion_plating_configurator.FpExpressActionBtns">
<div class="o_fp_xpr_action_stack">
<button class="o_fp_xpr_action_stack_btn"
@@ -20,7 +20,7 @@
class="o_fp_xpr_action_stack_btn o_fp_xpr_mask_btn"
t-on-click="onUploadMask"
t-att-disabled="!hasPart"
title="Attach masking reference image(s)/PDF(s) shown to the operator on the masking step">
title="Attach masking reference image(s)/PDF(s) - shown to the operator on the masking step">
MASK<t t-if="maskCount"> (<t t-esc="maskCount"/>)</t>
</button>
</div>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<!-- Express Orders Bake pill (click-to-edit) -->
<!-- Express Orders - Bake pill (click-to-edit) -->
<t t-name="fusion_plating_configurator.FpExpressBakePill">
<div class="o_fp_xpr_bake_wrap">
<t t-if="!state.editing">

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<!-- Express Orders Part cell template (3 stacked rows)
<!-- Express Orders - Part cell template (3 stacked rows)
Row 1: Part picker (Many2OneField display_name is just the
Row 1: Part picker (Many2OneField - display_name is just the
part_number when fp_express_part_picker context is set)
Row 2: editable part description (writes to part.name on blur)
Row 3: editable serial #s (parses comma-separated, creates fp.serial
@@ -11,7 +11,7 @@
-->
<t t-name="fusion_plating_configurator.FpExpressPartCell">
<div class="o_fp_xpr_part_cell">
<!-- Row 1 Part picker (left, with part-number overlay) + / + Revision (right)
<!-- Row 1 - Part picker (left, with part-number overlay) + / + Revision (right)
The overlay shows JUST the part_number_display when not focused;
on focus, the overlay hides so the user sees the autocomplete input. -->
<div class="o_fp_xpr_part_row o_fp_xpr_part_id">
@@ -26,25 +26,25 @@
t-att-class="{ 'o_fp_xpr_part_rev_empty': !partRev }"
t-esc="partRev or 'rev'"/>
</div>
<!-- Row 2 Editable part description (saves to part.name) -->
<!-- Row 2 - Editable part description (saves to part.name) -->
<div class="o_fp_xpr_part_row o_fp_xpr_part_name">
<input class="o_fp_xpr_part_name_input"
type="text"
t-att-value="partName"
t-att-disabled="!hasPart"
t-on-change="onNameChange"
placeholder=" part description "
title="Edit the part's name saves to the part record"/>
placeholder="- part description -"
title="Edit the part's name - saves to the part record"/>
</div>
<!-- Row 3 Editable serial list + bulk-add button -->
<!-- Row 3 - Editable serial list + bulk-add button -->
<div class="o_fp_xpr_part_row o_fp_xpr_part_serial">
<input class="o_fp_xpr_serial_input"
type="text"
t-att-value="serialsText"
t-att-disabled="!hasPart"
t-on-change="onSerialsChange"
placeholder="serial #(s) comma separated"
title="Type serials separated by commas creates fp.serial records as needed"/>
placeholder="serial #(s) - comma separated"
title="Type serials separated by commas - creates fp.serial records as needed"/>
<button class="o_fp_xpr_bulk_btn"
t-on-click="onBulkClick"
t-att-disabled="!hasPart"

View File

@@ -5,7 +5,7 @@
Part of the Fusion Plating product family.
OWL template for the part-scoped Process Composer client action.
Sub 9 multi-variant Composer.
Sub 9 - multi-variant Composer.
-->
<templates xml:space="preserve">
@@ -30,7 +30,7 @@
<span> Back to Part</span>
</button>
<div class="o_fp_part_composer_title">
<h2>Process Composer <t t-esc="state.part.display"/></h2>
<h2>Process Composer - <t t-esc="state.part.display"/></h2>
<small class="text-muted" t-if="state.part.customer">
Customer: <t t-esc="state.part.customer"/>
</small>
@@ -122,7 +122,7 @@
<label class="me-2">Template:</label>
<!-- Bumped min-width 280px → 360px and let it
flex-grow so long template names (e.g.
"Chemical Conversion Iridite Type II Cl 3")
"Chemical Conversion - Iridite Type II Cl 3")
don't truncate to "Chem…". Reported 2026-05-20. -->
<select class="form-select"
style="min-width: 360px; flex: 1 1 360px; max-width: 560px;"
@@ -143,13 +143,13 @@
t-on-click="() => this.onAddVariantFromTemplate('tree')"
t-att-disabled="state.busy or !state.selectedTemplateId"
title="Add the variant and open it in the Tree Editor">
<i class="fa fa-sitemap me-1"/> Add Tree
<i class="fa fa-sitemap me-1"/> Add - Tree
</button>
<button class="btn btn-primary"
t-on-click="() => this.onAddVariantFromTemplate('simple')"
t-att-disabled="state.busy or !state.selectedTemplateId"
title="Add the variant and open it in the Simple Editor">
<i class="fa fa-list me-1"/> Add Simple
<i class="fa fa-list me-1"/> Add - Simple
</button>
</div>
<p class="text-muted small mt-1">