metis-desk/modules/assets/src/api/module.ts
2026-10-10 17:57:05 +02:00

158 lines
6.4 KiB
TypeScript

import { UniqueID } from "@repo/rdx-ddd";
import { Maybe, Result } from "@repo/rdx-utils";
import type { RequestHandler } from "express";
import type { Sequelize } from "sequelize";
import type { InventoryContext } from "./application/asset.repository";
import { AssetUseCases } from "./application/asset.use-cases";
import { AssetCatalogUseCases } from "./application/asset-catalog.use-cases";
import { AssetRelationshipUseCases } from "./application/asset-relationship.use-cases";
import { BrandUseCases } from "./application/brand.use-cases";
import { SupportUseCases } from "./application/support.use-cases";
import { WorkstationUseCases } from "./application/workstation.use-cases";
import { seedAssetCatalog } from "./domain/asset-catalog-seed";
import { registerAssetRoutes } from "./infrastructure/http/asset.routes";
import { registerAssetCatalogRoutes } from "./infrastructure/http/asset-catalog.routes";
import { registerRelationshipRoutes } from "./infrastructure/http/asset-relationship.routes";
import { registerBrandRoutes } from "./infrastructure/http/brand.routes";
import { registerSupportRoutes } from "./infrastructure/http/support.routes";
import { registerWorkstationRoutes } from "./infrastructure/http/workstation.routes";
import { migrateInventory } from "./infrastructure/persistence/asset.model";
import {
CatalogModel,
migrateAssetCatalog,
} from "./infrastructure/persistence/asset-catalog.model";
import { AssetCatalogPersistenceMapper } from "./infrastructure/persistence/asset-catalog-persistence.mapper";
import { AssetDashboardReader } from "./infrastructure/persistence/asset-dashboard-reader";
import { enforceAssetRelationalConstraints } from "./infrastructure/persistence/asset-relational-constraints";
import { initializeAssetInventorySchema } from "./infrastructure/persistence/asset-relational-schema";
import { migrateRelationships } from "./infrastructure/persistence/asset-relationship.model";
import { migrateBrands } from "./infrastructure/persistence/brand-migration";
import { SequelizeAssetRepository } from "./infrastructure/persistence/sequelize-asset.repository";
import { SequelizeAssetCatalogRepository } from "./infrastructure/persistence/sequelize-asset-catalog.repository";
import { SequelizeBrandRepository } from "./infrastructure/persistence/sequelize-brand.repository";
import { SequelizeSupportRepository } from "./infrastructure/persistence/sequelize-support.repository";
import { migrateSupport } from "./infrastructure/persistence/support-migration";
import { migrateWorkstations } from "./infrastructure/persistence/workstation.model";
export async function createAssetModule(database: Sequelize, authenticate: RequestHandler) {
await migrateAssetCatalog(database);
await CatalogModel.findOrCreate({
where: { catalogKey: "global" },
defaults: { id: UniqueID.generateNewID().toPrimitive(), catalogKey: "global" },
});
const repository = new SequelizeAssetCatalogRepository(
database,
new AssetCatalogPersistenceMapper()
);
const seeded = await repository.update(Maybe.none(), (catalog) =>
Result.ok(seedAssetCatalog(catalog))
);
if (!seeded.isSuccess) {
throw new Error("No se pudo inicializar el catálogo de activos.");
}
await migrateBrands(database);
await migrateInventory(database);
const brands = new BrandUseCases(new SequelizeBrandRepository(database));
await migrateRelationships(database);
await migrateWorkstations(database);
await initializeAssetInventorySchema(database);
await enforceAssetRelationalConstraints(database);
await migrateSupport(database);
const support = new SupportUseCases(new SequelizeSupportRepository(database));
const assetRepository = new SequelizeAssetRepository(database);
const inventory = new AssetUseCases(assetRepository);
const workstations = new WorkstationUseCases(assetRepository);
const relationships = new AssetRelationshipUseCases(assetRepository);
const useCases = new AssetCatalogUseCases(repository);
return {
services: {
dashboard: new AssetDashboardReader(database),
inventory: {
workstations: async (companyId: UniqueID) => {
const result = await assetRepository.get(companyId);
if (!result.isSuccess) {
throw new Error("Puestos no disponibles.");
}
return result.data.workstations.map((item) => ({
id: item.id,
name: item.values.toPrimitive().name,
active: item.values.toPrimitive().active,
}));
},
list: async (companyId: UniqueID) => {
const result = await assetRepository.get(companyId);
if (!result.isSuccess) {
throw new Error("Inventario no disponible.");
}
return inventoryAssignments(result.data);
},
},
},
register: (app: Parameters<typeof registerAssetCatalogRoutes>[0]) => {
registerBrandRoutes(app, brands, authenticate);
registerAssetCatalogRoutes(app, useCases, authenticate);
registerAssetRoutes(app, inventory, authenticate);
registerRelationshipRoutes(app, relationships, authenticate);
registerWorkstationRoutes(app, workstations, authenticate);
registerSupportRoutes(app, support, authenticate);
},
};
}
function inventoryAssignments(context: InventoryContext) {
const peopleByAsset = new Map<string, Set<string>>();
for (const workstation of context.workstations) {
const values = workstation.values.toPrimitive();
if (!(values.active && values.personId)) {
continue;
}
for (const assetId of [...values.assetIds, ...values.sharedAssetIds]) {
const people = peopleByAsset.get(assetId) ?? new Set<string>();
people.add(values.personId);
peopleByAsset.set(assetId, people);
}
}
const assignments: {
id: UniqueID;
name: string;
personId: string | null;
available: boolean;
}[] = [];
for (const asset of context.assets) {
const values = asset.values.toPrimitive();
const people = peopleByAsset.get(asset.id.toPrimitive()) ?? new Set<string>();
if (people.size) {
for (const personId of people) {
assignments.push({
id: asset.id,
name: values.name,
personId,
available: values.status !== "retired",
});
}
} else {
assignments.push({
id: asset.id,
name: values.name,
personId: null,
available: values.status !== "retired",
});
}
}
return assignments;
}