backend: org-scoped pharmacy inventory model

Add an `inventory` resource mirroring the prescriptions feature end-to-end:
Drizzle table (org-scoped, indexed), domain type, zod validation, service
(list/get/create/update/delete), and an RBAC-gated CRUD router mounted at
/api/inventory. Grant the new `inventory` statement to roles — pharmacy gets
read/write, full clinicians read/write/delete, reception/lab none — in both the
backend access control and (mirrored) the frontend. Record writes in the
activity log via a new `inventory` entity type. Includes the migration and an
idempotent demo seed script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-12 19:16:28 +03:00
parent 91e4f4ed30
commit 67fdf15de4
13 changed files with 3061 additions and 0 deletions
+1
View File
@@ -6,6 +6,7 @@ export type ActivityEntityType =
| "note"
| "appointment"
| "prescription"
| "inventory"
| "task";
export type ActivityEntry = {
+18
View File
@@ -0,0 +1,18 @@
// The canonical InventoryItem shape returned by the API. Mirrors the frontend
// `lib/inventory.ts` InventoryItem type. Scoped to the active clinic. The
// derived availability ("in-stock" | "low" | "out") is computed on the client
// from stockQuantity vs reorderThreshold, not stored.
export type InventoryItem = {
id: string;
name: string;
form: string;
strength: string;
unit: string;
stockQuantity: number;
reorderThreshold: number;
location: string;
expiresAt: string | null; // YYYY-MM-DD
notes: string | null;
createdAt: string;
updatedAt: string;
};