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:
@@ -7,7 +7,7 @@ from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpChainOfCustody(models.Model):
|
||||
"""A single custody event — the audit trail for parts in transit.
|
||||
"""A single custody event - the audit trail for parts in transit.
|
||||
|
||||
A chain of custody record is created every time parts change hands:
|
||||
received from the customer, loaded on a vehicle, entered a facility,
|
||||
@@ -19,7 +19,7 @@ class FpChainOfCustody(models.Model):
|
||||
round-trip).
|
||||
"""
|
||||
_name = 'fusion.plating.chain.of.custody'
|
||||
_description = 'Fusion Plating — Chain of Custody Event'
|
||||
_description = 'Fusion Plating - Chain of Custody Event'
|
||||
_order = 'event_datetime desc, id desc'
|
||||
_rec_name = 'display_name'
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class FpDelivery(models.Model):
|
||||
migration.
|
||||
"""
|
||||
_name = 'fusion.plating.delivery'
|
||||
_description = 'Fusion Plating — Delivery'
|
||||
_description = 'Fusion Plating - Delivery'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'fp.parent.numbered.mixin']
|
||||
_order = 'scheduled_date desc, id desc'
|
||||
|
||||
@@ -64,7 +64,7 @@ class FpDelivery(models.Model):
|
||||
'Will become a Many2one once the job module ships.',
|
||||
tracking=True,
|
||||
)
|
||||
# ---- Sub 5 — traceability fields from the source MO --------------------
|
||||
# ---- Sub 5 - traceability fields from the source MO --------------------
|
||||
x_fc_serial_id = fields.Many2one(
|
||||
'fp.serial', string='Serial Number',
|
||||
ondelete='set null', index=True,
|
||||
@@ -76,13 +76,13 @@ class FpDelivery(models.Model):
|
||||
)
|
||||
x_fc_thickness_range = fields.Char(
|
||||
string='Thickness',
|
||||
help='Carried from the SO line — prints on packing slip / BoL.',
|
||||
help='Carried from the SO line - prints on packing slip / BoL.',
|
||||
)
|
||||
x_fc_revision_snapshot = fields.Char(
|
||||
string='Revision (snapshot)',
|
||||
)
|
||||
|
||||
# ---- Sub 8 — box parity ------------------------------------------------
|
||||
# ---- Sub 8 - box parity ------------------------------------------------
|
||||
# Shipping crew packs returns into the SAME boxes the parts arrived in
|
||||
# (client requirement). Receiving captures box_count_in; we capture
|
||||
# box_count_out here. action_mark_delivered posts a non-blocking
|
||||
@@ -131,7 +131,7 @@ class FpDelivery(models.Model):
|
||||
string='Packing List',
|
||||
)
|
||||
|
||||
# ---- Phase A — outbound carrier + shipment link ----------------------
|
||||
# ---- Phase A - outbound carrier + shipment link ----------------------
|
||||
# Mirrors the fields on fp.receiving. Populated by
|
||||
# fp.job._fp_create_delivery from the linked receiving when this
|
||||
# delivery is auto-created on job-done; shipping crew can override
|
||||
@@ -250,7 +250,7 @@ class FpDelivery(models.Model):
|
||||
# Parent-numbered mixin hooks (2026-05-12 numbering hierarchy)
|
||||
# ------------------------------------------------------------------
|
||||
def _fp_parent_sale_order(self):
|
||||
"""No direct sale_order_id on this model — resolve via
|
||||
"""No direct sale_order_id on this model - resolve via
|
||||
job_ref → fp.job.name → job.sale_order_id."""
|
||||
if not self.job_ref or 'fp.job' not in self.env:
|
||||
return self.env['sale.order']
|
||||
@@ -268,7 +268,7 @@ class FpDelivery(models.Model):
|
||||
def action_view_coc(self):
|
||||
"""Open the certificate record this delivery's CoC PDF came
|
||||
from. The attachment carries res_model + res_id, so we
|
||||
navigate to that record (operator gets all cert info — issue
|
||||
navigate to that record (operator gets all cert info - issue
|
||||
date, void wizard, reset, etc.) rather than just opening the
|
||||
raw PDF. Falls back to opening the attachment directly if
|
||||
someone manually attached a PDF that isn't a cert.
|
||||
@@ -286,7 +286,7 @@ class FpDelivery(models.Model):
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
}
|
||||
# Plain attachment — open via PDF preview helper if available.
|
||||
# Plain attachment - open via PDF preview helper if available.
|
||||
if hasattr(att, 'action_fusion_preview'):
|
||||
return att.action_fusion_preview(title=att.name or 'CoC')
|
||||
return {
|
||||
@@ -298,8 +298,8 @@ class FpDelivery(models.Model):
|
||||
def action_view_packing_list(self):
|
||||
"""Open the packing-list PDF via fusion_pdf_preview (or fall
|
||||
back to a new tab when the preview helper isn't installed).
|
||||
Packing lists don't have a backing model — they're attachments
|
||||
only — so we don't navigate to a record.
|
||||
Packing lists don't have a backing model - they're attachments
|
||||
only - so we don't navigate to a record.
|
||||
"""
|
||||
self.ensure_one()
|
||||
att = self.packing_list_attachment_id
|
||||
@@ -320,7 +320,7 @@ class FpDelivery(models.Model):
|
||||
def action_refresh_from_source(self):
|
||||
"""Re-pull delivery address / contact / scheduled date / source
|
||||
facility / carrier / CoC from the linked job → SO → receiving →
|
||||
cert chain. Only fills BLANK fields — never overwrites operator
|
||||
cert chain. Only fills BLANK fields - never overwrites operator
|
||||
edits. Use when an upstream value changed after the delivery
|
||||
was auto-created, or to backfill an old delivery that was
|
||||
created before the auto-populate hook existed.
|
||||
@@ -336,12 +336,12 @@ class FpDelivery(models.Model):
|
||||
)
|
||||
if not job:
|
||||
raise UserError(_(
|
||||
'Delivery %s has no linked job — nothing to '
|
||||
'Delivery %s has no linked job - nothing to '
|
||||
'refresh from.'
|
||||
) % rec.name)
|
||||
Delivery = rec.env['fusion.plating.delivery']
|
||||
defaults = job._fp_resolve_delivery_defaults(Delivery)
|
||||
# Drop fields the operator already filled — never clobber
|
||||
# Drop fields the operator already filled - never clobber
|
||||
# manual edits. Includes the partner/job links since those
|
||||
# are non-overridable.
|
||||
fill = {
|
||||
@@ -350,7 +350,7 @@ class FpDelivery(models.Model):
|
||||
}
|
||||
if not fill:
|
||||
rec.message_post(body=_(
|
||||
'Refresh from source: nothing to update — every '
|
||||
'Refresh from source: nothing to update - every '
|
||||
'field already populated.'
|
||||
))
|
||||
continue
|
||||
@@ -382,7 +382,7 @@ class FpDelivery(models.Model):
|
||||
@api.model
|
||||
def _default_name(self):
|
||||
"""Retained for any legacy caller. New code should rely on
|
||||
create() — the parent-numbered mixin sets the name there."""
|
||||
create() - the parent-numbered mixin sets the name there."""
|
||||
seq = self.env['ir.sequence'].next_by_code('fusion.plating.delivery')
|
||||
return seq or '/'
|
||||
|
||||
@@ -417,7 +417,7 @@ class FpDelivery(models.Model):
|
||||
the pattern used in fp_direct_order_wizard and the SO action_confirm
|
||||
manager-override). Non-managers can't bypass.
|
||||
|
||||
``getattr`` is defensive — the hold field lives in
|
||||
``getattr`` is defensive - the hold field lives in
|
||||
``fusion_plating_invoicing``; this module doesn't dep on it.
|
||||
"""
|
||||
for rec in self:
|
||||
@@ -438,7 +438,7 @@ class FpDelivery(models.Model):
|
||||
is_manager = self.env['res.partner']._fp_user_can_override_account_hold()
|
||||
if not is_manager:
|
||||
raise UserError(_(
|
||||
'Cannot %(action)s delivery "%(name)s" — customer "%(partner)s" '
|
||||
'Cannot %(action)s delivery "%(name)s" - customer "%(partner)s" '
|
||||
'is on account hold.\n'
|
||||
'Reason: %(reason)s\n\n'
|
||||
'Contact a manager to override.'
|
||||
@@ -466,14 +466,14 @@ class FpDelivery(models.Model):
|
||||
|
||||
Vehicle is encouraged but not strictly required (some shops
|
||||
let drivers grab whatever vehicle is open at the dock). Driver
|
||||
is non-negotiable — without it the chain-of-custody hand-off
|
||||
is non-negotiable - without it the chain-of-custody hand-off
|
||||
has no signed party and the POD can't be linked to a person.
|
||||
"""
|
||||
self._fp_check_account_hold(_('dispatch'))
|
||||
for rec in self:
|
||||
if not rec.assigned_driver_id:
|
||||
raise UserError(_(
|
||||
'Cannot mark delivery "%(name)s" en route — no driver '
|
||||
'Cannot mark delivery "%(name)s" en route - no driver '
|
||||
'assigned.\n\nPick a driver on the delivery (or wait for '
|
||||
'the auto-prefill to find one) before tapping Start Route.'
|
||||
) % {'name': rec.name or rec.display_name})
|
||||
@@ -485,7 +485,7 @@ class FpDelivery(models.Model):
|
||||
or rec.vehicle_id.display_name
|
||||
or 'Driver'),
|
||||
)
|
||||
# Packing slip travels with the shipment — render + attach it on
|
||||
# Packing slip travels with the shipment - render + attach it on
|
||||
# dispatch so the driver/customer get it and it's on the record.
|
||||
self._fp_generate_packing_slip()
|
||||
|
||||
@@ -495,7 +495,7 @@ class FpDelivery(models.Model):
|
||||
|
||||
Fired on dispatch (action_start_route) and as a catch-all on
|
||||
action_mark_delivered. Idempotent + best-effort: skips deliveries
|
||||
that already carry a slip (unless force=True) and never raises —
|
||||
that already carry a slip (unless force=True) and never raises -
|
||||
a report glitch must not block shipping. The report action is
|
||||
resolved at runtime so this module needs no hard dependency on
|
||||
fusion_plating_reports.
|
||||
@@ -542,7 +542,7 @@ class FpDelivery(models.Model):
|
||||
for rec in self:
|
||||
if not rec.pod_id:
|
||||
raise UserError(_(
|
||||
'Cannot mark delivery "%(name)s" delivered — no Proof '
|
||||
'Cannot mark delivery "%(name)s" delivered - no Proof '
|
||||
'of Delivery (POD) has been captured.\n\n'
|
||||
'On the iPad: Capture POD → enter recipient name + '
|
||||
'signature → save. Then mark delivered.'
|
||||
@@ -558,18 +558,18 @@ class FpDelivery(models.Model):
|
||||
or 'Driver'),
|
||||
to_party=rec.partner_id.display_name,
|
||||
)
|
||||
# Sub 8 — box-parity warning. Non-blocking; just posts to
|
||||
# Sub 8 - box-parity warning. Non-blocking; just posts to
|
||||
# chatter so the shipping supervisor sees it on the record.
|
||||
rec._fp_check_box_parity()
|
||||
# Catch-all: ensure a slip exists even if dispatch was skipped
|
||||
# (generate-if-missing — won't overwrite the dispatch-time one).
|
||||
# (generate-if-missing - won't overwrite the dispatch-time one).
|
||||
self._fp_generate_packing_slip()
|
||||
|
||||
def _fp_check_box_parity(self):
|
||||
"""Compare this delivery's boxes-out count to the boxes-in count
|
||||
captured at receiving. Post a chatter warning if they differ.
|
||||
|
||||
Never blocks — shipping has already happened by the time this
|
||||
Never blocks - shipping has already happened by the time this
|
||||
fires. The warning is for audit + shipping-supervisor review.
|
||||
"""
|
||||
self.ensure_one()
|
||||
@@ -577,7 +577,7 @@ class FpDelivery(models.Model):
|
||||
return
|
||||
if 'fp.receiving' not in self.env:
|
||||
return
|
||||
# Sub 11 — resolve SO via job_ref → fp.job.origin → SO.name.
|
||||
# Sub 11 - resolve SO via job_ref → fp.job.origin → SO.name.
|
||||
so_name = False
|
||||
if self.job_ref and 'fp.job' in self.env:
|
||||
job = self.env['fp.job'].sudo().search(
|
||||
|
||||
@@ -20,7 +20,7 @@ class FpPickupRequest(models.Model):
|
||||
automatically as the state transitions.
|
||||
"""
|
||||
_name = 'fusion.plating.pickup.request'
|
||||
_description = 'Fusion Plating — Pickup Request'
|
||||
_description = 'Fusion Plating - Pickup Request'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'fp.parent.numbered.mixin']
|
||||
_order = 'requested_date desc, id desc'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpProofOfDelivery(models.Model):
|
||||
"""Proof of delivery record — captured at the delivery point.
|
||||
"""Proof of delivery record - captured at the delivery point.
|
||||
|
||||
Captures:
|
||||
* recipient name
|
||||
@@ -20,7 +20,7 @@ class FpProofOfDelivery(models.Model):
|
||||
which pre-fills the delivery_id and timestamps the record.
|
||||
"""
|
||||
_name = 'fusion.plating.proof.of.delivery'
|
||||
_description = 'Fusion Plating — Proof of Delivery'
|
||||
_description = 'Fusion Plating - Proof of Delivery'
|
||||
_order = 'delivered_at desc, id desc'
|
||||
|
||||
name = fields.Char(
|
||||
|
||||
@@ -19,7 +19,7 @@ class FpRoute(models.Model):
|
||||
captures total distance (km) and elapsed time for costing.
|
||||
"""
|
||||
_name = 'fusion.plating.route'
|
||||
_description = 'Fusion Plating — Route'
|
||||
_description = 'Fusion Plating - Route'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'route_date desc, id desc'
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class FpRouteStop(models.Model):
|
||||
stops (fuel, break) just carry an address and a planned time.
|
||||
"""
|
||||
_name = 'fusion.plating.route.stop'
|
||||
_description = 'Fusion Plating — Route Stop'
|
||||
_description = 'Fusion Plating - Route Stop'
|
||||
_order = 'route_id, sequence, id'
|
||||
|
||||
route_id = fields.Many2one(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
#
|
||||
# Sub 5 — attach delivery reverse link to fp.serial. Lives here rather
|
||||
# Sub 5 - attach delivery reverse link to fp.serial. Lives here rather
|
||||
# than in fusion_plating_configurator because fusion.plating.delivery
|
||||
# is defined in this module, which loads after configurator.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class FpVehicle(models.Model):
|
||||
A vehicle belongs to a facility (its home base), carries insurance,
|
||||
registration and service dates, and tracks its current driver and
|
||||
state. Vehicles flagged `tdg_certified` are approved to carry TDG
|
||||
(Transportation of Dangerous Goods) loads — e.g. stripped parts with
|
||||
(Transportation of Dangerous Goods) loads - e.g. stripped parts with
|
||||
residue, waste drums, spent chemistry.
|
||||
|
||||
The module works without Odoo's Enterprise Fleet module: all
|
||||
@@ -20,7 +20,7 @@ class FpVehicle(models.Model):
|
||||
are fully supported.
|
||||
"""
|
||||
_name = 'fusion.plating.vehicle'
|
||||
_description = 'Fusion Plating — Vehicle'
|
||||
_description = 'Fusion Plating - Vehicle'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'name'
|
||||
|
||||
@@ -28,7 +28,7 @@ class FpVehicle(models.Model):
|
||||
string='Vehicle',
|
||||
required=True,
|
||||
tracking=True,
|
||||
help='Internal name — e.g. "Van 1" or "Box Truck 07".',
|
||||
help='Internal name - e.g. "Van 1" or "Box Truck 07".',
|
||||
)
|
||||
license_plate = fields.Char(
|
||||
string='License Plate',
|
||||
|
||||
@@ -25,7 +25,7 @@ class HrEmployee(models.Model):
|
||||
)
|
||||
x_fc_driver_licence_class = fields.Char(
|
||||
string='Licence Class',
|
||||
help='Driver licence class — e.g. G, G2, AZ, DZ.',
|
||||
help='Driver licence class - e.g. G, G2, AZ, DZ.',
|
||||
)
|
||||
x_fc_licence_expiry = fields.Date(
|
||||
string='Licence Expiry',
|
||||
|
||||
Reference in New Issue
Block a user