refactor(api): extract project computation graph snapshot loading

This commit is contained in:
2026-03-31 11:28:18 +02:00
parent be597dc1c5
commit 7dde6a7461
2 changed files with 109 additions and 85 deletions
@@ -4,92 +4,21 @@ import {
distributeHoursToWeeks,
summarizeEstimateDemandLines,
} from "@capakraken/engine";
import type { TRPCContext } from "../trpc.js";
import { fmtEur } from "../lib/format-utils.js";
import { loadProjectGraphSnapshot, type ProjectGraphInput } from "./computation-graph-project-snapshot.js";
import { type GraphLink, type GraphNode, fmtNum, l, n } from "./computation-graph-shared.js";
type ProjectGraphInput = {
projectId: string;
};
export async function readProjectGraphSnapshot(
ctx: { db: TRPCContext["db"] },
ctx: Parameters<typeof loadProjectGraphSnapshot>[0],
input: ProjectGraphInput,
) {
const project = await ctx.db.project.findUniqueOrThrow({
where: { id: input.projectId },
select: {
id: true,
name: true,
shortCode: true,
budgetCents: true,
winProbability: true,
startDate: true,
endDate: true,
},
});
const estimate = await ctx.db.estimate.findFirst({
where: { projectId: input.projectId },
select: {
id: true,
versions: {
orderBy: { versionNumber: "desc" },
take: 1,
select: {
id: true,
commercialTerms: true,
demandLines: {
select: {
id: true,
hours: true,
costRateCents: true,
billRateCents: true,
costTotalCents: true,
priceTotalCents: true,
chapter: true,
monthlySpread: true,
scopeItemId: true,
resourceId: true,
},
},
scopeItems: {
select: {
id: true,
name: true,
scopeType: true,
frameCount: true,
itemCount: true,
unitMode: true,
},
},
resourceSnapshots: {
select: {
id: true,
resourceId: true,
displayName: true,
chapter: true,
lcrCents: true,
ucrCents: true,
location: true,
level: true,
},
},
},
},
},
orderBy: { updatedAt: "desc" },
});
const latestVersion = estimate?.versions[0];
let effortRuleCount = 0;
let experienceRuleCount = 0;
try {
effortRuleCount = await ctx.db.effortRule.count();
experienceRuleCount = await ctx.db.experienceMultiplierRule.count();
} catch {
// tables may not exist yet
}
const {
project,
latestVersion,
effortRuleCount,
experienceRuleCount,
projectAllocations,
} = await loadProjectGraphSnapshot(ctx, input);
const nodes: GraphNode[] = [];
const links: GraphLink[] = [];
@@ -328,11 +257,6 @@ export async function readProjectGraphSnapshot(
}
}
const projectAllocations = await ctx.db.assignment.findMany({
where: { projectId: input.projectId },
select: { status: true, dailyCostCents: true, startDate: true, endDate: true, hoursPerDay: true },
});
if (projectAllocations.length > 0) {
const budgetStatus = computeBudgetStatus(
project.budgetCents,