- Job cards scanner: oil QR books litres + vehicle (no odo/photo), bundled onto the vehicle's job card (open job now, or the next job started the same day) - Fuel tab is diesel only; /api/clock/oil-out added - Users: Full / Clock-in user / Mechanic; mechanics see only Fuel + Job cards - Plant Register: QR code column (double-click to set), kept across re-seeds and used when matching scans Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
import { normaliseCostCode } from "./cost-code";
|
|
|
|
/** The oil/diesel drum QR reads "Diesel | FUEL-DSL" — name, then item code. */
|
|
export function oilCodeFromQr(raw: string): string | null {
|
|
const text = (raw || "").trim();
|
|
if (!text || text.length > 200) return null;
|
|
const code = text.includes("|") ? text.split("|").pop()!.trim() : text;
|
|
return code || null;
|
|
}
|
|
|
|
export const MAX_BOOK_OUT_LITRES = 5000;
|
|
|
|
/**
|
|
* Which vehicle does a scanned QR name? The register code is the join, compared the way
|
|
* purchase orders are ("SS-02" = "SS02"). Tolerant of how the sticker was printed: the bare
|
|
* code, "Name | CODE", or a link ending in the code. Never a fuzzy guess — no match, no vehicle.
|
|
*/
|
|
export function assetFromQr<T extends { code: string; qr?: string }>(raw: string, assets: T[]): T | null {
|
|
const text = (raw || "").trim();
|
|
if (!text || text.length > 300) return null;
|
|
// A QR typed in the Plant Register wins over the bare register code.
|
|
const byCode = new Map<string, T>();
|
|
for (const a of assets) byCode.set(normaliseCostCode(a.code), a);
|
|
for (const a of assets) if (a.qr && normaliseCostCode(a.qr)) byCode.set(normaliseCostCode(a.qr), a);
|
|
const whole = byCode.get(normaliseCostCode(text));
|
|
if (whole) return whole;
|
|
// Try each |, /, space, comma, colon or ? separated piece, last first (the code usually trails).
|
|
for (const part of text.split(/[|/\s,:;?=&#]+/).reverse()) {
|
|
const hit = part && byCode.get(normaliseCostCode(part));
|
|
if (hit) return hit;
|
|
}
|
|
return null;
|
|
}
|