diff --git a/api-v3/src/asset-master/asset-history.service.ts b/api-v3/src/asset-master/asset-history.service.ts index 5dc216d..ddd9f73 100644 --- a/api-v3/src/asset-master/asset-history.service.ts +++ b/api-v3/src/asset-master/asset-history.service.ts @@ -41,14 +41,14 @@ export interface AssetVersionDetail extends AssetVersionSummary { function assetNotFound(): NotFoundException { return new NotFoundException({ code: 'ASSET_NOT_FOUND', - message: 'Activo no encontrado', + message: 'Inventario no encontrado', }); } function versionNotFound(): NotFoundException { return new NotFoundException({ code: 'ASSET_VERSION_NOT_FOUND', - message: 'Versión de activo no encontrada', + message: 'Versión de Inventario no encontrada', }); } @@ -79,7 +79,7 @@ export class AssetHistoryService { const versionNumber = Number(versionRow.current_version); if (!Number.isInteger(versionNumber) || versionNumber < 1) { - throw new Error(`Versión de activo inválida después de incrementar: ${versionRow.current_version}`); + throw new Error(`Versión de Inventario inválida después de incrementar: ${versionRow.current_version}`); } const snapshot = await this.loadCurrentSnapshot(manager, assetId); @@ -101,17 +101,8 @@ export class AssetHistoryService { asset_id, version_number, change_type, changed_fields, snapshot, actor_user_id, actor_username, source, request_id ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, - [ - assetId, - versionNumber, - changeType, - changedFields, - snapshot, - principal.userId, - principal.username, - source, - request.requestId, - ], + [assetId, versionNumber, changeType, changedFields, snapshot, + principal.userId, principal.username, source, request.requestId], ); return versionNumber; } @@ -123,38 +114,21 @@ export class AssetHistoryService { parameters.push(value); return `$${parameters.length}`; }; - if (query.search?.trim()) { const search = add(`%${query.search.trim()}%`); - conditions.push(`( - version.snapshot->>'code' ILIKE ${search} - OR version.snapshot->>'name' ILIKE ${search} - OR version.actor_username ILIKE ${search} - )`); - } - if (query.typeId) { - conditions.push(`version.snapshot #>> '{type,id}' = ${add(query.typeId)}`); - } - if (query.status) { - conditions.push(`version.snapshot->>'informationStatus' = ${add(query.status)}`); - } - if (query.changeType) { - conditions.push(`version.change_type = ${add(query.changeType)}`); + conditions.push(`(version.snapshot->>'code' ILIKE ${search} OR version.snapshot->>'name' ILIKE ${search} OR version.actor_username ILIKE ${search})`); } + if (query.typeId) conditions.push(`version.snapshot #>> '{type,id}' = ${add(query.typeId)}`); + if (query.status) conditions.push(`version.snapshot->>'informationStatus' = ${add(query.status)}`); + if (query.changeType) conditions.push(`version.change_type = ${add(query.changeType)}`); if (query.from) conditions.push(`version.occurred_at >= ${add(new Date(query.from))}`); if (query.to) conditions.push(`version.occurred_at <= ${add(new Date(query.to))}`); - return this.listWithConditions(query.page, query.pageSize, conditions, parameters); } async listForAsset(assetId: string, query: AssetVersionPageQueryDto) { await this.requireAsset(assetId); - return this.listWithConditions( - query.page, - query.pageSize, - ['version.asset_id = $1'], - [assetId], - ); + return this.listWithConditions(query.page, query.pageSize, ['version.asset_id = $1'], [assetId]); } async getVersion(assetId: string, versionNumber: number): Promise { @@ -170,17 +144,9 @@ export class AssetHistoryService { return row; } - private async listWithConditions( - page: number, - pageSize: number, - conditions: string[], - parameters: unknown[], - ) { + private async listWithConditions(page: number, pageSize: number, conditions: string[], parameters: unknown[]) { const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : ''; - const [countRow] = (await this.dataSource.query( - `SELECT COUNT(*)::integer AS total FROM asset_versions version ${where}`, - parameters, - )) as Array<{ total: number }>; + const [countRow] = (await this.dataSource.query(`SELECT COUNT(*)::integer AS total FROM asset_versions version ${where}`, parameters)) as Array<{ total: number }>; const total = Number(countRow?.total ?? 0); const paginated = [...parameters, pageSize, (page - 1) * pageSize]; const limit = `$${parameters.length + 1}`; @@ -194,51 +160,29 @@ export class AssetHistoryService { LIMIT ${limit} OFFSET ${offset}`, paginated, )) as AssetVersionSummary[]; - - return { - data, - meta: { - page, - pageSize, - total, - totalPages: total === 0 ? 0 : Math.ceil(total / pageSize), - }, - }; + return { data, meta: { page, pageSize, total, totalPages: total === 0 ? 0 : Math.ceil(total / pageSize) } }; } private selectSummary(): string { return `SELECT - version.id, - version.asset_id AS "assetId", - version.snapshot->>'code' AS "assetCode", - version.snapshot->>'name' AS "assetName", - version.snapshot #>> '{type,id}' AS "typeId", - version.snapshot #>> '{type,name}' AS "typeName", + version.id, version.asset_id AS "assetId", + version.snapshot->>'code' AS "assetCode", version.snapshot->>'name' AS "assetName", + version.snapshot #>> '{type,id}' AS "typeId", version.snapshot #>> '{type,name}' AS "typeName", version.snapshot->>'informationStatus' AS "informationStatus", version.snapshot->>'operationalStatus' AS "operationalStatus", - version.version_number AS "versionNumber", - version.change_type AS "changeType", - version.changed_fields AS "changedFields", - version.occurred_at AS "occurredAt", - version.actor_user_id AS "actorUserId", - version.actor_username AS "actorUsername", - version.source, - version.request_id AS "requestId", + version.version_number AS "versionNumber", version.change_type AS "changeType", + version.changed_fields AS "changedFields", version.occurred_at AS "occurredAt", + version.actor_user_id AS "actorUserId", version.actor_username AS "actorUsername", + version.source, version.request_id AS "requestId", (version.version_number = current_asset.current_version) AS "isCurrent"`; } private async requireAsset(assetId: string): Promise { - const [row] = (await this.dataSource.query( - 'SELECT 1 FROM assets WHERE id = $1', - [assetId], - )) as unknown[]; + const [row] = (await this.dataSource.query('SELECT 1 FROM assets WHERE id = $1', [assetId])) as unknown[]; if (!row) throw assetNotFound(); } - private async loadCurrentSnapshot( - manager: EntityManager, - assetId: string, - ): Promise> { + private async loadCurrentSnapshot(manager: EntityManager, assetId: string): Promise> { const [row] = (await manager.query( `SELECT JSONB_BUILD_OBJECT( 'id', asset.id, @@ -246,82 +190,49 @@ export class AssetHistoryService { 'name', asset.name, 'commonName', asset.common_name, 'description', asset.description, - 'type', JSONB_BUILD_OBJECT( - 'id', asset_type.id, - 'code', asset_type.code, - 'name', asset_type.name, - 'operationalRole', asset_type.operational_role - ), - 'parent', CASE WHEN parent.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'id', parent.id, - 'code', parent.code, - 'name', parent.name - ) END, - 'operationalArea', CASE WHEN operational_area.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'id', operational_area.id, - 'code', operational_area.code, - 'name', operational_area.name - ) END, - 'operatorCompany', CASE WHEN operator_company.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'id', operator_company.id, - 'code', operator_company.code, - 'name', operator_company.name - ) END, + 'type', JSONB_BUILD_OBJECT('id', asset_type.id,'code', asset_type.code,'name', asset_type.name,'operationalRole', asset_type.operational_role), + 'parent', CASE WHEN parent.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT('id', parent.id,'code', parent.code,'name', parent.name) END, + 'operationalArea', CASE WHEN operational_area.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT('id', operational_area.id,'code', operational_area.code,'name', operational_area.name) END, + 'operatorCompany', CASE WHEN operator_company.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT('id', operator_company.id,'code', operator_company.code,'name', operator_company.name) END, 'informationStatus', asset.information_status, 'operationalStatus', asset.operational_status, + 'currentFunction', ( + SELECT JSONB_BUILD_OBJECT( + 'assignmentId', assignment.id, + 'id', fn.id, + 'code', fn.code, + 'name', fn.name, + 'validFrom', assignment.valid_from, + 'reason', assignment.change_reason + ) + FROM inventory_function_assignments assignment + JOIN inventory_functions fn ON fn.id=assignment.function_id + WHERE assignment.asset_id=asset.id AND assignment.valid_until IS NULL + ORDER BY assignment.valid_from DESC LIMIT 1 + ), 'attributes', COALESCE(( SELECT JSONB_AGG(JSONB_BUILD_OBJECT( - 'definitionId', definition.id, - 'code', definition.code, - 'name', definition.name, - 'dataType', definition.data_type, - 'isRequired', definition.is_required, - 'unit', definition.unit, - 'options', definition.options, - 'sortOrder', definition.sort_order, - 'value', value.value + 'definitionId', definition.id,'code', definition.code,'name', definition.name, + 'dataType', definition.data_type,'isRequired', definition.is_required, + 'unit', definition.unit,'options', definition.options,'sortOrder', definition.sort_order,'value', value.value ) ORDER BY definition.sort_order, definition.name) FROM asset_attribute_definitions definition - LEFT JOIN asset_attribute_values value - ON value.definition_id = definition.id - AND value.asset_id = asset.id - WHERE definition.asset_type_id = asset.asset_type_id - AND definition.is_active = true + LEFT JOIN asset_attribute_values value ON value.definition_id = definition.id AND value.asset_id = asset.id + WHERE definition.asset_type_id = asset.asset_type_id AND definition.is_active = true ), '[]'::jsonb), 'geometry', CASE WHEN geometry.asset_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'assetId', geometry.asset_id, - 'geometry', ST_AsGeoJSON(geometry.geometry)::jsonb, - 'geometryType', geometry.geometry_type, - 'source', geometry.source, - 'accuracyM', geometry.accuracy_m::double precision, - 'capturedAt', geometry.captured_at, - 'deviceLabel', geometry.device_label, - 'createdAt', geometry.created_at, - 'updatedAt', geometry.updated_at, - 'updatedBy', geometry.updated_by + 'assetId', geometry.asset_id,'geometry', ST_AsGeoJSON(geometry.geometry)::jsonb,'geometryType', geometry.geometry_type, + 'source', geometry.source,'accuracyM', geometry.accuracy_m::double precision,'capturedAt', geometry.captured_at, + 'deviceLabel', geometry.device_label,'createdAt', geometry.created_at,'updatedAt', geometry.updated_at,'updatedBy', geometry.updated_by ) END, 'media', COALESCE(( SELECT JSONB_AGG(JSONB_BUILD_OBJECT( - 'id', media.id, - 'kind', media.kind, - 'originalName', media.original_name, - 'mimeType', media.mime_type, - 'sizeBytes', media.size_bytes, - 'sha256', media.sha256, - 'title', media.title, - 'description', media.description, - 'capturedAt', media.captured_at, - 'latitude', media.latitude, - 'longitude', media.longitude, - 'accuracyM', media.accuracy_m, - 'source', media.source, - 'uploadedBy', media.uploaded_by, - 'createdAt', media.created_at, - 'updatedAt', media.updated_at + 'id', media.id,'kind', media.kind,'originalName', media.original_name,'mimeType', media.mime_type, + 'sizeBytes', media.size_bytes,'sha256', media.sha256,'title', media.title,'description', media.description, + 'capturedAt', media.captured_at,'latitude', media.latitude,'longitude', media.longitude,'accuracyM', media.accuracy_m, + 'source', media.source,'uploadedBy', media.uploaded_by,'createdAt', media.created_at,'updatedAt', media.updated_at ) ORDER BY media.created_at, media.id) - FROM asset_media media - WHERE media.asset_id = asset.id - AND media.deleted_at IS NULL + FROM asset_media media WHERE media.asset_id = asset.id AND media.deleted_at IS NULL ), '[]'::jsonb), 'organizationProfile', (SELECT TO_JSONB(profile) - 'created_at' - 'updated_at' FROM organization_profiles profile WHERE profile.asset_id=asset.id), 'organizationMemberships', COALESCE(( @@ -337,22 +248,8 @@ export class AssetHistoryService { 'legalRights', COALESCE(( SELECT JSONB_AGG(JSONB_BUILD_OBJECT('id',r.id,'rightType',r.right_type,'name',r.name,'instrumentNumber',r.instrument_number,'validFrom',r.valid_from,'validUntil',r.valid_until,'status',r.status,'sourceDocumentId',r.source_document_id,'notes',r.notes,'organizations',COALESCE((SELECT JSONB_AGG(JSONB_BUILD_OBJECT('id',o.id,'organizationId',o.organization_id,'role',o.role,'participationPercent',o.participation_percent::double precision,'validFrom',o.valid_from,'validUntil',o.valid_until,'notes',o.notes,'endReason',o.end_reason) ORDER BY o.valid_until NULLS FIRST,o.valid_from DESC) FROM area_legal_right_organizations o WHERE o.right_id=r.id),'[]'::jsonb)) ORDER BY r.valid_until DESC NULLS FIRST,r.valid_from DESC NULLS LAST) FROM area_legal_rights r WHERE r.area_id=asset.id ),'[]'::jsonb), - 'provenance', JSONB_BUILD_OBJECT( - 'origin', asset.data_origin, - 'sourceName', asset.source_name, - 'sourceReference', asset.source_reference, - 'observedAt', asset.source_observed_at, - 'notes', asset.source_notes, - 'verifiedAt', asset.provenance_verified_at, - 'verifiedBy', asset.provenance_verified_by, - 'updatedAt', asset.provenance_updated_at, - 'updatedBy', asset.provenance_updated_by - ), - 'createdAt', asset.created_at, - 'updatedAt', asset.updated_at, - 'createdBy', asset.created_by, - 'updatedBy', asset.updated_by, - 'currentVersion', asset.current_version + 'provenance', JSONB_BUILD_OBJECT('origin', asset.data_origin,'sourceName', asset.source_name,'sourceReference', asset.source_reference,'observedAt', asset.source_observed_at,'notes', asset.source_notes,'verifiedAt', asset.provenance_verified_at,'verifiedBy', asset.provenance_verified_by,'updatedAt', asset.provenance_updated_at,'updatedBy', asset.provenance_updated_by), + 'createdAt', asset.created_at,'updatedAt', asset.updated_at,'createdBy', asset.created_by,'updatedBy', asset.updated_by,'currentVersion', asset.current_version ) AS snapshot FROM assets asset INNER JOIN asset_types asset_type ON asset_type.id = asset.asset_type_id