diff --git a/clients/terminal-android/app/src/debug/AndroidManifest.xml b/clients/terminal-android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..4ca45d05 --- /dev/null +++ b/clients/terminal-android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/clients/terminal-android/app/src/debug/res/xml/network_security_config.xml b/clients/terminal-android/app/src/debug/res/xml/network_security_config.xml new file mode 100644 index 00000000..92185cb8 --- /dev/null +++ b/clients/terminal-android/app/src/debug/res/xml/network_security_config.xml @@ -0,0 +1,17 @@ + + + + + 10.0.2.2 + localhost + 127.0.0.1 + + diff --git a/clients/terminal-android/app/src/main/java/lu/rewardflow/terminal/ui/terminal/TerminalViewModel.kt b/clients/terminal-android/app/src/main/java/lu/rewardflow/terminal/ui/terminal/TerminalViewModel.kt index a67d3f03..04d09e49 100644 --- a/clients/terminal-android/app/src/main/java/lu/rewardflow/terminal/ui/terminal/TerminalViewModel.kt +++ b/clients/terminal-android/app/src/main/java/lu/rewardflow/terminal/ui/terminal/TerminalViewModel.kt @@ -25,6 +25,7 @@ import lu.rewardflow.terminal.data.model.TransactionItem import lu.rewardflow.terminal.data.network.NetworkMonitor import lu.rewardflow.terminal.data.repository.CategoryRepository import lu.rewardflow.terminal.data.repository.DeviceConfigRepository +import retrofit2.HttpException import javax.inject.Inject /** @@ -294,13 +295,34 @@ class TerminalViewModel @Inject constructor( _state.value = _state.value.copy( actionInProgress = false, actionResult = ActionResult.Failure( - result.exceptionOrNull()?.message ?: "Operation failed" + readableErrorMessage(result.exceptionOrNull()) ), ) } } } + /** Extract a human-readable message from a Retrofit/HttpException. + * + * Retrofit's default `message` is the HTTP status text ("Bad Request"), + * which hides the JSON ``{message, error_code, details}`` body the + * backend returns. Read the body and surface its message instead so + * cashiers see "Staff PIN is required" or "Card not found" rather + * than the generic 400. */ + private fun readableErrorMessage(t: Throwable?): String { + if (t is HttpException) { + val body = runCatching { t.response()?.errorBody()?.string() }.getOrNull() + if (!body.isNullOrBlank()) { + // Cheap JSON peek — avoid pulling in a full adapter for one field. + val match = "\"message\"\\s*:\\s*\"([^\"]+)\"".toRegex().find(body) + if (match != null) return match.groupValues[1] + return body.take(200) + } + return "HTTP ${t.code()}" + } + return t?.message ?: "Operation failed" + } + fun refreshCurrentCustomer() { val current = _state.value.customer ?: return viewModelScope.launch {