Files
Khalid Abdi 0754ce96a4 inventory: barcode scanner in Add-item dialog
Add a camera barcode/QR scanner (components/scan/barcode-scanner.tsx,
@zxing/browser lazy-loaded) and a Barcode/NDC field to the pharmacy
Add-item dialog. Scanning a GS1 DataMatrix auto-fills expiry (AI 17) and
lot (AI 10 -> notes), with the GTIN (AI 01) as the barcode; plain 1D
barcodes drop straight into the field. Persists a nullable inventory
barcode column (migration 0036) through the type/validation/service, and
surfaces it on the item detail. i18n added to all locales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 23:32:45 +03:00

69 lines
1.8 KiB
TypeScript

import { apiFetch } from "@/lib/api-client";
// A pharmacy stock item. Mirrors the backend `src/types/inventory.ts`. Scoped to
// the active clinic. `expiresAt` is an ISO YYYY-MM-DD date (or null).
export type InventoryItem = {
id: string;
name: string;
form: string;
strength: string;
unit: string;
stockQuantity: number;
reorderThreshold: number;
location: string;
barcode: string | null;
expiresAt: string | null;
notes: string | null;
createdAt: string;
updatedAt: string;
};
// The fields the inventory dialog collects.
export type InventoryInput = {
name: string;
form?: string;
strength?: string;
unit?: string;
stockQuantity?: number;
reorderThreshold?: number;
location?: string;
barcode?: string | null;
expiresAt?: string | null;
notes?: string | null;
};
export type Availability = "in-stock" | "low" | "out";
// Derived stock status: out at zero, low at/under the reorder threshold,
// otherwise in stock. Computed on the client (not persisted).
export function availabilityOf(item: InventoryItem): Availability {
if (item.stockQuantity <= 0) return "out";
if (item.stockQuantity <= item.reorderThreshold) return "low";
return "in-stock";
}
export function listInventory(): Promise<InventoryItem[]> {
return apiFetch<InventoryItem[]>("/api/inventory");
}
export function createInventory(input: InventoryInput): Promise<InventoryItem> {
return apiFetch<InventoryItem>("/api/inventory", {
method: "POST",
body: JSON.stringify(input),
});
}
export function updateInventory(
id: string,
input: InventoryInput,
): Promise<InventoryItem> {
return apiFetch<InventoryItem>(`/api/inventory/${id}`, {
method: "PUT",
body: JSON.stringify(input),
});
}
export function deleteInventory(id: string): Promise<void> {
return apiFetch<void>(`/api/inventory/${id}`, { method: "DELETE" });
}