Second slice of Phase 4 (stacked on 4a crew records). Leave (site-applied, Manager-decided) with clash auto-backfill, and the daily attendance calendar, per Crewing-Implementation-Spec §5.3/§8.9–8.10. Behind NEXT_PUBLIC_CREWING_ENABLED. What's in - Schema (crewing_leave_attendance migration): LeaveRequest (LeaveType, LeaveStatus) + Attendance (AttendanceStatus, unique per assignment+date) on CrewAssignment; CrewActionType += LEAVE_APPLIED/LEAVE_DECIDED/ATTENDANCE_RECORDED. - Leave (R1): site staff apply on behalf (apply_leave); Manager decides (decide_leave) → assignment ON_LEAVE; MPO has no leave role. Leave approvals also surface in the central /approvals queue (§8.13 Leave kind). Notification LEAVE_FOR_APPROVAL. - Clash auto-backfill (R6): lib/leave-clash.ts, required strength = 1 — approving a leave that leaves the vessel with zero active same-rank cover auto-raises a LEAVE requisition via the Phase-2 autoRaiseRequisition. - Attendance (R5): daily month calendar; site staff record (record_attendance), Manager views (view_attendance) but cannot edit, MPO neither. saveAttendance bulk-upserts dirty cells. - Screens: /crewing/leave (apply-on-behalf + Manager Approve/Decline) and /crewing/attendance (tap-to-cycle calendar + Save). Leave + Attendance added to the flag-gated nav (Manager + Site staff). Tests & docs - Integration: leave-attendance.test.ts (7) — apply/decide, clash auto-raise (and no-raise when cover remains), MPO/Manager attendance lockout, permission gating. type-check clean; full unit (240) + integration (182) green. - CLAUDE.md updated with the Phase 4b surface. Deferred: the 6-month leave-planner timeline (lightweight list for now); hours/ overtime attendance (A7). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
2.1 KiB
SQL
61 lines
2.1 KiB
SQL
-- CreateEnum
|
|
CREATE TYPE "LeaveType" AS ENUM ('ANNUAL', 'MEDICAL', 'EMERGENCY', 'UNPAID', 'OTHER');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "LeaveStatus" AS ENUM ('APPLIED', 'APPROVED', 'REJECTED', 'CANCELLED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "AttendanceStatus" AS ENUM ('PRESENT', 'ABSENT', 'HALF_DAY', 'ON_LEAVE', 'SIGN_OFF');
|
|
|
|
-- AlterEnum
|
|
-- This migration adds more than one value to an enum.
|
|
-- With PostgreSQL versions 11 and earlier, this is not possible
|
|
-- in a single migration. This can be worked around by creating
|
|
-- multiple migrations, each migration adding only one value to
|
|
-- the enum.
|
|
|
|
|
|
ALTER TYPE "CrewActionType" ADD VALUE 'LEAVE_APPLIED';
|
|
ALTER TYPE "CrewActionType" ADD VALUE 'LEAVE_DECIDED';
|
|
ALTER TYPE "CrewActionType" ADD VALUE 'ATTENDANCE_RECORDED';
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "LeaveRequest" (
|
|
"id" TEXT NOT NULL,
|
|
"assignmentId" TEXT NOT NULL,
|
|
"type" "LeaveType" NOT NULL DEFAULT 'ANNUAL',
|
|
"fromDate" TIMESTAMP(3) NOT NULL,
|
|
"toDate" TIMESTAMP(3) NOT NULL,
|
|
"reason" TEXT,
|
|
"status" "LeaveStatus" NOT NULL DEFAULT 'APPLIED',
|
|
"appliedById" TEXT NOT NULL,
|
|
"decidedById" TEXT,
|
|
"decidedAt" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "LeaveRequest_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Attendance" (
|
|
"id" TEXT NOT NULL,
|
|
"assignmentId" TEXT NOT NULL,
|
|
"date" DATE NOT NULL,
|
|
"status" "AttendanceStatus" NOT NULL,
|
|
"note" TEXT,
|
|
"recordedById" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Attendance_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Attendance_assignmentId_date_key" ON "Attendance"("assignmentId", "date");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "LeaveRequest" ADD CONSTRAINT "LeaveRequest_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "CrewAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Attendance" ADD CONSTRAINT "Attendance_assignmentId_fkey" FOREIGN KEY ("assignmentId") REFERENCES "CrewAssignment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|