refactor(api): extract project cost read procedures

This commit is contained in:
2026-03-31 08:58:22 +02:00
parent 57fb979754
commit 3a15f72f70
2 changed files with 77 additions and 72 deletions
@@ -0,0 +1,74 @@
import { listAssignmentBookings } from "@capakraken/application";
import { ProjectStatus } from "@capakraken/shared";
import { z } from "zod";
import { CursorInputSchema, paginateCursor } from "../db/pagination.js";
import { controllerProcedure } from "../trpc.js";
export const projectCostReadProcedures = {
listWithCosts: controllerProcedure
.input(
CursorInputSchema.extend({
status: z.nativeEnum(ProjectStatus).optional(),
search: z.string().optional(),
}),
)
.query(async ({ ctx, input }) => {
const { status, search, cursor } = input;
const where = {
...(status ? { status } : {}),
...(search
? {
OR: [
{ name: { contains: search, mode: "insensitive" as const } },
{ shortCode: { contains: search, mode: "insensitive" as const } },
],
}
: {}),
};
const whereWithCursor = cursor ? { ...where, id: { gt: cursor } } : where;
const result = await paginateCursor(
({ take }) =>
ctx.db.project.findMany({
where: whereWithCursor,
take,
orderBy: [{ startDate: "asc" }, { id: "asc" }],
}),
input,
);
const projectIds = result.items.map((project) => project.id);
const bookings = projectIds.length
? await listAssignmentBookings(ctx.db, {
startDate: new Date("1900-01-01T00:00:00.000Z"),
endDate: new Date("2100-12-31T23:59:59.999Z"),
projectIds,
})
: [];
const projects = result.items.map((project) => {
const projectBookings = bookings.filter((booking) => booking.projectId === project.id);
let totalCostCents = 0;
let totalPersonDays = 0;
for (const booking of projectBookings) {
const days =
(new Date(booking.endDate).getTime() - new Date(booking.startDate).getTime()) /
(1000 * 60 * 60 * 24) +
1;
totalCostCents += booking.dailyCostCents * days;
totalPersonDays += (booking.hoursPerDay * days) / 8;
}
const utilizationPercent = project.budgetCents > 0
? Math.round((totalCostCents / project.budgetCents) * 100)
: 0;
return {
...project,
totalCostCents: Math.round(totalCostCents),
totalPersonDays: Math.round(totalPersonDays * 10) / 10,
utilizationPercent,
};
});
return { projects, nextCursor: result.nextCursor };
}),
};