-
Notifications
You must be signed in to change notification settings - Fork 132
[Payment Method Improvements] Cash Payment Improvements #11414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
97b0a24
5607c0d
1eddaf5
8920cc0
4a3249e
e4c2823
3d77aa0
0face8a
23eb91d
18f5e20
85a6b79
62a5205
4ad0eb7
020cb3b
c21d8a1
cb30201
de54aa8
8c45aa3
a905ee5
886a518
6b05cc5
71a082d
ec6e679
5222b8f
f676784
27412c1
dcd90b0
a73d119
cb6c05a
76ad3ab
ff19eca
9776bc2
5acadcb
81fe1fc
c919f23
3a5645c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.woocommerce.android.ui.payments.methodselection | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.ComposeView | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.unit.dp | ||
import androidx.fragment.app.DialogFragment | ||
import androidx.fragment.app.viewModels | ||
import com.woocommerce.android.R | ||
import dagger.hilt.android.AndroidEntryPoint | ||
|
||
@AndroidEntryPoint | ||
class ChangeDueCalculatorFragment : DialogFragment() { | ||
|
||
private val viewModel: ChangeDueCalculatorViewModel by viewModels() | ||
|
||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { | ||
return ComposeView(requireContext()).apply { | ||
setContent { | ||
ChangeDueCalculatorScreen() | ||
} | ||
} | ||
} | ||
|
||
|
||
@Composable | ||
fun ChangeDueCalculatorScreen() { | ||
val uiState by viewModel.uiState.collectAsState() | ||
|
||
Column( | ||
modifier = Modifier | ||
.fillMaxSize() | ||
.padding(16.dp), | ||
verticalArrangement = Arrangement.Center, | ||
horizontalAlignment = Alignment.CenterHorizontally | ||
) { | ||
// Display dynamic content based on UI state | ||
when (uiState) { | ||
is ChangeDueCalculatorViewModel.UiState.Loading -> { | ||
Text(text = stringResource(R.string.loading), style = MaterialTheme.typography.h5) | ||
} | ||
|
||
is ChangeDueCalculatorViewModel.UiState.Success -> { | ||
val state = uiState as ChangeDueCalculatorViewModel.UiState.Success | ||
Text( | ||
text = stringResource(R.string.cash_payments_take_payment_title, state.amountDue), | ||
style = MaterialTheme.typography.h5, | ||
modifier = Modifier.padding(bottom = 16.dp) | ||
) | ||
|
||
} | ||
|
||
is ChangeDueCalculatorViewModel.UiState.Error -> { | ||
Text(text = stringResource(R.string.error_generic), style = MaterialTheme.typography.h5) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.woocommerce.android.ui.payments.methodselection | ||
|
||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import com.woocommerce.android.ui.orders.details.OrderDetailRepository | ||
import dagger.hilt.android.lifecycle.HiltViewModel | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.launch | ||
import java.math.BigDecimal | ||
|
||
|
||
import androidx.lifecycle.SavedStateHandle | ||
import javax.inject.Inject | ||
|
||
|
||
@HiltViewModel | ||
class ChangeDueCalculatorViewModel @Inject constructor( | ||
savedStateHandle: SavedStateHandle, | ||
private val orderDetailRepository: OrderDetailRepository | ||
) : ViewModel() { | ||
|
||
|
||
private val orderId: Long = savedStateHandle.get<Long>("orderId") | ||
?: throw IllegalArgumentException("OrderId is required") | ||
|
||
sealed class UiState { | ||
data object Loading : UiState() | ||
data class Success(val amountDue: BigDecimal, val change: BigDecimal) : UiState() | ||
data object Error : UiState() | ||
} | ||
|
||
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) | ||
val uiState: StateFlow<UiState> = _uiState | ||
|
||
init { | ||
loadOrderDetails() | ||
} | ||
|
||
fun loadOrderDetails() { | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
viewModelScope.launch { | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
try { | ||
val order = orderDetailRepository.getOrderById(orderId) | ||
if (order != null) { | ||
_uiState.value = UiState.Success(amountDue = order.total, 0.00.toBigDecimal()) | ||
} else { | ||
_uiState.value = UiState.Error | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} catch (e: Exception) { | ||
_uiState.value = UiState.Error | ||
} | ||
} | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import androidx.lifecycle.SavedStateHandle | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import com.woocommerce.android.ui.orders.details.OrderDetailRepository | ||
import com.woocommerce.android.ui.payments.methodselection.ChangeDueCalculatorViewModel | ||
import com.woocommerce.android.ui.payments.methodselection.ChangeDueCalculatorViewModel.UiState | ||
import com.woocommerce.android.viewmodel.BaseUnitTest | ||
import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.mockito.kotlin.mock | ||
import org.mockito.kotlin.whenever | ||
|
||
@ExperimentalCoroutinesApi | ||
class ChangeDueCalculatorViewModelTest : BaseUnitTest() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this unit test but I'm not sure there's much to test yet. Can I leave these TODO's for the next PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Fine to fix in the next PR. Think of it as test-driven development. You might even ticket that out: "Add implementation so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added it as a task on this ticket. I can easily convert to GH issue if it ends up being a separate PR from the other tasks. |
||
|
||
private lateinit var orderDetailRepository: OrderDetailRepository | ||
private lateinit var savedStateHandle: SavedStateHandle | ||
|
||
private lateinit var viewModel: ChangeDueCalculatorViewModel | ||
|
||
@Before | ||
fun setup() { | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
orderDetailRepository = mock() | ||
savedStateHandle = mock() | ||
|
||
whenever(savedStateHandle.get<Long>("orderId")).thenReturn(1L) | ||
viewModel = ChangeDueCalculatorViewModel(savedStateHandle, orderDetailRepository) | ||
} | ||
|
||
@Test | ||
fun `order details load successfully emits success state`() = testBlocking { | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//TODO fix this test | ||
whenever(orderDetailRepository.getOrderById(1L)).thenReturn(null) | ||
|
||
viewModel.loadOrderDetails() | ||
|
||
//TODO assert(viewModel.uiState.value == UiState.Success(BigDecimal.TEN, BigDecimal.ZERO)) | ||
} | ||
|
||
@Test | ||
fun `order details load failure emits error state`() = testBlocking { | ||
whenever(orderDetailRepository.getOrderById(1L)).thenReturn(null) | ||
|
||
viewModel.loadOrderDetails() | ||
|
||
assert(viewModel.uiState.value is UiState.Error) | ||
backwardstruck marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.