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,5 +1,5 @@
# -*- coding: utf-8 -*-
# End-to-end order walkthrough simulates each role on the shop floor.
# End-to-end order walkthrough - simulates each role on the shop floor.
#
# Run via odoo-shell:
# echo 'exec(open("/mnt/extra-addons/custom/fusion_plating_quality/scripts/sub12_e2e_walkthrough.py").read())' \
@@ -24,7 +24,7 @@ def walk():
print('====================== E2E ORDER WALKTHROUGH ======================')
# ------------------------------------------------------------------
# ROLE: Sales / Estimator open Plating > Sales > Quotations
# ROLE: Sales / Estimator - open Plating > Sales > Quotations
# ------------------------------------------------------------------
print('\n[ROLE: Estimator] Plating > Sales > Quotations > New Quote')
@@ -47,7 +47,7 @@ def walk():
part = Part.search([], limit=1)
if not part:
gap('Estimator', 'fp.part.catalog',
'No parts in catalog estimator has nothing to quote against')
'No parts in catalog - estimator has nothing to quote against')
return
print(f' Part chosen: {part.display_name} '
f'(part#={getattr(part, "part_number", "?")} '
@@ -68,7 +68,7 @@ def walk():
coating = e['fp.coating.config'].search([], limit=1)
if not coating:
gap('Estimator', 'fp.coating.config',
'No coating configs defined estimator cannot configure quote')
'No coating configs defined - estimator cannot configure quote')
else:
print(f' Coating chosen: {coating.display_name}')
@@ -91,7 +91,7 @@ def walk():
gap('Estimator', 'fp.quote.configurator.create', str(ex))
return
# 4a. Try the "Create Quotation" path what action confirms the SO?
# 4a. Try the "Create Quotation" path - what action confirms the SO?
so = False
for meth in ('action_create_quotation', 'action_promote_to_direct_order',
'action_create_sale_order', 'action_generate_quote'):
@@ -112,7 +112,7 @@ def walk():
# Fall back: create SO directly and see if the configurator workflow is wired.
gap('Estimator', 'configurator',
'No working "create quote" action found on the configurator '
' estimator has no button to make a quote')
'- estimator has no button to make a quote')
# Manual SO creation for the rest of the walkthrough
SO = e['sale.order']
try:
@@ -155,7 +155,7 @@ def walk():
if so.state == 'draft':
try:
so.action_confirm()
print(f' ✓ SO confirmed state={so.state}')
print(f' ✓ SO confirmed - state={so.state}')
except Exception as ex:
gap('Estimator', 'sale.order.action_confirm', str(ex))
return
@@ -167,7 +167,7 @@ def walk():
jobs = Job.search([('sale_order_id', '=', so.id)])
if not jobs:
gap('Planner', 'fp.job auto-create',
'No fp.job auto-created on SO confirm planner has nothing '
'No fp.job auto-created on SO confirm - planner has nothing '
'to plan against')
else:
print(f'{len(jobs)} fp.job(s) created: '
@@ -178,7 +178,7 @@ def walk():
receivings = Recv.search([('sale_order_id', '=', so.id)])
if not receivings:
gap('Receiver', 'fp.receiving auto-create',
'No fp.receiving auto-created on SO confirm receiver has '
'No fp.receiving auto-created on SO confirm - receiver has '
'nothing to count against')
else:
print(f' ✓ Receiving record(s): {", ".join(receivings.mapped("name"))}')
@@ -188,7 +188,7 @@ def walk():
insps = Insp.search([('sale_order_id', '=', so.id)])
if not insps and jobs:
gap('Racker', 'fp.racking.inspection auto-create',
'jobs exist but no racking inspection racker walks empty')
'jobs exist but no racking inspection - racker walks empty')
elif insps:
print(f' ✓ Racking inspection(s): '
f'{", ".join(insps.mapped("name"))}')
@@ -202,10 +202,10 @@ def walk():
f'{", ".join(pjs.mapped("name"))}')
else:
gap('Portal', 'portal job auto-create',
'No portal.job mirror customer sees nothing on portal')
'No portal.job mirror - customer sees nothing on portal')
# ------------------------------------------------------------------
# ROLE: Receiver Plating > Receiving > All Receiving
# ROLE: Receiver - Plating > Receiving > All Receiving
# ------------------------------------------------------------------
print('\n[ROLE: Receiver] Open the receiving record, count boxes')
if receivings:
@@ -218,7 +218,7 @@ def walk():
if hasattr(r, 'action_mark_counted'):
try:
r.action_mark_counted()
print(f' ✓ Marked counted state={r.state}')
print(f' ✓ Marked counted - state={r.state}')
except Exception as ex:
gap('Receiver', 'action_mark_counted', str(ex))
else:
@@ -227,7 +227,7 @@ def walk():
if hasattr(r, 'action_mark_staged'):
try:
r.action_mark_staged()
print(f' ✓ Marked staged state={r.state}')
print(f' ✓ Marked staged - state={r.state}')
except Exception as ex:
gap('Receiver', 'action_mark_staged', str(ex))
# Smart button to racking inspection?
@@ -239,7 +239,7 @@ def walk():
'no smart button; receiver navigates manually')
# ------------------------------------------------------------------
# ROLE: Racking Crew open the linked racking inspection
# ROLE: Racking Crew - open the linked racking inspection
# ------------------------------------------------------------------
print('\n[ROLE: Racker] Open the racking inspection from receiving smart button')
if insps:
@@ -253,18 +253,18 @@ def walk():
if hasattr(insp, 'action_start'):
try:
insp.action_start()
print(f' ✓ Inspection started state={insp.state}')
print(f' ✓ Inspection started - state={insp.state}')
except Exception as ex:
gap('Racker', 'racking_inspection.action_start', str(ex))
if hasattr(insp, 'action_complete'):
try:
insp.action_complete()
print(f' ✓ Inspection completed state={insp.state}')
print(f' ✓ Inspection completed - state={insp.state}')
except Exception as ex:
gap('Racker', 'racking_inspection.action_complete', str(ex))
# ------------------------------------------------------------------
# ROLE: Operator runs the plating job step-by-step
# ROLE: Operator - runs the plating job step-by-step
# ------------------------------------------------------------------
print('\n[ROLE: Operator] Open the job, run each step')
if jobs:
@@ -272,7 +272,7 @@ def walk():
steps = job.step_ids.sorted('sequence')
if not steps:
gap('Operator', 'fp.job.step_ids',
'job has no steps recipe not generated')
'job has no steps - recipe not generated')
else:
print(f' Job {job.name} has {len(steps)} steps')
ran = 0
@@ -286,17 +286,17 @@ def walk():
gap('Operator', f'step.{step.name}', str(ex))
else:
gap('Operator', f'step.{step.name}',
f"state={step.state} operator can't start it")
f"state={step.state} - operator can't start it")
print(f' ✓ Ran {ran} of 3 first steps')
# ------------------------------------------------------------------
# ROLE: Inspector walk the QC checklist if customer requires QC
# ROLE: Inspector - walk the QC checklist if customer requires QC
# ------------------------------------------------------------------
print('\n[ROLE: Inspector] Look for an open QC check on the job')
QC = e['fusion.plating.quality.check']
if jobs:
job = jobs[0]
# Customer might not be flagged x_fc_requires_qc flip it for the test.
# Customer might not be flagged x_fc_requires_qc - flip it for the test.
wants = ('x_fc_requires_qc' in customer._fields
and customer.x_fc_requires_qc)
print(f' Customer requires QC: {wants}')
@@ -309,7 +309,7 @@ def walk():
print(f' ✓ QC check found: {check.name}')
# ------------------------------------------------------------------
# ROLE: Operator try to mark job done (will hit QC gate if applicable)
# ROLE: Operator - try to mark job done (will hit QC gate if applicable)
# ------------------------------------------------------------------
print('\n[ROLE: Operator] Click Mark Done on the job')
if jobs:
@@ -329,7 +329,7 @@ def walk():
pass
try:
job.with_context(fp_skip_qc_gate=True).button_mark_done()
print(f' ✓ Job marked done (with QC bypass) state={job.state}')
print(f' ✓ Job marked done (with QC bypass) - state={job.state}')
except Exception as ex:
gap('Operator', 'fp.job.button_mark_done', str(ex))
@@ -344,7 +344,7 @@ def walk():
f'{", ".join(deliveries.mapped("name") or ["(none)"])}')
# ------------------------------------------------------------------
# ROLE: Driver picks up the delivery
# ROLE: Driver - picks up the delivery
# ------------------------------------------------------------------
print('\n[ROLE: Driver] Find the linked fusion.plating.delivery')
if Del is not None and jobs:
@@ -355,14 +355,14 @@ def walk():
if hasattr(d, 'action_mark_delivered'):
try:
d.action_mark_delivered()
print(f' ✓ Marked delivered state={d.state}')
print(f' ✓ Marked delivered - state={d.state}')
except Exception as ex:
gap('Driver', 'delivery.action_mark_delivered', str(ex))
else:
print(' No delivery linked to job checking by SO')
print(' No delivery linked to job - checking by SO')
# ------------------------------------------------------------------
# ROLE: Accountant invoice the SO
# ROLE: Accountant - invoice the SO
# ------------------------------------------------------------------
print('\n[ROLE: Accountant] Generate invoice')
print(f' invoice_status={so.invoice_status}')
@@ -376,7 +376,7 @@ def walk():
except Exception as ex:
gap('Accountant', 'sale.order._create_invoices', str(ex))
elif so.invoice_status == 'no':
# qty_delivered is 0 service products invoice on ordered qty by
# qty_delivered is 0 - service products invoice on ordered qty by
# default. If "no" persists, the SO has no invoiceable lines yet
# (e.g. delivered_qty=0 + invoice_policy='delivery').
print(f' Note: SO not yet invoiceable (qty_delivered=0). '
@@ -388,7 +388,7 @@ def walk():
# ------------------------------------------------------------------
print('\n=========================== SUMMARY ===========================')
if not GAPS:
print('NO GAPS FOUND workflow walked end-to-end clean')
print('NO GAPS FOUND - workflow walked end-to-end clean')
else:
print(f'{len(GAPS)} GAP(S) FOUND:')
for role, where, msg in GAPS: