fix cart and order

This commit is contained in:
shaulascr
2025-05-13 03:05:11 +07:00
parent 5bf89f3b86
commit dfdb00cf98
10 changed files with 215 additions and 120 deletions

View File

@ -6,7 +6,7 @@ data class OrderRequest (
@SerializedName("address_id")
val addressId : Int,
@SerializedName("payment_method_id")
@SerializedName("payment_info_id")
val paymentMethodId : Int,
@SerializedName("ship_price")
@ -21,7 +21,7 @@ data class OrderRequest (
@SerializedName("is_negotiable")
val isNego: Boolean,
@SerializedName("cart_items_id")
@SerializedName("cart_item_ids")
val cartItemId: List<Int>,
@SerializedName("ship_etd")

View File

@ -6,7 +6,7 @@ data class OrderRequestBuy (
@SerializedName("address_id")
val addressId : Int,
@SerializedName("payment_method_id")
@SerializedName("payment_info_id")
val paymentMethodId : Int,
@SerializedName("ship_price")

View File

@ -70,7 +70,7 @@ data class OrderItemItem(
data class Order(
@field:SerializedName("payment_method_id")
@field:SerializedName("payment_info_id")
val paymentMethodId: Int,
@field:SerializedName("auto_completed_at")

View File

@ -16,7 +16,7 @@ data class PaymentInfoItem(
val id: Int = 1,
@field:SerializedName("qris_image")
val qrisImage: String,
val qrisImage: String? = null,
@field:SerializedName("bank_num")
val bankNum: String,

View File

@ -2,7 +2,6 @@ package com.alya.ecommerce_serang.data.repository
import android.util.Log
import com.alya.ecommerce_serang.data.api.dto.AddEvidenceMultipartRequest
import com.alya.ecommerce_serang.data.api.dto.CartItem
import com.alya.ecommerce_serang.data.api.dto.CompletedOrderRequest
import com.alya.ecommerce_serang.data.api.dto.CourierCostRequest
import com.alya.ecommerce_serang.data.api.dto.CreateAddressRequest
@ -326,59 +325,35 @@ class OrderRepository(private val apiService: ApiService) {
}
}
// suspend fun uploadPaymentProof(request : AddEvidenceRequest): Result<AddEvidenceResponse> {
// return try {
// Log.d("OrderRepository", "Add Evidence : $request")
// val response = apiService.addEvidence(request)
//
// if (response.isSuccessful) {
// val addEvidenceResponse = response.body()
// if (addEvidenceResponse != null) {
// Log.d("OrderRepository", "Add Evidence successfully: ${addEvidenceResponse.message}")
// Result.Success(addEvidenceResponse)
// } else {
// Log.e("OrderRepository", "Response body was null")
// Result.Error(Exception("Empty response from server"))
// }
// } else {
// val errorBody = response.errorBody()?.string() ?: "Unknown error"
// Log.e("OrderRepository", "Error Add Evidence : $errorBody")
// Result.Error(Exception(errorBody))
// }
// } catch (e: Exception) {
// Log.e("OrderRepository", "Exception Add Evidence ", e)
// Result.Error(e)
// }
// }
suspend fun uploadPaymentProof(request: AddEvidenceMultipartRequest): Result<AddEvidenceResponse> {
return try {
Log.d("OrderRepository", "Uploading payment proof...")
suspend fun uploadPaymentProof(request: AddEvidenceMultipartRequest): Result<AddEvidenceResponse> {
return try {
Log.d("OrderRepository", "Uploading payment proof...")
val response = apiService.addEvidenceMultipart(
orderId = request.orderId,
amount = request.amount,
evidence = request.evidence
)
val response = apiService.addEvidenceMultipart(
orderId = request.orderId,
amount = request.amount,
evidence = request.evidence
)
if (response.isSuccessful) {
val addEvidenceResponse = response.body()
if (addEvidenceResponse != null) {
Log.d("OrderRepository", "Payment proof uploaded successfully: ${addEvidenceResponse.message}")
Result.Success(addEvidenceResponse)
if (response.isSuccessful) {
val addEvidenceResponse = response.body()
if (addEvidenceResponse != null) {
Log.d("OrderRepository", "Payment proof uploaded successfully: ${addEvidenceResponse.message}")
Result.Success(addEvidenceResponse)
} else {
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
}
} else {
Log.e("OrderRepository", "Response body was null")
Result.Error(Exception("Empty response from server"))
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error uploading payment proof: $errorBody")
Result.Error(Exception(errorBody))
}
} else {
val errorBody = response.errorBody()?.string() ?: "Unknown error"
Log.e("OrderRepository", "Error uploading payment proof: $errorBody")
Result.Error(Exception(errorBody))
} catch (e: Exception) {
Log.e("OrderRepository", "Exception uploading payment proof", e)
Result.Error(e)
}
} catch (e: Exception) {
Log.e("OrderRepository", "Exception uploading payment proof", e)
Result.Error(e)
}
}
suspend fun getOrderList(status: String): Result<OrderListResponse> {
return try {

View File

@ -1,6 +1,5 @@
package com.alya.ecommerce_serang.ui.cart
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
@ -101,11 +100,15 @@ class CartActivity : AppCompatActivity() {
if (viewModel.totalSelectedCount.value ?: 0 > 0) {
val selectedItems = viewModel.prepareCheckout()
if (selectedItems.isNotEmpty()) {
// Navigate to checkout
val intent = Intent(this, CheckoutActivity::class.java)
// You would pass the selected items to the next activity
// intent.putExtra("SELECTED_ITEMS", selectedItems)
startActivity(intent)
// Check if all items are from the same store
val storeId = viewModel.activeStoreId.value
if (storeId != null) {
// Get cart item ids to pass to checkout
val cartItemIds = selectedItems.map { it.cartItemId }
CheckoutActivity.startForCart(this, cartItemIds)
} else {
Toast.makeText(this, "Please select items from a single store only", Toast.LENGTH_SHORT).show()
}
}
} else {
Toast.makeText(this, "Pilih produk terlebih dahulu", Toast.LENGTH_SHORT).show()
@ -182,4 +185,7 @@ class CartActivity : AppCompatActivity() {
val format = NumberFormat.getCurrencyInstance(Locale("id", "ID"))
return format.format(amount).replace("Rp", "Rp ")
}
}
}

View File

@ -285,10 +285,12 @@ class CartViewModel(private val repository: OrderRepository) : ViewModel() {
val selectedItemsIds = _selectedItems.value ?: HashSet()
val result = mutableListOf<CartItemsItem>()
_cartItems.value?.forEach { dataItem ->
dataItem.cartItems.forEach { cartItem ->
if (selectedItemsIds.contains(cartItem.cartItemId)) {
result.add(cartItem)
if (activeStoreId != null){
_cartItems.value?.forEach { dataItem ->
dataItem.cartItems.forEach { cartItem ->
if (selectedItemsIds.contains(cartItem.cartItemId)) {
result.add(cartItem)
}
}
}
}

View File

@ -30,6 +30,7 @@ class CheckoutActivity : AppCompatActivity() {
private lateinit var binding: ActivityCheckoutBinding
private lateinit var sessionManager: SessionManager
private var paymentAdapter: PaymentMethodAdapter? = null
private var paymentMethodsLoaded = false
private val viewModel: CheckoutViewModel by viewModels {
BaseViewModelFactory {
@ -80,10 +81,10 @@ class CheckoutActivity : AppCompatActivity() {
}
}
viewModel.getPaymentMethods { paymentMethods ->
// Logging is just for debugging
Log.d("CheckoutActivity", "Loaded ${paymentMethods.size} payment methods")
}
// viewModel.getPaymentMethods { paymentMethods ->
// // Logging is just for debugging
// Log.d("CheckoutActivity", "Loaded ${paymentMethods.size} payment methods")
// }
}
private fun setupToolbar() {
@ -97,6 +98,12 @@ class CheckoutActivity : AppCompatActivity() {
viewModel.checkoutData.observe(this) { data ->
setupProductRecyclerView(data)
updateOrderSummary()
if (data != null) {
viewModel.getPaymentMethods { paymentMethods ->
Log.d("CheckoutActivity", "Loaded ${paymentMethods.size} payment methods")
}
}
}
// Observe address details
@ -106,23 +113,27 @@ class CheckoutActivity : AppCompatActivity() {
}
viewModel.availablePaymentMethods.observe(this) { paymentMethods ->
if (paymentMethods.isNotEmpty()) {
if (paymentMethods.isNotEmpty() && !paymentMethodsLoaded) {
Log.d("CheckoutActivity", "Setting up payment methods: ${paymentMethods.size} methods available")
setupPaymentMethodsRecyclerView(paymentMethods)
paymentMethodsLoaded = true
}
}
// Observe selected payment
viewModel.selectedPayment.observe(this) { selectedPayment ->
if (selectedPayment != null) {
// Update the adapter to show the selected payment
paymentAdapter?.setSelectedPaymentName(selectedPayment.name)
Log.d("CheckoutActivity", "Observer notified of selected payment: ${selectedPayment.name}")
// Optional: Update other UI elements to show the selected payment
// For example: binding.tvSelectedPaymentMethod.text = selectedPayment.name
// Update the adapter ONLY if it exists
paymentAdapter?.let { adapter ->
// This line was causing issues - using setSelectedPayment instead of setSelectedPaymentName
adapter.setSelectedPaymentId(selectedPayment.id)
Log.d("CheckoutActivity", "Updated adapter with selected payment: ${selectedPayment.id}")
}
}
}
// Observe loading state
viewModel.isLoading.observe(this) { isLoading ->
binding.btnPay.isEnabled = !isLoading
@ -156,14 +167,18 @@ class CheckoutActivity : AppCompatActivity() {
// Debug logging
Log.d("CheckoutActivity", "Setting up payment methods: ${paymentMethods.size} methods available")
paymentAdapter = PaymentMethodAdapter(paymentMethods) { payment ->
// We're using a hardcoded ID for now
viewModel.setPaymentMethod(1)
}
if (paymentAdapter == null) {
paymentAdapter = PaymentMethodAdapter(paymentMethods) { payment ->
Log.d("CheckoutActivity", "Payment selected in adapter: ${payment.name}")
binding.rvPaymentMethods.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = paymentAdapter
// Set this payment as selected in the ViewModel
viewModel.setPaymentMethod(payment.id)
}
binding.rvPaymentMethods.apply {
layoutManager = LinearLayoutManager(this@CheckoutActivity)
adapter = paymentAdapter
}
}
}
@ -347,7 +362,13 @@ class CheckoutActivity : AppCompatActivity() {
}
// Check if payment method is selected
if (viewModel.selectedPayment.value == null) {
val paymentMethodId = if (checkoutData.isBuyNow) {
(checkoutData.orderRequest as OrderRequestBuy).paymentMethodId
} else {
(checkoutData.orderRequest as OrderRequest).paymentMethodId
}
if (paymentMethodId <= 0) {
Toast.makeText(this, "Silakan pilih metode pembayaran", Toast.LENGTH_SHORT).show()
return false
}

View File

@ -112,6 +112,10 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
}
if (matchingItems.isNotEmpty() && storeData != null) {
if (matchingItems.size != cartItemIds.size) {
Log.w(TAG, "Not all requested cart items were found. Found ${matchingItems.size} out of ${cartItemIds.size}")
// Consider showing a warning or adjusting the list
}
// Create initial OrderRequest object
val orderRequest = OrderRequest(
addressId = 0, // Will be set when user selects address
@ -133,6 +137,10 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
isBuyNow = false,
cartItems = matchingItems
)
Log.d(TAG, "Setting sellerId to: ${storeData.storeId}")
calculateSubtotal()
calculateTotal()
} else {
_errorMessage.value = "No matching cart items found"
}
@ -153,6 +161,13 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
try {
val storeId = _checkoutData.value?.sellerId ?: return@launch
Log.d(TAG, "Attempting to fetch payment methods for storeId: $storeId")
if (storeId == null || storeId <= 0) {
Log.e(TAG, "Invalid storeId: $storeId - cannot fetch payment methods")
return@launch
}
// Use fetchStoreDetail instead of getStore
val storeResult = repository.fetchStoreDetail(storeId)
@ -161,7 +176,7 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
// This will be updated once the backend provides proper IDs
val paymentMethodsList = storeResult.data.paymentInfo.map { paymentInfo ->
PaymentInfoItem(
id = 1, // Hardcoded payment ID
id = paymentInfo.id ?: 1,
name = paymentInfo.name,
bankNum = paymentInfo.bankNum,
qrisImage = paymentInfo.qrisImage
@ -170,7 +185,10 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
Log.d(TAG, "Fetched ${paymentMethodsList.size} payment methods")
_availablePaymentMethods.value = paymentMethodsList
// Only update if we don't already have payment methods
if (_availablePaymentMethods.value.isNullOrEmpty()) {
_availablePaymentMethods.value = paymentMethodsList
}
callback(paymentMethodsList)
} else {
_availablePaymentMethods.value = emptyList()
@ -186,9 +204,6 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
// Updated setPaymentMethod function
fun setPaymentMethod(paymentId: Int) {
// We'll use the hardcoded ID (1) for now
val currentPaymentId = 1
viewModelScope.launch {
try {
// Get the available payment methods
@ -200,26 +215,30 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
return@launch
}
// Use the first payment method (or specific one if you prefer)
val selectedPayment = paymentMethods.first()
val selectedPayment = paymentMethods.find { it.id == paymentId }
// Set the selected payment
if (selectedPayment == null) {
Log.e(TAG, "Payment with ID $paymentId not found")
return@launch
}
// Set the selected payment - IMPORTANT: do this first
_selectedPayment.value = selectedPayment
Log.d(TAG, "Payment selected: Name=${selectedPayment.name}")
Log.d(TAG, "Payment selected: ID=${selectedPayment.id}, Name=${selectedPayment.name}")
// Update the order request with the payment method ID (hardcoded for now)
// Update the order request with the payment method ID
val currentData = _checkoutData.value ?: return@launch
// Different handling for Buy Now vs Cart checkout
if (currentData.isBuyNow) {
// For Buy Now checkout
val buyRequest = currentData.orderRequest as OrderRequestBuy
val updatedRequest = buyRequest.copy(paymentMethodId = currentPaymentId)
val updatedRequest = buyRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
} else {
// For Cart checkout
val cartRequest = currentData.orderRequest as OrderRequest
val updatedRequest = cartRequest.copy(paymentMethodId = currentPaymentId)
val updatedRequest = cartRequest.copy(paymentMethodId = paymentId)
_checkoutData.value = currentData.copy(orderRequest = updatedRequest)
}
} catch (e: Exception) {
@ -299,6 +318,39 @@ class CheckoutViewModel(private val repository: OrderRepository) : ViewModel() {
try {
val data = _checkoutData.value ?: throw Exception("No checkout data available")
if (data.orderRequest is OrderRequest) {
val request = data.orderRequest
// Check for required fields
if (request.addressId <= 0) {
_errorMessage.value = "Please select a delivery address"
_isLoading.value = false
return@launch
}
if (request.paymentMethodId <= 0) {
_errorMessage.value = "Please select a payment method"
_isLoading.value = false
return@launch
}
if (request.shipPrice <= 0 || request.shipName.isBlank() || request.shipService.isBlank()) {
_errorMessage.value = "Please select a shipping method"
_isLoading.value = false
return@launch
}
} else if (data.orderRequest is OrderRequestBuy) {
val request = data.orderRequest
// Similar validation for buy now
if (request.addressId <= 0 || request.paymentMethodId <= 0 ||
request.shipPrice <= 0 || request.shipName.isBlank() || request.shipService.isBlank()) {
_errorMessage.value = "Please complete all required checkout information"
_isLoading.value = false
return@launch
}
}
val response = if (data.isBuyNow) {
// For Buy Now, use the dedicated endpoint
val buyRequest = data.orderRequest as OrderRequestBuy

View File

@ -1,6 +1,8 @@
package com.alya.ecommerce_serang.ui.order
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.alya.ecommerce_serang.R
@ -14,8 +16,8 @@ class PaymentMethodAdapter(
private val onPaymentSelected: (PaymentInfoItem) -> Unit
) : RecyclerView.Adapter<PaymentMethodAdapter.PaymentMethodViewHolder>() {
// Selected payment name
private var selectedPaymentName: String? = null
// Track the selected payment by ID
private var selectedPaymentId: Int? = null
class PaymentMethodViewHolder(val binding: ItemPaymentMethodBinding) :
RecyclerView.ViewHolder(binding.root)
@ -38,16 +40,11 @@ class PaymentMethodAdapter(
// Set payment method name
tvPaymentMethodName.text = payment.name
// // Set bank account number if available
// if (!payment.bankNum.isNullOrEmpty()) {
// tvPaymentAccountNumber.visibility = View.VISIBLE
// tvPaymentAccountNumber.text = payment.bankNum
// } else {
// tvPaymentAccountNumber.visibility = View.GONE
// }
// Set radio button state based on selected payment ID
rbPaymentMethod.isChecked = payment.id == selectedPaymentId
// Set radio button state based on selected payment name
rbPaymentMethod.isChecked = payment.name == selectedPaymentName
// Debug log
Log.d("PaymentAdapter", "Binding item ${payment.name}, checked=${rbPaymentMethod.isChecked}")
// Load payment icon if available
if (!payment.qrisImage.isNullOrEmpty()) {
@ -55,31 +52,73 @@ class PaymentMethodAdapter(
.load(payment.qrisImage)
.apply(
RequestOptions()
.placeholder(R.drawable.outline_store_24)
.error(R.drawable.outline_store_24))
.placeholder(R.drawable.outline_store_24)
.error(R.drawable.outline_store_24)
)
.into(ivPaymentMethod)
} else {
// Default icon for bank transfers
ivPaymentMethod.setImageResource(R.drawable.outline_store_24)
}
// Handle click on the entire item
root.setOnClickListener {
onPaymentSelected(payment)
setSelectedPaymentName(payment.name)
// IMPORTANT: We need to fix the click handling to prevent re-fetching
val clickListener = View.OnClickListener {
val previousSelectedId = selectedPaymentId
selectedPaymentId = payment.id
// Force the radio button to be checked
rbPaymentMethod.isChecked = true
// Only notify if there was a change in selection
if (previousSelectedId != payment.id) {
notifyItemChanged(position)
// Notify previous selection if it exists
if (previousSelectedId != null) {
val prevPosition = paymentMethods.indexOfFirst { it.id == previousSelectedId }
if (prevPosition >= 0) {
notifyItemChanged(prevPosition)
}
}
// Call the callback ONLY ONCE
onPaymentSelected(payment)
Log.d("PaymentAdapter", "Payment selected: ${payment.name}")
}
}
// Handle click on the radio button
rbPaymentMethod.setOnClickListener {
onPaymentSelected(payment)
setSelectedPaymentName(payment.name)
// Apply the same click listener to both the root and the radio button
root.setOnClickListener(clickListener)
rbPaymentMethod.setOnClickListener(clickListener)
}
}
// Set selected payment
fun setSelectedPaymentId(paymentId: Int) {
if (selectedPaymentId != paymentId) {
val previousSelectedId = selectedPaymentId
selectedPaymentId = paymentId
Log.d("PaymentAdapter", "Setting selected payment ID to: $paymentId")
// Update affected items only
if (previousSelectedId != null) {
val prevPosition = paymentMethods.indexOfFirst { it.id == previousSelectedId }
if (prevPosition >= 0) {
notifyItemChanged(prevPosition)
}
}
val newPosition = paymentMethods.indexOfFirst { it.id == paymentId }
if (newPosition >= 0) {
notifyItemChanged(newPosition)
}
}
}
// Set selected payment by name and refresh the UI
fun setSelectedPaymentName(paymentName: String) {
selectedPaymentName = paymentName
notifyDataSetChanged() // Update all items to reflect selection change
// Set selected payment object
fun setSelectedPayment(payment: PaymentInfoItem) {
setSelectedPaymentId(payment.id)
}
}