From 4fc080f14645fcc93beaf928de7a4b75eb078847 Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Fri, 30 Jan 2026 11:40:49 +0530 Subject: [PATCH] admins can delete cash payments --- .../com/android/trisolarispms/MainActivity.kt | 4 ++ .../trisolarispms/data/api/BookingApi.kt | 8 +++ .../ui/payment/BookingPaymentsScreen.kt | 70 ++++++++++++++++--- .../ui/payment/BookingPaymentsViewModel.kt | 37 ++++++++++ .../trisolarispms/ui/payu/PayuQrScreen.kt | 5 ++ .../trisolarispms/ui/payu/PayuQrViewModel.kt | 4 ++ .../ui/roomstay/ActiveRoomStaysScreen.kt | 15 +--- 7 files changed, 120 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/android/trisolarispms/MainActivity.kt b/app/src/main/java/com/android/trisolarispms/MainActivity.kt index f461c2a..1c0fed5 100644 --- a/app/src/main/java/com/android/trisolarispms/MainActivity.kt +++ b/app/src/main/java/com/android/trisolarispms/MainActivity.kt @@ -91,6 +91,9 @@ class MainActivity : ComponentActivity() { it == "ADMIN" || it == "MANAGER" } == true } + val canDeleteCashPayment: (String) -> Boolean = { propertyId -> + state.isSuperAdmin || state.propertyRoles[propertyId]?.contains("ADMIN") == true + } BackHandler(enabled = currentRoute != AppRoute.Home) { when (currentRoute) { @@ -486,6 +489,7 @@ class MainActivity : ComponentActivity() { propertyId = currentRoute.propertyId, bookingId = currentRoute.bookingId, canAddCash = canManagePayuSettings(currentRoute.propertyId), + canDeleteCash = canDeleteCashPayment(currentRoute.propertyId), onBack = { route.value = AppRoute.BookingDetailsTabs( currentRoute.propertyId, diff --git a/app/src/main/java/com/android/trisolarispms/data/api/BookingApi.kt b/app/src/main/java/com/android/trisolarispms/data/api/BookingApi.kt index 046a766..1db3db2 100644 --- a/app/src/main/java/com/android/trisolarispms/data/api/BookingApi.kt +++ b/app/src/main/java/com/android/trisolarispms/data/api/BookingApi.kt @@ -23,6 +23,7 @@ import com.android.trisolarispms.data.api.model.PaymentCreateRequest import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.DELETE import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -128,4 +129,11 @@ interface BookingApi { @Path("bookingId") bookingId: String, @Body body: PaymentCreateRequest ): Response + + @DELETE("properties/{propertyId}/bookings/{bookingId}/payments/{paymentId}") + suspend fun deletePayment( + @Path("propertyId") propertyId: String, + @Path("bookingId") bookingId: String, + @Path("paymentId") paymentId: String + ): Response } diff --git a/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsScreen.kt index 33a7c9d..df47be4 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsScreen.kt @@ -2,6 +2,7 @@ package com.android.trisolarispms.ui.payment import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -12,6 +13,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults @@ -31,6 +33,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp @@ -45,6 +48,7 @@ fun BookingPaymentsScreen( propertyId: String, bookingId: String, canAddCash: Boolean, + canDeleteCash: Boolean, onBack: () -> Unit, viewModel: BookingPaymentsViewModel = viewModel() ) { @@ -87,6 +91,7 @@ fun BookingPaymentsScreen( onClick = { val amount = amountInput.value.toLongOrNull() ?: 0L viewModel.addCashPayment(propertyId, bookingId, amount) + amountInput.value = "" }, enabled = !state.isLoading, modifier = Modifier.fillMaxWidth() @@ -108,10 +113,18 @@ fun BookingPaymentsScreen( } LazyColumn( verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxWidth() + .weight(1f) ) { items(state.payments) { payment -> - PaymentCard(payment = payment) + PaymentCard( + payment = payment, + canDeleteCash = canDeleteCash, + onDelete = { paymentId -> + viewModel.deleteCashPayment(propertyId, bookingId, paymentId) + } + ) } } } @@ -119,7 +132,11 @@ fun BookingPaymentsScreen( } @Composable -private fun PaymentCard(payment: PaymentDto) { +private fun PaymentCard( + payment: PaymentDto, + canDeleteCash: Boolean, + onDelete: (String) -> Unit +) { val date = payment.receivedAt?.let { runCatching { OffsetDateTime.parse(it) }.getOrNull() } @@ -136,19 +153,52 @@ private fun PaymentCard(payment: PaymentDto) { modifier = Modifier.fillMaxWidth() ) { Column(modifier = Modifier.padding(12.dp)) { - val amountText = buildString { - append(payment.amount?.toString() ?: "-") - payment.currency?.let { append(" $it") } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + val amountText = buildString { + append(payment.amount?.toString() ?: "-") + payment.currency?.let { append(" $it") } + } + Text(text = amountText, style = MaterialTheme.typography.titleMedium) + if (canDeleteCash && isCash && !payment.id.isNullOrBlank()) { + IconButton(onClick = { onDelete(payment.id) }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete cash payment" + ) + } + } } - Text(text = amountText, style = MaterialTheme.typography.titleMedium) + val methodColor = if (isCash) { + MaterialTheme.colorScheme.tertiary + } else { + MaterialTheme.colorScheme.secondary + } + val upiColor = MaterialTheme.colorScheme.primary + val payerColor = MaterialTheme.colorScheme.secondary payment.method?.let { - Text(text = "Method: $it", style = MaterialTheme.typography.bodySmall) + Text( + text = "Method: $it", + style = MaterialTheme.typography.bodySmall, + color = methodColor + ) } payment.payerVpa?.takeIf { it.isNotBlank() }?.let { - Text(text = "UPI: $it", style = MaterialTheme.typography.bodySmall) + Text( + text = "UPI: $it", + style = MaterialTheme.typography.bodySmall, + color = upiColor + ) } payment.payerName?.takeIf { it.isNotBlank() }?.let { - Text(text = "Payer: $it", style = MaterialTheme.typography.bodySmall) + Text( + text = "Payer: $it", + style = MaterialTheme.typography.bodySmall, + color = payerColor + ) } dateText?.let { Text(text = "Received: $it", style = MaterialTheme.typography.bodySmall) diff --git a/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsViewModel.kt index 51ac3f2..5d307e4 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/payment/BookingPaymentsViewModel.kt @@ -87,4 +87,41 @@ class BookingPaymentsViewModel : ViewModel() { } } } + + fun deleteCashPayment(propertyId: String, bookingId: String, paymentId: String) { + viewModelScope.launch { + _state.update { it.copy(isLoading = true, error = null) } + try { + val api = ApiClient.create() + val response = api.deletePayment( + propertyId = propertyId, + bookingId = bookingId, + paymentId = paymentId + ) + if (response.isSuccessful) { + _state.update { current -> + current.copy( + isLoading = false, + payments = current.payments.filterNot { it.id == paymentId }, + error = null + ) + } + } else { + _state.update { + it.copy( + isLoading = false, + error = "Delete failed: ${response.code()}" + ) + } + } + } catch (e: Exception) { + _state.update { + it.copy( + isLoading = false, + error = e.localizedMessage ?: "Delete failed" + ) + } + } + } + } } diff --git a/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrScreen.kt index 624b398..4f6ac73 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrScreen.kt @@ -32,6 +32,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap @@ -59,6 +60,10 @@ fun PayuQrScreen( val clipboard = LocalClipboardManager.current val context = LocalContext.current + DisposableEffect(Unit) { + onDispose { viewModel.reset() } + } + LaunchedEffect(pendingAmount) { viewModel.setInitialAmount(pendingAmount) } diff --git a/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrViewModel.kt index f38fd22..c4dde9a 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/payu/PayuQrViewModel.kt @@ -20,6 +20,10 @@ class PayuQrViewModel : ViewModel() { ) val state: StateFlow = _state + fun reset() { + _state.value = PayuQrState(deviceInfo = buildDeviceInfo()) + } + fun onAmountChange(value: String) { val digits = value.filter { it.isDigit() } _state.update { it.copy(amountInput = digits, error = null) } diff --git a/app/src/main/java/com/android/trisolarispms/ui/roomstay/ActiveRoomStaysScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/roomstay/ActiveRoomStaysScreen.kt index de6f8ee..a590cb8 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/roomstay/ActiveRoomStaysScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/roomstay/ActiveRoomStaysScreen.kt @@ -126,9 +126,7 @@ fun ActiveRoomStaysScreen( items(state.checkedInBookings) { booking -> CheckedInBookingCard( booking = booking, - onClick = { selectedBooking.value = booking }, - onLongClick = { onOpenBookingDetails(booking) } - ) + onClick = { onOpenBookingDetails(booking) }) } } Spacer(modifier = Modifier.height(16.dp)) @@ -152,12 +150,6 @@ fun ActiveRoomStaysScreen( ) { Text("Manage room stay") } - TextButton(onClick = { selectedBooking.value = null }) { - Text("Balance") - } - TextButton(onClick = { selectedBooking.value = null }) { - Text("Add photos") - } TextButton(onClick = { selectedBooking.value = null }) { Text("Checkout") } @@ -173,14 +165,11 @@ fun ActiveRoomStaysScreen( @Composable private fun CheckedInBookingCard( booking: BookingListItem, - onClick: () -> Unit, - onLongClick: () -> Unit -) { + onClick: () -> Unit) { Card( colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), modifier = Modifier.combinedClickable( onClick = onClick, - onLongClick = onLongClick ) ) { Column(modifier = Modifier.padding(12.dp)) {