fix(android): scope mobile inspections to yacimiento
DH V2 CI / API · typecheck, tests, build (push) Successful in 5m16s
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Failing after 6m13s
DH V2 CI / WEB · typecheck, build (push) Successful in 4m52s
DH V2 CI / Docker / migrations / production images (push) Skipped
DH V2 CI / Promote verified main to deploy (push) Skipped
DH V2 CI / API · typecheck, tests, build (push) Successful in 5m16s
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Failing after 6m13s
DH V2 CI / WEB · typecheck, build (push) Successful in 4m52s
DH V2 CI / Docker / migrations / production images (push) Skipped
DH V2 CI / Promote verified main to deploy (push) Skipped
This commit is contained in:
+37
-11
@@ -25,22 +25,36 @@ data class MobilePlanningAssetResponse(
|
||||
)
|
||||
|
||||
data class OpenMobileInspectionRequest(
|
||||
val departmentId: String,
|
||||
val operationalAreaId: String,
|
||||
val scopeAssetId: String,
|
||||
val operatorCompanyId: String,
|
||||
)
|
||||
|
||||
private interface MobileInspectionOpenApi {
|
||||
@GET("inspection-visits/mobile/planning-context/areas")
|
||||
suspend fun areas(
|
||||
@GET("inspection-visits/mobile/planning-context/departments")
|
||||
suspend fun departments(
|
||||
@Header("Authorization") authorization: String,
|
||||
): MobilePlanningAssetResponse
|
||||
|
||||
@GET("inspection-visits/mobile/planning-context/areas/{areaId}/operators")
|
||||
suspend fun operators(
|
||||
@GET("inspection-visits/mobile/planning-context/departments/{departmentId}/areas")
|
||||
suspend fun areas(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Path("departmentId") departmentId: String,
|
||||
): MobilePlanningAssetResponse
|
||||
|
||||
@GET("inspection-visits/mobile/planning-context/areas/{areaId}/yacimientos")
|
||||
suspend fun yacimientos(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Path("areaId") areaId: String,
|
||||
): MobilePlanningAssetResponse
|
||||
|
||||
@GET("inspection-visits/mobile/planning-context/yacimientos/{yacimientoId}/operators")
|
||||
suspend fun operators(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Path("yacimientoId") yacimientoId: String,
|
||||
): MobilePlanningAssetResponse
|
||||
|
||||
@POST("inspection-visits/mobile/open")
|
||||
suspend fun open(
|
||||
@Header("Authorization") authorization: String,
|
||||
@@ -61,18 +75,31 @@ class MobileInspectionOpenRepository(context: Context) {
|
||||
.build()
|
||||
.create(MobileInspectionOpenApi::class.java)
|
||||
|
||||
suspend fun areas(): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.areas("Bearer ${session.accessToken}")
|
||||
suspend fun departments(): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.departments("Bearer ${session.accessToken}")
|
||||
}
|
||||
|
||||
suspend fun operators(areaId: String): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.operators("Bearer ${session.accessToken}", areaId)
|
||||
suspend fun areas(departmentId: String): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.areas("Bearer ${session.accessToken}", departmentId)
|
||||
}
|
||||
|
||||
suspend fun open(areaId: String, companyId: String): VisitDetail = authorized { session ->
|
||||
suspend fun yacimientos(areaId: String): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.yacimientos("Bearer ${session.accessToken}", areaId)
|
||||
}
|
||||
|
||||
suspend fun operators(yacimientoId: String): MobilePlanningAssetResponse = authorized { session ->
|
||||
api.operators("Bearer ${session.accessToken}", yacimientoId)
|
||||
}
|
||||
|
||||
suspend fun open(
|
||||
departmentId: String,
|
||||
areaId: String,
|
||||
yacimientoId: String,
|
||||
companyId: String,
|
||||
): VisitDetail = authorized { session ->
|
||||
api.open(
|
||||
"Bearer ${session.accessToken}",
|
||||
OpenMobileInspectionRequest(areaId, companyId),
|
||||
OpenMobileInspectionRequest(departmentId, areaId, yacimientoId, companyId),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,5 +116,4 @@ class MobileInspectionOpenRepository(context: Context) {
|
||||
|
||||
private suspend fun refresh(previous: StoredSession): StoredSession =
|
||||
MobileSessionCoordinator.refresh(previous, store::load, store::save, store::clear, api::refresh)
|
||||
|
||||
}
|
||||
|
||||
@@ -47,19 +47,41 @@ fun MobileHomeScreen(model: MainViewModel) {
|
||||
val openRepository = remember(context) { MobileInspectionOpenRepository(context) }
|
||||
|
||||
var showOpen by rememberSaveable { mutableStateOf(false) }
|
||||
var departments by remember { mutableStateOf<List<MobilePlanningAsset>>(emptyList()) }
|
||||
var areas by remember { mutableStateOf<List<MobilePlanningAsset>>(emptyList()) }
|
||||
var yacimientos by remember { mutableStateOf<List<MobilePlanningAsset>>(emptyList()) }
|
||||
var operators by remember { mutableStateOf<List<MobilePlanningAsset>>(emptyList()) }
|
||||
var selectedDepartmentId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var selectedAreaId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var selectedYacimientoId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var selectedCompanyId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var loadingContext by remember { mutableStateOf(false) }
|
||||
var opening by remember { mutableStateOf(false) }
|
||||
var localError by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
fun loadAreas() {
|
||||
fun loadDepartments() {
|
||||
scope.launch {
|
||||
loadingContext = true
|
||||
localError = null
|
||||
runCatching { openRepository.areas().data }
|
||||
runCatching { openRepository.departments().data }
|
||||
.onSuccess { departments = it }
|
||||
.onFailure { localError = DhRepository.humanError(it) }
|
||||
loadingContext = false
|
||||
}
|
||||
}
|
||||
|
||||
fun chooseDepartment(departmentId: String) {
|
||||
selectedDepartmentId = departmentId
|
||||
selectedAreaId = null
|
||||
selectedYacimientoId = null
|
||||
selectedCompanyId = null
|
||||
areas = emptyList()
|
||||
yacimientos = emptyList()
|
||||
operators = emptyList()
|
||||
scope.launch {
|
||||
loadingContext = true
|
||||
localError = null
|
||||
runCatching { openRepository.areas(departmentId).data }
|
||||
.onSuccess { areas = it }
|
||||
.onFailure { localError = DhRepository.humanError(it) }
|
||||
loadingContext = false
|
||||
@@ -68,20 +90,39 @@ fun MobileHomeScreen(model: MainViewModel) {
|
||||
|
||||
fun chooseArea(areaId: String) {
|
||||
selectedAreaId = areaId
|
||||
selectedYacimientoId = null
|
||||
selectedCompanyId = null
|
||||
yacimientos = emptyList()
|
||||
operators = emptyList()
|
||||
scope.launch {
|
||||
loadingContext = true
|
||||
localError = null
|
||||
runCatching { openRepository.yacimientos(areaId).data }
|
||||
.onSuccess { yacimientos = it }
|
||||
.onFailure { localError = DhRepository.humanError(it) }
|
||||
loadingContext = false
|
||||
}
|
||||
}
|
||||
|
||||
fun chooseYacimiento(yacimientoId: String) {
|
||||
selectedYacimientoId = yacimientoId
|
||||
selectedCompanyId = null
|
||||
operators = emptyList()
|
||||
scope.launch {
|
||||
loadingContext = true
|
||||
localError = null
|
||||
runCatching { openRepository.operators(areaId).data }
|
||||
.onSuccess { operators = it }
|
||||
runCatching { openRepository.operators(yacimientoId).data }
|
||||
.onSuccess {
|
||||
operators = it
|
||||
if (it.size == 1) selectedCompanyId = it.first().id
|
||||
}
|
||||
.onFailure { localError = DhRepository.humanError(it) }
|
||||
loadingContext = false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(showOpen) {
|
||||
if (showOpen && areas.isEmpty()) loadAreas()
|
||||
if (showOpen && departments.isEmpty()) loadDepartments()
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxSize().padding(top = 28.dp)) {
|
||||
@@ -142,27 +183,57 @@ fun MobileHomeScreen(model: MainViewModel) {
|
||||
) {
|
||||
Text("Abrir inspección en campo", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
Text(
|
||||
"Elegí el Área y la Operadora vigente. La inspección se crea autoasignada a vos y queda iniciada con la fecha y hora del servidor.",
|
||||
"Definí el contexto completo antes de abrir el Acta: Departamento → Área → Yacimiento → Operadora. La inspección queda autoasignada a vos y se inicia con la hora del servidor.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
|
||||
Text("1. Área", fontWeight = FontWeight.SemiBold)
|
||||
if (loadingContext && areas.isEmpty()) CircularProgressIndicator()
|
||||
Text("1. Departamento", fontWeight = FontWeight.SemiBold)
|
||||
if (loadingContext && departments.isEmpty()) CircularProgressIndicator()
|
||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
items(areas, key = { it.id }) { area ->
|
||||
items(departments, key = { it.id }) { department ->
|
||||
AssistChip(
|
||||
onClick = { chooseArea(area.id) },
|
||||
label = { Text(if (area.id == selectedAreaId) "✓ ${area.name}" else area.name) },
|
||||
onClick = { chooseDepartment(department.id) },
|
||||
label = { Text(if (department.id == selectedDepartmentId) "✓ ${department.name}" else department.name) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedDepartmentId != null) {
|
||||
Text("2. Área", fontWeight = FontWeight.SemiBold)
|
||||
if (loadingContext && areas.isEmpty()) CircularProgressIndicator()
|
||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
items(areas, key = { it.id }) { area ->
|
||||
AssistChip(
|
||||
onClick = { chooseArea(area.id) },
|
||||
label = { Text(if (area.id == selectedAreaId) "✓ ${area.name}" else area.name) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedAreaId != null) {
|
||||
Text("2. Operadora", fontWeight = FontWeight.SemiBold)
|
||||
Text("3. Yacimiento", fontWeight = FontWeight.SemiBold)
|
||||
if (loadingContext && yacimientos.isEmpty()) {
|
||||
CircularProgressIndicator()
|
||||
} else if (yacimientos.isEmpty()) {
|
||||
Text("No hay Yacimientos disponibles para el Área seleccionada.", color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
items(yacimientos, key = { it.id }) { yacimiento ->
|
||||
AssistChip(
|
||||
onClick = { chooseYacimiento(yacimiento.id) },
|
||||
label = { Text(if (yacimiento.id == selectedYacimientoId) "✓ ${yacimiento.name}" else yacimiento.name) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedYacimientoId != null) {
|
||||
Text("4. Operadora", fontWeight = FontWeight.SemiBold)
|
||||
if (loadingContext && operators.isEmpty()) {
|
||||
CircularProgressIndicator()
|
||||
} else if (operators.isEmpty()) {
|
||||
Text("No hay una Operadora vigente para el Área seleccionada.", color = MaterialTheme.colorScheme.error)
|
||||
Text("El Yacimiento no tiene una Operadora válida configurada.", color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
items(operators, key = { it.id }) { company ->
|
||||
@@ -176,12 +247,14 @@ fun MobileHomeScreen(model: MainViewModel) {
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
val departmentId = selectedDepartmentId ?: return@Button
|
||||
val areaId = selectedAreaId ?: return@Button
|
||||
val yacimientoId = selectedYacimientoId ?: return@Button
|
||||
val companyId = selectedCompanyId ?: return@Button
|
||||
scope.launch {
|
||||
opening = true
|
||||
localError = null
|
||||
runCatching { openRepository.open(areaId, companyId) }
|
||||
runCatching { openRepository.open(departmentId, areaId, yacimientoId, companyId) }
|
||||
.onSuccess { opened ->
|
||||
showOpen = false
|
||||
model.openVisit(opened.id)
|
||||
@@ -191,9 +264,9 @@ fun MobileHomeScreen(model: MainViewModel) {
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = selectedAreaId != null && selectedCompanyId != null && !opening && !loadingContext,
|
||||
enabled = selectedDepartmentId != null && selectedAreaId != null && selectedYacimientoId != null && selectedCompanyId != null && !opening && !loadingContext,
|
||||
) {
|
||||
Text(if (opening) "Abriendo…" else "Abrir inspección ahora")
|
||||
Text(if (opening) "Abriendo…" else "Abrir inspección y continuar al Acta")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,6 +301,7 @@ private fun MobileVisitCard(visit: VisitSummary, onOpen: () -> Unit) {
|
||||
Text(visit.code, fontWeight = FontWeight.Bold)
|
||||
Text(visitStatusLabelEs(visit.status))
|
||||
}
|
||||
Text(visit.scopeAsset?.name ?: "Yacimiento sin definir", fontWeight = FontWeight.SemiBold)
|
||||
Text(visit.operatorCompany?.name ?: "Operadora sin definir")
|
||||
Text(visit.operationalArea?.name ?: "Área sin definir", style = MaterialTheme.typography.bodySmall)
|
||||
visit.plannedStartAt?.let {
|
||||
|
||||
+1
-1
@@ -261,7 +261,7 @@ fun ModernMobileActsScreen(
|
||||
Column(Modifier.padding(18.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Text("Hallazgos", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
|
||||
Text(
|
||||
"Elegí una Instalación o Subinstalación existente, o creala en campo si todavía no está registrada.",
|
||||
"Elegí el Yacimiento, una Instalación o una Subinstalación. El Hallazgo siempre queda dentro del Yacimiento de esta Acta.",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Button(onClick = onGoInventory, modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
@@ -107,7 +107,9 @@ private data class ModernGeoSnapshot(
|
||||
@Composable
|
||||
fun ModernVisitRoot(model: MainViewModel) {
|
||||
val visit = model.visit ?: return
|
||||
var screenName by rememberSaveable(visit.id) { mutableStateOf(ModernVisitScreen.OVERVIEW.name) }
|
||||
var screenName by rememberSaveable(visit.id) {
|
||||
mutableStateOf(if (visit.status == "IN_PROGRESS") ModernVisitScreen.ACTS.name else ModernVisitScreen.OVERVIEW.name)
|
||||
}
|
||||
val screen = runCatching { ModernVisitScreen.valueOf(screenName) }.getOrDefault(ModernVisitScreen.OVERVIEW)
|
||||
|
||||
when (screen) {
|
||||
@@ -938,7 +940,7 @@ private fun ModernInventoryBrowse(
|
||||
OutlinedTextField(
|
||||
value = search,
|
||||
onValueChange = onSearchChange,
|
||||
label = { Text("Buscar Instalación o Subinstalación") },
|
||||
label = { Text("Buscar Yacimiento, Instalación o Subinstalación") },
|
||||
placeholder = { Text("Nombre, código o dato técnico") },
|
||||
leadingIcon = { Icon(Icons.Filled.Search, null) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
|
||||
@@ -8,8 +8,8 @@ class ReleaseMetadataTest {
|
||||
@Test
|
||||
fun debugBuildKeepsSeparateApplicationIdentity() {
|
||||
assertEquals("com.korexlabs.dhinspeccion.debug", BuildConfig.APPLICATION_ID)
|
||||
assertEquals(40, BuildConfig.VERSION_CODE)
|
||||
assertEquals("0.19.12-debug", BuildConfig.VERSION_NAME)
|
||||
assertEquals(41, BuildConfig.VERSION_CODE)
|
||||
assertEquals("0.19.13-debug", BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user