Compare commits

...

4 Commits

Author SHA1 Message Date
gsinghpal
27e12dd544 chore(shopfloor): register fp_rpc.js asset + bump to 19.0.30.2.0 (P6.3.6)
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled
Adds the Phase 6.3 fpRpc wrapper to the web.assets_backend bundle.
Placed before its consumers so the `import { fpRpc } from "./services/fp_rpc"`
calls in job_workspace, shopfloor_landing, manager_dashboard, and
hold_composer resolve.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:47:51 -04:00
gsinghpal
5f03080374 feat(shopfloor): switch action-path RPCs to fpRpc + wire plant_overview/move_card (P6.3.5)
JobWorkspace, ShopfloorLanding, ManagerDashboard, and the embedded
FpHoldComposer now call fpRpc() for write-path endpoints (start/finish
step, hold create, sign-off, milestone advance, work-centre move,
assign-worker, assign-tank, manager takeover). fpRpc auto-injects
tablet_tech_id from the tech_store so the server can rebind env via
env_for_tablet_tech() and credit the right user.

Read-path RPCs (workspace/load, landing/kanban, manager/overview,
manager/funnel, manager/approval_inbox, manager/at_risk, shopfloor/scan)
stay as plain rpc() — no audit benefit, no need for the extra plumbing.

Also wires tablet_tech_id into /fp/shopfloor/plant_overview/move_card
which I missed in P6.3.3 — surfaced when grepping JS for write callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:47:20 -04:00
gsinghpal
efaf16dffb feat(shopfloor): propagate tablet_tech_id to shopfloor + manager action endpoints (P6.3.3 + P6.3.4)
10 endpoints in shopfloor_controller (log_chemistry, start_bake, end_bake,
start_wo, stop_wo, bump_qty_done, bump_qty_scrapped, log_thickness_reading,
quality_hold, mark_gate) and 3 in manager_controller (assign_worker,
assign_tank, take_over) now accept a `tablet_tech_id` kwarg. Each rebinds
env via env_for_tablet_tech() so writes carry the correct uid even when
the OS session belongs to the persistent tablet user.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:43:44 -04:00
gsinghpal
e4000374ca feat(fusion_plating_shopfloor): wire tablet_tech_id into workspace endpoints (P6.3.2)
hold, sign_off, advance_milestone each accept tablet_tech_id and
rebind env via env_for_tablet_tech. Writes (Hold.create, button_finish,
action_advance_next_milestone) now carry the tech-of-record's uid.
load endpoint is read-only and untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:37:58 -04:00
8 changed files with 101 additions and 58 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Shop Floor',
'version': '19.0.30.1.0',
'version': '19.0.30.2.0',
'category': 'Manufacturing/Plating',
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, '
'first-piece inspection gates.',
@@ -85,6 +85,10 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved.
# ---- Phase 6.2 tablet PIN gate ----
'fusion_plating_shopfloor/static/src/js/services/tech_store.js',
'fusion_plating_shopfloor/static/src/js/services/activity_tracker.js',
# Phase 6.3 — fpRpc wrapper. MUST load before any consumer
# (job_workspace, shopfloor_landing, manager_dashboard,
# hold_composer) so `import { fpRpc }` resolves.
'fusion_plating_shopfloor/static/src/js/services/fp_rpc.js',
'fusion_plating_shopfloor/static/src/scss/components/_pin_pad.scss',
'fusion_plating_shopfloor/static/src/xml/components/pin_pad.xml',
'fusion_plating_shopfloor/static/src/js/components/pin_pad.js',

View File

