Files
CapaKraken/packages/api/src/router/timeline-allocation-inline-support.ts
T

100 lines
3.4 KiB
TypeScript

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,
buildTimelineAllocationEntryUpdate,
buildTimelineAllocationMetadata,
buildTimelineAllocationUpdateAuditChanges,
} from "./timeline-allocation-mutation-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<string, unknown> | 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<typeof updateAllocationEntry>[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;
});
}