import type { Result as DomainResult } from "@repo/rdx-utils"; import type { InferAttributes } from "sequelize"; import { z } from "zod/v4"; import { CompanyInputMapper } from "../../application/company-input.mapper"; import { Company } from "../../domain/company.aggregate"; import { Department } from "../../domain/department.entity"; import { AssignmentRevision, DepartmentAssignment, } from "../../domain/department-assignment.entity"; import { Person } from "../../domain/person.entity"; import { Site } from "../../domain/site.entity"; import type { CompanyModel, PersonModel, SiteModel } from "./company.model"; import type { AssignmentModel, AssignmentRevisionModel, DepartmentModel } from "./people.model"; export type CompanyRecord = { company: InferAttributes; people: InferAttributes[]; sites: InferAttributes[]; departments: InferAttributes[]; assignments: InferAttributes[]; revisions: InferAttributes[]; }; function persisted(result: DomainResult): T { if (!result.isSuccess) { throw new Error("Persistencia de compañía inconsistente."); } return result.data; } export class CompanyPersistenceMapper { private readonly input = new CompanyInputMapper(); toDomain(record: CompanyRecord): Company { const id = persisted(this.input.id(record.company.id)); const props = persisted(this.input.company(record.company)); const companyId = (value: string) => { const parent = persisted(this.input.id(value)); if (!parent.equals(id)) { throw new Error("Pertenencia persistida inconsistente."); } return parent; }; const people = record.people.map((row) => Person.rehydrate( { ...persisted(this.input.person(row)), companyId: companyId(row.companyId) }, persisted(this.input.id(row.id)) ) ); const sites = record.sites.map((row) => Site.rehydrate( { ...persisted(this.input.site(row)), companyId: companyId(row.companyId) }, persisted(this.input.id(row.id)) ) ); const departments = record.departments.map((row) => Department.rehydrate( { ...persisted(this.input.department(row)), companyId: companyId(row.companyId) }, persisted(this.input.id(row.id)) ) ); const assignments = record.assignments.map((row) => { const personId = persisted(this.input.id(row.personId)); const departmentId = persisted(this.input.id(row.departmentId)); if ( !( people.some((item) => item.id.equals(personId)) && departments.some((item) => item.id.equals(departmentId)) ) ) { throw new Error("Asignación persistida inconsistente."); } const revisions = record.revisions .filter((revision) => revision.assignmentId === row.id) .sort((a, b) => a.sequence - b.sequence); if (revisions.length === 0) { throw new Error("Historial persistido inconsistente."); } const values = persisted(this.input.assignment(row)); if (!DepartmentAssignment.isValidValues(values)) { throw new Error("Asignación persistida inconsistente."); } const history = revisions.map((revision, index) => { if ( revision.sequence !== index || !z.iso.datetime().safeParse(revision.recordedAt).success ) { throw new Error("Historial persistido inconsistente."); } const revisionValues = persisted(this.input.assignment(revision)); if (!DepartmentAssignment.isValidValues(revisionValues)) { throw new Error("Historial persistido inconsistente."); } return AssignmentRevision.rehydrate( { ...revisionValues, recordedAt: revision.recordedAt }, persisted(this.input.id(revision.id)) ); }); const latest = revisions.at(-1); if ( !latest || latest.position !== row.position || latest.startDate !== row.startDate || latest.endDate !== row.endDate || latest.isPrimary !== row.isPrimary ) { throw new Error("Historial persistido inconsistente."); } return DepartmentAssignment.rehydrate( { ...values, companyId: companyId(row.companyId), personId, departmentId, history, }, persisted(this.input.id(row.id)) ); }); return Company.rehydrate({ ...props, people, sites, departments, assignments }, id); } toPersistence(company: Company): CompanyRecord { const values = (item: DepartmentAssignment["values"]) => ({ position: item.position.getOrUndefined()?.toPrimitive() ?? null, startDate: item.startDate.toPrimitive(), endDate: item.endDate.getOrUndefined()?.toPrimitive() ?? null, isPrimary: item.isPrimary, }); return { company: { id: company.id.toPrimitive(), name: company.name.toPrimitive(), active: company.active, }, people: company.people.map((person) => ({ id: person.id.toPrimitive(), companyId: person.companyId.toPrimitive(), name: person.name.toPrimitive(), position: person.position.getOrUndefined()?.toPrimitive() ?? null, email: person.email.getOrUndefined()?.toPrimitive() ?? null, phone: person.phone.getOrUndefined()?.toPrimitive() ?? null, active: person.active, isContact: person.isContact, })), sites: company.sites.map((site) => ({ id: site.id.toPrimitive(), companyId: site.companyId.toPrimitive(), name: site.name.toPrimitive(), address: site.address.getOrUndefined()?.toPrimitive() ?? null, })), departments: company.departments.map((item) => ({ id: item.id.toPrimitive(), companyId: item.companyId.toPrimitive(), name: item.name.toPrimitive(), nameKey: item.name.toPrimitive().normalize("NFC").toLowerCase(), active: item.active, })), assignments: company.assignments.map((item) => ({ id: item.id.toPrimitive(), companyId: item.companyId.toPrimitive(), personId: item.personId.toPrimitive(), departmentId: item.departmentId.toPrimitive(), ...values(item.values), })), revisions: company.assignments.flatMap((item) => item.history.map((revision, sequence) => ({ id: revision.id.toPrimitive(), assignmentId: item.id.toPrimitive(), sequence, recordedAt: revision.values.recordedAt, ...values(revision.values), })) ), }; } }