metis-desk/modules/assets/src/api/infrastructure/persistence/workstation-persistence.mapper.ts

119 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-10-04 19:04:14 +00:00
import { z } from "zod/v4";
2026-10-09 08:38:19 +00:00
import { WorkstationValues } from "../../domain/value-objects/workstation-values.vo";
2026-10-04 19:04:14 +00:00
import { Workstation } from "../../domain/workstation.aggregate";
import { WorkstationRevision } from "../../domain/workstation-revision.entity";
import { WorkstationInputSchema } from "../../domain/workstation-values";
import { inventoryIdentity } from "./asset-persistence.mapper";
import type {
WorkstationAssetModel,
WorkstationModel,
WorkstationRevisionModel,
} from "./workstation.model";
function valuesOf(raw: unknown, siteId: string | null, personId: string | null) {
2026-10-05 07:53:29 +00:00
// Las instantáneas anteriores no tenían recursos compartidos. Se interpretan como una lista vacía sin reescribir el historial.
const legacy = WorkstationInputSchema.extend({
sharedAssetIds: z.array(z.uuid()).max(200).optional(),
}).parse(raw);
const values = WorkstationValues.create({
...legacy,
sharedAssetIds: legacy.sharedAssetIds ?? [],
});
2026-10-04 19:04:14 +00:00
if (
!values.isSuccess ||
values.data.toPrimitive().siteId !== siteId ||
values.data.toPrimitive().personId !== personId
) {
throw new Error("Valores de puesto persistidos inconsistentes.");
}
return values.data;
}
export class WorkstationPersistenceMapper {
toDomain(
row: WorkstationModel,
revisions: readonly WorkstationRevisionModel[],
references: readonly WorkstationAssetModel[]
) {
const values = valuesOf(row.values, row.siteId, row.personId);
const ordered = [...revisions].sort((a, b) => a.position - b.position);
const history = ordered.map((revision, position) => {
if (
revision.position !== position ||
!z.iso.datetime().safeParse(revision.recordedAt).success ||
revision.recordedAt < (ordered[position - 1]?.recordedAt ?? "")
) {
throw new Error("Historial de puesto inconsistente.");
}
return WorkstationRevision.rehydrate(
{
values: valuesOf(revision.values, revision.siteId, revision.personId),
actorId: inventoryIdentity(revision.actorId),
recordedAt: revision.recordedAt,
},
inventoryIdentity(revision.id)
);
});
if (!(history.length && history.at(-1)?.values.equals(values))) {
throw new Error("Última revisión de puesto inconsistente.");
}
2026-10-05 07:53:29 +00:00
const expected = new Set(
history.flatMap((item) => [
...item.values.toPrimitive().assetIds,
...item.values.toPrimitive().sharedAssetIds,
])
);
2026-10-04 19:04:14 +00:00
if (
references.length !== expected.size ||
references.some(
(item) =>
item.companyId !== row.companyId ||
!expected.has(item.assetId) ||
item.activeKey !==
(values.active && values.assetIds.some((id) => id.toPrimitive() === item.assetId)
? `${row.companyId}:${item.assetId}`
: null)
)
) {
throw new Error("Referencias de puesto inconsistentes.");
}
return Workstation.rehydrate(
{ companyId: inventoryIdentity(row.companyId), values, history },
inventoryIdentity(row.id)
);
}
toPersistence(item: Workstation) {
const values = item.values.toPrimitive();
const companyId = item.companyId.toPrimitive();
const id = item.id.toPrimitive();
return {
workstation: { id, companyId, values, siteId: values.siteId, personId: values.personId },
revisions: item.history.map((revision, position) => ({
id: revision.id.toPrimitive(),
workstationId: id,
actorId: revision.actorId.toPrimitive(),
recordedAt: revision.recordedAt,
values: revision.values.toPrimitive(),
position,
siteId: revision.values.toPrimitive().siteId,
personId: revision.values.toPrimitive().personId,
})),
references: [
2026-10-05 07:53:29 +00:00
...new Set(
item.history.flatMap((revision) => [
...revision.values.toPrimitive().assetIds,
...revision.values.toPrimitive().sharedAssetIds,
])
),
2026-10-04 19:04:14 +00:00
].map((assetId) => ({
workstationId: id,
companyId,
assetId,
activeKey:
values.active && values.assetIds.includes(assetId) ? `${companyId}:${assetId}` : null,
})),
};
}
}