feat(application): complete dispo import operator flow
This commit is contained in:
@@ -351,4 +351,201 @@ describe("commitDispoImportBatch", () => {
|
||||
|
||||
expect(db.$transaction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to the staged resource role when an assignment row has no role", async () => {
|
||||
const { db, tx } = createCommitDb({
|
||||
role: {
|
||||
upsert: vi.fn().mockResolvedValue({}),
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{ id: "role_ad", name: "Art Director" },
|
||||
{ id: "role_pm", name: "Project Manager" },
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
tx.stagedResource.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sr_roster",
|
||||
sourceKind: "ROSTER",
|
||||
canonicalExternalId: "catharina.voelkle",
|
||||
displayName: "Catharina Voelkle",
|
||||
email: null,
|
||||
chapter: "Art Direction",
|
||||
chargeabilityTarget: null,
|
||||
clientUnitName: null,
|
||||
countryCode: "DE",
|
||||
fte: 1,
|
||||
lcrCents: 1000,
|
||||
managementLevelGroupName: null,
|
||||
managementLevelName: null,
|
||||
metroCityName: null,
|
||||
resourceType: "EMPLOYEE",
|
||||
roleTokens: ["AD"],
|
||||
ucrCents: 1500,
|
||||
availability: {
|
||||
monday: 8,
|
||||
tuesday: 8,
|
||||
wednesday: 8,
|
||||
thursday: 8,
|
||||
friday: 8,
|
||||
},
|
||||
rawPayload: {
|
||||
department: "Digital Designer",
|
||||
},
|
||||
warnings: [],
|
||||
},
|
||||
]);
|
||||
|
||||
tx.stagedProject.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sp_1",
|
||||
shortCode: "generic",
|
||||
projectKey: "generic",
|
||||
name: "Generic Work",
|
||||
clientCode: "BMW",
|
||||
utilizationCategoryCode: "Chg",
|
||||
allocationType: "EXT",
|
||||
orderType: "CHARGEABLE",
|
||||
winProbability: 100,
|
||||
isInternal: false,
|
||||
isTbd: false,
|
||||
startDate: new Date("2026-03-12T00:00:00.000Z"),
|
||||
endDate: new Date("2026-03-12T00:00:00.000Z"),
|
||||
warnings: [],
|
||||
},
|
||||
]);
|
||||
|
||||
tx.stagedAssignment.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sa_missing_role",
|
||||
resourceExternalId: "catharina.voelkle",
|
||||
projectKey: "generic",
|
||||
assignmentDate: new Date("2026-03-12T00:00:00.000Z"),
|
||||
startDate: new Date("2026-03-12T00:00:00.000Z"),
|
||||
endDate: new Date("2026-03-12T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
percentage: 100,
|
||||
roleToken: null,
|
||||
roleName: null,
|
||||
utilizationCategoryCode: "Chg",
|
||||
isInternal: false,
|
||||
isUnassigned: false,
|
||||
isTbd: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await commitDispoImportBatch(db as never, {
|
||||
importBatchId: "batch_1",
|
||||
});
|
||||
|
||||
expect(result.counts.committedAssignments).toBe(1);
|
||||
expect(tx.assignment.upsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
create: expect.objectContaining({
|
||||
role: "Art Director",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("creates a role from the roster department when no canonical dispo role exists", async () => {
|
||||
const { db, tx } = createCommitDb({
|
||||
role: {
|
||||
upsert: vi.fn(async ({ where }: { where: { name: string } }) => ({
|
||||
id: `role_${where.name.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
name: where.name,
|
||||
})),
|
||||
findMany: vi.fn().mockResolvedValue([{ id: "role_ad", name: "Art Director" }]),
|
||||
},
|
||||
});
|
||||
|
||||
tx.stagedResource.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sr_roster",
|
||||
sourceKind: "ROSTER",
|
||||
canonicalExternalId: "dennis.steinschulte",
|
||||
displayName: "Dennis Steinschulte",
|
||||
email: null,
|
||||
chapter: "Product Data Management",
|
||||
chargeabilityTarget: null,
|
||||
clientUnitName: null,
|
||||
countryCode: "DE",
|
||||
fte: 1,
|
||||
lcrCents: 1000,
|
||||
managementLevelGroupName: null,
|
||||
managementLevelName: null,
|
||||
metroCityName: null,
|
||||
resourceType: "EMPLOYEE",
|
||||
roleTokens: [],
|
||||
ucrCents: 1500,
|
||||
availability: {
|
||||
monday: 8,
|
||||
tuesday: 8,
|
||||
wednesday: 8,
|
||||
thursday: 8,
|
||||
friday: 8,
|
||||
},
|
||||
rawPayload: {
|
||||
department: "Vis Logic",
|
||||
},
|
||||
warnings: [],
|
||||
},
|
||||
]);
|
||||
|
||||
tx.stagedProject.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sp_1",
|
||||
shortCode: "INT-MO",
|
||||
projectKey: "generic",
|
||||
name: "Management & Operations",
|
||||
clientCode: "BMW",
|
||||
utilizationCategoryCode: "M&O",
|
||||
allocationType: "INT",
|
||||
orderType: "NONCHARGEABLE",
|
||||
winProbability: 100,
|
||||
isInternal: true,
|
||||
isTbd: false,
|
||||
startDate: new Date("2026-01-12T00:00:00.000Z"),
|
||||
endDate: new Date("2026-01-12T00:00:00.000Z"),
|
||||
warnings: [],
|
||||
},
|
||||
]);
|
||||
|
||||
tx.stagedAssignment.findMany.mockResolvedValue([
|
||||
{
|
||||
id: "sa_missing_role",
|
||||
resourceExternalId: "dennis.steinschulte",
|
||||
projectKey: "generic",
|
||||
assignmentDate: new Date("2026-01-12T00:00:00.000Z"),
|
||||
startDate: new Date("2026-01-12T00:00:00.000Z"),
|
||||
endDate: new Date("2026-01-12T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
percentage: 100,
|
||||
roleToken: null,
|
||||
roleName: null,
|
||||
utilizationCategoryCode: "M&O",
|
||||
isInternal: true,
|
||||
isUnassigned: false,
|
||||
isTbd: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await commitDispoImportBatch(db as never, {
|
||||
importBatchId: "batch_1",
|
||||
});
|
||||
|
||||
expect(result.counts.committedAssignments).toBe(1);
|
||||
expect(tx.role.upsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { name: "Vis Logic" },
|
||||
}),
|
||||
);
|
||||
expect(tx.assignment.upsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
create: expect.objectContaining({
|
||||
role: "Vis Logic",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { deriveRoleTokens } from "../use-cases/dispo-import/shared.js";
|
||||
|
||||
describe("deriveRoleTokens", () => {
|
||||
it("recognizes common roster department and title variants", () => {
|
||||
expect(deriveRoleTokens("2D Nuke")).toEqual(["2D"]);
|
||||
expect(deriveRoleTokens("Motion Design")).toEqual(["2D"]);
|
||||
expect(deriveRoleTokens("3D Modeling")).toEqual(["3D"]);
|
||||
expect(deriveRoleTokens("Digital Producer")).toEqual(["PM"]);
|
||||
expect(deriveRoleTokens("Head of Delivery")).toEqual(["PM"]);
|
||||
expect(deriveRoleTokens("Digital Designer")).toEqual(["AD"]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user