Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0a5367920 | ||
|
|
ff30462259 |
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "com.korexlabs.dhinspeccion"
|
applicationId = "com.korexlabs.dhinspeccion"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 32
|
versionCode = 33
|
||||||
versionName = "0.19.4"
|
versionName = "0.19.5"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (type.familyRequired && familyId == null) {
|
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
|
return
|
||||||
}
|
}
|
||||||
launchBusy(mutation = true) {
|
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.FieldAttributeDefinition
|
||||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||||
import com.korexlabs.dhinspeccion.data.FieldType
|
import com.korexlabs.dhinspeccion.data.FieldType
|
||||||
|
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||||
import com.korexlabs.dhinspeccion.data.VisitSummary
|
import com.korexlabs.dhinspeccion.data.VisitSummary
|
||||||
import kotlinx.coroutines.launch
|
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?> =
|
private fun buildAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||||
type.attributes.mapNotNull { definition ->
|
fieldAttributePayload(type.attributes, values)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hasPermission(context: Context, permission: String): Boolean =
|
private fun hasPermission(context: Context, permission: String): Boolean =
|
||||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
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.FieldAttributeDefinition
|
||||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||||
import com.korexlabs.dhinspeccion.data.FieldType
|
import com.korexlabs.dhinspeccion.data.FieldType
|
||||||
|
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@@ -693,11 +694,11 @@ private fun DynamicFieldInventoryScreen(model: MainViewModel, onBack: () -> Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedType?.familyRequired == true) {
|
if (selectedType?.familyRequired == true) {
|
||||||
Text("Clasificación técnica *", fontWeight = FontWeight.Bold)
|
Text("Tipo de instalación *", fontWeight = FontWeight.Bold)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = familySearch,
|
value = familySearch,
|
||||||
onValueChange = { familySearch = it },
|
onValueChange = { familySearch = it },
|
||||||
label = { Text("Buscar clasificación") },
|
label = { Text("Buscar tipo de instalación") },
|
||||||
supportingText = { Text("${filteredFamilies.size} opciones compatibles") },
|
supportingText = { Text("${filteredFamilies.size} opciones compatibles") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
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?> =
|
private fun buildDynamicAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||||
type.attributes.mapNotNull { definition ->
|
fieldAttributePayload(type.attributes, values)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun dynamicHasPermission(context: Context, permission: String): Boolean =
|
private fun dynamicHasPermission(context: Context, permission: String): Boolean =
|
||||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
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.FieldAttributeDefinition
|
||||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||||
import com.korexlabs.dhinspeccion.data.FieldType
|
import com.korexlabs.dhinspeccion.data.FieldType
|
||||||
|
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
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?> =
|
private fun buildF3Attributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||||
type.attributes.mapNotNull { definition ->
|
fieldAttributePayload(type.attributes, values)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun f3HasPermission(context: Context, permission: String): Boolean =
|
private fun f3HasPermission(context: Context, permission: String): Boolean =
|
||||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
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.FieldAttributeDefinition
|
||||||
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
import com.korexlabs.dhinspeccion.data.FieldInventoryItem
|
||||||
import com.korexlabs.dhinspeccion.data.FieldType
|
import com.korexlabs.dhinspeccion.data.FieldType
|
||||||
|
import com.korexlabs.dhinspeccion.data.fieldAttributePayload
|
||||||
import com.korexlabs.dhinspeccion.data.VisitDetail
|
import com.korexlabs.dhinspeccion.data.VisitDetail
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
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 modernStatusLabel(status: String): String = visitStatusLabelEs(status)
|
||||||
|
|
||||||
private fun buildModernAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
private fun buildModernAttributes(type: FieldType, values: Map<String, String>): Map<String, Any?> =
|
||||||
type.attributes.mapNotNull { definition ->
|
fieldAttributePayload(type.attributes, values)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun modernHasPermission(context: Context, permission: String): Boolean =
|
private fun modernHasPermission(context: Context, permission: String): Boolean =
|
||||||
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ class DynamicFieldFlowContractTest {
|
|||||||
@Test
|
@Test
|
||||||
fun fastSubinstallationFlowKeepsParentClassificationAndCaptureSteps() {
|
fun fastSubinstallationFlowKeepsParentClassificationAndCaptureSteps() {
|
||||||
assertTrue(source.contains("Elegí la Instalación padre"))
|
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("Guardar y tomar foto"))
|
||||||
assertTrue(source.contains("model.loadFieldTypes(item.id)"))
|
assertTrue(source.contains("model.loadFieldTypes(item.id)"))
|
||||||
assertTrue(source.contains("parentId = 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_") })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(32, BuildConfig.VERSION_CODE)
|
assertEquals(33, BuildConfig.VERSION_CODE)
|
||||||
assertEquals("0.19.4-debug", BuildConfig.VERSION_NAME)
|
assertEquals("0.19.5-debug", BuildConfig.VERSION_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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', () => {
|
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 = 32/);
|
assert.match(gradle, /versionCode = 33/);
|
||||||
assert.match(gradle, /versionName = "0\.19\.4"/);
|
assert.match(gradle, /versionName = "0\.19\.5"/);
|
||||||
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"/);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user