Compare commits

..
Author SHA1 Message Date
admin d17b153808 test(f5): make runtime DI regression test metadata-stable 2026-09-09 06:59:25 -03:00
admin 2cfabe0cf0 test(f5): lock production dossier dependency injection 2026-09-09 06:56:55 -03:00
admin 699becc13e fix(f5): restore internal inventory function provider for dossier runtime 2026-09-09 06:56:39 -03:00
admin 2b87e07fd2 F5 · Android 0.13.0 de prueba y compatibilidad de familias
Integra el corte Android F5 validado por CI, con artifact 0.13.0-debug y compatibilidad correcta de familias Otro / no catalogado en Inventario de campo.
2026-09-09 06:50:06 -03:00
admin 35d4630581 F5 · Inventario operativo, territorio y catálogo autorizado (#25)
* fix(web): simplify inventory administration menu

* fix(web): remove legacy imports and function catalog routes

* fix(web): remove redundant inspections lifecycle legend

* feat(inventory): distinguish physical instances from structural records

* fix(dashboard): align inventory and act follow-up metrics

* fix(web): align dashboard summary contract

* fix(web): clarify dashboard act and report concepts

* feat(inventory): mark field-created records as real instances

* feat(inventory): map physical instance flag on asset entity

* fix(inventory): keep field yacimientos structural

* feat(inventory): classify future concrete instances at database level

* fix(inventory): count only installation and subinstallation instances

* feat(inventory): add authoritative F5 source snapshot

* feat(inventory): preload authoritative territory model

* feat(inventory): preload authoritative technical catalog

* fix(findings): use only authoritative F5 family catalog

* fix(inventory): preserve non-hierarchical operator snapshot compatibility

* feat(inventory): add inventory-only asset filter

* feat(inventory): add inventory-only tree filter

* feat(inventory): add inventory browser query contract

* feat(inventory): add area-owned inventory browser

* feat(inventory): expose area-owned inventory browser

* refactor(inventory): remove function catalog and add inventory browser

* fix(inventory): make operator relation temporal and non-owning

* feat(web): add inventory browser API client

* feat(inventory): extend inventory browser filters

* feat(inventory): add real inventory list endpoint logic

* feat(inventory): expose real inventory list

* feat(web): add real inventory list client

* refactor(web): make inventory hierarchy area-owned

* fix(web): show only real inventory instances

* fix(web): style act follow-up tabs and F5 inventory context

* fix(web): load F5 flow styles

* fix(inventory): apply area-owned operational guard on F5 up

* fix(inventory): treat company on asset as non-owning creation snapshot

* fix(inventory): resolve field inventory by area hierarchy, not company ownership

* fix(inventory): preserve custom catalog and apply authoritative universal findings

* fix(inventory): harden authoritative catalog migration checks

* fix(inventory): make authoritative territory preload safely reversible

* feat(inventory): allow independent company master creation

* fix(inventory): make guided creation area-owned and support companies

* feat(web): expose independent company master in inventory setup

* feat(web): create companies independently from physical inventory hierarchy

* fix(inventory): merge by physical area and preserve sealed documents

* test(inventory): lock F5 authoritative model and merge invariants

* feat(inventory): add family administration DTOs

* feat(inventory): administer installation and subinstallation classifications

* feat(inventory): expose family classification administration

* feat(web): add inventory classification administration API

* fix(web): configure finding applicability by inventory classification

* fix(web): redefine inventory configuration around hierarchy classifications and columns

* chore(release): identify F5 inventory model

* chore(release): bump API for F5 inventory model

* test(release): expect F5 health metadata

* chore(release): align WEB package with F5 inventory cut

* chore(release): expose F5 WEB phase

* test(dashboard): expect inspector activity and act follow-up metrics

* test(dashboard): route F5 summary query mocks explicitly

* ci: rehearse all migrations on clean PostGIS before merge

* ci: prove F5 migrations revert and reapply cleanly

* test(f5): align operational navigation contract

* test(f5): align operator lifecycle with area-owned inventory

* test(f5): make merge compatibility area-based

* test(f5): distinguish literal and normalized yacimiento counts

* test(f5): model normalized yacimiento collision explicitly

* ci(f5): bootstrap historical admin prerequisite in clean migration rehearsal

* ci(f5): bypass irreversible historical reset in clean rehearsal

* fix(f5): make territory SQL parameter types explicit

* fix(f5): guarantee canonical inventory hierarchy before territory preload

* ci(f5): include canonical hierarchy migration in rollback gate

* fix(f5): type relation backup markers explicitly

* fix(f5): make catalog SQL text parameter types explicit
2026-09-08 23:18:15 -03:00
6 changed files with 76 additions and 9 deletions
+7 -3
View File
@@ -1,9 +1,11 @@
name: Android APK
# F3.2: genera la APK debug verificable antes de promover la integración multi-Acta.
# F5: genera una APK debug verificable contra la API productiva F5.
on:
push:
branches:
- 'main'
- 'release/f5-android-test'
- 'feature/f2-2*'
- 'feature/f2-3*'
- 'feature/f2-4*'
@@ -16,6 +18,8 @@ on:
paths:
- 'android-app/**'
- 'api-v3/src/auth/**'
- 'api-v3/src/inspection-visits/**'
- 'api-v3/src/inspection-acts/**'
- '.github/workflows/android.yml'
workflow_dispatch:
@@ -58,7 +62,7 @@ jobs:
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: DH-Inspeccion-F3.2-0.12.0-debug
name: DH-Inspeccion-F5-0.13.0-debug
path: android-app/app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
retention-days: 14
retention-days: 30
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "com.korexlabs.dhinspeccion"
minSdk = 26
targetSdk = 36
versionCode = 19
versionName = "0.12.0"
versionCode = 20
versionName = "0.13.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -26,6 +26,7 @@ import { InventoryStructureController } from './inventory-structure.controller';
import { InventoryStructureService } from './inventory-structure.service';
import { InventoryFamilyCatalogController } from './inventory-family-catalog.controller';
import { InventoryFamilyCatalogService } from './inventory-family-catalog.service';
import { InventoryFunctionService } from './inventory-function.service';
import { FieldInventoryMergeController, InventoryMergeController } from './inventory-merge.controller';
import { InventoryMergeService } from './inventory-merge.service';
import { MergedInventoryDossierService } from './merged-inventory-dossier.service';
@@ -56,6 +57,7 @@ import { InventoryBrowserService } from './inventory-browser.service';
AssetsService,
InventoryStructureService,
InventoryFamilyCatalogService,
InventoryFunctionService,
InventoryBrowserService,
InventoryMergeService,
MergedInventoryDossierService,
@@ -44,6 +44,8 @@ const STRUCTURAL_CHILD: Record<string, string | undefined> = {
instalacion: 'subinstalacion',
};
const OTHER_FAMILY_SQL = "source_reference LIKE 'SYSTEM:F3.1:OTHER%' OR source_reference LIKE 'F5:SYSTEM:OTHER:%'";
@Injectable()
export class F3FieldInventoryStructureService {
constructor(
@@ -202,10 +204,10 @@ export class F3FieldInventoryStructureService {
return this.dataSource.query(`
SELECT id,code,name,level,information_labels AS "informationLabels",
source_reference AS "sourceReference",
(source_reference LIKE 'SYSTEM:F3.1:OTHER%') AS "isOther"
(${OTHER_FAMILY_SQL}) AS "isOther"
FROM inventory_families
WHERE level='INSTALLATION' AND is_active=true
ORDER BY (source_reference LIKE 'SYSTEM:F3.1:OTHER%') ASC,name,code
ORDER BY (${OTHER_FAMILY_SQL}) ASC,name,code
`) as Promise<FamilyRow[]>;
}
if (expectedTypeCode === 'subinstalacion') {
@@ -219,13 +221,13 @@ export class F3FieldInventoryStructureService {
SELECT family.id,family.code,family.name,family.level,
family.information_labels AS "informationLabels",
family.source_reference AS "sourceReference",
(family.source_reference LIKE 'SYSTEM:F3.1:OTHER%') AS "isOther"
(family.source_reference LIKE 'SYSTEM:F3.1:OTHER%' OR family.source_reference LIKE 'F5:SYSTEM:OTHER:%') AS "isOther"
FROM inventory_family_parent_rules rule
JOIN inventory_families family ON family.id=rule.child_family_id
WHERE rule.parent_family_id=$1::uuid
AND family.level='SUBINSTALLATION'
AND family.is_active=true
ORDER BY (family.source_reference LIKE 'SYSTEM:F3.1:OTHER%') ASC,family.name,family.code
ORDER BY (family.source_reference LIKE 'SYSTEM:F3.1:OTHER%' OR family.source_reference LIKE 'F5:SYSTEM:OTHER:%') ASC,family.name,family.code
`, [parent.inventoryFamilyId]) as Promise<FamilyRow[]>;
}
return [];
@@ -0,0 +1,28 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import test from 'node:test';
function mountedRepoFile(path: string): string {
return readFileSync(resolve(process.cwd(), '..', path), 'utf8');
}
test('F5 Android test cut targets production API and has a distinct installable debug version', () => {
const gradle = mountedRepoFile('android-app/app/build.gradle.kts');
assert.match(gradle, /versionCode = 20/);
assert.match(gradle, /versionName = "0\.13\.0"/);
assert.match(gradle, /https:\/\/dhv2\.korexlabs\.com\/api\/v3\//);
assert.match(gradle, /applicationIdSuffix = "\.debug"/);
});
test('F5 field inventory exposes Other families as reviewable choices to Android', () => {
const service = readFileSync(
resolve(process.cwd(), 'src/inspection-visits/f3-field-inventory-structure.service.ts'),
'utf8',
);
assert.match(service, /F5:SYSTEM:OTHER:%/);
assert.match(service, /AS "isOther"/);
assert.match(service, /isOtherFamily: family\.isOther/);
});
@@ -0,0 +1,31 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import test from 'node:test';
import { MODULE_METADATA } from '@nestjs/common/constants';
import { AssetMasterModule } from '../../src/asset-master/asset-master.module';
import { InventoryFunctionService } from '../../src/asset-master/inventory-function.service';
test('F5 AssetMasterModule keeps dossier-only function dependency injectable without reopening its controller', () => {
const providers = (Reflect.getMetadata(MODULE_METADATA.PROVIDERS, AssetMasterModule) ?? []) as unknown[];
const controllers = (Reflect.getMetadata(MODULE_METADATA.CONTROLLERS, AssetMasterModule) ?? []) as Array<{ name?: string }>;
const dossierSource = readFileSync(
resolve(process.cwd(), 'src/asset-master/merged-inventory-dossier.service.ts'),
'utf8',
);
assert.match(
dossierSource,
/private readonly functions: InventoryFunctionService/,
'MergedInventoryDossierService must still depend on InventoryFunctionService for historical dossier data',
);
assert.ok(
providers.includes(InventoryFunctionService),
'AssetMasterModule must provide InventoryFunctionService so production Nest bootstrap can resolve the dossier',
);
assert.equal(
controllers.some((controller) => controller?.name === 'InventoryFunctionController'),
false,
'F5 must not reopen the retired Inventory Function controller',
);
});