Mobile and barcode scanning

Supported input methods

  1. USB scanners (keyboard wedge). Plug in and scan into any scan box; the scanner's Enter suffix submits.
  2. Bluetooth scanners in HID/keyboard mode. Pair them with the phone/PC.
  3. Phone/tablet camera in the browser or installed app.

Camera engine

assets/js/scanner.js uses the browser's native BarcodeDetector when it supports at least four of the required formats (typically Chrome/Edge on Android, some desktop builds). Otherwise it loads ZXing (@zxing/library 0.21.3, MIT) on demand. This covers Safari/iOS and Firefox.

Formats: EAN-13, EAN-8, UPC-A, UPC-E, Code 128 (GS1-128), Code 39, QR, Data Matrix (GS1 DataMatrix).

Scanner component features: start/stop, live preview, camera selection, flashlight (when track.getCapabilities().torch), manual entry, result display, success/error sound + vibration + visual flash, duplicate protection (same value ignored for 1.5 s), clear permission and device errors, and stream cleanup on close, tab hide and page hide.

Requirements

  • HTTPS (secure context). On plain HTTP, browsers block camera access. The scanner then shows an explanation and manual entry still works.
  • A current Chrome, Edge or Safari. Camera permission is requested only when the user presses Start camera.

GS1 parsing (server side)

app/Services/Gs1Parser.php accepts the bracketed human-readable form (01)…(17)…(10)…, raw strings with GS (ASCII 29) separators, AIM symbology prefixes (]C1, ]d2, ]Q3) and raw strings without separators when variable-length fields come last. Supported AIs include 00, 01, 02, 10, 11, 13, 15, 16, 17, 21, 30, 37, 310n, 400 and 410. Day 00 means the end of the month. GTIN check digits are validated.

Lookup order in BarcodeService::resolve(): GS1 (GTIN → product via gtin/barcode, including GTIN-14 ↔ EAN-13), then bin barcode/code in the current warehouse, then product barcode/SKU, then packaging barcode (with factor), then alternate barcode, then package number.

Scan-enabled workflows

WorkflowScans
Goods receiptproduct / carton / GS1 (fills batch, expiry, mfg date, count)
Putaway / replenishmentproduct (verify) → batch (verify) → destination bin
Pickingbin → product → batch (must equal the FEFO-allocated batch)
Bin transfersource bin → stock row → destination bin
Stock countbin → product (GS1 batch) → quantity
Universal Scan pageany code → product stock / bin contents (read-only)

A scan never changes stock by itself. Every stock change is a POST that the server validates (permissions, warehouse, product/batch/bin match, capacity, quantities) and confirms. The UI shows success only after confirmation.

Manual test plan (devices)

Run on at least: an Android phone (Chrome), an iPhone (Safari and installed PWA), a tablet (landscape) and a desktop with a USB scanner.

  1. Open Scan, press Start camera, deny permission → the permission message appears; manual entry works.
  2. Re-allow permission in browser settings → scanning works; the flash button appears on devices with a torch.
  3. Scan an EAN-13, a Code 128 bin label, a QR code and a GS1-128 carton label → the correct product/bin appears; GS1 fields are shown.
  4. Hold the same barcode in view → only one result per 1.5 s.
  5. Close the scanner → the camera indicator turns off. Switch tabs while scanning → the stream stops.
  6. Putaway task: wrong product → error tone; correct flow → server confirmation and task DONE.
  7. Pick line: scan a different batch → rejected with the FEFO message.
  8. USB scanner on desktop: scanning into fields submits on Enter.
  9. Airplane mode during Confirm → network error toast, entries kept; retry after reconnecting does not double-post.

Record results in DEVELOPMENT_STATUS.md. Camera behaviour depends on real hardware and cannot be fully verified by automated tests.