Files
CapaKraken/packages/api/src/router/effort-rule-support.ts
T

163 lines
4.4 KiB
TypeScript

import type { Prisma } from "@capakraken/db";
import type {
EffortRuleInput,
ScopeItemInput,
} from "@capakraken/engine";
import {
CreateEffortRuleSetSchema,
UpdateEffortRuleSetSchema,
} from "@capakraken/shared";
import { z } from "zod";
type CreateEffortRuleSetInput = z.infer<typeof CreateEffortRuleSetSchema>;
type UpdateEffortRuleSetInput = z.infer<typeof UpdateEffortRuleSetSchema>;
type EffortRuleRowInput =
| CreateEffortRuleSetInput["rules"][number]
| NonNullable<UpdateEffortRuleSetInput["rules"]>[number];
type EffortRuleRecord = {
scopeType: string;
discipline: string;
chapter: string | null;
unitMode: string;
hoursPerUnit: number;
sortOrder: number;
};
type ScopeItemRecord = {
name: string;
scopeType: string;
frameCount: number | null;
itemCount: number | null;
unitMode: string | null;
};
type DemandLineRuleSetRecord = {
id: string;
name: string;
};
type ExpandedEffortLineRecord = {
scopeItemName: string;
discipline: string;
chapter?: string | null;
hours: number;
unitMode: string;
unitCount: number;
hoursPerUnit: number;
};
export const effortRuleInclude = {
rules: { orderBy: { sortOrder: "asc" as const } },
} as const;
function buildEffortRuleRow(input: EffortRuleRowInput, index: number) {
return {
scopeType: input.scopeType,
discipline: input.discipline,
...(input.chapter ? { chapter: input.chapter } : {}),
unitMode: input.unitMode,
hoursPerUnit: input.hoursPerUnit,
...(input.description ? { description: input.description } : {}),
sortOrder: input.sortOrder ?? index,
};
}
export function buildEffortRuleNestedCreateRows(
rules: EffortRuleRowInput[],
): Prisma.EffortRuleUncheckedCreateWithoutRuleSetInput[] {
return rules.map((rule, index) => ({
...buildEffortRuleRow(rule, index),
}));
}
export function buildEffortRuleCreateManyRows(
rules: EffortRuleRowInput[],
ruleSetId: string,
): Prisma.EffortRuleCreateManyInput[] {
return rules.map((rule, index) => ({
ruleSetId,
...buildEffortRuleRow(rule, index),
}));
}
export function buildEffortRuleSetCreateData(
input: CreateEffortRuleSetInput,
): Prisma.EffortRuleSetCreateInput {
return {
name: input.name,
...(input.description ? { description: input.description } : {}),
isDefault: input.isDefault,
rules: {
create: buildEffortRuleNestedCreateRows(input.rules),
},
};
}
export function buildEffortRuleSetUpdateData(
input: Omit<UpdateEffortRuleSetInput, "id" | "rules">,
): Prisma.EffortRuleSetUncheckedUpdateInput {
return {
...(input.name !== undefined ? { name: input.name } : {}),
...(input.description !== undefined ? { description: input.description } : {}),
...(input.isDefault !== undefined ? { isDefault: input.isDefault } : {}),
};
}
export function toScopeItemInputs(
scopeItems: ScopeItemRecord[],
): ScopeItemInput[] {
return scopeItems.map((scopeItem) => ({
name: scopeItem.name,
scopeType: scopeItem.scopeType as ScopeItemInput["scopeType"],
frameCount: scopeItem.frameCount,
itemCount: scopeItem.itemCount,
unitMode: (scopeItem.unitMode ?? null) as Exclude<ScopeItemInput["unitMode"], undefined>,
}) as ScopeItemInput);
}
export function toEffortRuleEngineInputs(
rules: EffortRuleRecord[],
): EffortRuleInput[] {
return rules.map((rule) => ({
scopeType: rule.scopeType as EffortRuleInput["scopeType"],
discipline: rule.discipline,
chapter: rule.chapter,
unitMode: rule.unitMode as EffortRuleInput["unitMode"],
hoursPerUnit: rule.hoursPerUnit,
sortOrder: rule.sortOrder,
}));
}
export function buildEstimateDemandLineRows(input: {
estimateVersionId: string;
currency: string;
ruleSet: DemandLineRuleSetRecord;
lines: ExpandedEffortLineRecord[];
}): Prisma.EstimateDemandLineUncheckedCreateInput[] {
return input.lines.map((line) => ({
estimateVersionId: input.estimateVersionId,
lineType: "LABOR",
name: `${line.discipline}${line.scopeItemName}`,
...(line.chapter ? { chapter: line.chapter } : {}),
hours: line.hours,
costRateCents: 0,
billRateCents: 0,
currency: input.currency,
costTotalCents: 0,
priceTotalCents: 0,
monthlySpread: {},
staffingAttributes: {},
metadata: {
effortRule: {
ruleSetId: input.ruleSet.id,
ruleSetName: input.ruleSet.name,
discipline: line.discipline,
unitMode: line.unitMode,
unitCount: line.unitCount,
hoursPerUnit: line.hoursPerUnit,
},
},
}));
}