refactor(api): extract effort rule support
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
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,
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user