refactor(api): extract allocation read procedures

This commit is contained in:
2026-03-31 11:02:35 +02:00
parent daed9c2d16
commit 269288f5df
2 changed files with 168 additions and 159 deletions
+3 -159
View File
@@ -26,9 +26,7 @@ import {
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { anonymizeResource, getAnonymizationDirectory } from "../lib/anonymization.js";
import { emitAllocationCreated, emitAllocationDeleted, emitAllocationUpdated } from "../sse/event-bus.js";
import { buildResourceAvailabilitySummary, buildResourceAvailabilityView } from "./allocation-availability.js";
import {
checkBudgetThresholdsInBackground,
createDemandRequirementWithEffects,
@@ -36,45 +34,19 @@ import {
fillDemandRequirementWithEffects,
invalidateDashboardCacheInBackground,
} from "./allocation-effects.js";
import {
ASSIGNMENT_INCLUDE,
DEMAND_INCLUDE,
toIsoDate,
} from "./allocation-shared.js";
import { allocationReadProcedures } from "./allocation-read-procedures.js";
import { ASSIGNMENT_INCLUDE, DEMAND_INCLUDE, toIsoDate } from "./allocation-shared.js";
import {
buildCreateDemandRequirementInput,
findAllocationEntryOrNull,
getDemandRequirementByIdOrThrow,
loadAllocationReadModel,
resolveAssignmentBySelection,
toAssignmentUpdateInput,
toDemandRequirementUpdateInput,
} from "./allocation-support.js";
import { createTRPCRouter, managerProcedure, planningReadProcedure, requirePermission } from "../trpc.js";
export const allocationRouter = createTRPCRouter({
list: planningReadProcedure
.input(
z.object({
projectId: z.string().optional(),
resourceId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).optional(),
}),
)
.query(async ({ ctx, input }) => {
const readModel = await loadAllocationReadModel(ctx.db, input);
return readModel.allocations;
}),
listView: planningReadProcedure
.input(
z.object({
projectId: z.string().optional(),
resourceId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).optional(),
}),
)
.query(async ({ ctx, input }) => loadAllocationReadModel(ctx.db, input)),
...allocationReadProcedures,
create: managerProcedure
.input(CreateAllocationSchema)
@@ -141,134 +113,6 @@ export const allocationRouter = createTRPCRouter({
return allocation;
}),
listDemands: planningReadProcedure
.input(
z.object({
projectId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).optional(),
roleId: z.string().optional(),
}),
)
.query(async ({ ctx, input }) => {
const demands = await ctx.db.demandRequirement.findMany({
where: {
...(input.projectId ? { projectId: input.projectId } : {}),
...(input.status ? { status: input.status } : {}),
...(input.roleId ? { roleId: input.roleId } : {}),
},
include: DEMAND_INCLUDE,
orderBy: { startDate: "asc" },
});
const dir = await getAnonymizationDirectory(ctx.db);
if (!dir) return demands;
return demands.map((d) => ({
...d,
assignments: d.assignments.map((a) =>
a.resource ? { ...a, resource: anonymizeResource(a.resource, dir) } : a,
),
}));
}),
listAssignments: planningReadProcedure
.input(
z.object({
projectId: z.string().optional(),
resourceId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).optional(),
demandRequirementId: z.string().optional(),
}),
)
.query(async ({ ctx, input }) => {
const assignments = await ctx.db.assignment.findMany({
where: {
...(input.projectId ? { projectId: input.projectId } : {}),
...(input.resourceId ? { resourceId: input.resourceId } : {}),
...(input.status ? { status: input.status } : {}),
...(input.demandRequirementId ? { demandRequirementId: input.demandRequirementId } : {}),
},
include: ASSIGNMENT_INCLUDE,
orderBy: { startDate: "asc" },
});
const dir = await getAnonymizationDirectory(ctx.db);
if (!dir) return assignments;
return assignments.map((a) =>
a.resource ? { ...a, resource: anonymizeResource(a.resource, dir) } : a,
);
}),
getAssignmentById: planningReadProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => {
const assignment = await findUniqueOrThrow(
ctx.db.assignment.findUnique({
where: { id: input.id },
include: ASSIGNMENT_INCLUDE,
}),
"Assignment",
);
const dir = await getAnonymizationDirectory(ctx.db);
if (!dir || !assignment.resource) {
return assignment;
}
return {
...assignment,
resource: anonymizeResource(assignment.resource, dir),
};
}),
resolveAssignment: planningReadProcedure
.input(z.object({
assignmentId: z.string().optional(),
resourceId: z.string().optional(),
projectId: z.string().optional(),
startDate: z.coerce.date().optional(),
endDate: z.coerce.date().optional(),
selectionMode: z.enum(["WINDOW", "EXACT_START"]).default("EXACT_START"),
excludeCancelled: z.boolean().default(false),
}))
.query(async ({ ctx, input }) => resolveAssignmentBySelection(ctx.db, input)),
getDemandRequirementById: planningReadProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => getDemandRequirementByIdOrThrow(ctx.db, input.id)),
/**
* Check a resource's availability for a date range.
* Returns working days, existing allocations, conflict days, and available capacity.
*/
checkResourceAvailability: planningReadProcedure
.input(z.object({
resourceId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
}))
.query(async ({ ctx, input }) => {
const { vacations: _vacations, ...availability } = await buildResourceAvailabilityView(ctx.db, input);
return availability;
}),
getResourceAvailabilityView: planningReadProcedure
.input(z.object({
resourceId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
}))
.query(async ({ ctx, input }) => buildResourceAvailabilityView(ctx.db, input)),
getResourceAvailabilitySummary: planningReadProcedure
.input(z.object({
resourceId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
}))
.query(async ({ ctx, input }) => {
const availability = await buildResourceAvailabilityView(ctx.db, input);
return buildResourceAvailabilitySummary(availability, input);
}),
createDemandRequirement: managerProcedure
.input(CreateDemandRequirementSchema)
.mutation(async ({ ctx, input }) => {