fix(shopfloor,reports): scanner status line + sticker rev cleanup
- Re-detect BarcodeDetector / window.jsQR at every modal open instead
of only at component setup. Avoids the trap where a stale cached
bundle reports "no decoder" even after a redeploy.
- Add a one-line status indicator at the top of the scan modal showing
exactly which decoder is active ("Decoder: native" / "Decoder: jsqr"
/ "Decoder: none — paste URL below"). Lets the operator see at a
glance whether scanning is even possible without round-tripping
through Safari Web Inspector.
- Sticker: strip a leading "Rev " (case-insensitive) from
fp.part.catalog.revision before printing so values like "Rev 1"
don't render as "Rev Rev 1".
Versions: shopfloor 19.0.18 -> 19.0.19, reports 19.0.7.16 -> 19.0.7.17.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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': [
|
||||
|
||||
@@ -306,8 +306,17 @@
|
||||
<span class="fp-sticker-strong"
|
||||
t-esc="_part.part_number"/>
|
||||
<t t-if="_part.revision">
|
||||
<!-- Some parts store the revision with a
|
||||
"Rev " prefix already (e.g. "Rev 1"),
|
||||
others store just the value ("1", "A").
|
||||
Strip a leading "Rev " (case insensitive)
|
||||
so we don't print "Rev Rev 1". -->
|
||||
<t t-set="_rev_clean" t-value="_part.revision.strip()"/>
|
||||
<t t-if="_rev_clean.lower().startswith('rev ')">
|
||||
<t t-set="_rev_clean" t-value="_rev_clean[4:].strip()"/>
|
||||
</t>
|
||||
<span class="fp-sticker-muted">
|
||||
Rev <span t-esc="_part.revision"/>
|
||||
Rev <span t-esc="_rev_clean"/>
|
||||
</span>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -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.',
|
||||
|
||||
Binary file not shown.
@@ -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 <video>;
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="o_fp_qr_modal_body">
|
||||
<div t-if="state.statusLine" class="o_fp_qr_status">
|
||||
<i class="fa fa-info-circle me-1"/>
|
||||
<span t-esc="state.statusLine"/>
|
||||
</div>
|
||||
<div t-if="!state.canScan and !state.error"
|
||||
class="o_fp_qr_warn">
|
||||
Live decoding isn't supported in this browser.
|
||||
|
||||
Reference in New Issue
Block a user