import type { POStatus, Role } from "@prisma/client"; export type POAction = | "submit" | "approve" | "approve_with_note" | "reject" | "request_edits" | "request_vendor_id" | "provide_vendor_id" | "process_payment" | "mark_paid" | "mark_partial_payment" | "confirm_receipt" | "confirm_partial_receipt"; export type SideEffect = | "EMAIL_MANAGER" | "EMAIL_SUBMITTER" | "EMAIL_ACCOUNTS" | "EMAIL_SUBMITTER_AND_MANAGER"; interface Transition { to: POStatus; allowedRoles: Role[]; requiresNote: boolean; sideEffects: SideEffect[]; } type TransitionMap = Partial>; const TRANSITIONS: Partial> = { DRAFT: { submit: { to: "SUBMITTED", allowedRoles: ["TECHNICAL", "MANNING", "MANAGER", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_MANAGER"], }, }, SUBMITTED: { // Auto-advances to MGR_REVIEW in the server action immediately after SUBMITTED }, MGR_REVIEW: { approve: { to: "MGR_APPROVED", allowedRoles: ["MANAGER", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_SUBMITTER", "EMAIL_ACCOUNTS"], }, approve_with_note: { to: "MGR_APPROVED", allowedRoles: ["MANAGER", "SUPERUSER"], requiresNote: true, sideEffects: ["EMAIL_SUBMITTER", "EMAIL_ACCOUNTS"], }, reject: { to: "REJECTED", allowedRoles: ["MANAGER", "SUPERUSER"], requiresNote: true, sideEffects: ["EMAIL_SUBMITTER"], }, request_edits: { to: "EDITS_REQUESTED", allowedRoles: ["MANAGER", "SUPERUSER"], requiresNote: true, sideEffects: ["EMAIL_SUBMITTER"], }, request_vendor_id: { to: "VENDOR_ID_PENDING", allowedRoles: ["MANAGER", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_SUBMITTER"], }, }, VENDOR_ID_PENDING: { provide_vendor_id: { to: "MGR_REVIEW", allowedRoles: ["TECHNICAL", "MANNING", "ACCOUNTS", "MANAGER", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_MANAGER"], }, }, EDITS_REQUESTED: { submit: { to: "SUBMITTED", allowedRoles: ["TECHNICAL", "MANNING", "MANAGER", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_MANAGER"], }, }, MGR_APPROVED: { process_payment: { to: "SENT_FOR_PAYMENT", allowedRoles: ["ACCOUNTS", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_SUBMITTER_AND_MANAGER"], }, }, SENT_FOR_PAYMENT: { mark_paid: { to: "PAID_DELIVERED", allowedRoles: ["ACCOUNTS", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_SUBMITTER", "EMAIL_MANAGER"], }, mark_partial_payment: { to: "PARTIALLY_PAID", allowedRoles: ["ACCOUNTS", "SUPERUSER"], requiresNote: false, sideEffects: [], }, }, PARTIALLY_PAID: { mark_paid: { to: "PAID_DELIVERED", allowedRoles: ["ACCOUNTS", "SUPERUSER"], requiresNote: false, sideEffects: [], }, mark_partial_payment: { to: "PARTIALLY_PAID", allowedRoles: ["ACCOUNTS", "SUPERUSER"], requiresNote: false, sideEffects: [], }, confirm_receipt: { to: "CLOSED", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: [], }, confirm_partial_receipt: { to: "PARTIALLY_PAID", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: [], }, }, PAID_DELIVERED: { confirm_receipt: { to: "CLOSED", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_MANAGER", "EMAIL_ACCOUNTS"], }, confirm_partial_receipt: { to: "PARTIALLY_CLOSED", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: [], }, }, PARTIALLY_CLOSED: { confirm_receipt: { to: "CLOSED", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: ["EMAIL_MANAGER", "EMAIL_ACCOUNTS"], }, confirm_partial_receipt: { to: "PARTIALLY_CLOSED", allowedRoles: ["TECHNICAL", "MANNING", "SUPERUSER"], requiresNote: false, sideEffects: [], }, }, }; export function getTransition(from: POStatus, action: POAction): Transition | null { return TRANSITIONS[from]?.[action] ?? null; } export function canPerformAction(from: POStatus, action: POAction, role: Role): boolean { const transition = getTransition(from, action); return transition?.allowedRoles.includes(role) ?? false; } export function getAvailableActions(status: POStatus, role: Role): POAction[] { const map = TRANSITIONS[status]; if (!map) return []; return (Object.keys(map) as POAction[]).filter((action) => canPerformAction(status, action, role) ); } export function requiresNote(from: POStatus, action: POAction): boolean { return getTransition(from, action)?.requiresNote ?? false; }