diff --git a/plugins/printedparts/api/routes.py b/plugins/printedparts/api/routes.py index be6191ba..58dc5688 100644 --- a/plugins/printedparts/api/routes.py +++ b/plugins/printedparts/api/routes.py @@ -75,6 +75,7 @@ def get_item(item_id: int): import glob import os +import re from flask import current_app from werkzeug.utils import secure_filename @@ -560,8 +561,14 @@ def _kiosk_find_item(itemcode): matches = [] for candidate in PrintedItem.query.filter_by(isactive=True).all(): for value in (candidate.itemcode, candidate.gagelabtag): - tail = ''.join(ch for ch in (value or '') if ch.isdigit()) - if tail and int(tail) == wanted: + # The TRAILING digit run, not every digit in the string. A + # prefix can contain digits - the shipped default is '3DP' - + # and collecting all of them turned 3DP0004 into 30004, so the + # operator typing the 4 printed on the bin got "no part matches + # that barcode" and had to work out that the 3 belonged to the + # prefix. The number on the label is the number at the end. + digits = re.search(r'(\d+)$', (value or '').strip()) + if digits and int(digits.group(1)) == wanted: matches.append(candidate) break if len(matches) == 1: diff --git a/plugins/printedparts/frontend/views/PartsKiosk.vue b/plugins/printedparts/frontend/views/PartsKiosk.vue index 37e7e82e..98ee9997 100644 --- a/plugins/printedparts/frontend/views/PartsKiosk.vue +++ b/plugins/printedparts/frontend/views/PartsKiosk.vue @@ -7,10 +7,31 @@ -
{{ error }}
+
+ {{ error }} +

+ Several scans in a row have found nothing. The kiosk may have been + switched off, or this screen's setup link may have been replaced - ask + an administrator to check Settings > 3D Printed Parts. +

+
+ + +
+

This screen is not set up yet

+

+ An administrator opens it once using the kiosk link from + Settings > 3D Printed Parts. The screen remembers it after that. +

+
-
+

Scan the barcode on the bin

@@ -108,6 +129,13 @@ const item = ref(null) const badge = ref('') const quantity = ref('') const error = ref('') +// Whether THIS browser holds the kiosk's device token. The server will not say +// (see the template), and it does not need to: an unprovisioned screen should +// say so rather than pretending to be a working kiosk with an empty catalog. +const provisioned = ref(true) +// Consecutive lookups that found nothing. Every scan missing usually means the +// kiosk was switched off, not that no bin is known. +const misses = ref(0) const doneMessage = ref('') const submitting = ref(false) const manualCode = ref('') @@ -118,6 +146,14 @@ let resetTimer = null onMounted(async () => { claimKioskToken(KIOSK_TOKEN_KEYS['/printedparts/kiosk/']) + try { + provisioned.value = Boolean( + localStorage.getItem(KIOSK_TOKEN_KEYS['/printedparts/kiosk/'])) + } catch { + // A browser refusing localStorage cannot be provisioned at all, and + // saying so is more use than a scan box that answers nothing. + provisioned.value = false + } // A kiosk left running for weeks reads this once at load, which is fine: the // label format is a property of the site's physical tags, not something that // changes during a shift. @@ -153,11 +189,17 @@ async function lookupItem(itemcode) { try { const response = await printedpartsApi.kioskItem(code) item.value = response.data.data + misses.value = 0 step.value = 'badge' manualCode.value = '' } catch (lookupError) { error.value = lookupError.response?.data?.data?.error?.message || 'No part matches that barcode' + // The gate and a genuine miss both answer 404, deliberately, so the client + // cannot tell them apart from one response. What it CAN say is that + // repeated misses may mean the kiosk was switched off rather than that + // every bin is unknown. + misses.value += 1 } focusEntry() } @@ -215,6 +257,14 @@ function reset() {