The first inventory vertical on the shared spine. Things you own — tools, gear, equipment — each living in a Location or inside another Item. - Item model (kind DURABLE|CONSUMABLE; consumable fields present but unused so Pantry needs no migration), self-nesting via parentItemId (toolbox holds drill, with a cycle guard), value/warranty/serial/brand/qty, qrSlug, photo - Label (m2m) + ItemAttachment (photos/manuals/receipts) + ItemLog (maintenance/repair/move/dispose) history; Task.itemId - API-first: /api/v1/items (CRUD), labels, logs, multipart attachments — the UI consumes the same scoped endpoints. items service shared with the coming MCP - Inventory pages: tree grouped by Location with items nested inside items; item detail with warranty status, photos, contents, history; add/edit/delete, log, photo upload. Nav entry. Backup/restore cover the new tables - Pure value.ts (warrantyStatus, totalValue) with tests; verified end-to-end against a running server (nest, cycle-guard 400, log 201) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
144 lines
4.1 KiB
SQL
144 lines
4.1 KiB
SQL
-- CreateEnum
|
|
CREATE TYPE "ItemKind" AS ENUM ('DURABLE', 'CONSUMABLE');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ItemLogType" AS ENUM ('NOTE', 'MAINTENANCE', 'MOVED', 'PURCHASED', 'REPAIRED', 'DISPOSED');
|
|
|
|
-- AlterTable
|
|
ALTER TABLE "PlantGroup" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
|
|
|
-- AlterTable
|
|
ALTER TABLE "Task" ADD COLUMN "itemId" TEXT;
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Item" (
|
|
"id" TEXT NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"description" TEXT,
|
|
"kind" "ItemKind" NOT NULL DEFAULT 'DURABLE',
|
|
"quantity" DECIMAL(10,2) NOT NULL DEFAULT 1,
|
|
"unit" TEXT,
|
|
"locationId" TEXT,
|
|
"parentItemId" TEXT,
|
|
"value" DECIMAL(10,2),
|
|
"purchaseDate" TIMESTAMP(3),
|
|
"warrantyExpiry" TIMESTAMP(3),
|
|
"serial" TEXT,
|
|
"modelNumber" TEXT,
|
|
"brand" TEXT,
|
|
"barcode" TEXT,
|
|
"bestBefore" TIMESTAMP(3),
|
|
"minStock" DECIMAL(10,2),
|
|
"qrSlug" TEXT,
|
|
"imageUrl" TEXT,
|
|
"notes" TEXT,
|
|
"active" BOOLEAN NOT NULL DEFAULT true,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Item_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Label" (
|
|
"id" TEXT NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"color" TEXT,
|
|
|
|
CONSTRAINT "Label_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "LabelOnItem" (
|
|
"itemId" TEXT NOT NULL,
|
|
"labelId" TEXT NOT NULL,
|
|
|
|
CONSTRAINT "LabelOnItem_pkey" PRIMARY KEY ("itemId","labelId")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ItemAttachment" (
|
|
"id" TEXT NOT NULL,
|
|
"itemId" TEXT NOT NULL,
|
|
"kind" TEXT NOT NULL,
|
|
"url" TEXT NOT NULL,
|
|
"name" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ItemAttachment_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ItemLog" (
|
|
"id" TEXT NOT NULL,
|
|
"itemId" TEXT NOT NULL,
|
|
"type" "ItemLogType" NOT NULL,
|
|
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"notes" TEXT,
|
|
"cost" DECIMAL(10,2),
|
|
"actorId" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "ItemLog_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Item_qrSlug_key" ON "Item"("qrSlug");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_locationId_idx" ON "Item"("locationId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_parentItemId_idx" ON "Item"("parentItemId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_kind_idx" ON "Item"("kind");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_active_idx" ON "Item"("active");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_name_idx" ON "Item"("name");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Item_barcode_idx" ON "Item"("barcode");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Label_name_key" ON "Label"("name");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "LabelOnItem_labelId_idx" ON "LabelOnItem"("labelId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ItemAttachment_itemId_idx" ON "ItemAttachment"("itemId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ItemLog_itemId_idx" ON "ItemLog"("itemId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ItemLog_date_idx" ON "ItemLog"("date");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Task_itemId_idx" ON "Task"("itemId");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Task" ADD CONSTRAINT "Task_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Item" ADD CONSTRAINT "Item_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Item" ADD CONSTRAINT "Item_parentItemId_fkey" FOREIGN KEY ("parentItemId") REFERENCES "Item"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "LabelOnItem" ADD CONSTRAINT "LabelOnItem_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "LabelOnItem" ADD CONSTRAINT "LabelOnItem_labelId_fkey" FOREIGN KEY ("labelId") REFERENCES "Label"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ItemAttachment" ADD CONSTRAINT "ItemAttachment_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ItemLog" ADD CONSTRAINT "ItemLog_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|