diff --git a/fusion_plating/fusion_plating_reports/__manifest__.py b/fusion_plating/fusion_plating_reports/__manifest__.py index a0da068d..084e21f7 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.16.0', + 'version': '19.0.7.17.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/report/report_fp_wo_sticker.xml b/fusion_plating/fusion_plating_reports/report/report_fp_wo_sticker.xml index 3812a0f8..706b8962 100644 --- a/fusion_plating/fusion_plating_reports/report/report_fp_wo_sticker.xml +++ b/fusion_plating/fusion_plating_reports/report/report_fp_wo_sticker.xml @@ -306,8 +306,17 @@ + + + + + - Rev + Rev diff --git a/fusion_plating/fusion_plating_shopfloor/__manifest__.py b/fusion_plating/fusion_plating_shopfloor/__manifest__.py index a43f57e4..9d15dd99 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.18.0.0', + 'version': '19.0.19.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/controllers/__pycache__/shopfloor_controller.cpython-312.pyc b/fusion_plating/fusion_plating_shopfloor/controllers/__pycache__/shopfloor_controller.cpython-312.pyc deleted file mode 100644 index 85f97b3e..00000000 Binary files a/fusion_plating/fusion_plating_shopfloor/controllers/__pycache__/shopfloor_controller.cpython-312.pyc and /dev/null differ 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 6dad2c29..38d10cfa 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 @@ -44,18 +44,17 @@ export class QrScanner extends Component { setup() { this.notification = useService("notification"); this.videoRef = useRef("video"); - const hasNative = typeof BarcodeDetector !== "undefined"; - const hasJsQR = typeof window !== "undefined" && typeof window.jsQR === "function"; this.state = useState({ 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 ; - // when false we fall through to the manual paste UI only. - canScan: hasNative || hasJsQR, - decoder: hasNative ? "native" : (hasJsQR ? "jsqr" : "none"), + // canScan / decoder are recomputed in open() — don't trust + // setup-time values because vendored libs may attach to + // window asynchronously after the bundle finishes parsing. + canScan: false, + decoder: "none", + statusLine: "", // visible diagnostic shown in modal }); this.stream = null; this.decodeLoopActive = false; @@ -68,9 +67,33 @@ export class QrScanner extends Component { onWillUnmount(() => this._stopCamera()); } + /** + * Check what decoder is available right now and update state. Run + * at every open() — not just setup() — because a stale bundle in + * the browser cache can flip results between page loads. + */ + _detectCapabilities() { + const hasNative = typeof BarcodeDetector !== "undefined"; + const hasJsQR = typeof window !== "undefined" && typeof window.jsQR === "function"; + this.state.canScan = hasNative || hasJsQR; + this.state.decoder = hasNative ? "native" : (hasJsQR ? "jsqr" : "none"); + // Build a one-line status the user can read in the modal so + // it's obvious whether the decoder loaded. Helps diagnose + // "nothing happens" reports without round-tripping through + // Safari Web Inspector. + this.state.statusLine = ( + "Decoder: " + this.state.decoder + + (hasNative ? " (native)" : "") + + (!hasNative && hasJsQR ? " (jsQR)" : "") + + (!this.state.canScan ? " — paste URL below" : "") + ); + } + async open() { + this._detectCapabilities(); this.state.open = true; this.state.error = null; + this.state.detected = ""; await this._startCamera(); } diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/qr_scanner.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/qr_scanner.scss index 3f111a8a..fd42bfb8 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/scss/qr_scanner.scss +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/qr_scanner.scss @@ -66,7 +66,8 @@ .o_fp_qr_error, .o_fp_qr_warn, -.o_fp_qr_detected { +.o_fp_qr_detected, +.o_fp_qr_status { padding: $fp-space-2 $fp-space-3; border-radius: $fp-radius-sm; background: $fp-card-soft; @@ -75,6 +76,13 @@ word-break: break-all; } +.o_fp_qr_status { + border-left: 3px solid $fp-accent; + color: $fp-ink; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 11px; +} + .o_fp_qr_error { border-left: 3px solid $fp-bad; } diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/qr_scanner.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/qr_scanner.xml index 5a34c7e1..6ce24767 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/xml/qr_scanner.xml +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/qr_scanner.xml @@ -25,6 +25,10 @@ + + + + Live decoding isn't supported in this browser.