chore(repo): initialize planarchy workspace

This commit is contained in:
2026-03-14 14:31:09 +01:00
commit dd55d0e78b
769 changed files with 166461 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
import type {
EstimateDemandLine,
EstimateDemandLineCalculationMetadata,
EstimateDemandLineRateMode,
EstimateDemandSummary,
} from "@planarchy/shared";
export interface EstimateDemandLineRateSnapshot {
resourceId?: string | null;
currency?: string | null;
lcrCents: number;
ucrCents: number;
}
export interface EstimateDemandLineForCalculation {
resourceId?: string | null | undefined;
hours: number;
rateSource?: string | null | undefined;
costRateCents: number;
billRateCents: number;
currency?: string | null | undefined;
costTotalCents: number;
priceTotalCents: number;
metadata?: Record<string, unknown> | null | undefined;
}
type ParsedDemandLineMetadata = Record<string, unknown> & {
calculation?: Partial<EstimateDemandLineCalculationMetadata>;
};
function parseRateMode(value: unknown): EstimateDemandLineRateMode | undefined {
return value === "resource" || value === "manual" ? value : undefined;
}
function parseDemandLineMetadata(
metadata: Record<string, unknown> | null | undefined,
): ParsedDemandLineMetadata {
const safeMetadata =
typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)
? metadata
: {};
const rawCalculation =
typeof safeMetadata.calculation === "object" &&
safeMetadata.calculation !== null &&
!Array.isArray(safeMetadata.calculation)
? (safeMetadata.calculation as Record<string, unknown>)
: undefined;
if (!rawCalculation) {
return safeMetadata as ParsedDemandLineMetadata;
}
const calculation: Partial<EstimateDemandLineCalculationMetadata> = {};
const costRateMode = parseRateMode(rawCalculation.costRateMode);
if (costRateMode) {
calculation.costRateMode = costRateMode;
}
const billRateMode = parseRateMode(rawCalculation.billRateMode);
if (billRateMode) {
calculation.billRateMode = billRateMode;
}
if (rawCalculation.totalMode === "computed") {
calculation.totalMode = "computed";
}
if (typeof rawCalculation.liveCostRateCents === "number") {
calculation.liveCostRateCents = rawCalculation.liveCostRateCents;
}
if (typeof rawCalculation.liveBillRateCents === "number") {
calculation.liveBillRateCents = rawCalculation.liveBillRateCents;
}
if (typeof rawCalculation.liveCurrency === "string") {
calculation.liveCurrency = rawCalculation.liveCurrency;
}
return {
...safeMetadata,
...(Object.keys(calculation).length > 0 ? { calculation } : {}),
};
}
function inferRateMode(
resourceSnapshot: EstimateDemandLineRateSnapshot | null | undefined,
effectiveRateCents: number,
liveRateCents: number | undefined,
explicitMode: EstimateDemandLineRateMode | undefined,
): EstimateDemandLineRateMode {
if (explicitMode) {
return explicitMode;
}
if (!resourceSnapshot || liveRateCents == null) {
return "manual";
}
return effectiveRateCents === liveRateCents ? "resource" : "manual";
}
export function getEstimateDemandLineCalculationMetadata(
line: Pick<
EstimateDemandLineForCalculation,
"resourceId" | "costRateCents" | "billRateCents" | "metadata"
>,
options?: {
resourceSnapshot?: EstimateDemandLineRateSnapshot | null | undefined;
},
): EstimateDemandLineCalculationMetadata {
const parsedMetadata = parseDemandLineMetadata(line.metadata);
const explicitCalculation = parsedMetadata.calculation;
const resourceSnapshot = options?.resourceSnapshot;
const liveCostRateCents = resourceSnapshot?.lcrCents;
const liveBillRateCents = resourceSnapshot?.ucrCents;
return {
costRateMode: inferRateMode(
resourceSnapshot,
line.costRateCents,
liveCostRateCents,
explicitCalculation?.costRateMode,
),
billRateMode: inferRateMode(
resourceSnapshot,
line.billRateCents,
liveBillRateCents,
explicitCalculation?.billRateMode,
),
totalMode: "computed",
liveCostRateCents: liveCostRateCents ?? null,
liveBillRateCents: liveBillRateCents ?? null,
liveCurrency: resourceSnapshot?.currency ?? null,
};
}
export function normalizeEstimateDemandLine<T extends EstimateDemandLineForCalculation>(
line: T,
options?: {
resourceSnapshot?: EstimateDemandLineRateSnapshot | null | undefined;
defaultCurrency?: string;
},
): T {
const resourceSnapshot = options?.resourceSnapshot;
const calculation = getEstimateDemandLineCalculationMetadata(line, {
resourceSnapshot,
});
const effectiveCostRateCents =
calculation.costRateMode === "resource" && resourceSnapshot
? resourceSnapshot.lcrCents
: line.costRateCents;
const effectiveBillRateCents =
calculation.billRateMode === "resource" && resourceSnapshot
? resourceSnapshot.ucrCents
: line.billRateCents;
const currency =
((calculation.costRateMode === "resource" ||
calculation.billRateMode === "resource") &&
resourceSnapshot?.currency
? resourceSnapshot.currency
: line.currency) ||
resourceSnapshot?.currency ||
options?.defaultCurrency ||
"EUR";
const metadata = parseDemandLineMetadata(line.metadata);
return {
...line,
costRateCents: effectiveCostRateCents,
billRateCents: effectiveBillRateCents,
currency,
costTotalCents: Math.round(line.hours * effectiveCostRateCents),
priceTotalCents: Math.round(line.hours * effectiveBillRateCents),
metadata: {
...metadata,
calculation,
},
};
}
export function summarizeEstimateDemandLines(
demandLines: Pick<
EstimateDemandLine,
"hours" | "costTotalCents" | "priceTotalCents"
>[],
): EstimateDemandSummary {
const totalHours = demandLines.reduce((sum, line) => sum + line.hours, 0);
const totalCostCents = demandLines.reduce(
(sum, line) => sum + line.costTotalCents,
0,
);
const totalPriceCents = demandLines.reduce(
(sum, line) => sum + line.priceTotalCents,
0,
);
const marginCents = totalPriceCents - totalCostCents;
const marginPercent =
totalPriceCents > 0 ? Math.round((marginCents / totalPriceCents) * 100) : 0;
return {
totalHours,
totalCostCents,
totalPriceCents,
marginCents,
marginPercent,
};
}