F5.1 · Inventario limpio, jerarquía manual y Hallazgos filtrados (#28)
Reset controlado de datos operativos e históricos, nueva jerarquía manual Departamento → Área → Yacimiento → Instalación → Subinstalación, filtros y administración inline de Hallazgos, y corrección de alta con Área sin Operadora.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
|
||||
export const INVENTORY_STRUCTURE_KINDS = [
|
||||
'EMPRESA',
|
||||
'DEPARTAMENTO',
|
||||
'AREA',
|
||||
'YACIMIENTO',
|
||||
'INSTALACION',
|
||||
|
||||
@@ -13,6 +13,16 @@ export class InventoryBrowserController {
|
||||
return this.inventoryBrowser.items(query);
|
||||
}
|
||||
|
||||
@Get('departments')
|
||||
departments(@Query() query: InventoryBrowserQueryDto) {
|
||||
return this.inventoryBrowser.departments(query);
|
||||
}
|
||||
|
||||
@Get('companies')
|
||||
companies(@Query() query: InventoryBrowserQueryDto) {
|
||||
return this.inventoryBrowser.companies(query);
|
||||
}
|
||||
|
||||
@Get('areas')
|
||||
areas(@Query() query: InventoryBrowserQueryDto) {
|
||||
return this.inventoryBrowser.areas(query);
|
||||
|
||||
@@ -16,9 +16,9 @@ export class InventoryBrowserService {
|
||||
async items(query: InventoryBrowserQueryDto) {
|
||||
const params: unknown[] = [];
|
||||
const conditions = [
|
||||
'asset.is_inventory_instance=true',
|
||||
"asset.information_status<>'INACTIVE'",
|
||||
'type.is_active=true',
|
||||
"(type.operational_role='COMPANY' OR lower(type.code) IN ('departamento','area','yacimiento','instalacion','subinstalacion'))",
|
||||
];
|
||||
const add = (value: unknown): string => {
|
||||
params.push(value);
|
||||
@@ -36,15 +36,21 @@ export class InventoryBrowserService {
|
||||
if (query.needsValidation === false) conditions.push("asset.information_status='VALIDATED'");
|
||||
if (query.hasGeometry === true) conditions.push('EXISTS (SELECT 1 FROM asset_geometries geometry_filter WHERE geometry_filter.asset_id=asset.id)');
|
||||
if (query.hasGeometry === false) conditions.push('NOT EXISTS (SELECT 1 FROM asset_geometries geometry_filter WHERE geometry_filter.asset_id=asset.id)');
|
||||
if (query.operationalAreaId) conditions.push(`asset.operational_area_id=${add(query.operationalAreaId)}::uuid`);
|
||||
if (query.operationalAreaId) {
|
||||
const area = add(query.operationalAreaId);
|
||||
conditions.push(`(asset.id=${area}::uuid OR asset.operational_area_id=${area}::uuid)`);
|
||||
}
|
||||
if (query.operatorCompanyId) {
|
||||
const company = add(query.operatorCompanyId);
|
||||
conditions.push(`EXISTS (
|
||||
SELECT 1 FROM area_company_relations relation
|
||||
WHERE relation.area_id=asset.operational_area_id
|
||||
AND relation.company_id=${company}::uuid
|
||||
AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL
|
||||
conditions.push(`(
|
||||
asset.id=${company}::uuid
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM area_company_relations relation
|
||||
WHERE (relation.area_id=asset.operational_area_id OR relation.area_id=asset.id)
|
||||
AND relation.company_id=${company}::uuid
|
||||
AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL
|
||||
)
|
||||
)`);
|
||||
}
|
||||
|
||||
@@ -76,7 +82,8 @@ export class InventoryBrowserService {
|
||||
SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name)
|
||||
FROM area_company_relations relation
|
||||
JOIN assets company ON company.id=relation.company_id
|
||||
WHERE relation.area_id=asset.operational_area_id
|
||||
WHERE relation.area_id=COALESCE(asset.operational_area_id,
|
||||
CASE WHEN type.operational_role='AREA' THEN asset.id ELSE NULL END)
|
||||
AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL
|
||||
ORDER BY relation.valid_from DESC,relation.created_at DESC
|
||||
@@ -84,7 +91,8 @@ export class InventoryBrowserService {
|
||||
) AS "operatorCompany",
|
||||
asset.information_status AS "informationStatus",
|
||||
asset.operational_status AS "operationalStatus",
|
||||
0::integer AS "childrenCount",
|
||||
(SELECT COUNT(*)::integer FROM assets child
|
||||
WHERE child.parent_id=asset.id AND child.information_status<>'INACTIVE') AS "childrenCount",
|
||||
EXISTS (SELECT 1 FROM asset_geometries geometry WHERE geometry.asset_id=asset.id) AS "hasGeometry",
|
||||
CASE WHEN geometry_type.type IS NULL THEN NULL ELSE geometry_type.type END AS "geometryType",
|
||||
(SELECT COUNT(*)::integer FROM asset_media media WHERE media.asset_id=asset.id AND media.deleted_at IS NULL) AS "mediaCount",
|
||||
@@ -104,7 +112,14 @@ export class InventoryBrowserService {
|
||||
LIMIT 1
|
||||
) geometry_type ON true
|
||||
WHERE ${where}
|
||||
ORDER BY asset.name,asset.code
|
||||
ORDER BY CASE
|
||||
WHEN lower(type.code)='departamento' THEN 0
|
||||
WHEN type.operational_role='COMPANY' THEN 1
|
||||
WHEN lower(type.code)='area' THEN 2
|
||||
WHEN lower(type.code)='yacimiento' THEN 3
|
||||
WHEN lower(type.code)='instalacion' THEN 4
|
||||
WHEN lower(type.code)='subinstalacion' THEN 5 ELSE 9 END,
|
||||
asset.name,asset.code
|
||||
LIMIT ${limit} OFFSET ${offsetParam}
|
||||
`,params);
|
||||
|
||||
@@ -119,28 +134,30 @@ export class InventoryBrowserService {
|
||||
};
|
||||
}
|
||||
|
||||
async areas(query: InventoryBrowserQueryDto) {
|
||||
async departments(query: InventoryBrowserQueryDto) {
|
||||
const params: unknown[] = [];
|
||||
const conditions = [
|
||||
"type.operational_role='AREA'",
|
||||
"area.information_status<>'INACTIVE'",
|
||||
"lower(type.code)='departamento'",
|
||||
"department.information_status<>'INACTIVE'",
|
||||
'type.is_active=true',
|
||||
];
|
||||
const add = (value: unknown): string => {
|
||||
params.push(value);
|
||||
return `$${params.length}`;
|
||||
};
|
||||
|
||||
if (query.search?.trim()) {
|
||||
const p = add(`%${query.search.trim()}%`);
|
||||
conditions.push(`(area.code ILIKE ${p} OR area.name ILIKE ${p} OR COALESCE(area.common_name,'') ILIKE ${p})`);
|
||||
conditions.push(`(department.code ILIKE ${p} OR department.name ILIKE ${p} OR COALESCE(department.common_name,'') ILIKE ${p})`);
|
||||
}
|
||||
if (query.operationalAreaId) conditions.push(`area.id=${add(query.operationalAreaId)}::uuid`);
|
||||
if (query.operatorCompanyId) {
|
||||
const p = add(query.operatorCompanyId);
|
||||
conditions.push(`EXISTS (
|
||||
SELECT 1 FROM area_company_relations relation
|
||||
WHERE relation.area_id=area.id
|
||||
SELECT 1
|
||||
FROM assets area
|
||||
JOIN asset_types atype ON atype.id=area.asset_type_id
|
||||
JOIN area_company_relations relation ON relation.area_id=area.id
|
||||
WHERE area.parent_id=department.id
|
||||
AND lower(atype.code)='area'
|
||||
AND relation.company_id=${p}::uuid
|
||||
AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL
|
||||
@@ -149,41 +166,86 @@ export class InventoryBrowserService {
|
||||
|
||||
const data = await this.dataSource.query(`
|
||||
SELECT
|
||||
area.id,area.code,area.name,area.common_name AS "commonName",
|
||||
department.id,department.code,department.name,department.common_name AS "commonName",
|
||||
JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type,
|
||||
area.information_status AS "informationStatus",
|
||||
area.operational_status AS "operationalStatus",
|
||||
(
|
||||
SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name)
|
||||
FROM area_company_relations relation
|
||||
JOIN assets company ON company.id=relation.company_id
|
||||
WHERE relation.area_id=area.id
|
||||
AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL
|
||||
ORDER BY relation.valid_from DESC,relation.created_at DESC
|
||||
LIMIT 1
|
||||
) AS "currentOperator",
|
||||
(
|
||||
SELECT COUNT(*)::integer
|
||||
FROM assets yacimiento
|
||||
JOIN asset_types ytype ON ytype.id=yacimiento.asset_type_id
|
||||
WHERE yacimiento.parent_id=area.id
|
||||
AND lower(ytype.code)='yacimiento'
|
||||
AND yacimiento.information_status<>'INACTIVE'
|
||||
) AS "yacimientoCount",
|
||||
(
|
||||
SELECT COUNT(*)::integer
|
||||
FROM assets inventory
|
||||
WHERE inventory.is_inventory_instance=true
|
||||
AND inventory.information_status<>'INACTIVE'
|
||||
AND inventory.operational_area_id=area.id
|
||||
) AS "inventoryCount"
|
||||
FROM assets area
|
||||
JOIN asset_types type ON type.id=area.asset_type_id
|
||||
department.information_status AS "informationStatus",
|
||||
department.operational_status AS "operationalStatus",
|
||||
(SELECT COUNT(*)::integer FROM assets area
|
||||
JOIN asset_types atype ON atype.id=area.asset_type_id
|
||||
WHERE area.parent_id=department.id AND lower(atype.code)='area'
|
||||
AND area.information_status<>'INACTIVE') AS "areaCount",
|
||||
(SELECT COUNT(*)::integer FROM assets child
|
||||
WHERE child.parent_id=department.id AND child.information_status<>'INACTIVE') AS "childrenCount"
|
||||
FROM assets department
|
||||
JOIN asset_types type ON type.id=department.asset_type_id
|
||||
WHERE ${conditions.join(' AND ')}
|
||||
ORDER BY department.name,department.code
|
||||
`, params);
|
||||
return { data, meta: { count: data.length } };
|
||||
}
|
||||
|
||||
async companies(query: InventoryBrowserQueryDto) {
|
||||
const params: unknown[] = [];
|
||||
const conditions = [
|
||||
"type.operational_role='COMPANY'",
|
||||
"company.information_status<>'INACTIVE'",
|
||||
'type.is_active=true',
|
||||
];
|
||||
if (query.search?.trim()) {
|
||||
params.push(`%${query.search.trim()}%`);
|
||||
conditions.push(`(company.code ILIKE $1 OR company.name ILIKE $1 OR COALESCE(company.common_name,'') ILIKE $1)`);
|
||||
}
|
||||
const data = await this.dataSource.query(`
|
||||
SELECT company.id,company.code,company.name,company.common_name AS "commonName",
|
||||
JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type,
|
||||
company.information_status AS "informationStatus",
|
||||
(SELECT COUNT(*)::integer FROM area_company_relations relation
|
||||
WHERE relation.company_id=company.id AND relation.relation_role='OPERATOR'
|
||||
AND relation.valid_until IS NULL) AS "areaCount"
|
||||
FROM assets company
|
||||
JOIN asset_types type ON type.id=company.asset_type_id
|
||||
WHERE ${conditions.join(' AND ')}
|
||||
ORDER BY company.name,company.code
|
||||
`,params);
|
||||
return { data,meta:{count:data.length} };
|
||||
}
|
||||
|
||||
async areas(query: InventoryBrowserQueryDto) {
|
||||
const params: unknown[] = [];
|
||||
const conditions = [
|
||||
"type.operational_role='AREA'",
|
||||
"area.information_status<>'INACTIVE'",
|
||||
'type.is_active=true',
|
||||
];
|
||||
const add = (value: unknown): string => { params.push(value); return `$${params.length}`; };
|
||||
if (query.search?.trim()) {
|
||||
const p = add(`%${query.search.trim()}%`);
|
||||
conditions.push(`(area.code ILIKE ${p} OR area.name ILIKE ${p} OR COALESCE(area.common_name,'') ILIKE ${p})`);
|
||||
}
|
||||
if (query.operationalAreaId) conditions.push(`area.id=${add(query.operationalAreaId)}::uuid`);
|
||||
if (query.operatorCompanyId) {
|
||||
const p = add(query.operatorCompanyId);
|
||||
conditions.push(`EXISTS (SELECT 1 FROM area_company_relations relation
|
||||
WHERE relation.area_id=area.id AND relation.company_id=${p}::uuid
|
||||
AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL)`);
|
||||
}
|
||||
const data = await this.dataSource.query(`
|
||||
SELECT area.id,area.code,area.name,area.common_name AS "commonName",
|
||||
JSONB_BUILD_OBJECT('id',type.id,'code',type.code,'name',type.name) AS type,
|
||||
area.information_status AS "informationStatus",area.operational_status AS "operationalStatus",
|
||||
(SELECT JSONB_BUILD_OBJECT('id',company.id,'code',company.code,'name',company.name)
|
||||
FROM area_company_relations relation JOIN assets company ON company.id=relation.company_id
|
||||
WHERE relation.area_id=area.id AND relation.relation_role='OPERATOR' AND relation.valid_until IS NULL
|
||||
ORDER BY relation.valid_from DESC,relation.created_at DESC LIMIT 1) AS "currentOperator",
|
||||
(SELECT COUNT(*)::integer FROM assets yacimiento JOIN asset_types ytype ON ytype.id=yacimiento.asset_type_id
|
||||
WHERE yacimiento.parent_id=area.id AND lower(ytype.code)='yacimiento'
|
||||
AND yacimiento.information_status<>'INACTIVE') AS "yacimientoCount",
|
||||
(SELECT COUNT(*)::integer FROM assets inventory
|
||||
WHERE inventory.information_status<>'INACTIVE' AND inventory.operational_area_id=area.id) AS "inventoryCount"
|
||||
FROM assets area JOIN asset_types type ON type.id=area.asset_type_id
|
||||
WHERE ${conditions.join(' AND ')}
|
||||
ORDER BY area.name,area.code
|
||||
`, params);
|
||||
|
||||
return { data, meta: { count: data.length } };
|
||||
}
|
||||
|
||||
@@ -199,9 +261,6 @@ export class InventoryBrowserService {
|
||||
"asset.information_status<>'INACTIVE'",
|
||||
'type.is_active=true',
|
||||
];
|
||||
if (allowedChildType === 'instalacion' || allowedChildType === 'subinstalacion') {
|
||||
conditions.push('asset.is_inventory_instance=true');
|
||||
}
|
||||
if (query.search?.trim()) {
|
||||
params.push(`%${query.search.trim()}%`);
|
||||
const p = `$${params.length}`;
|
||||
@@ -223,20 +282,12 @@ export class InventoryBrowserService {
|
||||
CASE WHEN family.id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT(
|
||||
'id',family.id,'code',family.code,'name',family.name,'level',family.level
|
||||
) END AS "inventoryFamily",
|
||||
(
|
||||
SELECT COUNT(*)::integer
|
||||
FROM assets child
|
||||
WHERE child.parent_id=asset.id
|
||||
AND child.information_status<>'INACTIVE'
|
||||
AND (
|
||||
lower(type.code)='area'
|
||||
OR child.is_inventory_instance=true
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM asset_types child_type
|
||||
WHERE child_type.id=child.asset_type_id AND lower(child_type.code)='yacimiento'
|
||||
)
|
||||
)
|
||||
) AS "childrenCount",
|
||||
(SELECT COUNT(*)::integer FROM assets child
|
||||
WHERE child.parent_id=asset.id AND child.information_status<>'INACTIVE') AS "childrenCount",
|
||||
CASE WHEN family.id IS NULL THEN 0 ELSE (
|
||||
SELECT COUNT(*)::integer FROM finding_catalog_item_inventory_families mapping
|
||||
WHERE mapping.inventory_family_id=family.id
|
||||
) END AS "findingCount",
|
||||
EXISTS (SELECT 1 FROM asset_geometries geometry WHERE geometry.asset_id=asset.id) AS "hasGeometry",
|
||||
asset.updated_at AS "updatedAt"
|
||||
FROM assets asset
|
||||
@@ -263,10 +314,10 @@ export class InventoryBrowserService {
|
||||
if (!parent) {
|
||||
throw new NotFoundException({ code:'INVENTORY_BROWSER_PARENT_NOT_FOUND',message:'El nivel de Inventario no existe' });
|
||||
}
|
||||
if (!['area','yacimiento','instalacion','subinstalacion'].includes(parent.typeCode.toLowerCase())) {
|
||||
if (!['departamento','area','yacimiento','instalacion','subinstalacion'].includes(parent.typeCode.toLowerCase())) {
|
||||
throw new BadRequestException({
|
||||
code:'INVENTORY_BROWSER_PARENT_TYPE_INVALID',
|
||||
message:'La navegación de Inventarios admite Área → Yacimiento → Instalación → Subinstalación',
|
||||
message:'La navegación de Inventarios admite Departamento → Área → Yacimiento → Instalación → Subinstalación',
|
||||
});
|
||||
}
|
||||
return parent;
|
||||
@@ -274,6 +325,7 @@ export class InventoryBrowserService {
|
||||
|
||||
private allowedChildType(typeCode: string): string | null {
|
||||
switch (typeCode.toLowerCase()) {
|
||||
case 'departamento': return 'area';
|
||||
case 'area': return 'yacimiento';
|
||||
case 'yacimiento': return 'instalacion';
|
||||
case 'instalacion': return 'subinstalacion';
|
||||
|
||||
@@ -38,8 +38,10 @@ type ParentRow = {
|
||||
typeCode: string;
|
||||
inventoryFamilyId: string | null;
|
||||
};
|
||||
type IdRow = { id: string };
|
||||
|
||||
const TYPE_CODE_BY_KIND: Record<Exclude<InventoryStructureKind, 'EMPRESA'>, string> = {
|
||||
DEPARTAMENTO: 'departamento',
|
||||
AREA: 'area',
|
||||
YACIMIENTO: 'yacimiento',
|
||||
INSTALACION: 'instalacion',
|
||||
@@ -47,7 +49,8 @@ const TYPE_CODE_BY_KIND: Record<Exclude<InventoryStructureKind, 'EMPRESA'>, stri
|
||||
};
|
||||
const PARENT_TYPE_BY_KIND: Record<InventoryStructureKind, string | null> = {
|
||||
EMPRESA: null,
|
||||
AREA: null,
|
||||
DEPARTAMENTO: null,
|
||||
AREA: 'departamento',
|
||||
YACIMIENTO: 'area',
|
||||
INSTALACION: 'yacimiento',
|
||||
SUBINSTALACION: 'instalacion',
|
||||
@@ -70,22 +73,24 @@ export class InventoryStructureService {
|
||||
SELECT id,code,name
|
||||
FROM asset_types
|
||||
WHERE (
|
||||
lower(code) IN ('area','yacimiento','instalacion','subinstalacion')
|
||||
lower(code) IN ('departamento','area','yacimiento','instalacion','subinstalacion')
|
||||
OR operational_role='COMPANY'
|
||||
) AND is_active=true
|
||||
ORDER BY CASE
|
||||
WHEN operational_role='COMPANY' THEN 0
|
||||
WHEN lower(code)='area' THEN 1
|
||||
WHEN lower(code)='yacimiento' THEN 2
|
||||
WHEN lower(code)='instalacion' THEN 3
|
||||
WHEN lower(code)='subinstalacion' THEN 4 ELSE 9 END
|
||||
WHEN lower(code)='departamento' THEN 1
|
||||
WHEN lower(code)='area' THEN 2
|
||||
WHEN lower(code)='yacimiento' THEN 3
|
||||
WHEN lower(code)='instalacion' THEN 4
|
||||
WHEN lower(code)='subinstalacion' THEN 5 ELSE 9 END
|
||||
`)) as StructureTypeRow[];
|
||||
const company = types.find((item) => ['empresa','organizacion'].includes(item.code.toLowerCase()));
|
||||
const departamento = types.find((item) => item.code.toLowerCase()==='departamento');
|
||||
const area = types.find((item) => item.code.toLowerCase()==='area');
|
||||
const yacimiento = types.find((item) => item.code.toLowerCase()==='yacimiento');
|
||||
const instalacion = types.find((item) => item.code.toLowerCase()==='instalacion');
|
||||
const subinstalacion = types.find((item) => item.code.toLowerCase()==='subinstalacion');
|
||||
if (!company || !area || !yacimiento || !instalacion || !subinstalacion) {
|
||||
if (!company || !departamento || !area || !yacimiento || !instalacion || !subinstalacion) {
|
||||
throw new ConflictException({
|
||||
code: 'INVENTORY_STRUCTURE_TYPES_INCOMPLETE',
|
||||
message: 'La configuración maestra de Empresa e Inventario todavía no está completa',
|
||||
@@ -109,7 +114,8 @@ export class InventoryStructureService {
|
||||
{ kind: 'EMPRESA', label: 'Empresa', type: company, parentKind: null, requiresFamily: false },
|
||||
],
|
||||
levels: [
|
||||
{ kind: 'AREA', label: 'Área', type: area, parentKind: null, requiresFamily: false },
|
||||
{ kind: 'DEPARTAMENTO', label: 'Departamento', type: departamento, parentKind: null, requiresFamily: false },
|
||||
{ kind: 'AREA', label: 'Área', type: area, parentKind: 'DEPARTAMENTO', requiresFamily: false },
|
||||
{ kind: 'YACIMIENTO', label: 'Yacimiento', type: yacimiento, parentKind: 'AREA', requiresFamily: false },
|
||||
{ kind: 'INSTALACION', label: 'Instalación', type: instalacion, parentKind: 'YACIMIENTO', requiresFamily: true },
|
||||
{ kind: 'SUBINSTALACION', label: 'Subinstalación', type: subinstalacion, parentKind: 'INSTALACION', requiresFamily: true },
|
||||
@@ -121,7 +127,7 @@ export class InventoryStructureService {
|
||||
|
||||
async parents(kindValue: string, search?: string) {
|
||||
const kind = kindValue.toUpperCase() as InventoryStructureKind;
|
||||
if (!(kind in PARENT_TYPE_BY_KIND) || kind === 'AREA' || kind === 'EMPRESA') {
|
||||
if (!(kind in PARENT_TYPE_BY_KIND) || kind === 'DEPARTAMENTO' || kind === 'EMPRESA') {
|
||||
throw new BadRequestException({
|
||||
code: 'INVENTORY_STRUCTURE_PARENT_KIND_INVALID',
|
||||
message: 'El nivel indicado no requiere un registro padre',
|
||||
@@ -153,7 +159,7 @@ export class InventoryStructureService {
|
||||
AND asset.information_status<>'INACTIVE'
|
||||
${searchSql}
|
||||
ORDER BY asset.name,asset.code
|
||||
LIMIT 80
|
||||
LIMIT 100
|
||||
`, parameters);
|
||||
return { data: rows };
|
||||
}
|
||||
@@ -169,7 +175,9 @@ export class InventoryStructureService {
|
||||
const parent = await this.requireParent(manager, dto.kind, dto.parentId ?? null);
|
||||
const family = await this.requireFamily(manager, dto.kind, dto.familyId ?? null, parent);
|
||||
const generatedCode = dto.code?.trim().toUpperCase() || this.generatedCode(dto.kind, dto.name);
|
||||
const operationalAreaId = parent ? await this.resolveAreaId(manager, parent) : null;
|
||||
const operationalAreaId = parent && ['YACIMIENTO','INSTALACION','SUBINSTALACION'].includes(dto.kind)
|
||||
? await this.resolveAreaId(manager, parent)
|
||||
: null;
|
||||
|
||||
const inserted = (await manager.query(`
|
||||
INSERT INTO assets (
|
||||
@@ -193,9 +201,9 @@ export class InventoryStructureService {
|
||||
AssetInformationStatus.DRAFT,
|
||||
AssetOperationalStatus.UNKNOWN,
|
||||
AssetDataOrigin.MANUAL,
|
||||
dto.kind === 'EMPRESA' ? 'Maestro de Empresas F5' : 'Estructura de Inventario F5',
|
||||
dto.kind === 'EMPRESA' ? 'Maestro manual de Empresas F5.1' : 'Estructura manual de Inventario F5.1',
|
||||
dto.kind === 'EMPRESA' ? 'inventory-master:empresa' : `inventory-structure:${dto.kind.toLowerCase()}`,
|
||||
family ? `Familia técnica: ${family.code} · ${family.name}` : null,
|
||||
family ? `Clasificación técnica: ${family.code} · ${family.name}` : null,
|
||||
principal.userId,
|
||||
])) as Array<{ id: string }>;
|
||||
const id = inserted[0]?.id;
|
||||
@@ -225,7 +233,7 @@ export class InventoryStructureService {
|
||||
id,
|
||||
parent?.id ?? null,
|
||||
operationalAreaId,
|
||||
dto.kind === 'EMPRESA' ? 'Alta guiada de Empresa independiente F5' : 'Alta guiada de estructura de Inventario F5',
|
||||
dto.kind === 'EMPRESA' ? 'Alta manual de Empresa independiente F5.1' : 'Alta manual de estructura de Inventario F5.1',
|
||||
versionNumber,
|
||||
request.requestId,
|
||||
principal.userId,
|
||||
@@ -285,7 +293,7 @@ export class InventoryStructureService {
|
||||
code: 'INVENTORY_ROOT_MUST_NOT_HAVE_PARENT',
|
||||
message: kind === 'EMPRESA'
|
||||
? 'Una Empresa es un maestro independiente y no puede tener padre'
|
||||
: 'Un Área es un registro raíz y no puede tener padre',
|
||||
: 'Un Departamento es un registro raíz y no puede tener padre',
|
||||
});
|
||||
}
|
||||
return null;
|
||||
@@ -309,7 +317,7 @@ export class InventoryStructureService {
|
||||
if (parent.typeCode.toLowerCase() !== expectedType) {
|
||||
throw new BadRequestException({
|
||||
code: 'INVENTORY_STRUCTURE_PARENT_INVALID',
|
||||
message: 'La jerarquía requerida es Área → Yacimiento → Instalación → Subinstalación',
|
||||
message: 'La jerarquía requerida es Departamento → Área → Yacimiento → Instalación → Subinstalación',
|
||||
});
|
||||
}
|
||||
return parent;
|
||||
@@ -349,13 +357,13 @@ export class InventoryStructureService {
|
||||
if (!expectedLevel) {
|
||||
if (familyId) throw new BadRequestException({
|
||||
code: 'INVENTORY_STRUCTURE_FAMILY_NOT_ALLOWED',
|
||||
message: 'Empresa, Área y Yacimiento no llevan familia técnica',
|
||||
message: 'Empresa, Departamento, Área y Yacimiento no llevan clasificación técnica',
|
||||
});
|
||||
return null;
|
||||
}
|
||||
if (!familyId) throw new BadRequestException({
|
||||
code: 'INVENTORY_STRUCTURE_FAMILY_REQUIRED',
|
||||
message: `Elegí la familia técnica de la ${kind.toLowerCase()}`,
|
||||
message: `Elegí la clasificación técnica de la ${kind.toLowerCase()}`,
|
||||
});
|
||||
const rows = (await manager.query(`
|
||||
SELECT family.id,family.code,family.name,family.level,
|
||||
@@ -369,22 +377,27 @@ export class InventoryStructureService {
|
||||
LIMIT 1
|
||||
`, [familyId])) as FamilyRow[];
|
||||
const family = rows[0];
|
||||
if (!family) throw new NotFoundException({ code: 'INVENTORY_FAMILY_NOT_FOUND', message: 'La familia técnica no existe' });
|
||||
if (!family) throw new NotFoundException({ code: 'INVENTORY_FAMILY_NOT_FOUND', message: 'La clasificación técnica no existe' });
|
||||
if (family.level !== expectedLevel) throw new BadRequestException({
|
||||
code: 'INVENTORY_FAMILY_LEVEL_INVALID',
|
||||
message: 'La familia técnica no corresponde al nivel seleccionado',
|
||||
message: 'La clasificación técnica no corresponde al nivel seleccionado',
|
||||
});
|
||||
if (kind === 'SUBINSTALACION' && family.parentFamilyId !== parent?.inventoryFamilyId) {
|
||||
throw new BadRequestException({
|
||||
code: 'INVENTORY_SUBINSTALLATION_FAMILY_PARENT_INVALID',
|
||||
message: 'La Subinstalación elegida no pertenece a la familia de la Instalación seleccionada',
|
||||
message: 'La Subinstalación elegida no pertenece a la clasificación de la Instalación seleccionada',
|
||||
});
|
||||
}
|
||||
return family;
|
||||
}
|
||||
|
||||
private generatedCode(kind: InventoryStructureKind, name: string): string {
|
||||
const prefix = kind === 'EMPRESA' ? 'EMP' : kind === 'YACIMIENTO' ? 'YAC' : kind === 'INSTALACION' ? 'INST' : kind === 'SUBINSTALACION' ? 'SUB' : 'AREA';
|
||||
const prefix = kind === 'EMPRESA' ? 'EMP'
|
||||
: kind === 'DEPARTAMENTO' ? 'DEP'
|
||||
: kind === 'AREA' ? 'AREA'
|
||||
: kind === 'YACIMIENTO' ? 'YAC'
|
||||
: kind === 'INSTALACION' ? 'INST'
|
||||
: 'SUB';
|
||||
const readable = name
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
@@ -417,5 +430,3 @@ export class InventoryStructureService {
|
||||
return rows[0];
|
||||
}
|
||||
}
|
||||
|
||||
type IdRow = { id: string };
|
||||
|
||||
Reference in New Issue
Block a user