fix(shopfloor): Manager Desk speaks fp.job/fp.job.step end-to-end

The previous shopfloor consolidation kept the data layer correct
(controller queries fp.job.step) but left the UI labels, JS
variables, and RPC kwargs in legacy WO/MO vocabulary. Result:
every label said 'Unassigned WOs' / 'X WO' even though the
underlying records are fp.job.step rows.

Renames throughout:
  wo → step (variable / loop / payload key)
  WO → Step (label)
  unassigned_wos → unassigned_steps  (KPI key)
  active_wos → active_steps
  ready_to_ship_mos → ready_to_ship_jobs
  mo_id / mo_name / expandedMoId → job_id / job_name / expandedJobId
  wo_kind → kind, wo_kind_label → kind_label
  o_fp_mgr_wo_* CSS classes → o_fp_mgr_step_*

RPC routes /fp/manager/assign_worker, /fp/manager/assign_tank,
/fp/manager/take_over: primary kwarg is step_id; workorder_id
accepted as a deprecated alias for one release with a logged
warning, so any uncaught caller doesn't break.

No layout / visual changes — same UI shape, native vocabulary.
SCSS class renames are mechanical (only `_wo_` → `_step_` in
selectors); XML updated in lockstep.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-25 10:38:50 -04:00
parent 596efa0ed3
commit 18b5918d3d
4 changed files with 149 additions and 114 deletions

View File

