mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +00:00
- migration r1_referentiel : Category (EQUIPMENT/COMPONENT_TYPE), Location (site → zone, lat/lng + colonne PostGIS générée geography(Point,4326) + index GIST), Asset (statut d'équipement), AssetComponent (organe sans emplacement PAR CONSTRUCTION), Team, invitation sur User ; migration autosuffisante (CREATE EXTENSION IF NOT EXISTS postgis) - contrat : 21 nouvelles opérations (26 total), générateur OpenAPI étendu aux paramètres de chemin ; spec + client web régénérés dans ce commit - API : modules categories/locations/assets/teams + gestion des personnes (liste, rôles, invitation lien 7 j à usage unique, activation publique qui connecte directement, mise à jour rôle/équipes) — tout sous @RequirePermission ; invariants en service (profondeur 2, kinds, catégorie jamais supprimée) - seed : parc de la maquette validée (5 sites + 8 zones, 8 appareils, organes A1/B2, 9 catégories, 2 équipes) — idempotent - 36 tests verts (couverture 96 % stmts / 85 % branches) : recette site→zone→appareil→organes, matrice vivante, invitation→activation ; smoke test sur build de prod - CI : postgres → postgis/postgis:18-3.6 (la migration R1 l'exige) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
146 lines
4.6 KiB
SQL
146 lines
4.6 KiB
SQL
-- Migration autosuffisante : les extensions requises sont créées si absentes
|
||
-- (la CI et tout environnement neuf n’ont pas notre init.sql).
|
||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "CategoryKind" AS ENUM ('EQUIPMENT', 'COMPONENT_TYPE');
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "AssetStatus" AS ENUM ('IN_SERVICE', 'OUT_OF_SERVICE', 'UNDER_MAINTENANCE');
|
||
|
||
-- AlterTable
|
||
ALTER TABLE "User" ADD COLUMN "activationExpiresAt" TIMESTAMP(3),
|
||
ADD COLUMN "activationToken" TEXT,
|
||
ADD COLUMN "phone" TEXT;
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "Category" (
|
||
"id" UUID NOT NULL,
|
||
"kind" "CategoryKind" NOT NULL,
|
||
"name" TEXT NOT NULL,
|
||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||
|
||
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "Location" (
|
||
"id" UUID NOT NULL,
|
||
"name" TEXT NOT NULL,
|
||
"parentId" UUID,
|
||
"address" TEXT,
|
||
"city" TEXT,
|
||
"guardianName" TEXT,
|
||
"guardianPhone" TEXT,
|
||
"latitude" DOUBLE PRECISION,
|
||
"longitude" DOUBLE PRECISION,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||
|
||
CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "Asset" (
|
||
"id" UUID NOT NULL,
|
||
"reference" TEXT NOT NULL,
|
||
"brand" TEXT NOT NULL,
|
||
"model" TEXT,
|
||
"serialNumber" TEXT,
|
||
"commissionedAt" TIMESTAMP(3),
|
||
"loadKg" INTEGER,
|
||
"floors" INTEGER,
|
||
"status" "AssetStatus" NOT NULL DEFAULT 'IN_SERVICE',
|
||
"categoryId" UUID NOT NULL,
|
||
"locationId" UUID NOT NULL,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||
|
||
CONSTRAINT "Asset_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "AssetComponent" (
|
||
"id" UUID NOT NULL,
|
||
"assetId" UUID NOT NULL,
|
||
"typeId" UUID NOT NULL,
|
||
"designation" TEXT,
|
||
|
||
CONSTRAINT "AssetComponent_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "Team" (
|
||
"id" UUID NOT NULL,
|
||
"name" TEXT NOT NULL,
|
||
"description" TEXT,
|
||
|
||
CONSTRAINT "Team_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "_TeamToUser" (
|
||
"A" UUID NOT NULL,
|
||
"B" UUID NOT NULL,
|
||
|
||
CONSTRAINT "_TeamToUser_AB_pkey" PRIMARY KEY ("A","B")
|
||
);
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "Category_kind_name_key" ON "Category"("kind", "name");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "Location_parentId_idx" ON "Location"("parentId");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "Asset_reference_key" ON "Asset"("reference");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "Asset_locationId_idx" ON "Asset"("locationId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "Asset_categoryId_idx" ON "Asset"("categoryId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "AssetComponent_assetId_idx" ON "AssetComponent"("assetId");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "Team_name_key" ON "Team"("name");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "_TeamToUser_B_index" ON "_TeamToUser"("B");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "User_activationToken_key" ON "User"("activationToken");
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "Location" ADD CONSTRAINT "Location_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "Asset" ADD CONSTRAINT "Asset_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "Asset" ADD CONSTRAINT "Asset_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "AssetComponent" ADD CONSTRAINT "AssetComponent_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "Asset"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "AssetComponent" ADD CONSTRAINT "AssetComponent_typeId_fkey" FOREIGN KEY ("typeId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "_TeamToUser" ADD CONSTRAINT "_TeamToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Team"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "_TeamToUser" ADD CONSTRAINT "_TeamToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||
|
||
|
||
-- ————— PostGIS (hors périmètre Prisma, décision modèle R1) —————
|
||
-- Position générée depuis latitude/longitude : rien à synchroniser côté app,
|
||
-- la colonne est prête pour les requêtes spatiales (affectation auto, backlog).
|
||
ALTER TABLE "Location"
|
||
ADD COLUMN "position" geography(Point, 4326)
|
||
GENERATED ALWAYS AS (ST_SetSRID(ST_MakePoint("longitude", "latitude"), 4326)::geography) STORED;
|
||
|
||
CREATE INDEX "Location_position_gix" ON "Location" USING GIST ("position");
|