163 lines
4.4 KiB
TypeScript
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,
|
|
},
|
|
},
|
|
}));
|
|
}
|