import { AllocationStatus } from "@capakraken/shared"; import { z } from "zod"; import { findUniqueOrThrow } from "../../db/helpers.js"; import { anonymizeResource, getAnonymizationDirectory } from "../../lib/anonymization.js"; import { planningReadProcedure } from "../../trpc.js"; import { buildResourceAvailabilitySummary, buildResourceAvailabilityView } from "./availability.js"; import { ASSIGNMENT_INCLUDE, DEMAND_INCLUDE } from "./shared.js"; import { getDemandRequirementByIdOrThrow, loadAllocationReadModel, resolveAssignmentBySelection, } from "./support.js"; export const allocationReadProcedures = { 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)), listDemands: planningReadProcedure .input( z.object({ projectId: z.string().optional(), status: z.nativeEnum(AllocationStatus).optional(), roleId: z.string().optional(), take: z.number().int().min(1).max(1000).default(500), skip: z.number().int().min(0).default(0), }), ) .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" }, take: input.take, skip: input.skip, }); const directory = await getAnonymizationDirectory(ctx.db); if (!directory) { return demands; } return demands.map((demand) => ({ ...demand, assignments: demand.assignments.map((assignment) => assignment.resource ? { ...assignment, resource: anonymizeResource(assignment.resource, directory) } : assignment, ), })); }), listAssignments: planningReadProcedure .input( z.object({ projectId: z.string().optional(), resourceId: z.string().optional(), status: z.nativeEnum(AllocationStatus).optional(), demandRequirementId: z.string().optional(), take: z.number().int().min(1).max(1000).default(500), skip: z.number().int().min(0).default(0), }), ) .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" }, take: input.take, skip: input.skip, }); const directory = await getAnonymizationDirectory(ctx.db); if (!directory) { return assignments; } return assignments.map((assignment) => assignment.resource ? { ...assignment, resource: anonymizeResource(assignment.resource, directory) } : assignment, ); }), 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 directory = await getAnonymizationDirectory(ctx.db); if (!directory || !assignment.resource) { return assignment; } return { ...assignment, resource: anonymizeResource(assignment.resource, directory), }; }), 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)), 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); }), };