metis-desk/modules/companies/src/api/application/ticket.use-cases.ts
2026-10-07 11:41:56 +02:00

98 lines
3.5 KiB
TypeScript

import { Name, UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import type { SaveTicketInput } from "../../common/tickets";
import { Ticket } from "../domain/ticket.aggregate";
import { TicketError } from "../domain/ticket-values";
import { TicketValues } from "../domain/value-objects/ticket-values";
import type { ITicketRepository } from "./ticket.repository";
import { mapTicketInput } from "./ticket-input.mapper";
export class TicketUseCases {
constructor(private readonly repository: ITicketRepository) {}
list(companyId?: string) {
const company = companyId ? UniqueID.create(companyId) : undefined;
if (company && !company.isSuccess) {
return Promise.resolve(Result.fail(new TicketError("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 TicketError("INVALID_REQUEST")));
}
save(companyId: string, id: string | undefined, input: SaveTicketInput, actorId: string) {
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 TicketError("INVALID_REQUEST")));
}
const normalizedId = identity?.isSuccess ? identity.data.toPrimitive() : undefined;
return this.repository.save(company.data, key.data, (context) => {
const previous = context.tickets.find((item) => item.id.toPrimitive() === normalizedId);
if (id && !previous) {
return Result.fail(new TicketError("TICKET_NOT_FOUND"));
}
if (!id && context.requestTicket) {
return replayTicket(context.requestTicket, company.data, actor.data, input);
}
if (previous && previous.history.at(-1)?.id.toPrimitive() !== input.expectedRevisionId) {
return Result.fail(new TicketError("TICKET_CHANGED"));
}
const values = mapTicketInput(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);
}
return createTicket(company.data, key.data, values.data, actor.data, context.nextNumber, now);
});
}
}
function replayTicket(
original: Ticket,
company: UniqueID,
actor: UniqueID,
input: SaveTicketInput
): Result<Ticket, TicketError> {
const values = TicketValues.create(input.values);
const first = original.history[0];
if (
!(
original.companyId.equals(company) &&
first?.actorId.equals(actor) &&
values.isSuccess &&
first.values.equals(values.data)
)
) {
return Result.fail(new TicketError("REQUEST_KEY_REUSED"));
}
return Result.ok(original);
}
function createTicket(
company: UniqueID,
key: UniqueID,
values: TicketValues,
actor: UniqueID,
number: number,
now: string
): Result<Ticket, TicketError> {
const reference = Name.create(`T-${String(number).padStart(6, "0")}`, 40);
if (!reference.isSuccess) {
return Result.fail(new TicketError("INVALID_REQUEST"));
}
return Ticket.create(company, reference.data, key, values, actor, now);
}