mega-plant/lib/stock-out.ts
David 8f3a74cf6e Job cards oil path, Fuel tab diesel-only, Mechanic role, Plant Register QR column
- 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>
2026-10-22 13:44:01 +02:00

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;
}