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>
This commit is contained in:
gsinghpal
2026-05-23 00:47:20 -04:00
parent efaf16dffb
commit 5f03080374
5 changed files with 19 additions and 14 deletions

View File

@@ -1108,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.'}
@@ -1132,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

@@ -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,
});