149 lines
5.7 KiB
TypeScript
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);
|
|
}
|