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>
34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
/**
|
|
* Requisition code generator. Format: REQ-<id>, e.g. REQ-9000.
|
|
*
|
|
* The id is a globally sequential integer floored at 9000 (mirroring the PO
|
|
* numbering convention in lib/po-number.ts) so generated codes never collide
|
|
* with any future imported/historical numbering. Call inside the same
|
|
* transaction that creates the requisition to minimise race windows.
|
|
*/
|
|
|
|
import { db } from "@/lib/db";
|
|
import type { Prisma } from "@prisma/client";
|
|
|
|
const PREFIX = "REQ-";
|
|
const FLOOR = 8999; // first generated id is 9000
|
|
|
|
/** Next sequential requisition id by scanning existing REQ- codes. */
|
|
async function nextRequisitionId(client: Prisma.TransactionClient | typeof db): Promise<number> {
|
|
const rows = await client.requisition.findMany({ select: { code: true } });
|
|
let maxId = FLOOR;
|
|
for (const { code } of rows) {
|
|
if (!code.startsWith(PREFIX)) continue;
|
|
const n = parseInt(code.slice(PREFIX.length), 10);
|
|
if (!isNaN(n) && n > maxId) maxId = n;
|
|
}
|
|
return maxId + 1;
|
|
}
|
|
|
|
/** Generate the next requisition code (e.g. "REQ-9000"). */
|
|
export async function generateRequisitionCode(
|
|
client: Prisma.TransactionClient | typeof db = db
|
|
): Promise<string> {
|
|
const id = await nextRequisitionId(client);
|
|
return `${PREFIX}${id}`;
|
|
}
|