Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2ec846721 |
+10
-4
@@ -1,20 +1,26 @@
|
|||||||
# DH Inspección Android · release final de campo 0.19.7
|
# DH Inspección Android · release final de campo 0.19.8
|
||||||
|
|
||||||
## Candidata vigente
|
## Candidata vigente
|
||||||
|
|
||||||
- Fase funcional: **Flujo final de campo · Inspección → Acta → Hallazgos → Firma**.
|
- Fase funcional: **Flujo final de campo · Inspección → Acta → Hallazgos → Firma**.
|
||||||
- `versionName`: **0.19.7**.
|
- `versionName`: **0.19.8**.
|
||||||
- `versionCode`: **35**.
|
- `versionCode`: **35**.
|
||||||
- Application ID release: `com.korexlabs.dhinspeccion`.
|
- Application ID release: `com.korexlabs.dhinspeccion`.
|
||||||
- Application ID debug/QA: `com.korexlabs.dhinspeccion.debug`.
|
- Application ID debug/QA: `com.korexlabs.dhinspeccion.debug`.
|
||||||
- API: `https://dhv2.korexlabs.com/api/v3/`.
|
- API: `https://dhv2.korexlabs.com/api/v3/`.
|
||||||
- Servidor compatible de esta candidata: **API 0.29.0-5 / WEB 0.23.0-3**.
|
- Servidor compatible de esta candidata: **API 0.29.0-6 / WEB 0.23.0-3**.
|
||||||
|
|
||||||
La variante debug es independiente de la app productiva y puede instalarse para QA/presentación sin sobrescribir una instalación release histórica.
|
La variante debug es independiente de la app productiva y puede instalarse para QA/presentación sin sobrescribir una instalación release histórica.
|
||||||
|
|
||||||
|
### Ajustes 0.19.8
|
||||||
|
|
||||||
|
- El encabezado de **Mis inspecciones** separa las acciones para que **Salir** nunca se comprima ni se corte en pantallas angostas.
|
||||||
|
- El alta de Hallazgos corrige la captura de versión que podía transformar el número en `NaN` después del INSERT y devolver error 500.
|
||||||
|
- Un Hallazgo admite **múltiples fotografías con GPS**; después de cada captura queda disponible **Agregar otra foto con GPS** y se muestra el contador.
|
||||||
|
|
||||||
## Procedimiento operativo validado
|
## Procedimiento operativo validado
|
||||||
|
|
||||||
La APK 0.19.7 fija como recorrido principal de campo:
|
La APK 0.19.8 fija como recorrido principal de campo:
|
||||||
|
|
||||||
1. **Iniciar Inspección**. Al iniciarla se habilita el circuito de **Actas y Hallazgos**; Inventario no es una acción independiente de la Inspección.
|
1. **Iniciar Inspección**. Al iniciarla se habilita el circuito de **Actas y Hallazgos**; Inventario no es una acción independiente de la Inspección.
|
||||||
2. **Crear o abrir un Acta**. Puede haber varias Actas dentro de una misma Inspección, pero sólo una puede permanecer en elaboración al mismo tiempo. La urgencia todavía no se define.
|
2. **Crear o abrir un Acta**. Puede haber varias Actas dentro de una misma Inspección, pero sólo una puede permanecer en elaboración al mismo tiempo. La urgencia todavía no se define.
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "com.korexlabs.dhinspeccion"
|
applicationId = "com.korexlabs.dhinspeccion"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 35
|
versionCode = 36
|
||||||
versionName = "0.19.7"
|
versionName = "0.19.8"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -238,13 +238,18 @@ fun FieldFindingScreen(model: MainViewModel) {
|
|||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val photoCount = evidence.count { it.kind == "PHOTO" }
|
||||||
Button(
|
Button(
|
||||||
onClick = { requestPhoto(finding.id) },
|
onClick = { requestPhoto(finding.id) },
|
||||||
enabled = !model.busy,
|
enabled = !model.busy,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Text("Tomar foto con GPS")
|
Text(if (photoCount == 0) "Agregar foto con GPS" else "Agregar otra foto con GPS")
|
||||||
}
|
}
|
||||||
|
Text(
|
||||||
|
if (photoCount == 0) "Podés adjuntar una o varias fotos." else "$photoCount foto${if (photoCount == 1) "" else "s"} adjunta${if (photoCount == 1) "" else "s"}. Podés seguir agregando.",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,7 +370,8 @@ fun FieldFindingScreen(model: MainViewModel) {
|
|||||||
label = { Text("Qué observaste *") },
|
label = { Text("Qué observaste *") },
|
||||||
placeholder = { Text("Describí brevemente el problema") },
|
placeholder = { Text("Describí brevemente el problema") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
minLines = 3,
|
||||||
|
maxLines = 6,
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||||
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Down) }),
|
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Down) }),
|
||||||
)
|
)
|
||||||
@@ -405,13 +411,19 @@ fun FieldFindingScreen(model: MainViewModel) {
|
|||||||
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
Text("Hallazgo registrado", fontWeight = FontWeight.Bold)
|
Text("Hallazgo registrado", fontWeight = FontWeight.Bold)
|
||||||
Text("${finding.code} · ${finding.title}")
|
Text("${finding.code} · ${finding.title}")
|
||||||
|
val createdEvidence = model.fieldFindingEvidence[finding.id].orEmpty()
|
||||||
|
val createdPhotoCount = createdEvidence.count { it.kind == "PHOTO" }
|
||||||
Button(
|
Button(
|
||||||
onClick = { requestPhoto(finding.id) },
|
onClick = { requestPhoto(finding.id) },
|
||||||
enabled = !model.busy,
|
enabled = !model.busy,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Text("Tomar foto con GPS")
|
Text(if (createdPhotoCount == 0) "Agregar foto con GPS" else "Agregar otra foto con GPS")
|
||||||
}
|
}
|
||||||
|
Text(
|
||||||
|
if (createdPhotoCount == 0) "Podés adjuntar todas las fotos que necesites." else "Fotos adjuntas: $createdPhotoCount. Podés seguir agregando.",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
Text("También podés registrar otro Hallazgo sobre el mismo Inventario.", style = MaterialTheme.typography.bodySmall)
|
Text("También podés registrar otro Hallazgo sobre el mismo Inventario.", style = MaterialTheme.typography.bodySmall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@@ -86,21 +85,33 @@ fun MobileHomeScreen(model: MainViewModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(Modifier.fillMaxSize().padding(top = 28.dp)) {
|
Column(Modifier.fillMaxSize().padding(top = 28.dp)) {
|
||||||
Row(
|
Column(
|
||||||
Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 10.dp),
|
Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
Text("Mis inspecciones", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
Text("Mis inspecciones", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
Text(session.displayName, style = MaterialTheme.typography.bodySmall)
|
Text(session.displayName, style = MaterialTheme.typography.bodySmall)
|
||||||
}
|
}
|
||||||
Row {
|
Row(
|
||||||
OutlinedButton(onClick = { model.loadVisits() }, enabled = !model.busy && !opening) {
|
Modifier.fillMaxWidth(),
|
||||||
Text("Actualizar")
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { model.loadVisits() },
|
||||||
|
enabled = !model.busy && !opening,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
) {
|
||||||
|
Text("Actualizar", maxLines = 1)
|
||||||
|
}
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { model.logout() },
|
||||||
|
enabled = !opening,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
) {
|
||||||
|
Text("Salir", maxLines = 1)
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
OutlinedButton(onClick = { model.logout() }, enabled = !opening) { Text("Salir") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ class ReleaseMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
fun debugBuildKeepsSeparateApplicationIdentity() {
|
fun debugBuildKeepsSeparateApplicationIdentity() {
|
||||||
assertEquals("com.korexlabs.dhinspeccion.debug", BuildConfig.APPLICATION_ID)
|
assertEquals("com.korexlabs.dhinspeccion.debug", BuildConfig.APPLICATION_ID)
|
||||||
assertEquals(35, BuildConfig.VERSION_CODE)
|
assertEquals(36, BuildConfig.VERSION_CODE)
|
||||||
assertEquals("0.19.7-debug", BuildConfig.VERSION_NAME)
|
assertEquals("0.19.8-debug", BuildConfig.VERSION_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "dhv2-api",
|
"name": "dhv2-api",
|
||||||
"version": "0.29.0-5",
|
"version": "0.29.0-6",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dhv2-api",
|
"name": "dhv2-api",
|
||||||
"version": "0.29.0-5",
|
"version": "0.29.0-6",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.0",
|
"@nestjs/common": "^11.0.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dhv2-api",
|
"name": "dhv2-api",
|
||||||
"version": "0.29.0-5",
|
"version": "0.29.0-6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1166,10 +1166,13 @@ export class InspectionFindingsService {
|
|||||||
principal: AuthPrincipal,
|
principal: AuthPrincipal,
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const [row] = await manager.query(`
|
const [row] = await manager.query(`
|
||||||
|
WITH updated AS (
|
||||||
UPDATE inspection_findings
|
UPDATE inspection_findings
|
||||||
SET current_version = current_version + 1
|
SET current_version = current_version + 1
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING current_version AS "versionNumber"
|
RETURNING current_version
|
||||||
|
)
|
||||||
|
SELECT current_version AS "versionNumber" FROM updated
|
||||||
`, [finding.id]) as Array<{ versionNumber: number }>;
|
`, [finding.id]) as Array<{ versionNumber: number }>;
|
||||||
const versionNumber = Number(row.versionNumber);
|
const versionNumber = Number(row.versionNumber);
|
||||||
finding.currentVersion = versionNumber;
|
finding.currentVersion = versionNumber;
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export const API_VERSION = '0.29.0-5';
|
export const API_VERSION = '0.29.0-6';
|
||||||
export const API_PHASE = 'F6.2';
|
export const API_PHASE = 'F6.2';
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ test('health metadata reports the current F6.1 release', () => {
|
|||||||
assert.equal(API_PHASE, 'F6.2');
|
assert.equal(API_PHASE, 'F6.2');
|
||||||
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')) as { version: string };
|
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')) as { version: string };
|
||||||
assert.equal(API_VERSION, pkg.version);
|
assert.equal(API_VERSION, pkg.version);
|
||||||
assert.equal(API_VERSION, '0.29.0-5');
|
assert.equal(API_VERSION, '0.29.0-6');
|
||||||
});
|
});
|
||||||
@@ -10,8 +10,8 @@ function mountedRepoFile(path: string): string {
|
|||||||
test('F6.3 Android test cut targets production API and has a distinct installable debug version', () => {
|
test('F6.3 Android test cut targets production API and has a distinct installable debug version', () => {
|
||||||
const gradle = mountedRepoFile('android-app/app/build.gradle.kts');
|
const gradle = mountedRepoFile('android-app/app/build.gradle.kts');
|
||||||
|
|
||||||
assert.match(gradle, /versionCode = 35/);
|
assert.match(gradle, /versionCode = 36/);
|
||||||
assert.match(gradle, /versionName = "0\.19\.7"/);
|
assert.match(gradle, /versionName = "0\.19\.8"/);
|
||||||
assert.match(gradle, /https:\/\/dhv2\.korexlabs\.com\/api\/v3\//);
|
assert.match(gradle, /https:\/\/dhv2\.korexlabs\.com\/api\/v3\//);
|
||||||
assert.match(gradle, /applicationIdSuffix = "\.debug"/);
|
assert.match(gradle, /applicationIdSuffix = "\.debug"/);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import test from 'node:test';
|
||||||
|
|
||||||
|
function source(path: string): string {
|
||||||
|
return readFileSync(resolve(process.cwd(), path), 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
test('F6.6 Hallazgo version capture uses a SELECT-shaped CTE so TypeORM never turns the version into NaN', () => {
|
||||||
|
const service = source('src/inspection-findings/inspection-findings.service.ts');
|
||||||
|
|
||||||
|
assert.match(service, /WITH updated AS \(/);
|
||||||
|
assert.match(service, /UPDATE inspection_findings/);
|
||||||
|
assert.match(service, /RETURNING current_version/);
|
||||||
|
assert.match(service, /SELECT current_version AS "versionNumber" FROM updated/);
|
||||||
|
assert.match(service, /const versionNumber = Number\(row\.versionNumber\)/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('F6.6 mobile home keeps Salir readable on narrow screens', () => {
|
||||||
|
const home = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileHomeScreen.kt');
|
||||||
|
|
||||||
|
assert.match(home, /Text\("Actualizar", maxLines = 1\)/);
|
||||||
|
assert.match(home, /Text\("Salir", maxLines = 1\)/);
|
||||||
|
assert.match(home, /modifier = Modifier\.weight\(1f\)/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('F6.6 Hallazgos can receive as many field photos as needed', () => {
|
||||||
|
const screen = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/FieldFindingScreen.kt');
|
||||||
|
const model = source('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/MainViewModel.kt');
|
||||||
|
|
||||||
|
assert.match(screen, /Agregar foto con GPS/);
|
||||||
|
assert.match(screen, /Agregar otra foto con GPS/);
|
||||||
|
assert.match(screen, /Podés adjuntar todas las fotos que necesites/);
|
||||||
|
assert.match(model, /loadEvidenceInternal\(findingId\)/);
|
||||||
|
assert.doesNotMatch(screen, /máximo de 1 foto|solo una foto/i);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user