feat(configurator): OWL widgets + Express form polish to match mockup

NEW OWL widgets:
- FpExpressPartCell (static/src/js/express_part_cell.js + .xml) — multi-row
  Part cell. Wraps Many2OneField for the part picker (row 1: part# / rev,
  bold). Below it: row 2 part description (italic muted), row 3 serial #s
  joined + '+ bulk' button that triggers the existing bulk-add wizard.
- FpExpressBakePill (static/src/js/express_bake_pill.js + .xml) — click-
  to-edit Bake pill. Renders amber pill when set, italic muted 'no bake'
  when empty. Click swaps to inline textarea + Save / Clear / Cancel.

NEW fields:
- fp.direct.order.line.part_number_display / part_revision_display /
  part_name_display (related Char from fp.part.catalog) — fed to the
  Part cell widget so it can render multi-row without RPC.
- fp.direct.order.wizard.tooling_charge (Monetary) — surfaced in the
  Totals card on the Express form.
- fp.direct.order.wizard.po_status (computed Selection
  received/pending/missing) — drives the PO Block status badge.
- sale.order.x_fc_tooling_charge (Monetary) — receives wizard.tooling_charge
  at confirm.

View updates (fp_express_order_views.xml):
- PO block header now shows the PO status pill (green Received,
  amber Pending, red Missing)
- Order Lines legend bar (Mask / Bake pill / DWG / OPEN explainers)
- Part Number column uses widget='fp_express_part_cell' — single cell
  with 3 internal rows
- Bake column uses widget='fp_express_bake_pill' — interactive pill
- Totals card now has Subtotal / Tooling Charge / Total Lines / Total
  Quantity / Grand Total + currency pill

SCSS adds:
- Multi-row part cell styles (internal borders, bold/italic/muted rows)
- Bake pill (has-bake amber, no-bake italic muted) + inline editor
- Legend bar (section background, gap-spaced explainer chips)
- PO status pill colour scheme
- Bulk button styling

Wizard's action_create_order now carries tooling_charge to the SO at
confirm so it persists on the resulting sale.order.
This commit is contained in:
gsinghpal
2026-05-26 22:13:54 -04:00
parent 1d674e587c
commit 0f2ed5cc16
10 changed files with 577 additions and 15 deletions

View File

@@ -460,6 +460,24 @@ class FpDirectOrderLine(models.Model):
)
# ---- Express Orders per-line flags (2026-05-26) ----
# Related read-only fields so the FpExpressPartCell OWL widget can
# render the multi-row Part cell (part# / revision / name / serials)
# without needing extra RPCs.
part_number_display = fields.Char(
related='part_catalog_id.part_number',
string='Part # (display)',
readonly=True,
)
part_revision_display = fields.Char(
related='part_catalog_id.revision',
string='Revision (display)',
readonly=True,
)
part_name_display = fields.Char(
related='part_catalog_id.name',
string='Part Name (display)',
readonly=True,
)
customer_line_ref = fields.Char(
string='Customer Line Job #',
help='Per-line customer sub-reference (e.g. ABC, DEF). Distinct from '

View File

@@ -274,6 +274,34 @@ class FpDirectOrderWizard(models.Model):
help='Which view created this draft. Drafts list routes click-action '
'to the matching form. Dropped at phase-out Phase 4.',
)
tooling_charge = fields.Monetary(
string='Tooling Charge',
currency_field='currency_id',
help='Optional one-time tooling fee added to the order total. '
'Carried to the SO and appears as a separate line on the '
'customer invoice.',
)
# ---- PO status pill (computed, display-only) ----
po_status = fields.Selection(
[('received', 'Received'),
('pending', 'Pending'),
('missing', 'Missing')],
string='PO Status',
compute='_compute_po_status',
help='Computed: Received if PDF attached, Pending if po_pending=True, '
'Missing otherwise.',
)
@api.depends('po_attachment_file', 'po_pending')
def _compute_po_status(self):
for rec in self:
if rec.po_attachment_file:
rec.po_status = 'received'
elif rec.po_pending:
rec.po_status = 'pending'
else:
rec.po_status = 'missing'
# ---- Lines ----
line_ids = fields.One2many(
@@ -674,6 +702,7 @@ class FpDirectOrderWizard(models.Model):
# Express Orders header (2026-05-26)
'x_fc_internal_notes': self.internal_notes or False,
'x_fc_material_process': self.material_process or False,
'x_fc_tooling_charge': self.tooling_charge or 0.0,
'pricelist_id': self.pricelist_id.id if self.pricelist_id else False,
'validity_date': self.validity_date or False,
'order_line': [],