refactor(api): extract experience multiplier support
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildExperienceMultiplierCreateManyRows,
|
||||
buildExperienceMultiplierDemandLineUpdateData,
|
||||
buildExperienceMultiplierInput,
|
||||
buildExperienceMultiplierNestedCreateRows,
|
||||
buildExperienceMultiplierSetCreateData,
|
||||
buildExperienceMultiplierSetUpdateData,
|
||||
experienceMultiplierRuleInclude,
|
||||
hasExperienceMultiplierChanges,
|
||||
toExperienceMultiplierEngineRules,
|
||||
} from "../router/experience-multiplier-support.js";
|
||||
|
||||
describe("experience multiplier support", () => {
|
||||
it("exposes the rule include ordering", () => {
|
||||
expect(experienceMultiplierRuleInclude).toEqual({
|
||||
rules: { orderBy: { sortOrder: "asc" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("builds create-many and nested-create rule rows", () => {
|
||||
expect(buildExperienceMultiplierCreateManyRows([
|
||||
{
|
||||
chapter: "VFX",
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.3,
|
||||
},
|
||||
{
|
||||
location: "India",
|
||||
level: "Senior",
|
||||
costMultiplier: 0.8,
|
||||
billMultiplier: 0.9,
|
||||
shoringRatio: 0.5,
|
||||
additionalEffortRatio: 0.1,
|
||||
description: "Offshore senior",
|
||||
sortOrder: 5,
|
||||
},
|
||||
], "ems_1")).toEqual([
|
||||
{
|
||||
multiplierSetId: "ems_1",
|
||||
chapter: "VFX",
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.3,
|
||||
sortOrder: 0,
|
||||
},
|
||||
{
|
||||
multiplierSetId: "ems_1",
|
||||
location: "India",
|
||||
level: "Senior",
|
||||
costMultiplier: 0.8,
|
||||
billMultiplier: 0.9,
|
||||
shoringRatio: 0.5,
|
||||
additionalEffortRatio: 0.1,
|
||||
description: "Offshore senior",
|
||||
sortOrder: 5,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(buildExperienceMultiplierNestedCreateRows([
|
||||
{
|
||||
chapter: "Animation",
|
||||
costMultiplier: 1.1,
|
||||
billMultiplier: 1.2,
|
||||
},
|
||||
])).toEqual([
|
||||
{
|
||||
chapter: "Animation",
|
||||
costMultiplier: 1.1,
|
||||
billMultiplier: 1.2,
|
||||
sortOrder: 0,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("builds create and sparse update payloads", () => {
|
||||
expect(buildExperienceMultiplierSetCreateData({
|
||||
name: "Standard Multipliers",
|
||||
description: "Default adjustments",
|
||||
isDefault: true,
|
||||
rules: [
|
||||
{
|
||||
chapter: "VFX",
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.2,
|
||||
sortOrder: 0,
|
||||
},
|
||||
],
|
||||
})).toEqual({
|
||||
name: "Standard Multipliers",
|
||||
description: "Default adjustments",
|
||||
isDefault: true,
|
||||
rules: {
|
||||
create: [
|
||||
{
|
||||
chapter: "VFX",
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.2,
|
||||
sortOrder: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(buildExperienceMultiplierSetUpdateData({
|
||||
description: null,
|
||||
isDefault: false,
|
||||
})).toEqual({
|
||||
description: null,
|
||||
isDefault: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("maps db rules and demand lines into engine-facing inputs", () => {
|
||||
expect(toExperienceMultiplierEngineRules([
|
||||
{
|
||||
chapter: "VFX",
|
||||
location: null,
|
||||
level: null,
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.1,
|
||||
shoringRatio: null,
|
||||
additionalEffortRatio: null,
|
||||
description: null,
|
||||
},
|
||||
])).toEqual([
|
||||
{
|
||||
chapter: "VFX",
|
||||
costMultiplier: 1.2,
|
||||
billMultiplier: 1.1,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(buildExperienceMultiplierInput({
|
||||
id: "dl_1",
|
||||
name: "Comp Senior",
|
||||
chapter: "VFX",
|
||||
costRateCents: 10000,
|
||||
billRateCents: 15000,
|
||||
hours: 100,
|
||||
metadata: { location: "India" },
|
||||
staffingAttributes: { level: "Senior" },
|
||||
})).toEqual({
|
||||
chapter: "VFX",
|
||||
costRateCents: 10000,
|
||||
billRateCents: 15000,
|
||||
hours: 100,
|
||||
location: "India",
|
||||
level: "Senior",
|
||||
});
|
||||
});
|
||||
|
||||
it("detects changes and builds demand line update data", () => {
|
||||
const line = {
|
||||
id: "dl_1",
|
||||
name: "Comp Senior",
|
||||
chapter: "VFX",
|
||||
costRateCents: 10000,
|
||||
billRateCents: 15000,
|
||||
hours: 100,
|
||||
metadata: { location: "India", existing: true },
|
||||
staffingAttributes: { level: "Senior" },
|
||||
};
|
||||
const result = {
|
||||
adjustedCostRateCents: 12000,
|
||||
adjustedBillRateCents: 18000,
|
||||
adjustedHours: 110,
|
||||
appliedRules: ["VFX uplift"],
|
||||
};
|
||||
|
||||
expect(hasExperienceMultiplierChanges(line, result)).toBe(true);
|
||||
expect(buildExperienceMultiplierDemandLineUpdateData({
|
||||
line,
|
||||
result,
|
||||
multiplierSet: { id: "ems_1", name: "Standard" },
|
||||
})).toEqual({
|
||||
costRateCents: 12000,
|
||||
billRateCents: 18000,
|
||||
hours: 110,
|
||||
costTotalCents: Math.round(12000 * 110),
|
||||
priceTotalCents: Math.round(18000 * 110),
|
||||
metadata: {
|
||||
location: "India",
|
||||
existing: true,
|
||||
experienceMultiplier: {
|
||||
setId: "ems_1",
|
||||
setName: "Standard",
|
||||
appliedRules: ["VFX uplift"],
|
||||
originalCostRateCents: 10000,
|
||||
originalBillRateCents: 15000,
|
||||
originalHours: 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user