forked from LibreMV/GridFlow
Pay any bill
This commit is contained in:
258
app/src/main/java/sh/sar/gridflow/PayWithoutAccountActivity.kt
Normal file
258
app/src/main/java/sh/sar/gridflow/PayWithoutAccountActivity.kt
Normal file
@@ -0,0 +1,258 @@
|
||||
package sh.sar.gridflow
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import sh.sar.gridflow.data.BillLookupResponse
|
||||
import sh.sar.gridflow.databinding.ActivityPayWithoutAccountBinding
|
||||
import sh.sar.gridflow.network.ApiResult
|
||||
import sh.sar.gridflow.network.FenakaApiService
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class PayWithoutAccountActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityPayWithoutAccountBinding
|
||||
private lateinit var apiService: FenakaApiService
|
||||
private var currentBill: BillLookupResponse? = null
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PayWithoutAccountActivity"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Force system theme (follows device dark mode setting)
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
|
||||
Log.d(TAG, "PayWithoutAccountActivity onCreate")
|
||||
|
||||
binding = ActivityPayWithoutAccountBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
apiService = FenakaApiService()
|
||||
setupClickListeners()
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
binding.btnContinue.setOnClickListener {
|
||||
handleContinue()
|
||||
}
|
||||
|
||||
binding.btnPay.setOnClickListener {
|
||||
handlePayment()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleContinue() {
|
||||
// Check if we're showing the card - if so, hide it and show input fields
|
||||
if (binding.billDetailsCard.visibility == View.VISIBLE) {
|
||||
Log.d(TAG, "Hiding bill details card and showing input fields")
|
||||
showInputFields()
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, perform bill search
|
||||
val billNumber = binding.etBillNumber.text.toString().trim()
|
||||
val subscriptionNumber = binding.etSubscriptionNumber.text.toString().trim()
|
||||
|
||||
Log.d(TAG, "handleContinue called with bill: $billNumber, subscription: $subscriptionNumber")
|
||||
|
||||
if (validateInput(billNumber, subscriptionNumber)) {
|
||||
Log.d(TAG, "Input validation passed, fetching bill details")
|
||||
fetchBillDetails(billNumber, subscriptionNumber)
|
||||
} else {
|
||||
Log.d(TAG, "Input validation failed")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showInputFields() {
|
||||
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||
binding.billDetailsCard.visibility = View.GONE
|
||||
currentBill = null
|
||||
// Update button text to Continue when showing input fields
|
||||
binding.btnContinue.text = "Continue"
|
||||
}
|
||||
|
||||
private fun fetchBillDetails(billNumber: String, subscriptionNumber: String) {
|
||||
setLoading(true)
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
when (val result = apiService.findBill(billNumber, subscriptionNumber)) {
|
||||
is ApiResult.Success -> {
|
||||
Log.d(TAG, "Bill fetch successful: ${result.data.billNumber}")
|
||||
currentBill = result.data
|
||||
setLoading(false)
|
||||
showBillDetails(result.data)
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
if (result.code == 404) {
|
||||
Log.d(TAG, "404 received, trying with swapped parameters")
|
||||
// Try again with swapped parameters
|
||||
when (val retryResult = apiService.findBill(subscriptionNumber, billNumber)) {
|
||||
is ApiResult.Success -> {
|
||||
Log.d(TAG, "Retry successful with swapped parameters: ${retryResult.data.billNumber}")
|
||||
currentBill = retryResult.data
|
||||
setLoading(false)
|
||||
showBillDetails(retryResult.data)
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
Log.d(TAG, "Retry also failed: ${retryResult.message} (code: ${retryResult.code})")
|
||||
setLoading(false)
|
||||
showError(retryResult.message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Bill fetch failed: ${result.message} (code: ${result.code})")
|
||||
setLoading(false)
|
||||
showError(result.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception in fetchBillDetails", e)
|
||||
setLoading(false)
|
||||
showError("Failed to fetch bill details: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePayment() {
|
||||
currentBill?.let { bill ->
|
||||
if (bill.status == "paid") {
|
||||
Toast.makeText(this, "This bill has already been paid", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
// TODO: Navigate to payment flow
|
||||
Toast.makeText(this, "Payment flow coming soon", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showError(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
// Show input fields and hide bill details
|
||||
showInputFields()
|
||||
// Don't clear input fields - user might have made a small mistake
|
||||
}
|
||||
|
||||
private fun showBillDetails(bill: BillLookupResponse) {
|
||||
// Hide input fields and show bill details
|
||||
binding.inputFieldsLayout.visibility = View.GONE
|
||||
binding.billDetailsCard.visibility = View.VISIBLE
|
||||
// Update button text to Search Another Bill when showing card
|
||||
binding.btnContinue.text = "Search Another Bill"
|
||||
|
||||
// Set bill information
|
||||
binding.tvBillNumber.text = bill.billNumber
|
||||
binding.tvBillAmount.text = "MVR ${bill.billAmount}"
|
||||
binding.tvBillStatus.text = bill.status.uppercase()
|
||||
|
||||
// Set customer information
|
||||
binding.tvCustomerName.text = bill.customer.name
|
||||
binding.tvAccountNumber.text = bill.customer.accountNumber
|
||||
binding.tvPhoneNumber.text = bill.customer.phone
|
||||
|
||||
// Set address information
|
||||
val address = "${bill.subscriptionAddress.property.name}, ${bill.subscriptionAddress.property.street.name}"
|
||||
binding.tvAddress.text = address
|
||||
|
||||
// Set subscription information
|
||||
binding.tvSubscriptionNumber.text = bill.subscription.subscriptionNumber
|
||||
binding.tvServiceType.text = if (bill.subscription.serviceId == 1) "Electricity" else "Water"
|
||||
|
||||
// Format and set dates
|
||||
val dueDateFormatted = formatDate(bill.dueDate)
|
||||
val billDateFormatted = formatDate(bill.billDate)
|
||||
binding.tvDueDate.text = dueDateFormatted
|
||||
binding.tvBillDate.text = billDateFormatted
|
||||
|
||||
// Set payment status and button
|
||||
val isPaid = bill.status == "paid"
|
||||
binding.btnPay.isEnabled = !isPaid
|
||||
binding.btnPay.alpha = if (isPaid) 0.5f else 1.0f
|
||||
binding.btnPay.text = if (isPaid) "Already Paid" else "Pay MVR ${bill.billAmount}"
|
||||
|
||||
// Set status color
|
||||
when (bill.status.lowercase()) {
|
||||
"paid" -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_green_dark))
|
||||
}
|
||||
"unpaid" -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_red_dark))
|
||||
}
|
||||
else -> {
|
||||
binding.tvBillStatus.setTextColor(getColor(android.R.color.holo_orange_dark))
|
||||
}
|
||||
}
|
||||
|
||||
// Show additional payment details if available
|
||||
bill.billPaymentDetails?.let { paymentDetails ->
|
||||
if (paymentDetails.paidAmount.toDoubleOrNull() ?: 0.0 > 0.0) {
|
||||
binding.tvPaidAmount.visibility = View.VISIBLE
|
||||
binding.tvPaidAmount.text = "Paid: MVR ${paymentDetails.paidAmount}"
|
||||
|
||||
if (paymentDetails.pendingAmount.toDoubleOrNull() ?: 0.0 > 0.0) {
|
||||
binding.tvPendingAmount.visibility = View.VISIBLE
|
||||
binding.tvPendingAmount.text = "Pending: MVR ${paymentDetails.pendingAmount}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDate(dateString: String): String {
|
||||
return try {
|
||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
|
||||
val outputFormat = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault())
|
||||
val date = inputFormat.parse(dateString)
|
||||
outputFormat.format(date ?: Date())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error formatting date: $dateString", e)
|
||||
dateString.substringBefore("T")
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateInput(billNumber: String, subscriptionNumber: String): Boolean {
|
||||
if (billNumber.isEmpty()) {
|
||||
binding.etBillNumber.error = "Bill number is required"
|
||||
return false
|
||||
}
|
||||
|
||||
if (subscriptionNumber.isEmpty()) {
|
||||
binding.etSubscriptionNumber.error = "Subscription number is required"
|
||||
return false
|
||||
}
|
||||
|
||||
// Basic validation - you can add more specific validation rules here
|
||||
if (billNumber.length < 3) {
|
||||
binding.etBillNumber.error = "Bill number must be at least 3 characters"
|
||||
return false
|
||||
}
|
||||
|
||||
if (subscriptionNumber.length < 3) {
|
||||
binding.etSubscriptionNumber.error = "Subscription number must be at least 3 characters"
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
binding.btnContinue.isEnabled = !isLoading
|
||||
binding.btnContinue.text = if (isLoading) "Searching Bill..." else "Continue"
|
||||
|
||||
binding.etBillNumber.isEnabled = !isLoading
|
||||
binding.etSubscriptionNumber.isEnabled = !isLoading
|
||||
|
||||
if (isLoading) {
|
||||
binding.billDetailsCard.visibility = View.GONE
|
||||
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user