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:
gsinghpal
2026-04-25 12:39:37 -04:00
parent 18b5918d3d
commit 74db636458
21 changed files with 1116 additions and 8 deletions

View File

@@ -1,11 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!--
Adds a "Process Tree" button to the fp.job form header, calling
fp.job.action_open_process_tree (which launches the canonical
fp_process_tree shopfloor client action with job_id in context).
Adds a "Process Tree" header button + smart-button row to the
fp.job form. The fp.job form in core has no button_box yet, so
we inject one at the top of the sheet (xpath //sheet position
"inside" with a sibling reference at the start).
Hidden while the job is in draft (no recipe-derived steps yet).
Smart buttons appear only when the underlying count is > 0
(except Steps, which always shows since every confirmed job
has steps). Pattern follows the existing oe_stat_button row
from sale.order / mrp.production.
Process Tree header button is hidden while the job is in draft
(no recipe-derived steps yet).
-->
<record id="view_fp_job_form_jobs_inherit" model="ir.ui.view">
<field name="name">fp.job.form.jobs.inherit</field>
@@ -19,6 +26,67 @@
icon="fa-sitemap"
invisible="state == 'draft'"/>
</xpath>
<!-- Inject a button_box at the top of the sheet, before the
oe_title block. Smart buttons drill into the matching
records the way sale.order does. -->
<xpath expr="//sheet/div[hasclass('oe_title')]" position="before">
<div class="oe_button_box" name="button_box">
<button name="action_view_sale_order" type="object"
class="oe_stat_button" icon="fa-shopping-cart"
invisible="sale_order_count == 0">
<field name="sale_order_count" widget="statinfo"
string="Sale Order"/>
</button>
<button name="action_view_steps" type="object"
class="oe_stat_button" icon="fa-list-ol">
<field name="step_count" widget="statinfo"
string="Steps"/>
</button>
<button name="action_view_deliveries" type="object"
class="oe_stat_button" icon="fa-truck"
invisible="delivery_count == 0">
<field name="delivery_count" widget="statinfo"
string="Delivery"/>
</button>
<button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o"
invisible="invoice_count == 0">
<field name="invoice_count" widget="statinfo"
string="Invoices"/>
</button>
<button name="action_view_payments" type="object"
class="oe_stat_button" icon="fa-money"
invisible="payment_count == 0">
<field name="payment_count" widget="statinfo"
string="Payments"/>
</button>
<button name="action_view_quality_holds" type="object"
class="oe_stat_button" icon="fa-pause-circle"
invisible="quality_hold_count == 0">
<field name="quality_hold_count" widget="statinfo"
string="Holds"/>
</button>
<button name="action_view_certificates" type="object"
class="oe_stat_button" icon="fa-certificate"
invisible="certificate_count == 0">
<field name="certificate_count" widget="statinfo"
string="Certificates"/>
</button>
<button name="action_view_timelogs" type="object"
class="oe_stat_button" icon="fa-clock-o"
invisible="timelog_count == 0">
<field name="timelog_count" widget="statinfo"
string="Time Logs"/>
</button>
<button name="action_view_portal_job" type="object"
class="oe_stat_button" icon="fa-globe"
invisible="portal_job_count == 0">
<field name="portal_job_count" widget="statinfo"
string="Portal Job"/>
</button>
</div>
</xpath>
</field>
</record>
</odoo>