diff --git a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt index 2d1581c..b353aed 100644 --- a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt +++ b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt @@ -2746,16 +2746,14 @@ private fun RouteStageScreen( label = { Text(weightLabel) }, trailingIcon = { Text( - "t", + "t / kg", color = TppTheme.colors.muted, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold, ) }, supportingText = { - if (weightBlocker != null) { - Text(weightBlocker) - } + Text(weightBlocker ?: routeWeightInputHelpText(state.routeStageWeightText)) }, isError = weightBlocker != null, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), diff --git a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt index 1f0e309..db050c1 100644 --- a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt +++ b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt @@ -1,11 +1,15 @@ package pl.firmatpp.kierowca.ui import java.io.File +import java.math.BigDecimal +import java.math.RoundingMode import java.time.DayOfWeek import java.time.LocalDate import java.time.OffsetDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit +import java.util.Locale +import kotlin.math.abs import pl.firmatpp.kierowca.data.model.DriverRouteDto import pl.firmatpp.kierowca.data.model.DispatchSheetReminderDto import pl.firmatpp.kierowca.data.model.RoutePhotoDto @@ -15,6 +19,8 @@ import pl.firmatpp.kierowca.data.upload.RouteActionStatus import pl.firmatpp.kierowca.data.upload.RouteActionType private val shortDateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM") +private val routeWeightKilogramInputThreshold = BigDecimal("1000") +private const val routeWeightMaxTons = 999.999 enum class RouteFlowStepState { Todo, @@ -150,6 +156,31 @@ fun routeStageRequirementIsVisible(requirement: String): Boolean = fun routeStageRequirementIsRequired(requirement: String): Boolean = normalizeRouteStageRequirement(requirement) == RouteStageRequirement.Required +fun parseRouteWeightTons(weightText: String): Double? { + val enteredWeight = weightText.trim().replace(',', '.').toBigDecimalOrNull() ?: return null + val tons = if (enteredWeight.abs() >= routeWeightKilogramInputThreshold) { + enteredWeight.movePointLeft(3) + } else { + enteredWeight + } + + return tons.setScale(3, RoundingMode.HALF_UP).toDouble() +} + +fun routeWeightInputHelpText(weightText: String): String { + val enteredWeight = weightText.trim().replace(',', '.').toDoubleOrNull() + val tons = parseRouteWeightTons(weightText) + val enteredAsKilograms = enteredWeight != null && + abs(enteredWeight) >= routeWeightKilogramInputThreshold.toDouble() && + tons != null + + return if (enteredAsKilograms) { + "Wpisano kilogramy — zapiszemy ${String.format(Locale("pl", "PL"), "%.3f", tons)} t." + } else { + "Wpisz tony (np. 23,4). Wartości od 1000 rozpoznajemy jako kilogramy (np. 23400)." + } +} + fun canSubmitRouteStageForm( weightText: String, serverPhotoCount: Int, @@ -188,13 +219,14 @@ fun loadingWeightSubmitBlocker( photoRequirement: String = RouteStageRequirement.Required, ): String? { val trimmed = weightText.trim() - val normalizedWeight = trimmed.replace(',', '.').toDoubleOrNull() + val normalizedWeight = parseRouteWeightTons(trimmed) val weightVisible = routeStageRequirementIsVisible(weightRequirement) return when { weightVisible && routeStageRequirementIsRequired(weightRequirement) && trimmed.isBlank() -> "Podaj tonaż." weightVisible && trimmed.isNotBlank() && normalizedWeight == null -> "Podaj poprawny tonaż." weightVisible && normalizedWeight != null && normalizedWeight <= 0.0 -> "Tonaż musi być większy od zera." + weightVisible && normalizedWeight != null && normalizedWeight > routeWeightMaxTons -> "Tonaż nie może przekraczać 999,999 t." loadingPhotosSubmitBlocker(serverPhotoCount, localUploadCount, photoRequirement) != null -> "Dodaj co najmniej jedno zdjęcie załadunku." else -> null } @@ -208,13 +240,14 @@ fun routeStageSubmitBlocker( photoRequirement: String = RouteStageRequirement.Required, ): String? { val trimmed = weightText.trim() - val normalizedWeight = trimmed.replace(',', '.').toDoubleOrNull() + val normalizedWeight = parseRouteWeightTons(trimmed) val weightVisible = routeStageRequirementIsVisible(weightRequirement) return when { weightVisible && routeStageRequirementIsRequired(weightRequirement) && trimmed.isBlank() -> "Podaj tonaż." weightVisible && trimmed.isNotBlank() && normalizedWeight == null -> "Podaj poprawny tonaż." weightVisible && normalizedWeight != null && normalizedWeight <= 0.0 -> "Tonaż musi być większy od zera." + weightVisible && normalizedWeight != null && normalizedWeight > routeWeightMaxTons -> "Tonaż nie może przekraczać 999,999 t." routeStageRequirementIsRequired(photoRequirement) && visiblePhotoAttachmentCount(serverPhotoCount, localUploadCount) <= 0 -> "Dodaj co najmniej jedno zdjęcie etapu." else -> null } diff --git a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverViewModel.kt b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverViewModel.kt index 91d7cf1..fa8d3f7 100644 --- a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverViewModel.kt +++ b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverViewModel.kt @@ -1103,7 +1103,7 @@ class DriverViewModel(application: Application) : AndroidViewModel(application) val photoRequirement = if (stage == "loading") snapshot.loadingPhotoRequirement else snapshot.unloadingPhotoRequirement val weightRequirement = if (stage == "loading") snapshot.loadingWeightRequirement else snapshot.unloadingWeightRequirement val weight = if (routeStageRequirementIsVisible(weightRequirement)) { - snapshot.routeStageWeightText.trim().replace(',', '.').toDoubleOrNull() + parseRouteWeightTons(snapshot.routeStageWeightText) } else { null } diff --git a/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt b/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt index d554c08..3baeed8 100644 --- a/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt +++ b/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt @@ -306,11 +306,20 @@ class DriverUiRulesTest { fun routeStageFormRequiresPositiveWeightAndAtLeastOnePhotoOrQueuedUpload() { assertTrue(canSubmitRouteStageForm(weightText = "12,5", serverPhotoCount = 1, localUploadCount = 0)) assertTrue(canSubmitRouteStageForm(weightText = "12.5", serverPhotoCount = 0, localUploadCount = 1)) + assertTrue(canSubmitRouteStageForm(weightText = "23400", serverPhotoCount = 1, localUploadCount = 0)) assertFalse(canSubmitRouteStageForm(weightText = "", serverPhotoCount = 1, localUploadCount = 0)) assertFalse(canSubmitRouteStageForm(weightText = "0", serverPhotoCount = 1, localUploadCount = 0)) assertFalse(canSubmitRouteStageForm(weightText = "12.5", serverPhotoCount = 0, localUploadCount = 0)) } + @Test + fun parsesRouteWeightsEnteredInTonsOrKilograms() { + assertEquals(23.4, parseRouteWeightTons("23,4") ?: 0.0, 0.0) + assertEquals(23.4, parseRouteWeightTons("23400") ?: 0.0, 0.0) + assertEquals(23.401, parseRouteWeightTons("23400,5") ?: 0.0, 0.0) + assertEquals("Wpisano kilogramy — zapiszemy 23,400 t.", routeWeightInputHelpText("23400")) + } + @Test fun filtersRoutePhotosByStageWithOtherFallback() { val photos = listOf(