refactor(api): extract project computation graph snapshot loading
This commit is contained in:
@@ -0,0 +1,100 @@
|
|||||||
|
import type { TRPCContext } from "../trpc.js";
|
||||||
|
|
||||||
|
export type ProjectGraphInput = {
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function loadProjectGraphSnapshot(
|
||||||
|
ctx: { db: TRPCContext["db"] },
|
||||||
|
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 projectAllocations = await ctx.db.assignment.findMany({
|
||||||
|
where: { projectId: input.projectId },
|
||||||
|
select: { status: true, dailyCostCents: true, startDate: true, endDate: true, hoursPerDay: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
project,
|
||||||
|
latestVersion,
|
||||||
|
effortRuleCount,
|
||||||
|
experienceRuleCount,
|
||||||
|
projectAllocations,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProjectGraphSnapshot = Awaited<ReturnType<typeof loadProjectGraphSnapshot>>;
|
||||||
@@ -4,92 +4,21 @@ import {
|
|||||||
distributeHoursToWeeks,
|
distributeHoursToWeeks,
|
||||||
summarizeEstimateDemandLines,
|
summarizeEstimateDemandLines,
|
||||||
} from "@capakraken/engine";
|
} from "@capakraken/engine";
|
||||||
import type { TRPCContext } from "../trpc.js";
|
|
||||||
import { fmtEur } from "../lib/format-utils.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";
|
import { type GraphLink, type GraphNode, fmtNum, l, n } from "./computation-graph-shared.js";
|
||||||
|
|
||||||
type ProjectGraphInput = {
|
|
||||||
projectId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function readProjectGraphSnapshot(
|
export async function readProjectGraphSnapshot(
|
||||||
ctx: { db: TRPCContext["db"] },
|
ctx: Parameters<typeof loadProjectGraphSnapshot>[0],
|
||||||
input: ProjectGraphInput,
|
input: ProjectGraphInput,
|
||||||
) {
|
) {
|
||||||
const project = await ctx.db.project.findUniqueOrThrow({
|
const {
|
||||||
where: { id: input.projectId },
|
project,
|
||||||
select: {
|
latestVersion,
|
||||||
id: true,
|
effortRuleCount,
|
||||||
name: true,
|
experienceRuleCount,
|
||||||
shortCode: true,
|
projectAllocations,
|
||||||
budgetCents: true,
|
} = await loadProjectGraphSnapshot(ctx, input);
|
||||||
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 nodes: GraphNode[] = [];
|
const nodes: GraphNode[] = [];
|
||||||
const links: GraphLink[] = [];
|
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) {
|
if (projectAllocations.length > 0) {
|
||||||
const budgetStatus = computeBudgetStatus(
|
const budgetStatus = computeBudgetStatus(
|
||||||
project.budgetCents,
|
project.budgetCents,
|
||||||
|
|||||||
Reference in New Issue
Block a user