LiveOrder.kt
/*
* This file is part of GNU Taler
* (C) 2020 Taler Systems S.A.
*
* GNU Taler is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3, or (at your option) any later version.
*
* GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
package net.taler.merchantpos.order
import androidx.annotation.UiThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.map
import net.taler.common.Amount
import net.taler.common.CombinedLiveData
import net.taler.common.CurrencySpecification
import net.taler.merchantpos.config.Category
import net.taler.merchantpos.config.ConfigProduct
import net.taler.merchantpos.order.RestartState.DISABLED
import net.taler.merchantpos.order.RestartState.ENABLED
import net.taler.merchantpos.order.RestartState.UNDO
internal enum class RestartState
{
ENABLED,
DISABLED,
UNDO
}
internal interface LiveOrder
{
val order: LiveData<Order?>
val orderTotal: LiveData<Amount>
val restartState: LiveData<RestartState>
val modifyOrderAllowed: LiveData<Boolean>
val increaseOrderAllowed: LiveData<Boolean>
val selectedProductKey: LiveData<String?>
fun restartOrUndo()
fun selectOrderLine(product: ConfigProduct?)
fun increaseSelectedOrderLine()
fun decreaseSelectedOrderLine()
}
internal class MutableLiveOrder(
val id: Int,
private val currency: String,
private val currencySpec: CurrencySpecification?,
private val productsByCategory: HashMap<Category, ArrayList<ConfigProduct>>,
private val canAddProduct: (ConfigProduct) -> Boolean,
private val onChanged: () -> Unit,
) : LiveOrder
{
private val selectedOrderLine = MutableLiveData<ConfigProduct?>()
private var undoOrder: Order? = null
private var undoSelection: String? = null
private val availableCategories: Map<Int, Category>
get() = productsByCategory.keys.map { it.id to it }.toMap()
override val order: MutableLiveData<Order?> =
MutableLiveData(Order(id, currency, currencySpec, availableCategories))
override val orderTotal: LiveData<Amount> =
order.map { it?.total ?: Amount.zero(currency).withSpec(currencySpec) }
override val restartState = MutableLiveData(DISABLED)
override val selectedProductKey: LiveData<String?> = selectedOrderLine.map { it?.id }
override val modifyOrderAllowed =
CombinedLiveData(restartState, selectedOrderLine) { restartState, selectedOrderLine ->
restartState != DISABLED && selectedOrderLine != null
}
override val increaseOrderAllowed =
CombinedLiveData(order, selectedOrderLine) { order, selectedOrderLine ->
order != null && selectedOrderLine != null && canAddProduct(selectedOrderLine)
}
@UiThread
internal fun addProduct(product: ConfigProduct)
{
if (!canAddProduct(product)) return
updateOrder(order.value!! + product, product.id)
restartState.value = ENABLED
onChanged()
}
@UiThread
internal fun removeProduct(product: ConfigProduct)
{
val modifiedOrder = order.value!! - product
updateOrder(modifiedOrder)
restartState.value = if (modifiedOrder.products.isEmpty()) DISABLED else ENABLED
onChanged()
}
@UiThread
internal fun isEmpty() = order.value!!.products.isEmpty()
@UiThread
override fun restartOrUndo()
{
if (restartState.value == UNDO)
{
updateOrder(checkNotNull(undoOrder), undoSelection)
restartState.value = ENABLED
undoOrder = null
undoSelection = null
}
else
{
undoOrder = order.value
undoSelection = selectedOrderLine.value?.id
updateOrder(Order(id, currency, currencySpec, availableCategories))
restartState.value = UNDO
}
onChanged()
}
@UiThread
override fun selectOrderLine(product: ConfigProduct?)
{
selectedOrderLine.value = order.value?.products?.find { it.id == product?.id }
}
private fun updateOrder(
updated: Order,
preferredProductKey: String? = selectedOrderLine.value?.id,
)
{
// Quantity edits preserve selection. Only a removed row needs a fallback.
selectedOrderLine.value =
updated.products.find { it.id == preferredProductKey } ?: updated.products.lastOrNull()
order.value = updated
}
@UiThread
override fun increaseSelectedOrderLine()
{
val orderLine = selectedOrderLine.value ?: throw IllegalStateException()
addProduct(orderLine)
}
@UiThread
override fun decreaseSelectedOrderLine()
{
val orderLine = selectedOrderLine.value ?: throw IllegalStateException()
removeProduct(orderLine)
}
}