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> { const context = await this.tickets.list(company); if (!context.isSuccess) { return Result.fail(new InterventionError(context.error.code)); } const roots = await this.database.query>( "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>( "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> { 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 ) { 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 } ); } } }