feat(context): add order-item-context-service skeleton + types

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-08-07 02:23:45 +02:00
parent 357decbb2b
commit 18afe7d2d9

View file

@ -0,0 +1,101 @@
/**
* order-item-context-service — deterministic SKU-based context for
* the order-sprint process modal + orders detail page.
*
* Mirrors the shape of `item-history-service.ts` but is anchored on
* `order_items.id` instead of `quote_items.id`. The four rows
* surfaced (last quote, last order, last invoiced, stock) plus a
* supplier/cheapest aggregate give the operator the historical
* record before they pick a supplier and cost.
*
* Pure helpers (`normaliseSkuForHistory`, `findBestDescriptionMatch`,
* `aggregateSuppliers`, `computeCheapest`, `daysAgo`, `isValidPrice`)
* are imported from `item-history-service.ts` — DO NOT duplicate them.
*/
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
import {
normaliseSkuForHistory,
findBestDescriptionMatch,
aggregateSuppliers,
computeCheapest,
daysAgo,
isValidPrice,
} from "@/lib/server/item-history-service";
import type {
ItemHistoryMatch,
ItemHistorySupplier,
ItemHistoryCheapest,
} from "@/lib/types";
export interface OrderItemLastQuote {
quote_item_id: string;
quote_id: string;
quote_number: string;
customer_name: string;
company: string;
status: string;
cost_price: number;
selling_price: number;
markup_percent: number;
days_ago: number;
}
export interface OrderItemLastOrder {
order_item_id: string;
order_id: string;
order_number: string;
company: string;
cost_price: number;
selling_price: number;
markup_percent: number;
item_status: string;
updated_at: string;
days_ago: number;
}
export interface OrderItemLastInvoiced {
order_item_id: string;
order_id: string;
order_number: string;
supplier_invoice_number: string;
customer_invoice_number: string;
invoiced_date: string;
cost_price: number;
quantity: number;
qty_invoiced: number;
days_ago: number;
}
export interface OrderItemStock {
stock_item_id: string;
name: string;
sku: string;
current_qty: number;
min_qty: number | null;
selling_price: number | null;
markup_percent: number | null;
status: "in_stock" | "low" | "out";
}
export interface OrderItemContext {
generated_at: string;
match: ItemHistoryMatch;
last_quote: OrderItemLastQuote | null;
last_order: OrderItemLastOrder | null;
last_invoiced: OrderItemLastInvoiced | null;
stock: OrderItemStock | null;
suppliers: ItemHistorySupplier[];
cheapest: ItemHistoryCheapest | null;
}
export interface OrderItemContextResponse extends OrderItemContext {
error?: string;
}
export async function getOrderItemContext(
orderItemId: string,
): Promise<OrderItemContextResponse> {
// Implementation lands in Task 2.
throw new Error("not implemented");
}