summaryrefslogtreecommitdiff
path: root/app/src/main/java/net/taler/merchantpos/order/OrderManager.kt
blob: 55a3be632aad01a923d0f32098a9f731fb2fe32d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package net.taler.merchantpos.order

import android.util.Log
import androidx.annotation.UiThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations.map
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import net.taler.merchantpos.Amount.Companion.fromString
import net.taler.merchantpos.config.ConfigurationReceiver
import net.taler.merchantpos.order.RestartState.DISABLED
import net.taler.merchantpos.order.RestartState.ENABLED
import net.taler.merchantpos.order.RestartState.UNDO
import org.json.JSONObject

enum class RestartState { ENABLED, DISABLED, UNDO }

class OrderManager(private val mapper: ObjectMapper) : ConfigurationReceiver {

    companion object {
        val TAG = OrderManager::class.java.simpleName
    }

    private val productsByCategory = HashMap<Category, ArrayList<Product>>()

    private val mOrder = MutableLiveData<Order>()
    private val newOrder  // an empty order containing only available categories
        get() = Order(productsByCategory.keys.map { it.id to it }.toMap())
    internal val order: LiveData<Order> = mOrder
    internal val orderTotal: LiveData<Double> = map(mOrder) { it.total }

    private val mProducts = MutableLiveData<List<Product>>()
    internal val products: LiveData<List<Product>> = mProducts

    private val mCategories = MutableLiveData<List<Category>>()
    internal val categories: LiveData<List<Category>> = mCategories

    private var undoOrder: Order? = null
    private val mRestartState = MutableLiveData<RestartState>().apply { value = DISABLED }
    internal val restartState: LiveData<RestartState> = mRestartState

    @Suppress("BlockingMethodInNonBlockingContext") // run on Dispatchers.Main
    override suspend fun onConfigurationReceived(json: JSONObject, currency: String): Boolean {
        // parse categories
        val categoriesStr = json.getJSONArray("categories").toString()
        val categoriesType = object : TypeReference<List<Category>>() {}
        val categories: List<Category> = mapper.readValue(categoriesStr, categoriesType)
        if (categories.isEmpty()) {
            Log.e(TAG, "No valid category found.")
            return false
        }
        // pre-select the first category
        categories[0].selected = true

        // parse products (live data gets updated in setCurrentCategory())
        val productsStr = json.getJSONArray("products").toString()
        val productsType = object : TypeReference<List<Product>>() {}
        val products: List<Product> = mapper.readValue(productsStr, productsType)

        // group products by categories
        productsByCategory.clear()
        products.forEach { product ->
            val productCurrency = fromString(product.price).currency
            if (productCurrency != currency) {
                Log.e(TAG, "Product $product has currency $productCurrency, $currency expected")
                return false
            }
            product.categories.forEach { categoryId ->
                val category = categories.find { it.id == categoryId }
                if (category == null) {
                    Log.e(TAG, "Product $product has unknown category $categoryId")
                    return false
                }
                if (productsByCategory.containsKey(category)) {
                    productsByCategory[category]?.add(product)
                } else {
                    productsByCategory[category] = ArrayList<Product>().apply { add(product) }
                }
            }
        }
        return if (productsByCategory.size > 0) {
            mCategories.postValue(categories)
            mProducts.postValue(productsByCategory[categories[0]])
            true
        } else {
            false
        }
    }

    internal fun setCurrentCategory(category: Category) {
        val newCategories = categories.value?.apply {
            forEach { if (it.selected) it.selected = false }
            category.selected = true
        }
        mCategories.postValue(newCategories)
        mProducts.postValue(productsByCategory[category])
    }

    @UiThread
    internal fun addProduct(product: Product) {
        val order = mOrder.value ?: newOrder
        mOrder.value = order + product
        mRestartState.value = ENABLED
    }

    @UiThread
    internal fun restartOrUndo() {
        if (restartState.value == UNDO) {
            mOrder.value = undoOrder
            mRestartState.value = ENABLED
            undoOrder = null
        } else {
            undoOrder = mOrder.value
            mOrder.value = newOrder
            mRestartState.value = UNDO
        }
    }

}