import { loadAllocationEntry, updateAllocationEntry } from "@capakraken/application"; import type { PrismaClient } from "@capakraken/db"; import type { RecurrencePattern, WeekdayAvailability } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import { assertTimelineDateRangeValid } from "./timeline-allocation-mutation-support.js"; import { buildTimelineAllocationEntryUpdate, buildTimelineAllocationMetadata, buildTimelineAllocationUpdateAuditChanges, } from "./timeline-allocation-update-support.js"; import { calculateTimelineAllocationDailyCost } from "./timeline-cost-support.js"; export async function applyTimelineInlineAllocationUpdate(input: { db: PrismaClient; allocationId: string; hoursPerDay?: number | undefined; startDate?: Date | undefined; endDate?: Date | undefined; includeSaturday?: boolean | undefined; role?: string | undefined; }) { const resolved = await loadAllocationEntry(input.db, input.allocationId); const existing = resolved.entry; const existingResource = resolved.resourceId ? await input.db.resource.findUnique({ where: { id: resolved.resourceId }, select: { id: true, lcrCents: true, availability: true }, }) : null; const newHoursPerDay = input.hoursPerDay ?? existing.hoursPerDay; const newStartDate = input.startDate ?? existing.startDate; const newEndDate = input.endDate ?? existing.endDate; assertTimelineDateRangeValid(newStartDate, newEndDate); const { metadata: newMeta, includeSaturday } = buildTimelineAllocationMetadata({ existingMetadata: existing.metadata as Record | null | undefined, includeSaturday: input.includeSaturday, }); let newDailyCostCents = 0; if (resolved.resourceId) { if (!existingResource) { throw new TRPCError({ code: "NOT_FOUND", message: "Resource not found" }); } const availability = existingResource.availability as unknown as WeekdayAvailability; const recurrence = newMeta.recurrence as RecurrencePattern | undefined; newDailyCostCents = await calculateTimelineAllocationDailyCost({ db: input.db, resourceId: resolved.resourceId, lcrCents: existingResource.lcrCents, hoursPerDay: newHoursPerDay, startDate: newStartDate, endDate: newEndDate, availability, includeSaturday, ...(recurrence ? { recurrence } : {}), }); } return input.db.$transaction(async (tx) => { const { allocation: updatedAllocation } = await updateAllocationEntry( tx as unknown as Parameters[0], { id: input.allocationId, ...buildTimelineAllocationEntryUpdate({ hoursPerDay: newHoursPerDay, startDate: newStartDate, endDate: newEndDate, metadata: newMeta, dailyCostCents: newDailyCostCents, role: input.role, }), }, ); await tx.auditLog.create({ data: { entityType: "Allocation", entityId: input.allocationId, action: "UPDATE", changes: buildTimelineAllocationUpdateAuditChanges({ allocationId: resolved.entry.id, previousHoursPerDay: existing.hoursPerDay, previousStartDate: existing.startDate, previousEndDate: existing.endDate, nextAllocationId: updatedAllocation.id, nextHoursPerDay: newHoursPerDay, nextStartDate: newStartDate, nextEndDate: newEndDate, includeSaturday, }), }, }); return updatedAllocation; }); }