From e4fbcf37484445cf804b93a0967b73968be9c772 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 22 Oct 2026 07:59:57 +0200 Subject: [PATCH] feat(orders): label lines split off a short collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shows "Short delivered · on " next to the description on /orders and the order page so the buyer doesn't re-order units already on back order. Co-Authored-By: Claude Opus 5.5 --- src/app/(app)/orders/[id]/page.tsx | 2 + src/app/(app)/orders/page.tsx | 6 ++- .../orders/short-delivered-pill.test.ts | 19 +++++++++ .../orders/short-delivered-pill.tsx | 41 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/components/orders/short-delivered-pill.test.ts create mode 100644 src/components/orders/short-delivered-pill.tsx diff --git a/src/app/(app)/orders/[id]/page.tsx b/src/app/(app)/orders/[id]/page.tsx index 0fc5b67..31540d8 100644 --- a/src/app/(app)/orders/[id]/page.tsx +++ b/src/app/(app)/orders/[id]/page.tsx @@ -70,6 +70,7 @@ import type { MatchedAttachment } from "@/components/orders/attachment-list"; import { AiItemHistoryTrigger } from "@/components/ai-item-history-trigger"; import type { Order, OrderItem, PurchaseOrder, Quote, ItemIntel, ItemStatus } from "@/lib/types"; +import { ShortDeliveredPill } from "@/components/orders/short-delivered-pill"; import type { ScoreBreakdown, LineMatchBreakdown } from "@/lib/server/order-quote-match"; import type { OrderDetail } from "@/lib/server/orders-service"; @@ -687,6 +688,7 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri className="mt-0.5" /> {item.description} + {item.po_description && item.po_description !== item.description && (
diff --git a/src/app/(app)/orders/page.tsx b/src/app/(app)/orders/page.tsx index 1ef1245..697f810 100644 --- a/src/app/(app)/orders/page.tsx +++ b/src/app/(app)/orders/page.tsx @@ -37,6 +37,7 @@ import type { } from "@/lib/server/orders-service"; import { AiItemHistoryTrigger } from "@/components/ai-item-history-trigger"; import { ItemStatusPill } from "@/components/item-status-pill"; +import { ShortDeliveredPill } from "@/components/orders/short-delivered-pill"; import { TAB_GROUP_VISUAL, TAB_GROUP_ITEM_STATUSES, @@ -1004,7 +1005,10 @@ function OrderCard({ intel={null} className="-my-1" /> - {item.description} + + {item.description} + + ×{item.quantity ?? "—"} {item.selling_price !== null && item.quantity !== null diff --git a/src/components/orders/short-delivered-pill.test.ts b/src/components/orders/short-delivered-pill.test.ts new file mode 100644 index 0000000..5ddb86f --- /dev/null +++ b/src/components/orders/short-delivered-pill.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; +import { shortDeliveredLabel } from "./short-delivered-pill"; + +describe("shortDeliveredLabel", () => { + it("names the PO the remainder is already on", () => { + const label = shortDeliveredLabel({ + order_item_details: JSON.stringify({ + shortCollection: { sourceItemId: "o", ordered: 770, collected: 549, remainderQty: 221, poNumber: "PO202610005BID", reason: "[Other]" }, + }), + }); + expect(label?.text).toBe("Short delivered · on PO202610005BID"); + expect(label?.title).toContain("Only 549 of 770"); + }); + + it("is null for the collected part and for ordinary lines", () => { + expect(shortDeliveredLabel({ order_item_details: { shortCollection: { remainderItemId: "r" } } })).toBeNull(); + expect(shortDeliveredLabel({ order_item_details: null })).toBeNull(); + }); +}); diff --git a/src/components/orders/short-delivered-pill.tsx b/src/components/orders/short-delivered-pill.tsx new file mode 100644 index 0000000..04a01ea --- /dev/null +++ b/src/components/orders/short-delivered-pill.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { readShortCollectionMarker } from "@/lib/short-collection"; + +/** + * Label for a line split off a short collection (see + * `src/lib/short-collection.ts`), shown next to its description so the + * buyer doesn't order it a second time: the supplier already has it on + * the original PO, it just came short. + */ +export function shortDeliveredLabel(item: { + order_item_details?: string | Record | null; +}): { text: string; title: string } | null { + const marker = readShortCollectionMarker(item.order_item_details); + if (!marker || marker.role !== "remainder") return null; + const po = marker.poNumber || "the original PO"; + return { + text: `Short delivered · on ${po}`, + title: + `Only ${marker.collected} of ${marker.ordered} came in on ${po}. ` + + `These ${marker.remainderQty} were already ordered — check with the supplier before ordering again.` + + (marker.reason ? ` Driver: ${marker.reason}` : ""), + }; +} + +export function ShortDeliveredPill({ + item, +}: { + item: { order_item_details?: string | Record | null }; +}) { + const label = shortDeliveredLabel(item); + if (!label) return null; + return ( + + {label.text} + + ); +}