feat(planning): ship holiday-aware planning and assistant upgrades

This commit is contained in:
2026-03-28 22:49:28 +01:00
parent 2a005794e7
commit 4f48afe7b4
151 changed files with 17738 additions and 1940 deletions
@@ -134,12 +134,14 @@ describe("project router", () => {
create: vi.fn().mockResolvedValue(created),
},
auditLog: { create: vi.fn().mockResolvedValue({}) },
webhook: { findMany: vi.fn().mockResolvedValue([]) },
};
const caller = createManagerCaller(db);
const result = await caller.create({
shortCode: "PRJ-001",
name: "Test Project",
responsiblePerson: "Alice",
orderType: OrderType.CHARGEABLE,
allocationType: AllocationType.INT,
winProbability: 80,
@@ -167,6 +169,7 @@ describe("project router", () => {
caller.create({
shortCode: "PRJ-001",
name: "Duplicate",
responsiblePerson: "Alice",
orderType: OrderType.CHARGEABLE,
allocationType: AllocationType.INT,
budgetCents: 100_00,
@@ -189,6 +192,7 @@ describe("project router", () => {
caller.create({
shortCode: "PRJ-002",
name: "Blocked",
responsiblePerson: "Alice",
orderType: OrderType.CHARGEABLE,
allocationType: AllocationType.INT,
budgetCents: 100_00,
@@ -239,6 +243,64 @@ describe("project router", () => {
});
});
describe("getShoringRatio", () => {
it("excludes regional holidays from shoring weighting", async () => {
const db = {
project: {
findUnique: vi.fn().mockResolvedValue({
id: "project_1",
name: "Test Project",
shoringThreshold: 55,
onshoreCountryCode: "DE",
}),
},
assignment: {
findMany: vi.fn().mockResolvedValue([
{
id: "a1",
resourceId: "res_de",
startDate: new Date("2026-01-05T00:00:00.000Z"),
endDate: new Date("2026-01-06T00:00:00.000Z"),
hoursPerDay: 8,
resource: {
id: "res_de",
countryId: "country_de",
federalState: "BY",
metroCityId: null,
availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0 },
country: { id: "country_de", code: "DE" },
metroCity: null,
},
},
{
id: "a2",
resourceId: "res_es",
startDate: new Date("2026-01-05T00:00:00.000Z"),
endDate: new Date("2026-01-06T00:00:00.000Z"),
hoursPerDay: 8,
resource: {
id: "res_es",
countryId: "country_es",
federalState: null,
metroCityId: null,
availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0 },
country: { id: "country_es", code: "ES" },
metroCity: null,
},
},
]),
},
};
const caller = createProtectedCaller(db);
const result = await caller.getShoringRatio({ projectId: "project_1" });
expect(result.totalHours).toBe(24);
expect(result.onshoreRatio).toBe(33);
expect(result.offshoreRatio).toBe(67);
});
});
// ─── update ───────────────────────────────────────────────────────────────
describe("update", () => {
@@ -294,6 +356,7 @@ describe("project router", () => {
project: {
update: vi.fn().mockResolvedValue(updated),
},
webhook: { findMany: vi.fn().mockResolvedValue([]) },
};
const caller = createManagerCaller(db);