@@ -2,11 +2,11 @@
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
"""JSON-RPC endpoints for the Manager Dashboard (client action).
"""JSON-RPC endpoints for the Manager Desk (client action).
Native fp.job / fp.job.step edition (consolidated 2026-04-24). All
endpoint URLs are preserved (`/fp/manager/*`); the underlying data
layer is now fp.job + fp.job.step.
Native fp.job / fp.job.step edition. Speaks fp.job/fp.job.step
end-to-end — payload keys, variables, and RPC kwargs all use the
job/step vocabulary.
Manager Desk ergonomics:
- Column 1 ("Needs a Worker") = jobs that have at least one step
@@ -34,8 +34,7 @@ _NEG_JOB_STATES = ('done', 'cancelled')
_ACTIVE_JOB_STATES = ('confirmed', 'in_progress', 'on_hold')
# A step needs an operator and (for wet/bake/mask) the right equipment
# before the operator can tap Start. Mirrors the legacy
# x_fc_is_release_ready compute on mrp.workorder.
# before the operator can tap Start.
def _step_release_readiness(step):
"""Return (is_release_ready, missing_str) for a fp.job.step."""
missing = []
@@ -55,7 +54,7 @@ def _step_release_readiness(step):
def _priority_int(priority):
"""fp.job.priority → int 0/1/2 (parallel of legacy x_fc_priority)."""
"""fp.job.priority → int 0/1/2."""
return {'rush': 2, 'high': 1, 'normal': 0, 'low': 0}.get(priority, 0)
@@ -120,10 +119,10 @@ class FpManagerDashboardController(http.Controller):
)
steps_iter = steps_iter.sorted('sequence')
wo_rows = []
step_rows = []
for s in steps_iter:
ready, missing = readiness_by_step.get(s.id, (False, ''))
wo_rows.append({
step_rows.append({
'id': s.id,
'name': s.name or '',
'workcenter': s.work_centre_id.name or '',
@@ -138,8 +137,8 @@ class FpManagerDashboardController(http.Controller):
'assigned_user_name': s.assigned_user_id.name or '',
'role_id': False,
'role_name': '',
'wo_kind': s.kind or 'other',
'wo_kind_label': dict(s._fields['kind'].selection).get(
'kind': s.kind or 'other',
'kind_label': dict(s._fields['kind'].selection).get(
s.kind, '',
) if s.kind else '',
'is_release_ready': ready,
@@ -150,8 +149,8 @@ class FpManagerDashboardController(http.Controller):
})
return {
'mo_id': job.id,
'mo_name': job.name or '',
'job_id': job.id,
'job_name': job.name or '',
'so_name': job.origin or '',
'customer': partner.name if partner else '',
'product': job.product_id.display_name if job.product_id else '',
@@ -163,7 +162,7 @@ class FpManagerDashboardController(http.Controller):
'recipe': job.recipe_id.name if job.recipe_id else '',
'priority_any': _priority_int(job.priority),
'current_location': job.current_location or '',
'wos': wo_rows,
'steps': step_rows,
}
unassigned_cards = [_job_card(j) for j in unassigned_jobs]
@@ -256,14 +255,14 @@ class FpManagerDashboardController(http.Controller):
ready_to_ship_jobs = Job.search_count([('state', '=', 'done')])
kpis = {
'unassigned_wos': len(all_steps.filtered(
'unassigned_steps': len(all_steps.filtered(
lambda s: not readiness_by_step.get(s.id, (False, ''))[0],
)),
'active_wos': len(all_steps.filtered(
'active_steps': len(all_steps.filtered(
lambda s: readiness_by_step.get(s.id, (False, ''))[0]
and s.state in ('ready', 'in_progress'),
)),
'ready_to_ship_mos': ready_to_ship_jobs,
'ready_to_ship_jobs': ready_to_ship_jobs,
'pending_accept_sos': pending_accept_sos,
}
@@ -295,10 +294,20 @@ class FpManagerDashboardController(http.Controller):
# Assign a worker to a step
# ------------------------------------------------------------------
@http.route('/fp/manager/assign_worker', type='jsonrpc', auth='user')
def assign_worker(self, workorder_id, user_id):
"""`workorder_id` is the canonical kwarg name from the legacy
XML; it now resolves to a fp.job.step id."""
step = request.env['fp.job.step'].browse(int(workorder_id))
def assign_worker(self, step_id=None, user_id=None, workorder_id=None, **kwargs):
"""Assign an operator to a step. ``step_id`` is the canonical
kwarg; ``workorder_id`` is accepted as a deprecated alias for
one release so any caller we missed doesn't break.
"""
if step_id is None and workorder_id is not None:
_logger.warning(
"workorder_id kwarg is deprecated; use step_id "
"(/fp/manager/assign_worker)",
)
step_id = workorder_id
if not step_id:
return {'ok': False, 'error': 'step_id required'}
step = request.env['fp.job.step'].browse(int(step_id))
if not step.exists():
return {'ok': False, 'error': 'Step not found.'}
step.assigned_user_id = int(user_id) if user_id else False
@@ -313,8 +322,19 @@ class FpManagerDashboardController(http.Controller):
# Reassign or swap tank on a step
# ------------------------------------------------------------------
@http.route('/fp/manager/assign_tank', type='jsonrpc', auth='user')
def assign_tank(self, workorder_id, tank_id):
step = request.env['fp.job.step'].browse(int(workorder_id))
def assign_tank(self, step_id=None, tank_id=None, workorder_id=None, **kwargs):
"""Swap the tank on a step. ``step_id`` is the canonical kwarg;
``workorder_id`` is accepted as a deprecated alias.
"""
if step_id is None and workorder_id is not None:
_logger.warning(
"workorder_id kwarg is deprecated; use step_id "
"(/fp/manager/assign_tank)",
)
step_id = workorder_id
if not step_id:
return {'ok': False, 'error': 'step_id required'}
step = request.env['fp.job.step'].browse(int(step_id))
if not step.exists():
return {'ok': False, 'error': 'Step not found.'}
step.tank_id = int(tank_id) if tank_id else False
@@ -329,8 +349,19 @@ class FpManagerDashboardController(http.Controller):
# Manager takes over a step (no-show coverage)
# ------------------------------------------------------------------
@http.route('/fp/manager/take_over', type='jsonrpc', auth='user')
def take_over(self, workorder_id):
step = request.env['fp.job.step'].browse(int(workorder_id))
def take_over(self, step_id=None, workorder_id=None, **kwargs):
"""Manager takes over a step. ``step_id`` is the canonical kwarg;
``workorder_id`` is accepted as a deprecated alias.
"""
if step_id is None and workorder_id is not None:
_logger.warning(
"workorder_id kwarg is deprecated; use step_id "
"(/fp/manager/take_over)",
)
step_id = workorder_id
if not step_id:
return {'ok': False, 'error': 'step_id required'}
step = request.env['fp.job.step'].browse(int(step_id))
if not step.exists():
return {'ok': False, 'error': 'Step not found.'}
user = request.env.user