@@ -25,6 +25,8 @@ from odoo import fields, http
from odoo.addons.fusion_plating.models.fp_tz import fp_format
from odoo.http import request
from ._tablet_audit import env_for_tablet_tech
_logger = logging.getLogger(__name__)
@@ -387,7 +389,8 @@ class FpManagerDashboardController(http.Controller):
# Assign a worker to a step
# ------------------------------------------------------------------
@http.route('/fp/manager/assign_worker', type='jsonrpc', auth='user')
def assign_worker(self, step_id=None, user_id=None, workorder_id=None, **kwargs):
def assign_worker(self, step_id=None, user_id=None, workorder_id=None,
tablet_tech_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.
@@ -400,7 +403,8 @@ class FpManagerDashboardController(http.Controller):
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))
env = env_for_tablet_tech(request.env, tablet_tech_id)
step = 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
@@ -415,7 +419,8 @@ 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, step_id=None, tank_id=None, workorder_id=None, **kwargs):
def assign_tank(self, step_id=None, tank_id=None, workorder_id=None,
tablet_tech_id=None, **kwargs):
"""Swap the tank on a step. ``step_id`` is the canonical kwarg;
``workorder_id`` is accepted as a deprecated alias.
"""
@@ -427,7 +432,8 @@ class FpManagerDashboardController(http.Controller):
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))
env = env_for_tablet_tech(request.env, tablet_tech_id)
step = 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
@@ -442,7 +448,8 @@ 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, step_id=None, workorder_id=None, **kwargs):
def take_over(self, step_id=None, workorder_id=None,
tablet_tech_id=None, **kwargs):
"""Manager takes over a step. ``step_id`` is the canonical kwarg;
``workorder_id`` is accepted as a deprecated alias.
"""
@@ -454,10 +461,11 @@ class FpManagerDashboardController(http.Controller):
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))
env = env_for_tablet_tech(request.env, tablet_tech_id)
step = env['fp.job.step'].browse(int(step_id))
if not step.exists():
return {'ok': False, 'error': 'Step not found.'}
user = request.env.user
user = env.user
previous = step.assigned_user_id.name or ''
step.assigned_user_id = user.id
step.message_post(

View File

@@ -20,6 +20,8 @@ from odoo.addons.fusion_plating.models.fp_tz import (
from odoo.exceptions import UserError
from odoo.http import request
from ._tablet_audit import env_for_tablet_tech
_logger = logging.getLogger(__name__)
@@ -255,11 +257,13 @@ class FpShopfloorController(http.Controller):
# Quick chemistry log from the tablet
# ----------------------------------------------------------------------
@http.route('/fp/shopfloor/log_chemistry', type='jsonrpc', auth='user')
def log_chemistry(self, bath_id, readings, shift=None, notes=None):
def log_chemistry(self, bath_id, readings, shift=None, notes=None,
tablet_tech_id=None):
"""Create a fusion.plating.bath.log with one line per reading."""
env = env_for_tablet_tech(request.env, tablet_tech_id)
if not bath_id:
raise UserError("bath_id required")
bath = request.env['fusion.plating.bath'].browse(int(bath_id))
bath = env['fusion.plating.bath'].browse(int(bath_id))
if not bath.exists():
raise UserError(f"Bath {bath_id} not found")
@@ -274,7 +278,7 @@ class FpShopfloorController(http.Controller):
'value': float(value) if value not in (None, '') else 0.0,
}))
log = request.env['fusion.plating.bath.log'].create({
log = env['fusion.plating.bath.log'].create({
'bath_id': bath.id,
'shift': shift or False,
'notes': notes or False,
@@ -291,10 +295,11 @@ class FpShopfloorController(http.Controller):
# Bake window controls
# ----------------------------------------------------------------------
@http.route('/fp/shopfloor/start_bake', type='jsonrpc', auth='user')
def start_bake(self, bake_window_id, oven_id=None):
def start_bake(self, bake_window_id, oven_id=None, tablet_tech_id=None):
# action_start_bake raises UserError for S6 missed_window. Wrap
# the same way as start_wo so operator gets a clean flash.
bw = request.env['fusion.plating.bake.window'].browse(int(bake_window_id))
env = env_for_tablet_tech(request.env, tablet_tech_id)
bw = env['fusion.plating.bake.window'].browse(int(bake_window_id))
if not bw.exists():
return {'ok': False, 'error': f'Bake window {bake_window_id} not found'}
if oven_id:
@@ -306,12 +311,13 @@ class FpShopfloorController(http.Controller):
return {
'ok': True,
'state': bw.state,
'bake_start_time': fp_format(request.env, bw.bake_start_time),
'bake_start_time': fp_format(env, bw.bake_start_time),
}
@http.route('/fp/shopfloor/end_bake', type='jsonrpc', auth='user')
def end_bake(self, bake_window_id):
bw = request.env['fusion.plating.bake.window'].browse(int(bake_window_id))
def end_bake(self, bake_window_id, tablet_tech_id=None):
env = env_for_tablet_tech(request.env, tablet_tech_id)
bw = env['fusion.plating.bake.window'].browse(int(bake_window_id))
if not bw.exists():
return {'ok': False, 'error': f'Bake window {bake_window_id} not found'}
try:
@@ -321,7 +327,7 @@ class FpShopfloorController(http.Controller):
return {
'ok': True,
'state': bw.state,
'bake_end_time': fp_format(request.env, bw.bake_end_time),
'bake_end_time': fp_format(env, bw.bake_end_time),
'bake_duration_hours': bw.bake_duration_hours,
}
@@ -340,8 +346,15 @@ class FpShopfloorController(http.Controller):
step = request.env['fp.job.step'].browse(int(sid))
return step if step.exists() else False
def _resolve_step_in_env(self, env, step_id=None, workorder_id=None):
sid = step_id if step_id else workorder_id
if not sid:
return False
step = env['fp.job.step'].browse(int(sid))
return step if step.exists() else False
@http.route('/fp/shopfloor/start_wo', type='jsonrpc', auth='user')
def start_wo(self, workorder_id=None, step_id=None):
def start_wo(self, workorder_id=None, step_id=None, tablet_tech_id=None):
"""Start the timer on a fp.job.step (called from the tablet).
button_start() can raise UserError for any guarded condition
@@ -350,7 +363,8 @@ class FpShopfloorController(http.Controller):
the explicit state check, so the tablet flashes a clean toast
instead of popping a stack-trace dialog at the operator.
"""
step = self._resolve_step(step_id, workorder_id)
env = env_for_tablet_tech(request.env, tablet_tech_id)
step = self._resolve_step_in_env(env, step_id, workorder_id)
if not step:
return {'ok': False, 'error': 'Step not found'}
if not _step_can_start(step):
@@ -369,7 +383,8 @@ class FpShopfloorController(http.Controller):
}
@http.route('/fp/shopfloor/stop_wo', type='jsonrpc', auth='user')
def stop_wo(self, workorder_id=None, step_id=None, finish=False):
def stop_wo(self, workorder_id=None, step_id=None, finish=False,
tablet_tech_id=None):
"""Finish the timer on a fp.job.step.
finish=True calls button_finish(); other values are no-ops for
@@ -380,7 +395,8 @@ class FpShopfloorController(http.Controller):
not provided). Wrapped same as start_wo so the operator gets a
clean flash, not a stack-trace dialog.
"""
step = self._resolve_step(step_id, workorder_id)
env = env_for_tablet_tech(request.env, tablet_tech_id)
step = self._resolve_step_in_env(env, step_id, workorder_id)
if not step:
return {'ok': False, 'error': 'Step not found'}
if finish:
@@ -409,11 +425,12 @@ class FpShopfloorController(http.Controller):
# both with a single tap. Scrap auto-spawns a hold via fp.job.write
# (S17 hook, no extra wiring needed here).
@http.route('/fp/shopfloor/bump_qty_done', type='jsonrpc', auth='user')
def bump_qty_done(self, job_id, delta=1):
def bump_qty_done(self, job_id, delta=1, tablet_tech_id=None):
"""Increment job.qty_done by `delta` (defaults to +1).
Returns the new totals so the tablet can update without a full refresh.
"""
job = request.env['fp.job'].browse(int(job_id))
env = env_for_tablet_tech(request.env, tablet_tech_id)
job = env['fp.job'].browse(int(job_id))
if not job.exists():
return {'ok': False, 'error': 'Job not found'}
try:
@@ -433,13 +450,15 @@ class FpShopfloorController(http.Controller):
}
@http.route('/fp/shopfloor/bump_qty_scrapped', type='jsonrpc', auth='user')
def bump_qty_scrapped(self, job_id, delta=1, reason=None):
def bump_qty_scrapped(self, job_id, delta=1, reason=None,
tablet_tech_id=None):
"""Increment job.qty_scrapped by `delta`. The S17 write-hook on
fp.job auto-spawns a fusion.plating.quality.hold for the delta;
the operator can edit the description on that hold later.
`reason` is optional — passed through to the hold's description.
"""
job = request.env['fp.job'].browse(int(job_id))
env = env_for_tablet_tech(request.env, tablet_tech_id)
job = env['fp.job'].browse(int(job_id))
if not job.exists():
return {'ok': False, 'error': 'Job not found'}
try:
@@ -470,20 +489,22 @@ class FpShopfloorController(http.Controller):
position_label=None, reading_number=None,
equipment_model=None, calibration_std_ref=None,
microscope_image=None,
microscope_image_filename=None):
microscope_image_filename=None,
tablet_tech_id=None):
"""Record a single Fischerscope reading against a job.
`job_id` is the canonical kwarg; `production_id` is accepted as an
alias for older clients. The reading auto-links to an existing
CoC certificate for the job when one exists.
"""
Reading = request.env.get('fp.thickness.reading')
env = env_for_tablet_tech(request.env, tablet_tech_id)
Reading = env.get('fp.thickness.reading')
if Reading is None:
return {'ok': False, 'error': 'Certificates module not installed'}
target_id = job_id or production_id
if not target_id:
return {'ok': False, 'error': 'job_id required'}
job = request.env['fp.job'].browse(int(target_id))
job = env['fp.job'].browse(int(target_id))
if not job.exists():
return {'ok': False, 'error': f'Job {target_id} not found'}
@@ -508,7 +529,7 @@ class FpShopfloorController(http.Controller):
'ni_percent': float(ni_percent or 0.0),
'p_percent': float(p_percent or 0.0),
'position_label': position_label or '',
'operator_id': request.env.user.id,
'operator_id': env.user.id,
}
if equipment_model:
@@ -516,7 +537,7 @@ class FpShopfloorController(http.Controller):
if calibration_std_ref:
vals['calibration_std_ref'] = calibration_std_ref
if microscope_image:
att = request.env['ir.attachment'].create({
att = env['ir.attachment'].create({
'name': microscope_image_filename or f'thickness_{reading_number}.jpg',
'datas': microscope_image,
'res_model': 'fp.thickness.reading',
@@ -525,7 +546,7 @@ class FpShopfloorController(http.Controller):
vals['microscope_image_id'] = att.id
# Auto-link to an existing CoC if there is one for this job.
Cert = request.env.get('fp.certificate')
Cert = env.get('fp.certificate')
if Cert is not None:
if 'x_fc_job_id' in Cert._fields:
cert_field = 'x_fc_job_id'
@@ -557,7 +578,8 @@ class FpShopfloorController(http.Controller):
part_ref=None, qty_on_hold=0, qty_original=0,
hold_reason='other', description=None,
mark_for_scrap=False, facility_id=None,
work_center_id=None, current_process_node=None):
work_center_id=None, current_process_node=None,
tablet_tech_id=None):
"""Create a quality hold record, splitting qty from the original lot.
The hold is linked to the fp.job and (when provided) the
@@ -566,7 +588,8 @@ class FpShopfloorController(http.Controller):
if not qty_on_hold or int(qty_on_hold) <= 0:
raise UserError("qty_on_hold must be a positive integer.")
Hold = request.env['fusion.plating.quality.hold']
env = env_for_tablet_tech(request.env, tablet_tech_id)
Hold = env['fusion.plating.quality.hold']
vals = {
'part_ref': part_ref or '',
@@ -583,7 +606,7 @@ class FpShopfloorController(http.Controller):
if work_center_id:
vals['work_center_id'] = int(work_center_id)
if portal_job_id:
pj = request.env['fusion.plating.portal.job'].browse(
pj = env['fusion.plating.portal.job'].browse(
int(portal_job_id),
)
if pj.exists():
@@ -594,7 +617,7 @@ class FpShopfloorController(http.Controller):
# via fusion_plating_jobs (Phase 3) as `x_fc_job_id` / `x_fc_step_id`.
step_target_id = step_id or workorder_id
if step_target_id:
step = request.env['fp.job.step'].browse(int(step_target_id))
step = env['fp.job.step'].browse(int(step_target_id))
if step.exists():
if 'x_fc_step_id' in Hold._fields:
vals['x_fc_step_id'] = step.id
@@ -605,7 +628,7 @@ class FpShopfloorController(http.Controller):
# set it through the step.
if (job_id and 'x_fc_job_id' in Hold._fields
and not vals.get('x_fc_job_id')):
j = request.env['fp.job'].browse(int(job_id))
j = env['fp.job'].browse(int(job_id))
if j.exists():
vals['x_fc_job_id'] = j.id
@@ -995,8 +1018,9 @@ class FpShopfloorController(http.Controller):
# Mark a first-piece gate result from the tablet
# ----------------------------------------------------------------------
@http.route('/fp/shopfloor/mark_gate', type='jsonrpc', auth='user')
def mark_gate(self, gate_id, result):
gate = request.env['fusion.plating.first.piece.gate'].browse(int(gate_id))
def mark_gate(self, gate_id, result, tablet_tech_id=None):
env = env_for_tablet_tech(request.env, tablet_tech_id)
gate = env['fusion.plating.first.piece.gate'].browse(int(gate_id))
if not gate.exists():
return {'ok': False, 'error': 'Gate not found.'}
try:
@@ -1084,21 +1108,23 @@ class FpShopfloorController(http.Controller):
@http.route('/fp/shopfloor/plant_overview/move_card',
type='jsonrpc', auth='user')
def plant_overview_move_card(self, card_id, source_model=None,
target_workcenter_id=None):
target_workcenter_id=None,
tablet_tech_id=None):
"""Move a step card to a different work centre (drag & drop).
`source_model` is accepted for backward compatibility but ignored —
Plant Overview now only ever serves fp.job.step cards. A target
of 0 / falsy clears the work centre.
"""
Step = request.env['fp.job.step']
env = env_for_tablet_tech(request.env, tablet_tech_id)
Step = env['fp.job.step']
step = Step.browse(int(card_id))
if not step.exists():
return {'ok': False, 'error': f'Step {card_id} not found.'}
wc_id = int(target_workcenter_id) if target_workcenter_id else False
if wc_id:
wc = request.env['fp.work.centre'].browse(wc_id)
wc = env['fp.work.centre'].browse(wc_id)
if not wc.exists():
return {'ok': False,
'error': f'Work centre {target_workcenter_id} not found.'}
@@ -1108,7 +1134,7 @@ class FpShopfloorController(http.Controller):
_logger.info(
'Plant Overview: moved step %s (%s) → WC %s by uid %s',
step.id, step.name, wc_id or 'unassigned',
request.env.uid,
env.uid,
)
except Exception as exc:
_logger.exception('Plant Overview move_card failed')

View File

@@ -23,6 +23,8 @@ from odoo import fields, http
from odoo.addons.fusion_plating.models.fp_tz import fp_format
from odoo.http import request
from ._tablet_audit import env_for_tablet_tech
_logger = logging.getLogger(__name__)
@@ -190,8 +192,8 @@ class FpWorkspaceController(http.Controller):
@http.route('/fp/workspace/hold', type='jsonrpc', auth='user')
def hold(self, job_id, reason='other', qty_on_hold=1, description='',
part_ref='', step_id=None, mark_for_scrap=False,
photo_data=None, photo_filename=None):
env = request.env
photo_data=None, photo_filename=None, tablet_tech_id=None):
env = env_for_tablet_tech(request.env, tablet_tech_id)
job = env['fp.job'].browse(int(job_id))
if not job.exists():
return {'ok': False, 'error': f'Job {job_id} not found'}
@@ -253,8 +255,8 @@ class FpWorkspaceController(http.Controller):
# /fp/workspace/sign_off — capture signature + finish step atomically
# ======================================================================
@http.route('/fp/workspace/sign_off', type='jsonrpc', auth='user')
def sign_off(self, step_id, signature_data_uri):
env = request.env
def sign_off(self, step_id, signature_data_uri, tablet_tech_id=None):
env = env_for_tablet_tech(request.env, tablet_tech_id)
sig = (signature_data_uri or '').strip()
if not sig:
_logger.warning("workspace/sign_off: empty signature for step %s", step_id)
@@ -302,8 +304,8 @@ class FpWorkspaceController(http.Controller):
# /fp/workspace/advance_milestone — fire next_milestone_action
# ======================================================================
@http.route('/fp/workspace/advance_milestone', type='jsonrpc', auth='user')
def advance_milestone(self, job_id):
env = request.env
def advance_milestone(self, job_id, tablet_tech_id=None):
env = env_for_tablet_tech(request.env, tablet_tech_id)
job = env['fp.job'].browse(int(job_id))
if not job.exists():
return {'ok': False, 'error': f'Job {job_id} not found'}

View File

@@ -14,7 +14,7 @@
import { Component, useState } from "@odoo/owl";
import { Dialog } from "@web/core/dialog/dialog";
import { rpc } from "@web/core/network/rpc";
import { fpRpc } from "../services/fp_rpc";
import { useService } from "@web/core/utils/hooks";
// Hold reasons kept here so the picker doesn't need a server roundtrip.
@@ -75,7 +75,7 @@ export class FpHoldComposer extends Component {
}
this.state.submitting = true;
try {
const res = await rpc("/fp/workspace/hold", {
const res = await fpRpc("/fp/workspace/hold", {
job_id: this.props.jobId,
step_id: this.props.stepId || null,
part_ref: this.props.partRef || "",

View File

@@ -20,6 +20,7 @@
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";
import { fpRpc } from "./services/fp_rpc";
import { useService } from "@web/core/utils/hooks";
import { WorkflowChip } from "./components/workflow_chip";
import { GateViz } from "./components/gate_viz";
@@ -116,7 +117,7 @@ export class FpJobWorkspace extends Component {
// ---- Step actions ------------------------------------------------------
async onStartStep(stepId) {
try {
const res = await rpc("/fp/shopfloor/start_wo", { workorder_id: stepId });
const res = await fpRpc("/fp/shopfloor/start_wo", { workorder_id: stepId });
if (res && res.ok) {
this.notification.add("Step started.", { type: "success" });
await this.refresh();
@@ -135,7 +136,7 @@ export class FpJobWorkspace extends Component {
contextLabel: `${this.state.data.job.display_wo_name} · Step ${step.sequence_display}: ${step.name}`,
onSubmit: async (dataUri) => {
try {
const res = await rpc("/fp/workspace/sign_off", {
const res = await fpRpc("/fp/workspace/sign_off", {
step_id: step.id,
signature_data_uri: dataUri,
});
@@ -154,7 +155,7 @@ export class FpJobWorkspace extends Component {
}
// Plain finish — no signature required
try {
const res = await rpc("/fp/shopfloor/stop_wo", {
const res = await fpRpc("/fp/shopfloor/stop_wo", {
workorder_id: step.id, finish: true,
});
if (res && res.ok) {
@@ -201,7 +202,7 @@ export class FpJobWorkspace extends Component {
async onAdvanceMilestone() {
try {
const res = await rpc("/fp/workspace/advance_milestone", {
const res = await fpRpc("/fp/workspace/advance_milestone", {
job_id: this.state.jobId,
});
if (res && res.ok) {

View File

@@ -15,6 +15,7 @@
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";
import { fpRpc } from "./services/fp_rpc";
import { useService } from "@web/core/utils/hooks";
import { QrScanner } from "./qr_scanner";
import { FpTabletLock } from "./tablet_lock";
@@ -208,7 +209,7 @@ export class ManagerDashboard extends Component {
async onAssignWorker(step, userIdRaw) {
const userId = parseInt(userIdRaw) || null;
try {
const res = await rpc("/fp/manager/assign_worker", {
const res = await fpRpc("/fp/manager/assign_worker", {
step_id: step.id, user_id: userId,
});
if (res && res.ok) {
@@ -226,7 +227,7 @@ export class ManagerDashboard extends Component {
async onAssignTank(step, tankIdRaw) {
const tankId = parseInt(tankIdRaw) || null;
try {
const res = await rpc("/fp/manager/assign_tank", {
const res = await fpRpc("/fp/manager/assign_tank", {
step_id: step.id, tank_id: tankId,
});
if (res && res.ok) {
@@ -243,7 +244,7 @@ export class ManagerDashboard extends Component {
async onTakeOver(step) {
try {
const res = await rpc("/fp/manager/take_over", {
const res = await fpRpc("/fp/manager/take_over", {
step_id: step.id,
});
if (res && res.ok) {

View File

@@ -20,6 +20,7 @@
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";
import { fpRpc } from "./services/fp_rpc";
import { useService } from "@web/core/utils/hooks";
import { QrScanner } from "./qr_scanner";
import { FpKanbanCard } from "./components/kanban_card";
@@ -254,7 +255,7 @@ export class FpShopfloorLanding extends Component {
this._movesInFlight += 1;
this._lastDropAt = Date.now();
try {
const res = await rpc("/fp/shopfloor/plant_overview/move_card", {
const res = await fpRpc("/fp/shopfloor/plant_overview/move_card", {
card_id: dragged.id,
target_workcenter_id: col.work_center_id,
});