134 lines
3.3 KiB
TypeScript
134 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
normalizeEstimateDemandLine,
|
|
summarizeEstimateDemandLines,
|
|
} from "../estimate/index.js";
|
|
|
|
describe("summarizeEstimateDemandLines", () => {
|
|
it("aggregates hours, totals, and margin", () => {
|
|
expect(
|
|
summarizeEstimateDemandLines([
|
|
{ hours: 10, costTotalCents: 100_00, priceTotalCents: 180_00 },
|
|
{ hours: 6.5, costTotalCents: 80_00, priceTotalCents: 120_00 },
|
|
]),
|
|
).toEqual({
|
|
totalHours: 16.5,
|
|
totalCostCents: 180_00,
|
|
totalPriceCents: 300_00,
|
|
marginCents: 120_00,
|
|
marginPercent: 40,
|
|
});
|
|
});
|
|
|
|
it("returns zero margin percent when no price exists", () => {
|
|
expect(
|
|
summarizeEstimateDemandLines([
|
|
{ hours: 4, costTotalCents: 50_00, priceTotalCents: 0 },
|
|
]),
|
|
).toEqual({
|
|
totalHours: 4,
|
|
totalCostCents: 50_00,
|
|
totalPriceCents: 0,
|
|
marginCents: -50_00,
|
|
marginPercent: 0,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("normalizeEstimateDemandLine", () => {
|
|
it("recomputes linked live rates and totals from the resource snapshot", () => {
|
|
expect(
|
|
normalizeEstimateDemandLine(
|
|
{
|
|
resourceId: "res_1",
|
|
hours: 12,
|
|
rateSource: "RESOURCE",
|
|
costRateCents: 4000,
|
|
billRateCents: 6500,
|
|
currency: "EUR",
|
|
costTotalCents: 0,
|
|
priceTotalCents: 0,
|
|
metadata: {
|
|
calculation: {
|
|
costRateMode: "resource",
|
|
billRateMode: "resource",
|
|
totalMode: "computed",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
resourceSnapshot: {
|
|
resourceId: "res_1",
|
|
currency: "USD",
|
|
lcrCents: 5000,
|
|
ucrCents: 8000,
|
|
},
|
|
defaultCurrency: "EUR",
|
|
},
|
|
),
|
|
).toMatchObject({
|
|
costRateCents: 5000,
|
|
billRateCents: 8000,
|
|
currency: "USD",
|
|
costTotalCents: 60000,
|
|
priceTotalCents: 96000,
|
|
metadata: {
|
|
calculation: {
|
|
costRateMode: "resource",
|
|
billRateMode: "resource",
|
|
totalMode: "computed",
|
|
liveCostRateCents: 5000,
|
|
liveBillRateCents: 8000,
|
|
liveCurrency: "USD",
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it("preserves manual override rates while still updating trace metadata", () => {
|
|
expect(
|
|
normalizeEstimateDemandLine(
|
|
{
|
|
resourceId: "res_2",
|
|
hours: 10,
|
|
rateSource: "RESOURCE",
|
|
costRateCents: 7200,
|
|
billRateCents: 9500,
|
|
currency: "EUR",
|
|
costTotalCents: 0,
|
|
priceTotalCents: 0,
|
|
metadata: {
|
|
calculation: {
|
|
costRateMode: "manual",
|
|
billRateMode: "resource",
|
|
totalMode: "computed",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
resourceSnapshot: {
|
|
resourceId: "res_2",
|
|
currency: "EUR",
|
|
lcrCents: 5000,
|
|
ucrCents: 9000,
|
|
},
|
|
defaultCurrency: "EUR",
|
|
},
|
|
),
|
|
).toMatchObject({
|
|
costRateCents: 7200,
|
|
billRateCents: 9000,
|
|
costTotalCents: 72000,
|
|
priceTotalCents: 90000,
|
|
metadata: {
|
|
calculation: {
|
|
costRateMode: "manual",
|
|
billRateMode: "resource",
|
|
liveCostRateCents: 5000,
|
|
liveBillRateCents: 9000,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
});
|