import { buildSplitAllocationReadModel } from "@capakraken/application"; import type { PrismaClient } from "@capakraken/db"; import { AllocationStatus } from "@capakraken/shared"; import { ROLE_BRIEF_SELECT } from "../db/selects.js"; export const PROJECT_PLANNING_ALLOCATION_INCLUDE = { resource: { select: { id: true, displayName: true, eid: true, chapter: true, lcrCents: true, availability: true, }, }, project: { select: { id: true, name: true, shortCode: true, orderType: true, clientId: true, budgetCents: true, winProbability: true, status: true, startDate: true, endDate: true, staffingReqs: true, responsiblePerson: true, color: true, }, }, roleEntity: { select: ROLE_BRIEF_SELECT, }, } as const; export const PROJECT_PLANNING_DEMAND_INCLUDE = { project: PROJECT_PLANNING_ALLOCATION_INCLUDE.project, roleEntity: PROJECT_PLANNING_ALLOCATION_INCLUDE.roleEntity, } as const; export const PROJECT_PLANNING_ASSIGNMENT_INCLUDE = { resource: PROJECT_PLANNING_ALLOCATION_INCLUDE.resource, project: PROJECT_PLANNING_ALLOCATION_INCLUDE.project, roleEntity: PROJECT_PLANNING_ALLOCATION_INCLUDE.roleEntity, } as const; /** * Lighter resource select for timeline rendering (hot path). * Omits `availability` which is only needed for budget/cost calculations. */ const TIMELINE_RESOURCE_SELECT = { select: { id: true, displayName: true, eid: true, chapter: true, lcrCents: true, }, } as const; export const TIMELINE_ASSIGNMENT_INCLUDE = { resource: TIMELINE_RESOURCE_SELECT, project: PROJECT_PLANNING_ALLOCATION_INCLUDE.project, roleEntity: PROJECT_PLANNING_ALLOCATION_INCLUDE.roleEntity, } as const; type ProjectPlanningReadDbClient = Pick< PrismaClient, "demandRequirement" | "assignment" >; export interface LoadProjectPlanningReadModelInput { projectId: string; activeOnly?: boolean; } export async function loadProjectPlanningReadModel( db: ProjectPlanningReadDbClient, input: LoadProjectPlanningReadModelInput, ) { const statusFilter = input.activeOnly ? { status: { not: AllocationStatus.CANCELLED } } : {}; const [demandRequirements, assignments] = await Promise.all([ db.demandRequirement.findMany({ where: { projectId: input.projectId, ...statusFilter, }, include: PROJECT_PLANNING_DEMAND_INCLUDE, orderBy: [{ startDate: "asc" }, { projectId: "asc" }], }), db.assignment.findMany({ where: { projectId: input.projectId, ...statusFilter, }, include: PROJECT_PLANNING_ASSIGNMENT_INCLUDE, orderBy: [{ startDate: "asc" }, { resourceId: "asc" }], }), ]); return { demandRequirements, assignments, readModel: buildSplitAllocationReadModel({ demandRequirements, assignments, }), }; }