From 172d562f06280de28c4fca47a443553bd8d26fa4 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Thu, 30 Jul 2026 15:00:18 +0200 Subject: [PATCH] fix(buyer-tasks): generate PO succeeds + supplier stripe + PO badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../buyer-task-group-card.test.tsx | 19 +++++ .../buyer-tasks/buyer-task-group-card.tsx | 71 ++++++++++++++++--- src/lib/server/buyer-tasks-service.test.ts | 17 ++++- src/lib/server/buyer-tasks-service.ts | 14 ++++ src/lib/server/po-number.ts | 44 ++++++++++++ src/lib/supplier-color.test.ts | 28 ++++++++ src/lib/supplier-color.ts | 47 ++++++++++++ 7 files changed, 227 insertions(+), 13 deletions(-) create mode 100644 src/lib/server/po-number.ts create mode 100644 src/lib/supplier-color.test.ts create mode 100644 src/lib/supplier-color.ts diff --git a/src/components/buyer-tasks/buyer-task-group-card.test.tsx b/src/components/buyer-tasks/buyer-task-group-card.test.tsx index a96218d..126981b 100644 --- a/src/components/buyer-tasks/buyer-task-group-card.test.tsx +++ b/src/components/buyer-tasks/buyer-task-group-card.test.tsx @@ -62,4 +62,23 @@ describe("BuyerTaskGroupCard", () => { fireEvent.click(screen.getByTestId("buyer-task-items-toggle")); expect(screen.getByText("HSS drill")).toBeInTheDocument(); }); + + it("shows the PO number prominently at the top when one exists", () => { + render( + , + ); + expect( + screen.getByTestId(`buyer-task-po-number-${baseGroup.groupId}`), + ).toHaveTextContent("PO-2607-001"); + }); + + it("omits the PO number badge on the needs_po tab (no PO yet)", () => { + render(); + expect( + screen.queryByTestId(`buyer-task-po-number-${baseGroup.groupId}`), + ).toBeNull(); + }); }); diff --git a/src/components/buyer-tasks/buyer-task-group-card.tsx b/src/components/buyer-tasks/buyer-task-group-card.tsx index 54e4400..e800cd8 100644 --- a/src/components/buyer-tasks/buyer-task-group-card.tsx +++ b/src/components/buyer-tasks/buyer-task-group-card.tsx @@ -1,9 +1,12 @@ "use client"; +import { FileText } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { BuyerTaskItemsList, type BuyerTaskItem } from "./buyer-task-items-list"; import type { BuyerTaskGroup } from "@/lib/server/buyer-tasks-service"; +import { supplierColor } from "@/lib/supplier-color"; +import { withAlpha } from "@/lib/item-status-ui"; const ACTION_LABEL: Record = { needs_po: "Generate PO", @@ -26,20 +29,68 @@ export function BuyerTaskGroupCard({ onAction: (group: BuyerTaskGroup) => void; disabled?: boolean; }) { + // Deterministic supplier stripe colour. Same supplier → same colour + // across cards and reloads. + const stripe = supplierColor(group.supplierId); + return ( - - + + {/* Supplier colour stripe — sits along the left edge so the + operator can spot which supplier this card is for at a + glance when many cards are stacked. Pinned full height via + absolute positioning so it lines up with the Card outline. */} +