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 }; }), };