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:
@@ -116,7 +116,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
return partner.commercial_partner_id
|
||||
|
||||
# ==========================================================================
|
||||
# Sidebar — items + active-state resolution
|
||||
# Sidebar - items + active-state resolution
|
||||
# ==========================================================================
|
||||
# Sidebar item structure: list of dicts with `type` = 'item' | 'section_label'.
|
||||
# Items have label / url / icon / key. Key matches either a page_name set by
|
||||
@@ -150,7 +150,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
'fp_account_summary': 'fp_account_summary',
|
||||
}
|
||||
_FP_URL_PREFIX_TO_SIDEBAR_KEY = [
|
||||
# Order matters — first match wins, so list longer prefixes first.
|
||||
# Order matters - first match wins, so list longer prefixes first.
|
||||
('/my/orders', 'odoo_orders'),
|
||||
('/my/quotes', 'odoo_orders'), # /my/quotes is also sale_portal
|
||||
('/my/invoices', 'fp_account_summary'),
|
||||
@@ -194,7 +194,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
partner = request.env.user.partner_id
|
||||
commercial = partner.commercial_partner_id
|
||||
values['fp_partner_display_name'] = commercial.name or partner.name
|
||||
# Internal staff (share=False) get the clean employee experience — no
|
||||
# Internal staff (share=False) get the clean employee experience - no
|
||||
# customer sidebar. Customers (share=True / portal users) keep it.
|
||||
values['fp_show_customer_sidebar'] = bool(request.env.user.share)
|
||||
return values
|
||||
@@ -204,7 +204,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
# funnel through this helper. It sets up chatter/pager but doesn't
|
||||
# touch _prepare_portal_layout_values, so our sidebar context wouldn't
|
||||
# otherwise reach those templates. Inject our keys conservatively via
|
||||
# setdefault — never overwrite anything the page already set.
|
||||
# setdefault - never overwrite anything the page already set.
|
||||
values = super()._get_page_view_values(
|
||||
document, access_token, values, session_history, no_breadcrumbs, **kwargs,
|
||||
)
|
||||
@@ -219,7 +219,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
# ==========================================================================
|
||||
# 5 customer-facing stages aligned with the dashboard stepper.
|
||||
# Each entry: (label, timestamp_field_name_on_fp_portal_job).
|
||||
# Inspected and Plating share `in_progress_started_at` — when state moves
|
||||
# Inspected and Plating share `in_progress_started_at` - when state moves
|
||||
# away from 'received' it means inspection finished and plating started.
|
||||
_FP_STAGES = [
|
||||
('Received', 'received_at'),
|
||||
@@ -255,7 +255,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
Records created post-hook never hit the interpolation branch.
|
||||
"""
|
||||
state_idx = self._FP_STATE_TO_STEP_IDX.get(job.state, 0)
|
||||
# Baseline datetime for interpolation — prefer the precise received_at
|
||||
# Baseline datetime for interpolation - prefer the precise received_at
|
||||
# but fall through to received_date (Date) converted to midnight.
|
||||
baseline = job.received_at
|
||||
if not baseline and job.received_date:
|
||||
@@ -321,7 +321,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
of {label, sub, url, icon_class, icon, pending}.
|
||||
"""
|
||||
# 5 fixed groups in display order. Indices used in the appends below
|
||||
# — if you reorder, update every groups[N] reference.
|
||||
# - if you reorder, update every groups[N] reference.
|
||||
# 0 = from_you, 1 = specs, 2 = work_order, 3 = quality, 4 = shipping
|
||||
groups = [
|
||||
{'key': 'from_you', 'label': 'From You', 'docs': []},
|
||||
@@ -331,7 +331,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
{'key': 'shipping', 'label': 'Shipping', 'docs': []},
|
||||
]
|
||||
|
||||
# FROM YOU — surface the Sales Order Confirmation via the fp.job
|
||||
# FROM YOU - surface the Sales Order Confirmation via the fp.job
|
||||
# link added by fusion_plating_jobs (job.x_fc_job_id.sale_order_id).
|
||||
# When no SO is linked, fall back to the placeholder so customers
|
||||
# know where to upload their PO + drawings.
|
||||
@@ -381,7 +381,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
'icon': '📄',
|
||||
})
|
||||
|
||||
# SPECIFICATIONS (idx 1) — V1: placeholder (V2 will pull customer spec)
|
||||
# SPECIFICATIONS (idx 1) - V1: placeholder (V2 will pull customer spec)
|
||||
groups[1]['docs'].append({
|
||||
'label': 'Customer Specification',
|
||||
'sub': 'Will appear when EN Plating links the spec',
|
||||
@@ -389,7 +389,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
'icon': '📋',
|
||||
})
|
||||
|
||||
# WORK ORDER (idx 2) — EN Plating WO Detail PDF via
|
||||
# WORK ORDER (idx 2) - EN Plating WO Detail PDF via
|
||||
# fusion_plating_jobs.report_fp_job_wo_detail_template. Requires a
|
||||
# linked backend fp.job; placeholder otherwise.
|
||||
if backend_job:
|
||||
@@ -408,7 +408,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
'icon': '🛠',
|
||||
})
|
||||
|
||||
# QUALITY (idx 3) — CoC from coc_attachment_id (the legacy direct field)
|
||||
# QUALITY (idx 3) - CoC from coc_attachment_id (the legacy direct field)
|
||||
if job.coc_attachment_id:
|
||||
groups[3]['docs'].append({
|
||||
'label': 'Certificate of Conformance',
|
||||
@@ -428,7 +428,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
'icon': '📑',
|
||||
})
|
||||
|
||||
# SHIPPING (idx 4) — packing list + tracking. Two separate
|
||||
# SHIPPING (idx 4) - packing list + tracking. Two separate
|
||||
# docs so each can be pending/ready independently. Previously
|
||||
# combined into one entry; that broke when tracking_ref landed
|
||||
# before the packing slip (KeyError 'url').
|
||||
@@ -480,7 +480,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
return '%.1f MB' % (size / (1024 * 1024))
|
||||
|
||||
# ==========================================================================
|
||||
# Account Summary (Sub-A IA) — invoices + credits + statements
|
||||
# Account Summary (Sub-A IA) - invoices + credits + statements
|
||||
# ==========================================================================
|
||||
_FP_ACCOUNT_SUMMARY_TABS = [
|
||||
('invoices', 'Invoices', 'out_invoice'),
|
||||
@@ -516,18 +516,18 @@ class FpCustomerPortal(CustomerPortal):
|
||||
search, sort, page):
|
||||
"""Return {records, total, pager_offset} for one tab+filter combination.
|
||||
|
||||
tab — 'invoices' | 'credit_memos' | 'statements'
|
||||
filter_state — 'open' | 'closed' | 'all'
|
||||
search — substring matched against name OR ref (case-insensitive)
|
||||
sort — key from _FP_ACCOUNT_SUMMARY_SORTS
|
||||
page — 1-indexed
|
||||
tab - 'invoices' | 'credit_memos' | 'statements'
|
||||
filter_state - 'open' | 'closed' | 'all'
|
||||
search - substring matched against name OR ref (case-insensitive)
|
||||
sort - key from _FP_ACCOUNT_SUMMARY_SORTS
|
||||
page - 1-indexed
|
||||
|
||||
Uses commercial_partner.env so this helper works both in HTTP
|
||||
context and in unit tests without requiring request to be active.
|
||||
"""
|
||||
env = commercial_partner.env
|
||||
if tab == 'statements':
|
||||
# V1 placeholder — Statements is a 'coming soon' tab.
|
||||
# V1 placeholder - Statements is a 'coming soon' tab.
|
||||
return {'records': env['account.move'].browse(), 'total': 0,
|
||||
'offset': 0}
|
||||
|
||||
@@ -621,7 +621,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
)
|
||||
def home(self, **kw):
|
||||
# Internal staff don't belong on the customer dashboard. Send them to
|
||||
# the employee clock portal — but only when fusion_clock is installed
|
||||
# the employee clock portal - but only when fusion_clock is installed
|
||||
# (x_fclk_enable_clock proves it) AND the user actually has an employee
|
||||
# record, otherwise /my/clock -> /my would bounce into a redirect loop.
|
||||
user = request.env.user
|
||||
@@ -851,7 +851,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
methods=['GET'],
|
||||
)
|
||||
def portal_new_quote_request(self, **kw):
|
||||
"""GET — legacy entry point, redirected to the configurator wizard."""
|
||||
"""GET - legacy entry point, redirected to the configurator wizard."""
|
||||
return request.redirect('/my/configurator/new')
|
||||
|
||||
# ==========================================================================
|
||||
@@ -1285,7 +1285,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
type='http', auth='user', website=True,
|
||||
)
|
||||
def portal_my_purchase_orders(self, **kw):
|
||||
"""Legacy URL — redirected to Odoo default /my/orders (Sub-A IA)."""
|
||||
"""Legacy URL - redirected to Odoo default /my/orders (Sub-A IA)."""
|
||||
return request.redirect('/my/orders')
|
||||
|
||||
# ==========================================================================
|
||||
@@ -1296,7 +1296,7 @@ class FpCustomerPortal(CustomerPortal):
|
||||
type='http', auth='user', website=True,
|
||||
)
|
||||
def portal_my_fp_invoices(self, **kw):
|
||||
"""Legacy URL — redirected to /my/account_summary (Sub-A IA)."""
|
||||
"""Legacy URL - redirected to /my/account_summary (Sub-A IA)."""
|
||||
return request.redirect('/my/account_summary')
|
||||
|
||||
# ==========================================================================
|
||||
|
||||
Reference in New Issue
Block a user