161 lines
6.3 KiB
TypeScript
161 lines
6.3 KiB
TypeScript
import type { UniqueID } from "@repo/rdx-ddd";
|
|
import { Result } from "@repo/rdx-utils";
|
|
import { QueryTypes, type Sequelize, type Transaction } from "sequelize";
|
|
import { z } from "zod/v4";
|
|
|
|
import type {
|
|
IInterventionRepository,
|
|
InterventionContext,
|
|
InterventionLookups,
|
|
InterventionSaveContext,
|
|
} from "../../application/intervention.repository";
|
|
import type { IInterventionTemplateRepository } from "../../application/intervention-template.repository";
|
|
import type { ITicketRepository } from "../../application/ticket.repository";
|
|
import type { Intervention } from "../../domain/intervention.aggregate";
|
|
import { InterventionError } from "../../domain/intervention-values";
|
|
|
|
import { InterventionPersistenceMapper } from "./intervention-persistence.mapper";
|
|
export class SequelizeInterventionRepository implements IInterventionRepository {
|
|
private readonly mapper = new InterventionPersistenceMapper();
|
|
constructor(
|
|
private readonly database: Sequelize,
|
|
private readonly tickets: ITicketRepository,
|
|
private readonly templates: IInterventionTemplateRepository
|
|
) {}
|
|
private async read(
|
|
company: UniqueID | undefined,
|
|
transaction: Transaction
|
|
): Promise<Result<InterventionContext, InterventionError>> {
|
|
const context = await this.tickets.list(company);
|
|
if (!context.isSuccess) {
|
|
return Result.fail(new InterventionError(context.error.code));
|
|
}
|
|
const roots = await this.database.query<Record<string, unknown>>(
|
|
"SELECT id,companyId,reference,requestKey,ticketId,assigneeId,`values`,`procedure` FROM interventions" +
|
|
(company ? " WHERE companyId=:company" : ""),
|
|
{
|
|
transaction,
|
|
type: QueryTypes.SELECT,
|
|
replacements: { company: company?.toPrimitive() ?? null },
|
|
}
|
|
);
|
|
const revisions = await this.database.query<Record<string, unknown>>(
|
|
"SELECT r.id,r.interventionId,r.actorId,r.ticketId,r.assigneeId,r.`values`,r.recordedAt,r.position FROM intervention_revisions r JOIN interventions i ON i.id=r.interventionId" +
|
|
(company ? " WHERE i.companyId=:company" : ""),
|
|
{
|
|
transaction,
|
|
type: QueryTypes.SELECT,
|
|
replacements: { company: company?.toPrimitive() ?? null },
|
|
}
|
|
);
|
|
return Result.ok({
|
|
...context.data,
|
|
interventions: roots.map((row) =>
|
|
this.mapper.toDomain(
|
|
row,
|
|
revisions.filter((item) => item.interventionId === row.id)
|
|
)
|
|
),
|
|
});
|
|
}
|
|
list(company?: UniqueID) {
|
|
return this.database.transaction((transaction) => this.read(company, transaction));
|
|
}
|
|
async lookups(company: UniqueID): Promise<Result<InterventionLookups, InterventionError>> {
|
|
const [context, lookups, templates] = await Promise.all([
|
|
this.tickets.list(company),
|
|
this.tickets.lookups(company),
|
|
this.templates.list(company),
|
|
]);
|
|
if (!context.isSuccess) {
|
|
return Result.fail(new InterventionError(context.error.code));
|
|
}
|
|
if (!lookups.isSuccess) {
|
|
return Result.fail(new InterventionError(lookups.error.code));
|
|
}
|
|
if (!templates.isSuccess) {
|
|
return Result.fail(new InterventionError(templates.error.code));
|
|
}
|
|
return Result.ok({
|
|
context: context.data,
|
|
lookups: lookups.data,
|
|
templates: templates.data.templates,
|
|
templateActors: templates.data.actorNames,
|
|
});
|
|
}
|
|
save(
|
|
company: UniqueID,
|
|
requestKey: UniqueID,
|
|
action: (context: InterventionSaveContext) => Result<Intervention, InterventionError>
|
|
) {
|
|
return this.database.transaction(async (transaction) => {
|
|
await this.database.query("SELECT id FROM companies WHERE id=:id FOR UPDATE", {
|
|
replacements: { id: company.toPrimitive() },
|
|
transaction,
|
|
type: QueryTypes.SELECT,
|
|
});
|
|
await this.database.query("SELECT id FROM intervention_counter WHERE id=1 FOR UPDATE", {
|
|
transaction,
|
|
type: QueryTypes.SELECT,
|
|
});
|
|
const all = await this.read(undefined, transaction);
|
|
const lookups = await this.lookups(company);
|
|
if (!all.isSuccess) {
|
|
return Result.fail(all.error);
|
|
}
|
|
if (!lookups.isSuccess) {
|
|
return Result.fail(lookups.error);
|
|
}
|
|
const sequence = await this.database.query<{ nextNumber: number }>(
|
|
"SELECT nextNumber FROM intervention_counter WHERE id=1",
|
|
{ transaction, type: QueryTypes.SELECT }
|
|
);
|
|
const scoped = {
|
|
...all.data,
|
|
...lookups.data,
|
|
tickets: lookups.data.context.tickets,
|
|
interventions: all.data.interventions.filter((item) => item.companyId.equals(company)),
|
|
nextNumber: z.number().int().positive().parse(sequence[0]?.nextNumber),
|
|
requestIntervention: all.data.interventions.find((item) =>
|
|
item.requestKey.equals(requestKey)
|
|
),
|
|
};
|
|
const result = action(scoped);
|
|
if (!result.isSuccess) {
|
|
return Result.fail(result.error);
|
|
}
|
|
const previous = all.data.interventions.find((item) => item.id.equals(result.data.id));
|
|
await this.write(result.data, previous, transaction);
|
|
return Result.ok({ intervention: result.data, context: all.data });
|
|
});
|
|
}
|
|
private async write(
|
|
intervention: Intervention,
|
|
previous: Intervention | undefined,
|
|
transaction: Transaction
|
|
) {
|
|
if (previous === intervention) {
|
|
return;
|
|
}
|
|
const persisted = this.mapper.toPersistence(intervention);
|
|
await this.database.query(
|
|
previous
|
|
? "UPDATE interventions SET ticketId=:ticketId,assigneeId=:assigneeId,`values`=:values WHERE id=:id"
|
|
: "INSERT INTO interventions (id,companyId,reference,requestKey,ticketId,assigneeId,`values`,`procedure`) VALUES (:id,:companyId,:reference,:requestKey,:ticketId,:assigneeId,:values,:procedure)",
|
|
{ replacements: persisted.root, transaction }
|
|
);
|
|
for (const row of persisted.revisions.slice(previous?.history.length ?? 0)) {
|
|
await this.database.query(
|
|
"INSERT INTO intervention_revisions (id,interventionId,actorId,ticketId,assigneeId,`values`,recordedAt,position) VALUES (:id,:interventionId,:actorId,:ticketId,:assigneeId,:values,:recordedAt,:position)",
|
|
{ replacements: row, transaction }
|
|
);
|
|
}
|
|
if (!previous) {
|
|
await this.database.query(
|
|
"UPDATE intervention_counter SET nextNumber=nextNumber+1 WHERE id=1",
|
|
{ transaction }
|
|
);
|
|
}
|
|
}
|
|
}
|