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
@@ -1168,6 +1168,65 @@ describe("estimate router", () => {
expect.objectContaining({ code: "PRECONDITION_FAILED" }),
);
});
it("throws PRECONDITION_FAILED for demand-line project windows without working days", async () => {
const approvedEstimate = {
...baseEstimate,
projectId: "project_1",
status: EstimateStatus.APPROVED,
versions: [
{
...baseVersion,
id: "ver_approved",
status: EstimateVersionStatus.APPROVED,
lockedAt: new Date("2026-03-13"),
demandLines: [
{
id: "line_1",
name: "Staffing Gap",
hours: 16,
fte: 1,
resourceId: null,
},
],
},
],
};
const findUnique = vi.fn().mockResolvedValue(approvedEstimate);
const projectFindUnique = vi.fn().mockResolvedValue({
id: "project_1",
shortCode: "PRJ1",
name: "Weekend Project",
status: "ACTIVE",
startDate: new Date("2026-03-15"),
endDate: new Date("2026-03-15"),
orderType: "CHARGEABLE",
allocationType: "INT",
winProbability: 100,
budgetCents: 100_000_00,
responsiblePerson: "Test",
});
const db = {
estimate: { findUnique },
project: { findUnique: projectFindUnique },
demandRequirement: { findMany: vi.fn().mockResolvedValue([]) },
assignment: { findMany: vi.fn().mockResolvedValue([]) },
resource: { findMany: vi.fn().mockResolvedValue([]) },
auditLog: { create: vi.fn() },
$transaction: vi.fn(async (callback: (tx: unknown) => unknown) => callback(db)),
};
const caller = createManagerCaller(db);
await expect(
caller.createPlanningHandoff({ estimateId: "est_1" }),
).rejects.toThrow(
expect.objectContaining({
code: "PRECONDITION_FAILED",
message: 'Project window has no working days for demand line "Staffing Gap"',
}),
);
});
});
// ─── RBAC ──────────────────────────────────────────────────────────────────