refactor(api): extract scenario router helpers

This commit is contained in:
2026-03-31 11:03:50 +02:00
parent e013d1af9f
commit 3e0d9d9af7
5 changed files with 705 additions and 632 deletions
@@ -0,0 +1,114 @@
import { TRPCError } from "@trpc/server";
import {
calculateScenarioEntryHours,
getScenarioAvailability,
loadScenarioAvailabilityContexts,
scenarioBaselineAssignmentInclude,
scenarioBaselineProjectSelect,
scenarioDemandInclude,
type ScenarioDb,
} from "./scenario-shared.js";
export async function readProjectScenarioBaseline(
db: ScenarioDb,
projectId: string,
) {
const project = await db.project.findUnique({
where: { id: projectId },
select: scenarioBaselineProjectSelect,
});
if (!project) {
throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" });
}
const assignments = await db.assignment.findMany({
where: {
projectId,
status: { not: "CANCELLED" },
},
include: scenarioBaselineAssignmentInclude,
});
const demands = await db.demandRequirement.findMany({
where: {
projectId,
status: { not: "CANCELLED" },
},
include: scenarioDemandInclude,
});
const assignmentRangeStart = assignments.length > 0
? new Date(Math.min(...assignments.map((assignment) => assignment.startDate.getTime())))
: project.startDate;
const assignmentRangeEnd = assignments.length > 0
? new Date(Math.max(...assignments.map((assignment) => assignment.endDate.getTime())))
: project.endDate;
const contexts = await loadScenarioAvailabilityContexts(
db,
assignments
.flatMap((assignment) => (assignment.resource ? [assignment.resource] : [])),
assignmentRangeStart,
assignmentRangeEnd,
);
const baselineAllocations = assignments.map((assignment) => {
const availability = getScenarioAvailability(assignment.resource?.availability);
const lcrCents = assignment.resource?.lcrCents ?? 0;
const totalHours = calculateScenarioEntryHours({
resourceId: assignment.resourceId,
lcrCents,
hoursPerDay: assignment.hoursPerDay,
startDate: assignment.startDate,
endDate: assignment.endDate,
availability,
}, {
periodStart: assignmentRangeStart,
periodEnd: assignmentRangeEnd,
contexts,
});
const costCents = Math.round(totalHours * lcrCents);
const workingDays = assignment.hoursPerDay > 0
? Math.round((totalHours / assignment.hoursPerDay) * 100) / 100
: 0;
return {
id: assignment.id,
resourceId: assignment.resourceId,
resourceName: assignment.resource?.displayName ?? "Unknown",
resourceEid: assignment.resource?.eid ?? "",
lcrCents,
roleId: assignment.roleId,
roleName: assignment.roleEntity?.name ?? assignment.role ?? "",
roleColor: assignment.roleEntity?.color ?? null,
startDate: assignment.startDate.toISOString(),
endDate: assignment.endDate.toISOString(),
hoursPerDay: assignment.hoursPerDay,
status: assignment.status,
costCents,
totalHours,
workingDays,
};
});
const baselineDemands = demands.map((demand) => ({
id: demand.id,
roleId: demand.roleId,
roleName: demand.roleEntity?.name ?? demand.role ?? "",
roleColor: demand.roleEntity?.color ?? null,
startDate: demand.startDate.toISOString(),
endDate: demand.endDate.toISOString(),
hoursPerDay: demand.hoursPerDay,
headcount: demand.headcount,
status: demand.status,
}));
return {
project,
assignments: baselineAllocations,
demands: baselineDemands,
totalCostCents: baselineAllocations.reduce((sum, allocation) => sum + allocation.costCents, 0),
totalHours: baselineAllocations.reduce((sum, allocation) => sum + allocation.totalHours, 0),
budgetCents: project.budgetCents,
};
}