import type { SplitAssignmentRecord, SplitDemandRequirementRecord, } from "@capakraken/application"; import { validateShift } from "@capakraken/engine"; import type { ShiftValidationResult } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import type { TimelineShiftPlan } from "./timeline-shift-planning.js"; export interface TimelineShiftProjectRecord { id: string; budgetCents: number; winProbability: number; startDate: Date; endDate: Date; } export interface LoadedTimelineShiftContext { project: TimelineShiftProjectRecord; demandRequirements: SplitDemandRequirementRecord[]; assignments: SplitAssignmentRecord[]; shiftPlan: TimelineShiftPlan; } export interface TimelineProjectShiftEventPayload extends Record { projectId: string; newStartDate: string; newEndDate: string; costDeltaCents: number; resourceIds: Array; } export function buildTimelineProjectShiftValidation(input: { context: LoadedTimelineShiftContext; newStartDate: Date; newEndDate: Date; }) { const { context, newStartDate, newEndDate } = input; return validateShift({ project: { id: context.project.id, budgetCents: context.project.budgetCents, winProbability: context.project.winProbability, startDate: context.project.startDate, endDate: context.project.endDate, }, newStartDate, newEndDate, allocations: context.shiftPlan.validationAllocations, }); } export function assertTimelineProjectShiftValid( validation: ShiftValidationResult, ): void { if (validation.valid) { return; } throw new TRPCError({ code: "BAD_REQUEST", message: `Shift validation failed: ${validation.errors.map((error) => error.message).join(", ")}`, }); } export function buildTimelineProjectShiftEventPayload(input: { projectId: string; newStartDate: Date; newEndDate: Date; validation: ShiftValidationResult; assignments: SplitAssignmentRecord[]; }): TimelineProjectShiftEventPayload { return { projectId: input.projectId, newStartDate: input.newStartDate.toISOString(), newEndDate: input.newEndDate.toISOString(), costDeltaCents: input.validation.costImpact.deltaCents, resourceIds: input.assignments.map((assignment) => assignment.resourceId), }; }