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:
@@ -46,11 +46,11 @@ class TestFpJobStateMachine(TransactionCase):
|
||||
job.action_confirm()
|
||||
|
||||
def test_cannot_cancel_done(self):
|
||||
# Done jobs cannot be cancelled — covers the UserError branch in
|
||||
# Done jobs cannot be cancelled - covers the UserError branch in
|
||||
# action_cancel.
|
||||
job = self._make_job()
|
||||
job.action_confirm()
|
||||
# Force the state to 'done' for the test (no public action yet —
|
||||
# Force the state to 'done' for the test (no public action yet -
|
||||
# done is set by step-completion logic landing in Task 1.5+).
|
||||
job.state = 'done'
|
||||
with self.assertRaises(UserError):
|
||||
@@ -71,7 +71,7 @@ class TestFpJobStateMachine(TransactionCase):
|
||||
job = self._make_job()
|
||||
# Force state to 'done' (no public action yet)
|
||||
job.state = 'done'
|
||||
# Recompute — Odoo's compute is auto on read
|
||||
# Recompute - Odoo's compute is auto on read
|
||||
self.assertEqual(job.current_location, 'Done')
|
||||
|
||||
def test_margin_zero_when_no_revenue(self):
|
||||
|
||||
@@ -33,7 +33,7 @@ class TestFpJobStepStateMachine(TransactionCase):
|
||||
|
||||
def test_button_start_requires_ready_or_paused(self):
|
||||
step = self._make_step()
|
||||
# state is 'pending' — should raise
|
||||
# state is 'pending' - should raise
|
||||
with self.assertRaises(UserError):
|
||||
step.button_start()
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class TestFpWorkCentre(TransactionCase):
|
||||
|
||||
def test_facility_optional_at_create(self):
|
||||
# Facility is soft-required (warning at confirm, not constraint
|
||||
# at create) — verify a centre without facility still creates.
|
||||
# at create) - verify a centre without facility still creates.
|
||||
wc = self.env['fp.work.centre'].create({
|
||||
'name': 'Test',
|
||||
'code': 'T',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
"""Phase E (Plating permissions overhaul) — role-based landing dispatch.
|
||||
"""Phase E (Plating permissions overhaul) - role-based landing dispatch.
|
||||
|
||||
Section 3 of the design spec covers per-role landing pages:
|
||||
|
||||
@@ -57,7 +57,7 @@ class TestLandingResolver(TransactionCase):
|
||||
|
||||
The resolver lives on `ir.actions.act_window` (helper method, not a
|
||||
column). It can return an action dict for either an act_window or a
|
||||
client action — both carry an `xml_id` key once we go through
|
||||
client action - both carry an `xml_id` key once we go through
|
||||
`_render_resolved`.
|
||||
"""
|
||||
Window = self.env['ir.actions.act_window']
|
||||
@@ -123,7 +123,7 @@ class TestLandingResolver(TransactionCase):
|
||||
"""The legacy 'fp_shopfloor_landing' component was retired
|
||||
2026-05-25. The ``fusion_plating_shopfloor.layout`` flag is now
|
||||
orphaned (kept in res.config.settings for one release cycle) and
|
||||
flipping it must NOT change the landing — every technician lands
|
||||
flipping it must NOT change the landing - every technician lands
|
||||
on the plant kanban."""
|
||||
self.env['ir.config_parameter'].sudo().set_param(
|
||||
'fusion_plating_shopfloor.layout', 'legacy')
|
||||
|
||||
@@ -14,7 +14,7 @@ class TestMenuVisibility(TransactionCase):
|
||||
'email': f'menu_{name}@example.com',
|
||||
'group_ids': [(6, 0, [self.env.ref(xmlid).id])] if xmlid else [(6, 0, [])],
|
||||
})
|
||||
# "No" user has only base.group_user — no plating group
|
||||
# "No" user has only base.group_user - no plating group
|
||||
no_user = Users.create({
|
||||
'login': 'menu_no', 'name': 'Menu Test no',
|
||||
'email': 'menu_no@example.com',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Phase 1 — rack-load core model tests.
|
||||
# Phase 1 - rack-load core model tests.
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class TestRoleGroupsStructure(TransactionCase):
|
||||
|
||||
def test_all_seven_groups_exist(self):
|
||||
"""The 7 new res.groups records must all be defined. (The 8th role 'No'
|
||||
is implicit — absence of any plating group.)"""
|
||||
is implicit - absence of any plating group.)"""
|
||||
xmlids = {
|
||||
'group_fp_technician', 'group_fp_sales_rep',
|
||||
'group_fp_shop_manager_v2', 'group_fp_sales_manager',
|
||||
@@ -33,7 +33,7 @@ class TestRoleGroupsStructure(TransactionCase):
|
||||
'Owner must transitively imply base.group_system')
|
||||
|
||||
def test_manager_implies_both_branches(self):
|
||||
"""Manager is the diamond apex — must imply both Shop Manager and Sales Manager."""
|
||||
"""Manager is the diamond apex - must imply both Shop Manager and Sales Manager."""
|
||||
mgr = self.env.ref('fusion_plating.group_fp_manager')
|
||||
sm = self.env.ref('fusion_plating.group_fp_shop_manager_v2')
|
||||
sales_mgr = self.env.ref('fusion_plating.group_fp_sales_manager')
|
||||
|
||||
@@ -36,7 +36,7 @@ class TestSalesManagerGate(TransactionCase):
|
||||
self.assertEqual(self.so.state, 'sale')
|
||||
|
||||
def test_manager_can_confirm(self):
|
||||
# Manager implies Sales Manager via the diamond — should also be able to confirm
|
||||
# Manager implies Sales Manager via the diamond - should also be able to confirm
|
||||
u_mgr = self.env['res.users'].with_context(no_reset_password=True).create({
|
||||
'login': 'gate_mgr', 'name': 'Gate Mgr',
|
||||
'email': 'gate_mgr@example.com',
|
||||
|
||||
@@ -78,7 +78,7 @@ class TestSimpleRecipeFlatten(TransactionCase):
|
||||
def test_nested_operations_surface_with_path(self):
|
||||
ops = self._flatten()
|
||||
names = [n.name for n, _ in ops]
|
||||
# Op B / Op C live INSIDE Sub-X — the old load returned 3 ops
|
||||
# Op B / Op C live INSIDE Sub-X - the old load returned 3 ops
|
||||
# (Op A, Op D, plus Sub-X itself); the new one returns 4
|
||||
# operations and skips the sub_process node.
|
||||
self.assertEqual(names, ['Op A', 'Op B', 'Op C', 'Op D'])
|
||||
@@ -156,7 +156,7 @@ class TestSimpleRecipeFlatten(TransactionCase):
|
||||
'name': 'A1', 'node_type': 'step',
|
||||
'parent_id': self.op_a.id, 'sequence': 10,
|
||||
})
|
||||
# Mock the request — load() reads request.env.
|
||||
# Mock the request - load() reads request.env.
|
||||
from unittest.mock import patch
|
||||
ctrl = SimpleRecipeController()
|
||||
class FakeReq:
|
||||
@@ -230,7 +230,7 @@ class TestSimpleRecipeFlatten(TransactionCase):
|
||||
path = ('odoo.addons.fusion_plating.controllers.'
|
||||
'simple_recipe_controller.request')
|
||||
# Demote Op D into Sub-X (its preceding operation is op_a at
|
||||
# the recipe root, but Sub-X is between them — the preceding
|
||||
# the recipe root, but Sub-X is between them - the preceding
|
||||
# OPERATION sibling at the recipe root is op_a).
|
||||
with patch(path, FakeReq()):
|
||||
res = SimpleRecipeController().step_demote(self.op_d.id)
|
||||
@@ -242,7 +242,7 @@ class TestSimpleRecipeFlatten(TransactionCase):
|
||||
self.assertEqual(self.op_d.parent_id.id, self.op_a.id)
|
||||
|
||||
def test_demote_blocks_when_operation_has_children(self):
|
||||
# op_a gets a substep — now demoting op_a should fail because
|
||||
# op_a gets a substep - now demoting op_a should fail because
|
||||
# it has children.
|
||||
self.env['fusion.plating.process.node'].create({
|
||||
'name': 'A-child', 'node_type': 'step',
|
||||
@@ -277,7 +277,7 @@ class TestSimpleRecipeFlatten(TransactionCase):
|
||||
env = self.env
|
||||
path = ('odoo.addons.fusion_plating.controllers.'
|
||||
'simple_recipe_controller.request')
|
||||
# Send reversed order — s2 should come out at seq=10, s1 at 20.
|
||||
# Send reversed order - s2 should come out at seq=10, s1 at 20.
|
||||
with patch(path, FakeReq()):
|
||||
SimpleRecipeController().step_reorder([s2.id, s1.id])
|
||||
s1.invalidate_recordset()
|
||||
|
||||
@@ -3,7 +3,7 @@ from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
@tagged('-at_install', 'post_install', 'fp_perms')
|
||||
class TestTeamPage(TransactionCase):
|
||||
"""Phase F — Owner-only Team management page.
|
||||
"""Phase F - Owner-only Team management page.
|
||||
Covers x_fc_plating_role compute/inverse + audit chatter + menu visibility."""
|
||||
|
||||
def setUp(self):
|
||||
@@ -38,7 +38,7 @@ class TestTeamPage(TransactionCase):
|
||||
# Technician is implied via shop_manager_v2.implied_ids → so it IS in user's
|
||||
# transitive group set. But the inverse should NOT have ADDED it directly.
|
||||
# Verify by checking groups_id (which Odoo stores as the union of explicit
|
||||
# + implied groups) — Technician will be present via implication. That's
|
||||
# + implied groups) - Technician will be present via implication. That's
|
||||
# correct. What we want to verify is no OTHER plating role is set explicitly.
|
||||
# Easier assertion: after setting to shop_manager, compute should return
|
||||
# shop_manager (highest plating role held).
|
||||
|
||||
Reference in New Issue
Block a user