feat(platform): harden access scoping and delivery baseline

This commit is contained in:
2026-03-30 00:27:31 +02:00
parent 00b936fa1f
commit 819345acfa
109 changed files with 26142 additions and 8081 deletions
@@ -11,6 +11,7 @@ vi.mock("@capakraken/application", async (importOriginal) => {
getDashboardTopValueResources: vi.fn(),
getDashboardChargeabilityOverview: vi.fn(),
getDashboardBudgetForecast: vi.fn(),
getDashboardProjectHealth: vi.fn(),
};
});
@@ -31,6 +32,7 @@ import {
getDashboardTopValueResources,
getDashboardChargeabilityOverview,
getDashboardBudgetForecast,
getDashboardProjectHealth,
} from "@capakraken/application";
import { dashboardRouter } from "../router/dashboard.js";
import { createCallerFactory } from "../trpc.js";
@@ -97,7 +99,7 @@ describe("dashboard router", () => {
vi.mocked(getDashboardOverview).mockResolvedValue(overview);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
const result = await caller.getOverview();
expect(result).toMatchObject({
@@ -115,6 +117,72 @@ describe("dashboard router", () => {
});
});
describe("getStatisticsDetail", () => {
it("returns assistant-friendly statistics derived from the canonical dashboard overview", async () => {
vi.mocked(getDashboardOverview).mockResolvedValue({
totalResources: 12,
activeResources: 10,
inactiveResources: 2,
totalProjects: 7,
activeProjects: 4,
inactiveProjects: 3,
totalAllocations: 21,
activeAllocations: 18,
cancelledAllocations: 3,
approvedVacations: 6,
totalEstimates: 9,
budgetSummary: {
totalBudgetCents: 1_234_56,
totalCostCents: 654_32,
avgUtilizationPercent: 53,
},
budgetBasis: {
remainingBudgetCents: 58_024,
budgetedProjects: 5,
unbudgetedProjects: 2,
trackedAssignmentCount: 18,
windowStart: null,
windowEnd: null,
},
projectsByStatus: [
{ status: "ACTIVE", count: 4 },
{ status: "DRAFT", count: 2 },
{ status: "DONE", count: 1 },
],
chapterUtilization: [
{ chapter: "CGI", resourceCount: 5, avgChargeabilityTarget: 78 },
{ chapter: "Compositing", resourceCount: 3, avgChargeabilityTarget: 74 },
{ chapter: "Unassigned", resourceCount: 2, avgChargeabilityTarget: 0 },
],
recentActivity: [],
});
const caller = createControllerCaller({});
const result = await caller.getStatisticsDetail();
expect(getDashboardOverview).toHaveBeenCalledTimes(1);
expect(result).toEqual({
activeResources: 10,
totalProjects: 7,
activeProjects: 4,
totalAllocations: 21,
approvedVacations: 6,
totalEstimates: 9,
totalBudget: "1.234,56 EUR",
projectsByStatus: {
ACTIVE: 4,
DRAFT: 2,
DONE: 1,
},
topChapters: [
{ chapter: "CGI", count: 5 },
{ chapter: "Compositing", count: 3 },
{ chapter: "Unassigned", count: 2 },
],
});
});
});
// ─── getPeakTimes ─────────────────────────────────────────────────────────
describe("getPeakTimes", () => {
@@ -126,7 +194,7 @@ describe("dashboard router", () => {
vi.mocked(getDashboardPeakTimes).mockResolvedValue(peakData);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
const result = await caller.getPeakTimes({
startDate: "2026-03-01T00:00:00.000Z",
endDate: "2026-06-30T00:00:00.000Z",
@@ -148,7 +216,7 @@ describe("dashboard router", () => {
it("passes week granularity to application layer", async () => {
vi.mocked(getDashboardPeakTimes).mockResolvedValue([]);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
await caller.getPeakTimes({
startDate: "2026-03-01T00:00:00.000Z",
endDate: "2026-03-31T00:00:00.000Z",
@@ -177,7 +245,7 @@ describe("dashboard router", () => {
vi.mocked(getDashboardDemand).mockResolvedValue(demandData);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
const result = await caller.getDemand({
startDate: "2026-01-01T00:00:00.000Z",
endDate: "2026-12-31T00:00:00.000Z",
@@ -194,7 +262,7 @@ describe("dashboard router", () => {
it("supports grouping by chapter", async () => {
vi.mocked(getDashboardDemand).mockResolvedValue([]);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
await caller.getDemand({
startDate: "2026-06-01T00:00:00.000Z",
endDate: "2026-06-30T00:00:00.000Z",
@@ -208,6 +276,73 @@ describe("dashboard router", () => {
});
});
describe("getProjectHealthDetail", () => {
it("returns assistant-friendly health detail derived from the canonical dashboard read model", async () => {
vi.mocked(getDashboardProjectHealth).mockResolvedValue([
{
id: "project_critical",
projectName: "Critical Project",
shortCode: "CRIT",
status: "ACTIVE",
clientId: "client_1",
clientName: "Acme",
budgetHealth: 25,
staffingHealth: 40,
timelineHealth: 30,
compositeScore: 35,
},
{
id: "project_healthy",
projectName: "Healthy Project",
shortCode: "HLTH",
status: "ACTIVE",
clientId: "client_1",
clientName: "Acme",
budgetHealth: 90,
staffingHealth: 92,
timelineHealth: 86,
compositeScore: 89,
},
]);
const caller = createControllerCaller({});
const result = await caller.getProjectHealthDetail();
expect(getDashboardProjectHealth).toHaveBeenCalledTimes(1);
expect(result).toEqual({
projects: [
{
projectId: "project_critical",
projectName: "Critical Project",
shortCode: "CRIT",
status: "ACTIVE",
overall: 35,
budget: 25,
staffing: 40,
timeline: 30,
rating: "critical",
},
{
projectId: "project_healthy",
projectName: "Healthy Project",
shortCode: "HLTH",
status: "ACTIVE",
overall: 89,
budget: 90,
staffing: 92,
timeline: 86,
rating: "healthy",
},
],
summary: {
healthy: 1,
atRisk: 0,
critical: 1,
},
});
});
});
// ─── getTopValueResources ─────────────────────────────────────────────────
describe("getTopValueResources", () => {
@@ -219,7 +354,7 @@ describe("dashboard router", () => {
vi.mocked(getDashboardTopValueResources).mockResolvedValue(resources);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
const result = await caller.getTopValueResources({ limit: 10 });
expect(result).toHaveLength(2);
@@ -232,7 +367,7 @@ describe("dashboard router", () => {
it("respects custom limit", async () => {
vi.mocked(getDashboardTopValueResources).mockResolvedValue([]);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
await caller.getTopValueResources({ limit: 5 });
expect(getDashboardTopValueResources).toHaveBeenCalledWith(
@@ -334,7 +469,7 @@ describe("dashboard router", () => {
},
]);
const caller = createProtectedCaller({});
const caller = createControllerCaller({});
const result = await caller.getBudgetForecast();
expect(result).toHaveLength(1);
@@ -351,5 +486,177 @@ describe("dashboard router", () => {
});
expect(getDashboardBudgetForecast).toHaveBeenCalledTimes(1);
});
it("returns assistant-friendly budget forecast detail derived from the canonical dashboard read model", async () => {
vi.mocked(getDashboardBudgetForecast).mockResolvedValue([
{
projectId: "project_1",
projectName: "Alpha",
shortCode: "ALPHA",
clientId: "client_1",
clientName: "Client One",
budgetCents: 100_000,
spentCents: 40_000,
remainingCents: 60_000,
burnRate: 10_000,
estimatedExhaustionDate: "2026-06-30",
pctUsed: 40,
activeAssignmentCount: 2,
calendarLocations: [
{
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Munich",
activeAssignmentCount: 2,
burnRateCents: 10_000,
},
],
},
]);
const caller = createControllerCaller({});
const result = await caller.getBudgetForecastDetail();
expect(getDashboardBudgetForecast).toHaveBeenCalledTimes(1);
expect(result).toEqual({
forecasts: [
expect.objectContaining({
projectId: "project_1",
projectName: "Alpha",
shortCode: "ALPHA",
budgetCents: 100_000,
spentCents: 40_000,
remainingCents: 60_000,
projectedCents: 100_000,
burnRateCents: 10_000,
utilization: "40%",
burnStatus: "on_track",
calendarLocations: [
expect.objectContaining({
countryCode: "DE",
federalState: "BY",
metroCityName: "Munich",
}),
],
}),
],
});
});
});
describe("getDetail", () => {
it("returns the canonical assistant dashboard detail payload", async () => {
vi.mocked(getDashboardOverview).mockResolvedValue({
budgetBasis: {
windowStart: "2026-01-01T00:00:00.000Z",
windowEnd: "2026-06-30T00:00:00.000Z",
},
chapterUtilization: [
{
chapter: "Delivery",
resourceCount: 4,
avgChargeabilityTarget: 78,
},
],
});
vi.mocked(getDashboardPeakTimes).mockResolvedValue([
{
period: "2026-03",
totalHours: 320.4,
capacityHours: 400.2,
utilizationPct: 80,
},
]);
vi.mocked(getDashboardTopValueResources).mockResolvedValue([
{
id: "res_1",
eid: "pparker",
displayName: "Peter Parker",
chapter: "Delivery",
valueScore: 91,
lcrCents: 9_500,
},
]);
vi.mocked(getDashboardDemand).mockResolvedValue([
{
id: "project_1",
name: "Gelddruckmaschine",
shortCode: "GDM",
allocatedHours: 120,
requiredFTEs: 4,
resourceCount: 2,
derivation: {
calendarLocations: [
{
countryCode: "DE",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 2,
allocatedHours: 120,
},
],
},
},
]);
const caller = createControllerCaller({});
const result = await caller.getDetail({ section: "all" });
expect(result).toEqual({
peakTimes: [
{
month: "2026-03",
totalHours: 320.4,
totalHoursPerDay: 320.4,
capacityHours: 400.2,
utilizationPct: 80,
},
],
topResources: [
{
name: "Peter Parker",
eid: "pparker",
chapter: "Delivery",
lcr: "95,00 EUR",
valueScore: 91,
},
],
demandPipeline: [
{
project: "Gelddruckmaschine (GDM)",
needed: 2,
requiredFTEs: 4,
allocatedResources: 2,
allocatedHours: 120,
calendarLocations: [
{
countryCode: "DE",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 2,
allocatedHours: 120,
},
],
},
],
chargeabilityByChapter: [
{
chapter: "Delivery",
headcount: 4,
avgTarget: "78%",
},
],
});
expect(getDashboardPeakTimes).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
startDate: new Date("2026-01-01T00:00:00.000Z"),
endDate: new Date("2026-06-30T00:00:00.000Z"),
granularity: "month",
groupBy: "project",
}),
);
});
});
});