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

@@ -2,7 +2,7 @@
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
#
# sale.order.action_confirm hook creates fp.job records on confirm.
# sale.order.action_confirm hook - creates fp.job records on confirm.
# Sub 11 (2026-04-26) removed MRP entirely; fp.job is the only fulfilment
# path. The former x_fc_use_native_jobs migration toggle was dropped in
# 19.0.8.19.0 once the legacy bridge_mrp fallback became unreachable.
@@ -59,11 +59,11 @@ class SaleOrder(models.Model):
help='The quote-stage name (e.g. Q202605-200). Preserved when '
'the SO is renamed on confirm.',
)
# Per-model counters monotonic, never decrement. Source of truth
# Per-model counters - monotonic, never decrement. Source of truth
# for the next sibling's x_fc_doc_index. Updated via row-locked SQL
# in fp.parent.numbered.mixin so concurrent creates can't drift.
#
# Naming: `x_fc_pn_*_count` the `pn_` infix distinguishes our
# Naming: `x_fc_pn_*_count` - the `pn_` infix distinguishes our
# storage counters from pre-existing compute fields (e.g. the
# `x_fc_delivery_count` compute in bridge_mrp, `x_fc_ncr_count`
# in configurator, `x_fc_receiving_count` in fp_receiving) which
@@ -82,7 +82,7 @@ class SaleOrder(models.Model):
x_fc_pn_rma_count = fields.Integer(string='Parent: RMA Count', readonly=True, copy=False, default=0)
# ------------------------------------------------------------------
# Phase 4 (Sub 11) workflow-stage field + assigned-manager field
# Phase 4 (Sub 11) - workflow-stage field + assigned-manager field
# relocated from fusion_plating_bridge_mrp. Field re-declared with
# the same selection + compute pointer; jobs is now the source of
# truth so Phase 5 can delete bridge_mrp without losing the field.
@@ -236,7 +236,7 @@ class SaleOrder(models.Model):
return action
def action_view_fp_certificates(self):
"""Smart-button target open the certificate(s) linked to this
"""Smart-button target - open the certificate(s) linked to this
SO. One cert → form view; many → list view filtered to this SO."""
self.ensure_one()
certs = self.env['fp.certificate'].search([
@@ -258,7 +258,7 @@ class SaleOrder(models.Model):
return action
# ------------------------------------------------------------------
# Parent-number hierarchy quote naming on create
# Parent-number hierarchy - quote naming on create
# ------------------------------------------------------------------
@api.model_create_multi
def create(self, vals_list):
@@ -287,7 +287,7 @@ class SaleOrder(models.Model):
Parent number is drawn from fp.parent.number; the quote name
was already saved to x_fc_quote_ref on create() so it survives
the rename. Idempotent if x_fc_parent_number is already set,
the rename. Idempotent - if x_fc_parent_number is already set,
the rename is skipped (re-confirm scenarios)."""
Seq = self.env['ir.sequence']
for so in self:
@@ -344,7 +344,7 @@ class SaleOrder(models.Model):
))._create_invoices(grouped=grouped, final=final, date=date)
def unlink(self):
"""Spec §6.2 confirmed SOs are part of the compliance audit
"""Spec §6.2 - confirmed SOs are part of the compliance audit
trail and cannot be deleted. Cancellation must go through the
state machine instead. Draft SOs (no parent_number assigned
yet) remain freely deletable per Odoo standard. Applies to
@@ -352,7 +352,7 @@ class SaleOrder(models.Model):
for so in self:
if so.x_fc_parent_number:
raise UserError(_(
'Sale Order "%(name)s" cannot be deleted it has '
'Sale Order "%(name)s" cannot be deleted - it has '
'been confirmed (parent number %(parent)s issued) '
'and is part of the compliance audit trail. Cancel '
'it instead. This rule applies to all users '
@@ -364,14 +364,14 @@ class SaleOrder(models.Model):
"""Recipe resolution with Express-Orders SO header fallback.
Priority (most-specific first):
1. line.x_fc_process_variant_id explicit per-line variant
1. line.x_fc_process_variant_id - explicit per-line variant
(always wins; this is where G3 propagation lands a value).
2. self.x_fc_material_process Express Orders order-level
2. self.x_fc_material_process - Express Orders order-level
recipe. Catches the case where G3 propagation failed to
reach the line but the header has the recipe.
3. part.default_process_id part's flagged default
3. part.default_process_id - part's flagged default
variant. Customer-and-part-tuned recipe.
4. part.recipe_id legacy fallback.
4. part.recipe_id - legacy fallback.
Returns the recipe record or an empty recordset.
"""
Node = self.env['fusion.plating.process.node']
@@ -428,7 +428,7 @@ class SaleOrder(models.Model):
'x_fc_part_catalog_id' in self._fields and self.x_fc_part_catalog_id
):
_logger.info(
'SO %s: no line-level part but header carries one '
'SO %s: no line-level part but header carries one - '
'treating all lines as a single plating job.', self.name,
)
plating_lines = self.order_line
@@ -439,7 +439,7 @@ class SaleOrder(models.Model):
# Group by (recipe, part, spec, thickness, serial). Lines that
# share ALL FIVE collapse into one WO. Bundling lines with
# different specs / thicknesses / serials under one WO would
# carry the first line's values onto the cert + sticker
# carry the first line's values onto the cert + sticker -
# silent mis-attestation. No-recipe lines still get their own
# group each.
groups = {}
@@ -513,7 +513,7 @@ class SaleOrder(models.Model):
if recipe:
vals['recipe_id'] = recipe.id
# Customer references mirror onto the job so the shop floor
# Customer references - mirror onto the job so the shop floor
# has them without round-tripping to the SO.
if 'x_fc_customer_job_number' in self._fields \
and self.x_fc_customer_job_number:
@@ -523,7 +523,7 @@ class SaleOrder(models.Model):
if 'x_fc_rush_order' in self._fields:
vals['x_fc_rush_order'] = bool(self.x_fc_rush_order)
# Scheduling targets mirror the SO's customer-facing dates.
# Scheduling targets - mirror the SO's customer-facing dates.
if 'x_fc_internal_deadline' in self._fields \
and self.x_fc_internal_deadline:
vals['x_fc_internal_deadline'] = self.x_fc_internal_deadline
@@ -531,7 +531,7 @@ class SaleOrder(models.Model):
and self.x_fc_planned_start_date:
vals['x_fc_planned_start_date'] = self.x_fc_planned_start_date
# Operational notes mirror so the shop has them on the WO.
# Operational notes - mirror so the shop has them on the WO.
if 'x_fc_internal_note' in self._fields \
and self.x_fc_internal_note:
vals['x_fc_internal_note'] = self.x_fc_internal_note
@@ -539,7 +539,7 @@ class SaleOrder(models.Model):
and self.x_fc_external_note:
vals['x_fc_external_note'] = self.x_fc_external_note
# Customer spec / facility / manager copy from SO if present
# Customer spec / facility / manager - copy from SO if present
if 'x_fc_customer_spec_id' in self._fields and self.x_fc_customer_spec_id:
vals['customer_spec_id'] = self.x_fc_customer_spec_id.id
if 'x_fc_facility_id' in self._fields and self.x_fc_facility_id:
@@ -568,12 +568,12 @@ class SaleOrder(models.Model):
self.name, job.name, qty, (recipe.name if recipe else '-'),
)
# Express Orders (2026-05-26) apply per-line masking + bake
# Express Orders (2026-05-26) - apply per-line masking + bake
# overrides to the new job. This runs BEFORE step generation
# (which happens in fp.job.action_confirm) so the override rows
# are in place when _generate_steps_from_recipe reads override_map.
# Step.instructions writes are deferred to a second pass after
# step gen see fp.job.action_confirm override.
# step gen - see fp.job.action_confirm override.
if job.recipe_id and 'x_fc_masking_enabled' in self.env['sale.order.line']._fields:
for sol in lines:
if hasattr(sol, '_fp_apply_express_overrides_to_job'):
@@ -590,7 +590,7 @@ class SaleOrder(models.Model):
return True
# ------------------------------------------------------------------
# Phase 4 (Sub 11) workflow stage action buttons.
# Phase 4 (Sub 11) - workflow stage action buttons.
# Native versions of bridge_mrp's action_fp_* methods. Drop the
# mrp.production lookups; talk to fp.job and fp.receiving directly.
# ------------------------------------------------------------------
@@ -619,7 +619,7 @@ class SaleOrder(models.Model):
if Recv is None:
return False
for rec in Recv.search([('sale_order_id', '=', self.id)]):
# Push receiving to its terminal state 'closed' is the
# Push receiving to its terminal state - 'closed' is the
# post-Sub-8 terminal; 'accepted' kept as a legacy fallback
# only for old records still in pre-Sub-8 states.
if rec.state in ('draft', 'counted', 'staged'):
@@ -628,7 +628,7 @@ class SaleOrder(models.Model):
rec.state = 'accepted'
if 'x_fc_receiving_status' in self._fields:
self.x_fc_receiving_status = 'received'
self.message_post(body=_('Parts accepted ready to assign manager.'))
self.message_post(body=_('Parts accepted - ready to assign manager.'))
return True
def action_fp_assign_to_me(self):
@@ -687,6 +687,6 @@ class SaleOrder(models.Model):
return {
'type': 'ir.actions.client',
'tag': 'fp_plant_overview',
'name': _('Shop Floor %s') % self.name,
'name': _('Shop Floor - %s') % self.name,
'target': 'current',
}