From b93633d7285e305d41f4c63d7d8974a9e4188227 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sat, 25 Apr 2026 13:14:06 -0400 Subject: [PATCH] fix(shopfloor,reports): make QR scan actually navigate after decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs were colluding to make iPhone scans look like "nothing happens": 1. The in-app scanner was calling action.doAction({res_model: 'fp.job', res_id: }). Old physical stickers (still on every box) encode /fp/wo/ — that id space doesn't match fp.job, so the form opened on a non-existent record and silently showed nothing. New /fp/job/ stickers happened to work because the IDs lined up by coincidence. 2. The /fp/wo/ controller redirected to mrp.production / mrp.workorder forms, both of which still exist as legacy records but aren't the canonical source of truth post-migration. Fix: - qr_scanner._handleCode now navigates via window.location.href instead of action.doAction. It hands /fp/job/ and /fp/wo/ URLs straight to the existing server-side controllers, which know how to resolve the right record. Bare numeric ids pasted manually -> /fp/job/. Anything else surfaces the decoded text as an error so the operator can see decode worked but the value isn't a sticker. - Modal now shows "Detected: " the moment a code is decoded (before navigation), so even on slow phones the operator sees immediate feedback that the camera read the QR. - wo_scan.py now resolves in this order: 1. fp.job by legacy_mrp_production_id (migration-aware — old stickers route to the new model) 2. mrp.production direct browse 3. mrp.workorder direct browse 4. fall back to /odoo/plating-jobs (or work-orders list) Versions: shopfloor 19.0.17.0.0 -> 19.0.18.0.0, reports 19.0.7.15.0 -> 19.0.7.16.0. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fusion_plating_reports/__manifest__.py | 2 +- .../controllers/wo_scan.py | 35 ++++++--- .../fusion_plating_shopfloor/__manifest__.py | 2 +- .../static/src/js/qr_scanner.js | 72 ++++++++++++++----- .../static/src/scss/qr_scanner.scss | 14 +++- .../static/src/xml/qr_scanner.xml | 5 ++ 6 files changed, 99 insertions(+), 31 deletions(-) diff --git a/fusion_plating/fusion_plating_reports/__manifest__.py b/fusion_plating/fusion_plating_reports/__manifest__.py index c53bc336..a0da068d 100644 --- a/fusion_plating/fusion_plating_reports/__manifest__.py +++ b/fusion_plating/fusion_plating_reports/__manifest__.py @@ -3,7 +3,7 @@ # License OPL-1 (Odoo Proprietary License v1.0) { 'name': 'Fusion Plating — Reports', - 'version': '19.0.7.15.0', + 'version': '19.0.7.16.0', 'category': 'Manufacturing/Plating', 'summary': 'PDF reports for Fusion Plating: quote, SO, WO, packing, BoL, CoC, invoice, receipt, quality + compliance.', 'depends': [ diff --git a/fusion_plating/fusion_plating_reports/controllers/wo_scan.py b/fusion_plating/fusion_plating_reports/controllers/wo_scan.py index 424928bd..fcd462af 100644 --- a/fusion_plating/fusion_plating_reports/controllers/wo_scan.py +++ b/fusion_plating/fusion_plating_reports/controllers/wo_scan.py @@ -22,25 +22,42 @@ class FpWoScanController(http.Controller): def wo_scan_redirect(self, wo_id, **kwargs): """Redirect a scanned sticker to the right backend form. - Stickers are printed from two sources — mrp.workorder (WO) and - mrp.production (MO) — and both embed their own numeric id in - the QR. Try the MO table first (operators live on the MO - form — customer, SO, all WOs visible) and fall back to WO. + Resolution order: + 1. fp.job mapped from this MO id via legacy_mrp_production_id + (post-migration: physical stickers still encode the old MO + id, but the canonical record is now an fp.job) + 2. mrp.production with this id (pre-migration callers, or if + the legacy mapping wasn't run) + 3. mrp.workorder with this id (older stickers that encoded + the WO id rather than the MO id) + 4. fall back to the jobs list so staff can search manually. """ - MO = request.env['mrp.production'].sudo() - WO = request.env['mrp.workorder'].sudo() + env = request.env - mo = MO.browse(wo_id).exists() + # 1) New native model — preferred when migration has run. + if 'fp.job' in env and 'legacy_mrp_production_id' in env['fp.job']._fields: + job = env['fp.job'].sudo().search( + [('legacy_mrp_production_id', '=', wo_id)], limit=1) + if job: + return request.redirect( + '/odoo/action-fusion_plating.action_fp_job/%d' % job.id + ) + + # 2) Legacy MO form (pre-migration or non-migrated records). + mo = env['mrp.production'].sudo().browse(wo_id).exists() if mo: return request.redirect( '/odoo/action-mrp.mrp_production_action/%d' % mo.id ) - wo = WO.browse(wo_id).exists() + # 3) Legacy WO form. + wo = env['mrp.workorder'].sudo().browse(wo_id).exists() if wo: return request.redirect( '/odoo/action-mrp.action_mrp_workorder/%d' % wo.id ) - # Neither resolved — land on the WO list so staff can search manually. + # 4) Fall back: native jobs list if it exists, otherwise WO list. + if 'fp.job' in env: + return request.redirect('/odoo/plating-jobs') return request.redirect('/odoo/manufacturing/work-orders') diff --git a/fusion_plating/fusion_plating_shopfloor/__manifest__.py b/fusion_plating/fusion_plating_shopfloor/__manifest__.py index d7f35980..a43f57e4 100644 --- a/fusion_plating/fusion_plating_shopfloor/__manifest__.py +++ b/fusion_plating/fusion_plating_shopfloor/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating — Shop Floor', - 'version': '19.0.17.0.0', + 'version': '19.0.18.0.0', 'category': 'Manufacturing/Plating', 'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, ' 'first-piece inspection gates.', diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/qr_scanner.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/qr_scanner.js index a2d213c4..6dad2c29 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/js/qr_scanner.js +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/qr_scanner.js @@ -42,7 +42,6 @@ export class QrScanner extends Component { }; setup() { - this.action = useService("action"); this.notification = useService("notification"); this.videoRef = useRef("video"); const hasNative = typeof BarcodeDetector !== "undefined"; @@ -51,6 +50,7 @@ export class QrScanner extends Component { open: false, error: null, manualUrl: "", + detected: "", // last decoded value (for user feedback) // True whenever ANY decoder (native or jsQR) is available. // Drives the template: when true we show the camera