import { PermissionKey } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import { describe, expect, it } from "vitest"; import { buildChargeabilityReportDetail, getChargeabilityReportDetail, } from "../router/chargeability-report-procedure-support.js"; describe("chargeability report procedure support", () => { it("builds a filtered detailed report with rounded percentages", () => { const result = buildChargeabilityReportDetail( { monthKeys: ["2026-03"], groupTotals: [ { monthKey: "2026-03", totalFte: 1.234, chg: 0.456, target: 0.8, gap: -0.344, chargeabilityRatio: 0.456, targetRatio: 0.8, gapRatio: -0.344, }, ], resources: [ { id: "resource_1", eid: "E-001", displayName: "Alice", fte: 1.234, country: "ES", federalState: null, city: "Barcelona", orgUnit: "CGI", mgmtGroup: "Senior", mgmtLevel: "L7", targetPct: 0.8, months: [ { monthKey: "2026-03", sah: 120.44, sahHours: 120.44, chg: 0.456, bd: 0.1, mdi: 0.05, mo: 0.07, pdr: 0.08, absence: 0.09, unassigned: 0.154, chargeabilityRatio: 0.456, businessDevelopmentRatio: 0.1, marketDevelopmentInnovationRatio: 0.05, managementOverheadRatio: 0.07, peopleDevelopmentRecruitingRatio: 0.08, plannedAbsenceRatio: 0.09, unassignedRatio: 0.154, chargeabilityHours: 54.9, businessDevelopmentHours: 12, marketDevelopmentInnovationHours: 6, managementOverheadHours: 8.4, peopleDevelopmentRecruitingHours: 9.6, plannedAbsenceHours: 10.8, unassignedHours: 18.5, targetRatio: 0.8, targetHours: 96.4, gapRatio: -0.344, gapHours: -41.4, derivation: { baseAvailableHours: 128, publicHolidayCount: 1, publicHolidayWorkdayCount: 1, publicHolidayHoursDeduction: 8, absenceDayEquivalent: 0.5, absenceHoursDeduction: 4, effectiveAvailableHours: 120, }, }, ], }, { id: "resource_2", eid: "E-002", displayName: "Bob", fte: 1, country: "DE", federalState: "BY", city: "Munich", orgUnit: "CGI", mgmtGroup: "Senior", mgmtLevel: "L7", targetPct: 0.75, months: [], }, ], }, { startMonth: "2026-03", endMonth: "2026-03", resourceQuery: "ali", resourceLimit: 1, includeProposed: false, }, ); expect(result.filters).toEqual({ startMonth: "2026-03", endMonth: "2026-03", orgUnitId: null, managementLevelGroupId: null, countryId: null, includeProposed: false, resourceQuery: "ali", }); expect(result.groupTotals).toEqual([ { monthKey: "2026-03", totalFte: 1.2, chargeabilityPct: 45.6, targetPct: 80, gapPct: -34.4, }, ]); expect(result.resourceCount).toBe(1); expect(result.returnedResourceCount).toBe(1); expect(result.truncated).toBe(false); expect(result.resources[0]).toEqual( expect.objectContaining({ displayName: "Alice", targetPct: 80, managementLevelGroup: "Senior", managementLevel: "L7", months: [ expect.objectContaining({ monthKey: "2026-03", sah: 120.4, chargeabilityPct: 45.6, targetPct: 80, gapPct: -34.4, }), ], }), ); }); it("rejects detailed reports without the cost-view permission", async () => { await expect( getChargeabilityReportDetail( { db: { resource: { findMany: async () => [] }, } as never, permissions: new Set(), }, { startMonth: "2026-03", endMonth: "2026-03", includeProposed: false, }, ), ).rejects.toEqual( expect.objectContaining>({ code: "FORBIDDEN", message: `Permission required: ${PermissionKey.VIEW_COSTS}`, }), ); }); });