WFDASH-WEBAPP/src/lib/collection-driver-verdict.ts
Dave 9dd2da3e99 feat(driver): read-only invoice check endpoint for the driver app
POST /api/v1/driver/collection-check runs the collection AI check for a PO
and returns a per-line verdict (right item, right quantity) for drivers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 19:38:38 +02:00

69 lines
2 KiB
TypeScript

import type { CollectionAiCheck } from "@/lib/types";
import { computeLineTicks } from "@/lib/collection-ai-ticks";
export type DriverVerdict = "green" | "amber" | "red" | "unchecked";
export interface DriverLineVerdict {
id: string;
kind: "customer" | "stock";
description: string;
qty_collected: number;
verdict: DriverVerdict;
/** One short plain sentence for the driver; empty when green. */
reason: string;
}
type Row = {
id: string;
kind: "customer" | "stock";
description?: string;
sku: string;
qty_collected: number;
order_qty: number;
cost_price: number | null;
ai_check: CollectionAiCheck | null;
};
/**
* What the driver needs to know at the supplier counter: is it the right
* product, in the right quantity? Price and SKU are the office's concern
* and never turn a driver's line amber or red.
*/
export function driverVerdict(row: Row): DriverLineVerdict {
const base = {
id: row.id,
kind: row.kind,
description: row.description ?? "",
qty_collected: row.qty_collected,
};
const check = row.ai_check;
if (!check || (check.pair_failed && !check.doc_line)) {
return { ...base, verdict: "unchecked", reason: "" };
}
const doc = check.doc_line;
if (!doc || (check.same_item !== "sure" && check.same_item !== "likely")) {
return {
...base,
verdict: "red",
reason: check.reason || "Not found on the invoice photo.",
};
}
const t = computeLineTicks(
{ sku: row.sku, qty_collected: row.qty_collected, order_qty: row.order_qty, cost_price: row.cost_price },
check,
);
if (t.qty === "red") {
return {
...base,
verdict: "red",
reason: `Invoice says ${doc.quantity}, you collected ${row.qty_collected}.`,
};
}
if (t.qty === "amber") {
return { ...base, verdict: "amber", reason: `Invoice says ${doc.quantity}, ordered ${row.order_qty}.` };
}
if (t.desc === "amber") {
return { ...base, verdict: "amber", reason: check.reason || "Looks like the same item, but not certain." };
}
return { ...base, verdict: "green", reason: "" };
}