feat(jobs,shopfloor): smart buttons + QR scanner + NFC tank pages
Three connected operator-workflow features for entech.
A. fp.job smart buttons — count fields and action methods for sale
order, steps, deliveries, invoices, payments, quality holds,
certificates, time logs, and portal job. Each is an oe_stat_button
that drills into the matching records, mirroring the sale.order
pattern. Cross-module models are runtime-detected so the form
stays clean when bridge modules are uninstalled.
B. Reusable QR scanner OWL component (`<QrScanner/>`) wired into the
Manager Desk, Tablet Station, Plant Overview, and Process Tree
headers. Click → modal with rear-camera stream (getUserMedia) +
BarcodeDetector live decode → opens the matching fp.job form via
the action service. Falls back to a manual URL paste box on
browsers without BarcodeDetector. Works on iOS 17+ Safari and
Android Chrome. Width uses `min(420px, 92vw)` wrapped in #{} so
dart-sass passes it through verbatim instead of trying to compute
incompatible units at compile time.
C. /fp/tank/<id> public-but-auth-required tank status page for NFC
taps. Renders the tank's current step (in-progress / paused),
queued ready steps, and most recent bath chemistry log (lines
table) on a mobile-first page. URL-based so it works on iOS Safari
without the Web NFC API — the operator taps the NFC tag, the URL
opens in the default browser, the page auto-renders. New
web.assets_frontend bundle entry pulls in the design tokens +
tank_status.scss.
Manifest version bumps: jobs 19.0.5.0.0, shopfloor 19.0.16.0.0.
Tests: 44 pass (3 new smart-button assertions added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,3 +4,4 @@
|
||||
|
||||
from . import shopfloor_controller
|
||||
from . import manager_controller
|
||||
from . import tank_status
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
#
|
||||
# /fp/tank/<id> — mobile-friendly tank status page. Linked from NFC
|
||||
# tags on the physical tank. The operator taps the tag with a phone,
|
||||
# the tag's URL opens this page in their default browser.
|
||||
#
|
||||
# Auth is `user` so an operator must be logged in (no public exposure
|
||||
# of bath chemistry / job-customer data). Operators stay logged in on
|
||||
# the shopfloor tablet, so this is friction-free in practice.
|
||||
#
|
||||
# Why URL-based and not Web NFC API: Web NFC is Chrome-Android only;
|
||||
# iOS Safari does not expose any NFC API. iOS instead reads the URL
|
||||
# off the tag's NDEF record and opens it in the default browser. As
|
||||
# long as the tag stores the URL, both platforms Just Work.
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class FpTankStatusController(http.Controller):
|
||||
|
||||
@http.route(
|
||||
'/fp/tank/<int:tank_id>',
|
||||
type='http',
|
||||
auth='user',
|
||||
website=False,
|
||||
)
|
||||
def fp_tank_status(self, tank_id, **kwargs):
|
||||
Tank = request.env['fusion.plating.tank'].sudo()
|
||||
tank = Tank.browse(tank_id).exists()
|
||||
if not tank:
|
||||
return request.render(
|
||||
'fusion_plating_shopfloor.tank_status_not_found',
|
||||
{'tank_id': tank_id},
|
||||
)
|
||||
|
||||
# Find the active step on this tank (in progress or paused).
|
||||
# fp.job.step.tank_id was added in fusion_plating core.
|
||||
Step = request.env['fp.job.step'].sudo()
|
||||
active_step = Step.search([
|
||||
('tank_id', '=', tank.id),
|
||||
('state', 'in', ('in_progress', 'paused')),
|
||||
], order='date_started desc', limit=1)
|
||||
|
||||
# Up to 5 ready steps for this tank — the operator's "what's
|
||||
# coming next" signal.
|
||||
ready_steps = Step.search([
|
||||
('tank_id', '=', tank.id),
|
||||
('state', '=', 'ready'),
|
||||
], order='sequence asc', limit=5)
|
||||
|
||||
# Most recent bath log. Readings are line-level
|
||||
# (fusion.plating.bath.log.line), keyed by parameter_code (pH,
|
||||
# temperature, nickel, etc.). The template iterates the lines.
|
||||
bath_log = request.env['fusion.plating.bath.log'].sudo().search(
|
||||
[('tank_id', '=', tank.id)],
|
||||
order='log_date desc, create_date desc',
|
||||
limit=1,
|
||||
)
|
||||
|
||||
return request.render(
|
||||
'fusion_plating_shopfloor.tank_status_page',
|
||||
{
|
||||
'tank': tank,
|
||||
'active_step': active_step,
|
||||
'ready_steps': ready_steps,
|
||||
'bath_log': bath_log,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user