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:
@@ -1108,21 +1108,23 @@ class FpShopfloorController(http.Controller):
|
|||||||
@http.route('/fp/shopfloor/plant_overview/move_card',
|
@http.route('/fp/shopfloor/plant_overview/move_card',
|
||||||
type='jsonrpc', auth='user')
|
type='jsonrpc', auth='user')
|
||||||
def plant_overview_move_card(self, card_id, source_model=None,
|
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).
|
"""Move a step card to a different work centre (drag & drop).
|
||||||
|
|
||||||
`source_model` is accepted for backward compatibility but ignored —
|
`source_model` is accepted for backward compatibility but ignored —
|
||||||
Plant Overview now only ever serves fp.job.step cards. A target
|
Plant Overview now only ever serves fp.job.step cards. A target
|
||||||
of 0 / falsy clears the work centre.
|
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))
|
step = Step.browse(int(card_id))
|
||||||
if not step.exists():
|
if not step.exists():
|
||||||
return {'ok': False, 'error': f'Step {card_id} not found.'}
|
return {'ok': False, 'error': f'Step {card_id} not found.'}
|
||||||
|
|
||||||
wc_id = int(target_workcenter_id) if target_workcenter_id else False
|
wc_id = int(target_workcenter_id) if target_workcenter_id else False
|
||||||
if wc_id:
|
if wc_id:
|
||||||
wc = request.env['fp.work.centre'].browse(wc_id)
|
wc = env['fp.work.centre'].browse(wc_id)
|
||||||
if not wc.exists():
|
if not wc.exists():
|
||||||
return {'ok': False,
|
return {'ok': False,
|
||||||
'error': f'Work centre {target_workcenter_id} not found.'}
|
'error': f'Work centre {target_workcenter_id} not found.'}
|
||||||
@@ -1132,7 +1134,7 @@ class FpShopfloorController(http.Controller):
|
|||||||
_logger.info(
|
_logger.info(
|
||||||
'Plant Overview: moved step %s (%s) → WC %s by uid %s',
|
'Plant Overview: moved step %s (%s) → WC %s by uid %s',
|
||||||
step.id, step.name, wc_id or 'unassigned',
|
step.id, step.name, wc_id or 'unassigned',
|
||||||
request.env.uid,
|
env.uid,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
_logger.exception('Plant Overview move_card failed')
|
_logger.exception('Plant Overview move_card failed')
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import { Component, useState } from "@odoo/owl";
|
import { Component, useState } from "@odoo/owl";
|
||||||
import { Dialog } from "@web/core/dialog/dialog";
|
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";
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
|
||||||
// Hold reasons kept here so the picker doesn't need a server roundtrip.
|
// 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;
|
this.state.submitting = true;
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/workspace/hold", {
|
const res = await fpRpc("/fp/workspace/hold", {
|
||||||
job_id: this.props.jobId,
|
job_id: this.props.jobId,
|
||||||
step_id: this.props.stepId || null,
|
step_id: this.props.stepId || null,
|
||||||
part_ref: this.props.partRef || "",
|
part_ref: this.props.partRef || "",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
||||||
import { registry } from "@web/core/registry";
|
import { registry } from "@web/core/registry";
|
||||||
import { rpc } from "@web/core/network/rpc";
|
import { rpc } from "@web/core/network/rpc";
|
||||||
|
import { fpRpc } from "./services/fp_rpc";
|
||||||
import { useService } from "@web/core/utils/hooks";
|
import { useService } from "@web/core/utils/hooks";
|
||||||
import { WorkflowChip } from "./components/workflow_chip";
|
import { WorkflowChip } from "./components/workflow_chip";
|
||||||
import { GateViz } from "./components/gate_viz";
|
import { GateViz } from "./components/gate_viz";
|
||||||
@@ -116,7 +117,7 @@ export class FpJobWorkspace extends Component {
|
|||||||
// ---- Step actions ------------------------------------------------------
|
// ---- Step actions ------------------------------------------------------
|
||||||
async onStartStep(stepId) {
|
async onStartStep(stepId) {
|
||||||
try {
|
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) {
|
if (res && res.ok) {
|
||||||
this.notification.add("Step started.", { type: "success" });
|
this.notification.add("Step started.", { type: "success" });
|
||||||
await this.refresh();
|
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}`,
|
contextLabel: `${this.state.data.job.display_wo_name} · Step ${step.sequence_display}: ${step.name}`,
|
||||||
onSubmit: async (dataUri) => {
|
onSubmit: async (dataUri) => {
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/workspace/sign_off", {
|
const res = await fpRpc("/fp/workspace/sign_off", {
|
||||||
step_id: step.id,
|
step_id: step.id,
|
||||||
signature_data_uri: dataUri,
|
signature_data_uri: dataUri,
|
||||||
});
|
});
|
||||||
@@ -154,7 +155,7 @@ export class FpJobWorkspace extends Component {
|
|||||||
}
|
}
|
||||||
// Plain finish — no signature required
|
// Plain finish — no signature required
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/shopfloor/stop_wo", {
|
const res = await fpRpc("/fp/shopfloor/stop_wo", {
|
||||||
workorder_id: step.id, finish: true,
|
workorder_id: step.id, finish: true,
|
||||||
});
|
});
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
@@ -201,7 +202,7 @@ export class FpJobWorkspace extends Component {
|
|||||||
|
|
||||||
async onAdvanceMilestone() {
|
async onAdvanceMilestone() {
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/workspace/advance_milestone", {
|
const res = await fpRpc("/fp/workspace/advance_milestone", {
|
||||||
job_id: this.state.jobId,
|
job_id: this.state.jobId,
|
||||||
});
|
});
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
||||||
import { registry } from "@web/core/registry";
|
import { registry } from "@web/core/registry";
|
||||||
import { rpc } from "@web/core/network/rpc";
|
import { rpc } from "@web/core/network/rpc";
|
||||||
|
import { fpRpc } from "./services/fp_rpc";
|
||||||
import { useService } from "@web/core/utils/hooks";
|
import { useService } from "@web/core/utils/hooks";
|
||||||
import { QrScanner } from "./qr_scanner";
|
import { QrScanner } from "./qr_scanner";
|
||||||
import { FpTabletLock } from "./tablet_lock";
|
import { FpTabletLock } from "./tablet_lock";
|
||||||
@@ -208,7 +209,7 @@ export class ManagerDashboard extends Component {
|
|||||||
async onAssignWorker(step, userIdRaw) {
|
async onAssignWorker(step, userIdRaw) {
|
||||||
const userId = parseInt(userIdRaw) || null;
|
const userId = parseInt(userIdRaw) || null;
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/manager/assign_worker", {
|
const res = await fpRpc("/fp/manager/assign_worker", {
|
||||||
step_id: step.id, user_id: userId,
|
step_id: step.id, user_id: userId,
|
||||||
});
|
});
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
@@ -226,7 +227,7 @@ export class ManagerDashboard extends Component {
|
|||||||
async onAssignTank(step, tankIdRaw) {
|
async onAssignTank(step, tankIdRaw) {
|
||||||
const tankId = parseInt(tankIdRaw) || null;
|
const tankId = parseInt(tankIdRaw) || null;
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/manager/assign_tank", {
|
const res = await fpRpc("/fp/manager/assign_tank", {
|
||||||
step_id: step.id, tank_id: tankId,
|
step_id: step.id, tank_id: tankId,
|
||||||
});
|
});
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
@@ -243,7 +244,7 @@ export class ManagerDashboard extends Component {
|
|||||||
|
|
||||||
async onTakeOver(step) {
|
async onTakeOver(step) {
|
||||||
try {
|
try {
|
||||||
const res = await rpc("/fp/manager/take_over", {
|
const res = await fpRpc("/fp/manager/take_over", {
|
||||||
step_id: step.id,
|
step_id: step.id,
|
||||||
});
|
});
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
import { Component, useState, onMounted, onWillUnmount } from "@odoo/owl";
|
||||||
import { registry } from "@web/core/registry";
|
import { registry } from "@web/core/registry";
|
||||||
import { rpc } from "@web/core/network/rpc";
|
import { rpc } from "@web/core/network/rpc";
|
||||||
|
import { fpRpc } from "./services/fp_rpc";
|
||||||
import { useService } from "@web/core/utils/hooks";
|
import { useService } from "@web/core/utils/hooks";
|
||||||
import { QrScanner } from "./qr_scanner";
|
import { QrScanner } from "./qr_scanner";
|
||||||
import { FpKanbanCard } from "./components/kanban_card";
|
import { FpKanbanCard } from "./components/kanban_card";
|
||||||
@@ -254,7 +255,7 @@ export class FpShopfloorLanding extends Component {
|
|||||||
this._movesInFlight += 1;
|
this._movesInFlight += 1;
|
||||||
this._lastDropAt = Date.now();
|
this._lastDropAt = Date.now();
|
||||||
try {
|
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,
|
card_id: dragged.id,
|
||||||
target_workcenter_id: col.work_center_id,
|
target_workcenter_id: col.work_center_id,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user