generatePoForGroup was creating purchase_orders records without
`po_number`, which is required by the PB schema. PB rejected the
create with 500 ("Failed to create record.") and the operator never
got a PO. Fix:
- New src/lib/server/po-number.ts — server-side generatePONumber()
that mirrors the client version in pb-helpers.ts (PO-YYYY-MM-NNN
format) but uses getAdminPocketBase(). Per the data-access rules,
don't extend pb-helpers.ts; server business logic lives under
lib/server/.
- generatePoForGroup now generates the PO number, includes
total_amount (sum of costPrice * quantity) and order_item
(back-link to the items) in the create payload — mirrors the
canonical /orders/[id]/process PO-creation flow so the PB admin
row shows related lines without an extra getList call.
- Test updated to assert the new payload (po_number regex,
total_amount computed, order_item back-link).
UI while here:
- BuyerTaskGroupCard now renders a deterministic supplier colour
stripe on the left edge. New src/lib/supplier-color.ts hashes the
supplierId into a small Tailwind 600 palette (18 colours) so the
same supplier always shows the same colour, and the supplier name
gets tinted with the same colour for quick scanning.
- PO number is now shown prominently at the top of the card (in a
warm-toned badge that matches the AI Insights theme) when one
exists — po_draft and po_sent tabs. Needs PO tab omits it.
Co-authored-by: Orca <help@stably.ai>
47 lines
No EOL
1.5 KiB
TypeScript
47 lines
No EOL
1.5 KiB
TypeScript
/**
|
|
* Deterministic supplier color from the supplier id.
|
|
*
|
|
* Hashes the supplierId into a small palette of saturated Tailwind
|
|
* 600 swatches so the same supplier always renders the same stripe
|
|
* colour, and different suppliers are visually distinguishable at a
|
|
* glance. Used by buyer-tasks cards today; other supplier-tinted UIs
|
|
* (e.g. supplier lists) can call this too.
|
|
*
|
|
* The hash is a tiny djb2 variant — collision-resistance doesn't
|
|
* matter, only that the colour is stable for a given id and
|
|
* reasonably spread across the palette.
|
|
*/
|
|
|
|
const PALETTE: readonly string[] = [
|
|
"#dc2626", // red 600
|
|
"#ea580c", // orange 600
|
|
"#d97706", // amber 600
|
|
"#ca8a04", // yellow 600
|
|
"#65a30d", // lime 600
|
|
"#16a34a", // green 600
|
|
"#059669", // emerald 600
|
|
"#0d9488", // teal 600
|
|
"#0891b2", // cyan 600
|
|
"#0284c7", // sky 600
|
|
"#2563eb", // blue 600
|
|
"#4f46e5", // indigo 600
|
|
"#7c3aed", // violet 600
|
|
"#9333ea", // purple 600
|
|
"#c026d3", // fuchsia 600
|
|
"#db2777", // pink 600
|
|
"#e11d48", // rose 600
|
|
"#475569", // slate 600
|
|
];
|
|
|
|
const FALLBACK_COLOR = "#64748b"; // slate-500 — neutral when no id
|
|
|
|
export function supplierColor(supplierId: string | null | undefined): string {
|
|
if (!supplierId) return FALLBACK_COLOR;
|
|
let hash = 5381;
|
|
for (let i = 0; i < supplierId.length; i++) {
|
|
// (hash * 33) ^ char — classic djb2
|
|
hash = ((hash << 5) + hash + supplierId.charCodeAt(i)) | 0;
|
|
}
|
|
const index = Math.abs(hash) % PALETTE.length;
|
|
return PALETTE[index]!;
|
|
} |