refactor(api): extract project cost read procedures
This commit is contained in:
@@ -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 };
|
||||
}),
|
||||
};
|
||||
Reference in New Issue
Block a user