metis-desk/modules/companies/src/api/application/intervention.use-cases.ts

149 lines
5.7 KiB
TypeScript

import { Name, UniqueID } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import type { SaveInterventionInput } from "../../common/interventions";
import { SaveInterventionSchema } from "../../common/interventions";
import { Intervention } from "../domain/intervention.aggregate";
import { InterventionError } from "../domain/intervention-values";
import { InterventionProcedure } from "../domain/value-objects/intervention-procedure";
import { InterventionValues } from "../domain/value-objects/intervention-values";
import type { IInterventionRepository, InterventionSaveContext } from "./intervention.repository";
import { mapInterventionInput } from "./intervention-input.mapper";
export class InterventionUseCases {
constructor(private readonly repository: IInterventionRepository) {}
list(companyId?: string) {
const company = companyId ? UniqueID.create(companyId) : undefined;
if (company && !company.isSuccess) {
return Promise.resolve(Result.fail(new InterventionError("INVALID_REQUEST")));
}
return this.repository.list(company?.isSuccess ? company.data : undefined);
}
lookups(companyId: string) {
const company = UniqueID.create(companyId);
return company.isSuccess
? this.repository.lookups(company.data)
: Promise.resolve(Result.fail(new InterventionError("INVALID_REQUEST")));
}
save(companyId: string, id: string | undefined, input: SaveInterventionInput, actorId: string) {
const parsed = SaveInterventionSchema.safeParse(input);
if (!parsed.success || (id && input.template !== null)) {
return Promise.resolve(Result.fail(new InterventionError("INVALID_REQUEST")));
}
const company = UniqueID.create(companyId);
const actor = UniqueID.create(actorId);
const key = UniqueID.create(input.requestKey);
const identity = id ? UniqueID.create(id) : undefined;
if (
!(company.isSuccess && actor.isSuccess && key.isSuccess) ||
(identity && !identity.isSuccess) ||
(!id && input.expectedRevisionId !== null)
) {
return Promise.resolve(Result.fail(new InterventionError("INVALID_REQUEST")));
}
const normalizedId = identity?.isSuccess ? identity.data.toPrimitive() : undefined;
return this.repository.save(company.data, key.data, (context) => {
const previous = context.interventions.find((item) => item.id.toPrimitive() === normalizedId);
if (id && !previous) {
return Result.fail(new InterventionError("INTERVENTION_NOT_FOUND"));
}
if (!id && context.requestIntervention) {
return replayIntervention(context.requestIntervention, company.data, actor.data, input);
}
if (previous && previous.history.at(-1)?.id.toPrimitive() !== input.expectedRevisionId) {
return Result.fail(new InterventionError("INTERVENTION_CHANGED"));
}
const values = mapInterventionInput(input.values, context, previous);
if (!values.isSuccess) {
return Result.fail(values.error);
}
const now = new Date().toISOString();
if (previous) {
return previous.update(values.data, actor.data, now);
}
const procedure = appliedProcedure(input, context);
if (!procedure.isSuccess) {
return Result.fail(procedure.error);
}
return createIntervention(
company.data,
key.data,
values.data,
actor.data,
context.nextNumber,
now,
procedure.data
);
});
}
}
function replayIntervention(
original: Intervention,
company: UniqueID,
actor: UniqueID,
input: SaveInterventionInput
): Result<Intervention, InterventionError> {
const values = InterventionValues.create(input.values);
const first = original.history[0];
if (
!(
sameProcedure(original, input) &&
original.companyId.equals(company) &&
first?.actorId.equals(actor) &&
values.isSuccess &&
first.values.equals(values.data)
)
) {
return Result.fail(new InterventionError("REQUEST_KEY_REUSED"));
}
return Result.ok(original);
}
function createIntervention(
company: UniqueID,
key: UniqueID,
values: InterventionValues,
actor: UniqueID,
number: number,
now: string,
procedure: Maybe<InterventionProcedure>
): Result<Intervention, InterventionError> {
const reference = Name.create(`I-${String(number).padStart(6, "0")}`, 40);
if (!reference.isSuccess) {
return Result.fail(new InterventionError("INVALID_REQUEST"));
}
return Intervention.create(company, reference.data, key, values, procedure, actor, now);
}
function sameProcedure(intervention: Intervention, input: SaveInterventionInput) {
const source = intervention.procedure.getOrUndefined();
return source
? source.templateId.toPrimitive() === input.template?.id.toLowerCase() &&
source.revisionId.toPrimitive() === input.template.revisionId.toLowerCase()
: input.template === null;
}
function appliedProcedure(
input: SaveInterventionInput,
context: InterventionSaveContext
): Result<Maybe<InterventionProcedure>, InterventionError> {
if (!input.template) {
return Result.ok(Maybe.none());
}
const template = context.templates.find(
(item) => item.id.toPrimitive() === input.template?.id.toLowerCase()
);
if (!(template && template.values.active)) {
return Result.fail(new InterventionError("TEMPLATE_UNAVAILABLE"));
}
if (template.history.at(-1)?.id.toPrimitive() !== input.template.revisionId.toLowerCase()) {
return Result.fail(new InterventionError("TEMPLATE_CHANGED"));
}
const procedure = InterventionProcedure.create({
templateId: template.id.toPrimitive(),
revisionId: input.template.revisionId,
values: template.values.toPrimitive(),
});
return procedure.isSuccess ? Result.ok(Maybe.some(procedure.data)) : Result.fail(procedure.error);
}