import { calculateSAH, getMonthRange, DEFAULT_CALCULATION_RULES, } from "@capakraken/engine"; import type { CalculationRule, SpainScheduleRule, WeekdayAvailability } from "@capakraken/shared"; import type { TRPCContext } from "../trpc.js"; import { loadResourceGraphAvailability } from "./computation-graph-resource-availability.js"; export type ResourceGraphInput = { resourceId: string; month: string; }; export async function loadResourceGraphSnapshot( ctx: { db: TRPCContext["db"] }, input: ResourceGraphInput, ) { const [year, month] = input.month.split("-").map(Number) as [number, number]; const { start: monthStart, end: monthEnd } = getMonthRange(year, month); const resource = await ctx.db.resource.findUniqueOrThrow({ where: { id: input.resourceId }, select: { id: true, displayName: true, eid: true, fte: true, lcrCents: true, chargeabilityTarget: true, countryId: true, federalState: true, metroCityId: true, availability: true, country: { select: { id: true, code: true, name: true, dailyWorkingHours: true, scheduleRules: true } }, metroCity: { select: { id: true, name: true } }, managementLevelGroup: { select: { id: true, name: true, targetPercentage: true } }, }, }); const dailyHours = resource.country?.dailyWorkingHours ?? 8; const scheduleRules = resource.country?.scheduleRules as SpainScheduleRule | null; const targetPct = resource.managementLevelGroup?.targetPercentage ?? (resource.chargeabilityTarget / 100); const availability = resource.availability as WeekdayAvailability | null; const weeklyAvailability: WeekdayAvailability = availability ?? { monday: dailyHours, tuesday: dailyHours, wednesday: dailyHours, thursday: dailyHours, friday: dailyHours, saturday: 0, sunday: 0, }; const assignments = await ctx.db.assignment.findMany({ where: { resourceId: input.resourceId, startDate: { lte: monthEnd }, endDate: { gte: monthStart }, status: { in: ["CONFIRMED", "ACTIVE", "PROPOSED"] }, }, select: { id: true, hoursPerDay: true, startDate: true, endDate: true, dailyCostCents: true, status: true, project: { select: { id: true, name: true, shortCode: true, budgetCents: true, winProbability: true, utilizationCategory: { select: { code: true } }, }, }, }, }); const availabilitySummary = await loadResourceGraphAvailability({ db: ctx.db, resource, weeklyAvailability, monthStart, monthEnd, }); let calcRules: CalculationRule[] = DEFAULT_CALCULATION_RULES; try { const dbRules = await ctx.db.calculationRule.findMany({ where: { isActive: true }, orderBy: [{ priority: "desc" }], }); if (dbRules.length > 0) { calcRules = dbRules as unknown as CalculationRule[]; } } catch { // table may not exist yet } const sahResult = calculateSAH({ dailyWorkingHours: dailyHours, scheduleRules, fte: resource.fte, periodStart: monthStart, periodEnd: monthEnd, publicHolidays: availabilitySummary.publicHolidayStrings, absenceDays: availabilitySummary.absenceDateStrings, }); return { assignments, calcRules, dailyHours, monthEnd, monthStart, resource, sahResult, scheduleRules, targetPct, weeklyAvailability, ...availabilitySummary, }; }