Second slice of the Crewing module per wiki Crewing-Implementation-Spec §12 (build order item 2). Everything stays behind NEXT_PUBLIC_CREWING_ENABLED; production is unchanged. Schema is added incrementally — this lands the requisition lifecycle layer. What's in - Schema: Requisition (OPEN→SHORTLISTING→PROPOSING→INTERVIEWING→SELECTED→FILLED, →CANCELLED), ReliefRequest, CrewAction (the POAction mirror) + their enums. Migration crewing_requisitions. - State machine: lib/requisition-state-machine.ts mirrors po-state-machine (selection Manager-only; orthogonal cancel from OPEN/SHORTLISTING by cancel_requisition holders, §6). Codes REQ-9000… via lib/requisition-number.ts. - Actions: raise/cancel/transition + requestReliefCover/convertReliefToRequisition, each guarding flag+permission+state, writing a CrewAction and notifying. Shared autoRaiseRequisition() (lib/requisition-service.ts) is the backfill entry point for sign-off / leave-clash (later phases). - Notifier: notifyCrew() PO-independent path + CrewNotificationEvent. - Screens: /crewing/requisitions (list + Raise modal + relief convert) and /crewing/requisitions/[id] (detail). Requisitions added to the flag-gated Crewing sidebar (Manager + MPO, §7). Tests & docs - Unit: requisition-state-machine.test.ts (11). - Integration: requisitions.test.ts (15) — raise/cancel/transition, relief request + convert, auto-raise, permission gating. - CLAUDE.md "Crewing" section updated with the Phase 2 surface. Deferred: sign-off/experience (Epic K, §12 item 2) depends on the crew/assignment models from Phase 3/4; autoRaiseRequisition() is ready for it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
101 lines
4.1 KiB
SQL
101 lines
4.1 KiB
SQL
-- CreateEnum
|
|
CREATE TYPE "RequisitionStatus" AS ENUM ('OPEN', 'SHORTLISTING', 'PROPOSING', 'INTERVIEWING', 'SELECTED', 'FILLED', 'CANCELLED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "RequisitionReason" AS ENUM ('NEW_VACANCY', 'REPLACEMENT', 'LEAVE', 'SIGN_OFF', 'END_OF_CONTRACT', 'OTHER');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ReliefRequestStatus" AS ENUM ('OPEN', 'CONVERTED', 'CANCELLED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "CrewActionType" AS ENUM ('REQUISITION_RAISED', 'REQUISITION_ADVANCED', 'REQUISITION_FILLED', 'REQUISITION_CANCELLED', 'RELIEF_REQUESTED', 'RELIEF_CONVERTED', 'RELIEF_CANCELLED');
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Requisition" (
|
|
"id" TEXT NOT NULL,
|
|
"code" TEXT NOT NULL,
|
|
"status" "RequisitionStatus" NOT NULL DEFAULT 'OPEN',
|
|
"reason" "RequisitionReason" NOT NULL DEFAULT 'NEW_VACANCY',
|
|
"autoRaised" BOOLEAN NOT NULL DEFAULT false,
|
|
"neededBy" TIMESTAMP(3),
|
|
"notes" TEXT,
|
|
"cancelledAt" TIMESTAMP(3),
|
|
"cancellationReason" TEXT,
|
|
"filledAt" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"rankId" TEXT NOT NULL,
|
|
"vesselId" TEXT,
|
|
"siteId" TEXT,
|
|
"raisedById" TEXT,
|
|
|
|
CONSTRAINT "Requisition_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ReliefRequest" (
|
|
"id" TEXT NOT NULL,
|
|
"status" "ReliefRequestStatus" NOT NULL DEFAULT 'OPEN',
|
|
"note" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"rankId" TEXT NOT NULL,
|
|
"vesselId" TEXT,
|
|
"siteId" TEXT,
|
|
"requestedById" TEXT NOT NULL,
|
|
"convertedRequisitionId" TEXT,
|
|
|
|
CONSTRAINT "ReliefRequest_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "CrewAction" (
|
|
"id" TEXT NOT NULL,
|
|
"actionType" "CrewActionType" NOT NULL,
|
|
"note" TEXT,
|
|
"metadata" JSONB,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"actorId" TEXT,
|
|
"requisitionId" TEXT,
|
|
|
|
CONSTRAINT "CrewAction_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Requisition_code_key" ON "Requisition"("code");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "ReliefRequest_convertedRequisitionId_key" ON "ReliefRequest"("convertedRequisitionId");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Requisition" ADD CONSTRAINT "Requisition_rankId_fkey" FOREIGN KEY ("rankId") REFERENCES "Rank"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Requisition" ADD CONSTRAINT "Requisition_vesselId_fkey" FOREIGN KEY ("vesselId") REFERENCES "Vessel"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Requisition" ADD CONSTRAINT "Requisition_siteId_fkey" FOREIGN KEY ("siteId") REFERENCES "Site"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Requisition" ADD CONSTRAINT "Requisition_raisedById_fkey" FOREIGN KEY ("raisedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ReliefRequest" ADD CONSTRAINT "ReliefRequest_rankId_fkey" FOREIGN KEY ("rankId") REFERENCES "Rank"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ReliefRequest" ADD CONSTRAINT "ReliefRequest_vesselId_fkey" FOREIGN KEY ("vesselId") REFERENCES "Vessel"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ReliefRequest" ADD CONSTRAINT "ReliefRequest_siteId_fkey" FOREIGN KEY ("siteId") REFERENCES "Site"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ReliefRequest" ADD CONSTRAINT "ReliefRequest_requestedById_fkey" FOREIGN KEY ("requestedById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ReliefRequest" ADD CONSTRAINT "ReliefRequest_convertedRequisitionId_fkey" FOREIGN KEY ("convertedRequisitionId") REFERENCES "Requisition"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CrewAction" ADD CONSTRAINT "CrewAction_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "CrewAction" ADD CONSTRAINT "CrewAction_requisitionId_fkey" FOREIGN KEY ("requisitionId") REFERENCES "Requisition"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|