Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -795,30 +795,32 @@ private fun sampleOrderDetails(
dateTime = "Aug 28, 2025 at 10:31 AM",
customerEmail = "johndoe@mail.com",
status = PosOrderStatus(text = "Completed", colorKey = OrderStatusColorKey.COMPLETED),
lineItems = listOf(
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 101,
name = "Cup",
attributesDescription = null,
qtyAndUnitPrice = "1 x $8.50",
lineTotal = "$15.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 102,
name = "Coffee Container",
attributesDescription = "Blue, Large",
qtyAndUnitPrice = "1 x $10.00",
lineTotal = "$8.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 103,
name = "Paper Filter",
attributesDescription = null,
qtyAndUnitPrice = "1 x $4.50",
lineTotal = "$8.00",
imageUrl = null
lineItems = WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemsState.Loaded(
listOf(
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 101,
name = "Cup",
attributesDescription = null,
qtyAndUnitPrice = "1 x $8.50",
lineTotal = "$15.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 102,
name = "Coffee Container",
attributesDescription = "Blue, Large",
qtyAndUnitPrice = "1 x $10.00",
lineTotal = "$8.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 103,
name = "Paper Filter",
attributesDescription = null,
qtyAndUnitPrice = "1 x $4.50",
lineTotal = "$8.00",
imageUrl = null
)
)
),
breakdown = WooPosOrdersState.OrderDetailsViewState.Computed.Details.TotalsBreakdown(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,23 @@ sealed class WooPosOrdersState {
val customerEmail: String?,
val status: PosOrderStatus,

val lineItems: List<LineItemRow>,
val lineItems: LineItemsState = LineItemsState.Loading,
val refundedLineItems: LineItemsState = LineItemsState.Loading,
val breakdown: TotalsBreakdown,
val total: String,
val totalPaid: String,
val paymentMethodTitle: String?,
val actionsState: OrderActionsState
) {
@Immutable
sealed interface LineItemsState {
@Immutable
data object Loading : LineItemsState

@Immutable
data class Loaded(val items: List<LineItemRow>) : LineItemsState
}

@Immutable
sealed interface BookingInfo {
@Immutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender
import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState
import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState
import com.woocommerce.android.ui.woopos.orders.WooPosOrdersState.OrderDetailsViewState.Computed.Details.BookingInfo
import com.woocommerce.android.ui.woopos.orders.WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemsState
import com.woocommerce.android.ui.woopos.orders.details.WooPosBookingInfoMapper
import com.woocommerce.android.ui.woopos.orders.details.WooPosOrderDetailsMapper
import com.woocommerce.android.ui.woopos.orders.details.WooPosOrderItemMapper
Expand Down Expand Up @@ -253,6 +254,8 @@ class WooPosOrdersViewModel @Inject constructor(
val actions = orderActionsProvider.getAvailableActions(order)
val refundInfo = refundInfoBuilder.buildRefundInfo(order, refundsResult)
val updatedBreakdown = refundInfoBuilder.buildTotalsBreakdown(order, refundInfo)
val refundedLineItems = orderDetailsMapper.buildRefundedLineItems(order, refundsResult)
val nonRefundedLineItems = orderDetailsMapper.buildNonRefundedLineItems(order, refundsResult)

val updatedState = _state.value as? WooPosOrdersState.Content ?: return@launch
if (updatedState.selectedDetails?.id == orderId &&
Expand All @@ -261,9 +264,17 @@ class WooPosOrdersViewModel @Inject constructor(
_state.value = updatedState.withUpdatedDetails(orderId) { details ->
details.copy(
actionsState = WooPosOrdersState.OrderActionsState.Loaded(actions),
breakdown = updatedBreakdown
breakdown = updatedBreakdown,
lineItems = LineItemsState.Loaded(nonRefundedLineItems),
refundedLineItems = LineItemsState.Loaded(refundedLineItems)
)
}

val stateAfterUpdate = _state.value as? WooPosOrdersState.Content
val updatedDetails = stateAfterUpdate?.selectedDetails
if (updatedDetails != null) {
sideLoadBookings(orderId, updatedDetails)
}
}
}
}
Expand All @@ -272,7 +283,8 @@ class WooPosOrdersViewModel @Inject constructor(
orderId: Long,
details: WooPosOrdersState.OrderDetailsViewState.Computed.Details
) {
val loadingItems = details.lineItems.filter { it.bookingInfo is BookingInfo.Loading }
val loadedItems = (details.lineItems as? LineItemsState.Loaded)?.items ?: return
val loadingItems = loadedItems.filter { it.bookingInfo is BookingInfo.Loading }
if (loadingItems.isEmpty()) return

viewModelScope.launch {
Expand All @@ -289,10 +301,14 @@ class WooPosOrdersViewModel @Inject constructor(
if (currentState.selectedDetails?.id != orderId) return@launch

_state.value = currentState.withUpdatedDetails(orderId) { details ->
val currentItems = (details.lineItems as? LineItemsState.Loaded)?.items
?: return@withUpdatedDetails details
details.copy(
lineItems = details.lineItems.map { lineItem ->
results[lineItem.id]?.let { lineItem.copy(bookingInfo = it) } ?: lineItem
}
lineItems = LineItemsState.Loaded(
currentItems.map { lineItem ->
results[lineItem.id]?.let { lineItem.copy(bookingInfo = it) } ?: lineItem
}
)
)
}
}
Expand Down Expand Up @@ -640,20 +656,11 @@ class WooPosOrdersViewModel @Inject constructor(
val orderDetails = loadedItems.items.values.firstOrNull { it.orderId == orderId }
?: error("Order $orderId not found in state")

return orderDetailsMapper.mapOrderDetails(
when (orderDetails) {
is WooPosOrdersState.OrderDetailsViewState.Lazy -> orderDetails.order
is WooPosOrdersState.OrderDetailsViewState.Computed -> {
loadedItems.items.keys.firstOrNull { it.id == orderId }?.let {
ordersDataSource.getOrderById(it.id).getOrNull()
} ?: error("Order $orderId not found")
}
},
when (orderDetails) {
is WooPosOrdersState.OrderDetailsViewState.Lazy -> orderDetails.refundResult
is WooPosOrdersState.OrderDetailsViewState.Computed -> RefundsFetchResult.Error
}
)
return when (orderDetails) {
is WooPosOrdersState.OrderDetailsViewState.Computed -> orderDetails.details
is WooPosOrdersState.OrderDetailsViewState.Lazy ->
orderDetailsMapper.mapOrderDetails(orderDetails.order, orderDetails.refundResult)
}
}

private suspend fun getOrComputeDetailsWithoutActions(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.woocommerce.android.ui.woopos.orders.details

import com.woocommerce.android.model.Order
import com.woocommerce.android.model.Refund
import java.math.RoundingMode
import javax.inject.Inject
import kotlin.math.abs

class WooPosGetNonRefundedItems @Inject constructor() {
operator fun invoke(
order: Order,
refunds: List<Refund>,
): List<Order.Item> {
if (refunds.isEmpty()) return order.items

val refundedByItemId = refunds
.flatMap { it.items }
.groupingBy { it.orderItemId }
.fold(0) { acc, item -> acc + abs(item.quantity) }

return order.items.mapNotNull { item ->
val refundedQty = (refundedByItemId[item.itemId] ?: 0).toFloat()

if (item.quantity == 0f) {
return@mapNotNull if (refundedQty == 0f) item else null
}

val remaining = item.quantity - refundedQty
when {
remaining <= 0f -> null
remaining == item.quantity -> item
else -> {
val newTotal = (item.total * remaining.toBigDecimal()).divide(
item.quantity.toBigDecimal(),
item.total.scale(),
RoundingMode.HALF_UP
)
item.copy(quantity = remaining, total = newTotal)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.woocommerce.android.ui.woopos.orders.details

import com.woocommerce.android.model.Refund
import java.math.BigDecimal
import java.math.RoundingMode
import javax.inject.Inject

class WooPosGroupRefundedItems @Inject constructor() {
operator fun invoke(refunds: List<Refund>): List<Refund.Item> {
val allRefundItems = refunds.flatMap { it.items }
if (allRefundItems.isEmpty()) return emptyList()

return allRefundItems
.groupBy { it.orderItemId }
.map { (orderItemId, items) ->
val quantity = items.sumOf { it.quantity }
val total = items.fold(BigDecimal.ZERO) { acc, item -> acc + item.total }
items.first().copy(
orderItemId = orderItemId,
quantity = quantity,
total = total,
price = if (quantity != 0) {
total.divide(BigDecimal.valueOf(quantity.toLong()), total.scale(), RoundingMode.HALF_UP)
} else {
total
},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ fun WooPosOrderDetails(

Spacer(Modifier.height(WooPosSpacing.Large.value))

OrdersProducts(lineItems = details.lineItems)
val lineItemsState = details.lineItems
if (lineItemsState is WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemsState.Loaded &&
lineItemsState.items.isNotEmpty()
) {
OrdersProducts(lineItems = lineItemsState.items)
}

Spacer(Modifier.height(WooPosSpacing.Medium.value))

Expand Down Expand Up @@ -470,41 +475,43 @@ fun WooPosOrderDetailsPreview() {
dateTime = "Aug 28, 2025 at 10:31 AM",
customerEmail = "johndoe@mail.com",
status = PosOrderStatus(text = "Completed", colorKey = OrderStatusColorKey.COMPLETED),
lineItems = listOf(
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 101,
name = "Cup",
attributesDescription = null,
qtyAndUnitPrice = "2 x $4.00",
lineTotal = "$8.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 102,
name = "T-Shirt",
attributesDescription = "Blue, Large",
qtyAndUnitPrice = "1 x $10.00",
lineTotal = "$10.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 103,
name = "A vey tasty coffee that incidentally has a very long name " +
"and should go over a few lines without overlapping anything",
attributesDescription = "Medium roast, Decaf",
qtyAndUnitPrice = "1 x $5.00",
lineTotal = "$5.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 104,
name = "Women's Haircut",
attributesDescription = null,
qtyAndUnitPrice = "1 x $55.00",
lineTotal = "$55.00",
imageUrl = null,
bookingInfo = WooPosOrdersState.OrderDetailsViewState.Computed.Details.BookingInfo.Loaded(
"Booking #33 \u00B7 Jul 5, 2025, 10:00 AM - 10:30 AM"
lineItems = WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemsState.Loaded(
listOf(
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 101,
name = "Cup",
attributesDescription = null,
qtyAndUnitPrice = "2 x $4.00",
lineTotal = "$8.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 102,
name = "T-Shirt",
attributesDescription = "Blue, Large",
qtyAndUnitPrice = "1 x $10.00",
lineTotal = "$10.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 103,
name = "A vey tasty coffee that incidentally has a very long name " +
"and should go over a few lines without overlapping anything",
attributesDescription = "Medium roast, Decaf",
qtyAndUnitPrice = "1 x $5.00",
lineTotal = "$5.00",
imageUrl = null
),
WooPosOrdersState.OrderDetailsViewState.Computed.Details.LineItemRow(
id = 104,
name = "Women's Haircut",
attributesDescription = null,
qtyAndUnitPrice = "1 x $55.00",
lineTotal = "$55.00",
imageUrl = null,
bookingInfo = WooPosOrdersState.OrderDetailsViewState.Computed.Details.BookingInfo.Loaded(
"Booking #33 \u00B7 Jul 5, 2025, 10:00 AM - 10:30 AM"
)
)
)
),
Expand Down
Loading