fix(android): send inventory attributes by definition id
Android CI / RC / Android · lint, tests, debug APK, release compile (pull_request) Failing after 2m13s
DH V2 CI / WEB · typecheck, build (pull_request) Canceled after 0s
DH V2 CI / Docker / scripts contract (pull_request) Canceled after 0s
Production dependency audit / API · production dependencies (pull_request) Canceled after 0s
Production dependency audit / WEB · production dependencies (pull_request) Canceled after 0s
DH V2 CI / API · typecheck, tests, build (pull_request) Canceled after 29s
Android CI / RC / Android · lint, tests, debug APK, release compile (pull_request) Failing after 2m13s
DH V2 CI / WEB · typecheck, build (pull_request) Canceled after 0s
DH V2 CI / Docker / scripts contract (pull_request) Canceled after 0s
Production dependency audit / API · production dependencies (pull_request) Canceled after 0s
Production dependency audit / WEB · production dependencies (pull_request) Canceled after 0s
DH V2 CI / API · typecheck, tests, build (pull_request) Canceled after 29s
This commit is contained in:
@@ -12,8 +12,8 @@ android {
|
||||
applicationId = "com.korexlabs.dhinspeccion"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 32
|
||||
versionName = "0.19.4"
|
||||
versionCode = 33
|
||||
versionName = "0.19.5"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -244,7 +244,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
return
|
||||
}
|
||||
if (type.familyRequired && familyId == null) {
|
||||
error = "Elegí una clasificación técnica o la opción Otro / no catalogado."
|
||||
error = "Elegí el Tipo de instalación o subinstalación correspondiente."
|
||||
return
|
||||
}
|
||||
launchBusy(mutation = true) {
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.korexlabs.dhinspeccion.data
|
||||
|
||||
/** Builds the API payload using definition IDs while the form keeps stable field codes. */
|
||||
internal fun fieldAttributePayload(
|
||||
definitions: List<FieldAttributeDefinition>,
|
||||
valuesByCode: Map<String, String>,
|
||||
): Map<String, Any?> = definitions.mapNotNull { definition ->
|
||||
val raw = valuesByCode[definition.code]?.trim().orEmpty()
|
||||
if (raw.isBlank()) return@mapNotNull null
|
||||
definition.id to coerceFieldAttribute(definition, raw)
|
||||
}.toMap()
|
||||
|
||||
private fun coerceFieldAttribute(definition: FieldAttributeDefinition, raw: String): Any =
|
||||
when (definition.dataType.uppercase()) {
|
||||
"INTEGER", "INT" -> raw.toLongOrNull() ?: raw
|
||||
"NUMBER", "DECIMAL", "FLOAT", "DOUBLE" -> raw.replace(',', '.').toDoubleOrNull() ?: raw
|
||||
"BOOLEAN", "BOOL" -> raw.lowercase() in setOf("true", "1", "si", "sí", "yes")
|
||||
else -> raw
|
||||
}
|
||||
@@ -60,6 +60,7 @@ import com.korexlabs.dhinspeccion.MainViewModel
|
||||
import com.korexlabs.dhinspeccion.data.FieldAttributeDefinition
|
||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||
import com.korexlabs.dhinspeccion.data.FieldType
|
||||
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||
import com.korexlabs.dhinspeccion.data.VisitSummary
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -521,18 +522,7 @@ private fun InventoryCard(item: FieldInventoryItem, canModify: Boolean, onSelect
|
||||
}
|
||||
|
||||
private fun buildAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||
type.attributes.mapNotNull { definition ->
|
||||
val raw = values[definition.code]?.trim().orEmpty()
|
||||
if (raw.isBlank()) return@mapNotNull null
|
||||
definition.code to coerceAttribute(definition, raw)
|
||||
}.toMap()
|
||||
|
||||
private fun coerceAttribute(definition: FieldAttributeDefinition, raw: String): Any = when (definition.dataType.uppercase()) {
|
||||
"INTEGER", "INT" -> raw.toLongOrNull() ?: raw
|
||||
"NUMBER", "DECIMAL", "FLOAT", "DOUBLE" -> raw.replace(',', '.').toDoubleOrNull() ?: raw
|
||||
"BOOLEAN", "BOOL" -> raw.lowercase() in setOf("true", "1", "si", "sí", "yes")
|
||||
else -> raw
|
||||
}
|
||||
fieldAttributePayload(type.attributes, values)
|
||||
|
||||
private fun hasPermission(context: Context, permission: String): Boolean =
|
||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
@@ -56,6 +56,7 @@ import com.korexlabs.dhinspeccion.MainViewModel
|
||||
import com.korexlabs.dhinspeccion.data.FieldAttributeDefinition
|
||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||
import com.korexlabs.dhinspeccion.data.FieldType
|
||||
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
@@ -693,11 +694,11 @@ private fun DynamicFieldInventoryScreen(model: MainViewModel, onBack: () -> Unit
|
||||
}
|
||||
|
||||
if (selectedType?.familyRequired == true) {
|
||||
Text("Clasificación técnica *", fontWeight = FontWeight.Bold)
|
||||
Text("Tipo de instalación *", fontWeight = FontWeight.Bold)
|
||||
OutlinedTextField(
|
||||
value = familySearch,
|
||||
onValueChange = { familySearch = it },
|
||||
label = { Text("Buscar clasificación") },
|
||||
label = { Text("Buscar tipo de instalación") },
|
||||
supportingText = { Text("${filteredFamilies.size} opciones compatibles") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
@@ -906,18 +907,7 @@ private fun dynamicStatusLabel(status: String): String = when (status) {
|
||||
}
|
||||
|
||||
private fun buildDynamicAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||
type.attributes.mapNotNull { definition ->
|
||||
val raw = values[definition.code]?.trim().orEmpty()
|
||||
if (raw.isBlank()) return@mapNotNull null
|
||||
definition.code to coerceDynamicAttribute(definition, raw)
|
||||
}.toMap()
|
||||
|
||||
private fun coerceDynamicAttribute(definition: FieldAttributeDefinition, raw: String): Any = when (definition.dataType.uppercase()) {
|
||||
"INTEGER", "INT" -> raw.toLongOrNull() ?: raw
|
||||
"NUMBER", "DECIMAL", "FLOAT", "DOUBLE" -> raw.replace(',', '.').toDoubleOrNull() ?: raw
|
||||
"BOOLEAN", "BOOL" -> raw.lowercase() in setOf("true", "1", "si", "sí", "yes")
|
||||
else -> raw
|
||||
}
|
||||
fieldAttributePayload(type.attributes, values)
|
||||
|
||||
private fun dynamicHasPermission(context: Context, permission: String): Boolean =
|
||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.korexlabs.dhinspeccion.MainViewModel
|
||||
import com.korexlabs.dhinspeccion.data.FieldAttributeDefinition
|
||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||
import com.korexlabs.dhinspeccion.data.FieldType
|
||||
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
@@ -720,18 +721,7 @@ private fun statusLabel(status: String): String = when (status) {
|
||||
}
|
||||
|
||||
private fun buildF3Attributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||
type.attributes.mapNotNull { definition ->
|
||||
val raw = values[definition.code]?.trim().orEmpty()
|
||||
if (raw.isBlank()) return@mapNotNull null
|
||||
definition.code to coerceF3Attribute(definition, raw)
|
||||
}.toMap()
|
||||
|
||||
private fun coerceF3Attribute(definition: FieldAttributeDefinition, raw: String): Any = when (definition.dataType.uppercase()) {
|
||||
"INTEGER", "INT" -> raw.toLongOrNull() ?: raw
|
||||
"NUMBER", "DECIMAL", "FLOAT", "DOUBLE" -> raw.replace(',', '.').toDoubleOrNull() ?: raw
|
||||
"BOOLEAN", "BOOL" -> raw.lowercase() in setOf("true", "1", "si", "sí", "yes")
|
||||
else -> raw
|
||||
}
|
||||
fieldAttributePayload(type.attributes, values)
|
||||
|
||||
private fun f3HasPermission(context: Context, permission: String): Boolean =
|
||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
@@ -82,6 +82,7 @@ import com.korexlabs.dhinspeccion.MainViewModel
|
||||
import com.korexlabs.dhinspeccion.data.FieldAttributeDefinition
|
||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||
import com.korexlabs.dhinspeccion.data.FieldType
|
||||
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -1099,18 +1100,7 @@ private fun modernNormalize(value: String): String = value.trim().lowercase()
|
||||
private fun modernStatusLabel(status: String): String = visitStatusLabelEs(status)
|
||||
|
||||
private fun buildModernAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||
type.attributes.mapNotNull { definition ->
|
||||
val raw = values[definition.code]?.trim().orEmpty()
|
||||
if (raw.isBlank()) return@mapNotNull null
|
||||
definition.code to coerceModernAttribute(definition, raw)
|
||||
}.toMap()
|
||||
|
||||
private fun coerceModernAttribute(definition: FieldAttributeDefinition, raw: String): Any = when (definition.dataType.uppercase()) {
|
||||
"INTEGER", "INT" -> raw.toLongOrNull() ?: raw
|
||||
"NUMBER", "DECIMAL", "FLOAT", "DOUBLE" -> raw.replace(',', '.').toDoubleOrNull() ?: raw
|
||||
"BOOLEAN", "BOOL" -> raw.lowercase() in setOf("true", "1", "si", "sí", "yes")
|
||||
else -> raw
|
||||
}
|
||||
fieldAttributePayload(type.attributes, values)
|
||||
|
||||
private fun modernHasPermission(context: Context, permission: String): Boolean =
|
||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ class DynamicFieldFlowContractTest {
|
||||
@Test
|
||||
fun fastSubinstallationFlowKeepsParentClassificationAndCaptureSteps() {
|
||||
assertTrue(source.contains("Elegí la Instalación padre"))
|
||||
assertTrue(source.contains("Clasificación técnica *"))
|
||||
assertTrue(source.contains("Tipo de instalación *"))
|
||||
assertTrue(source.contains("Guardar y tomar foto"))
|
||||
assertTrue(source.contains("model.loadFieldTypes(item.id)"))
|
||||
assertTrue(source.contains("parentId = item.id"))
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.korexlabs.dhinspeccion
|
||||
|
||||
import com.korexlabs.dhinspeccion.data.FieldAttributeDefinition
|
||||
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class FieldAttributePayloadTest {
|
||||
@Test
|
||||
fun payloadUsesDefinitionIdsExpectedByApiAndKeepsTypedValues() {
|
||||
val definitions = listOf(
|
||||
FieldAttributeDefinition("id-marca", "campo_marca", "Marca", "TEXT"),
|
||||
FieldAttributeDefinition("id-capacidad", "campo_capacidad", "Capacidad", "NUMBER"),
|
||||
FieldAttributeDefinition("id-serie", "campo_numero_serie", "Número de serie", "TEXT"),
|
||||
)
|
||||
|
||||
val payload = fieldAttributePayload(
|
||||
definitions,
|
||||
mapOf("campo_marca" to " algo ", "campo_capacidad" to "37,73", "campo_numero_serie" to " "),
|
||||
)
|
||||
|
||||
assertEquals("algo", payload["id-marca"])
|
||||
assertEquals(37.73, payload["id-capacidad"])
|
||||
assertFalse(payload.containsKey("id-serie"))
|
||||
assertTrue(payload.keys.none { it.startsWith("campo_") })
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ class ReleaseMetadataTest {
|
||||
fun debugBuildKeepsSeparateApplicationIdentity() {
|
||||
assertEquals("com.korexlabs.dhinspeccion.debug", BuildConfig.APPLICATION_ID)
|
||||
assertEquals(32, BuildConfig.VERSION_CODE)
|
||||
assertEquals("0.19.4-debug", BuildConfig.VERSION_NAME)
|
||||
assertEquals("0.19.5-debug", BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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', () => {
|
||||
const gradle = mountedRepoFile('android-app/app/build.gradle.kts');
|
||||
|
||||
assert.match(gradle, /versionCode = 32/);
|
||||
assert.match(gradle, /versionName = "0\.19\.4"/);
|
||||
assert.match(gradle, /versionCode = 33/);
|
||||
assert.match(gradle, /versionName = "0\.19\.5"/);
|
||||
assert.match(gradle, /https:\/\/dhv2\.korexlabs\.com\/api\/v3\//);
|
||||
assert.match(gradle, /applicationIdSuffix = "\.debug"/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user