taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

Order.kt (4877B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2020 Taler Systems S.A.
      4  *
      5  * GNU Taler is free software; you can redistribute it and/or modify it under the
      6  * terms of the GNU General Public License as published by the Free Software
      7  * Foundation; either version 3, or (at your option) any later version.
      8  *
      9  * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License along with
     14  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 package net.taler.merchantpos.order
     18 
     19 import net.taler.common.Amount
     20 import net.taler.common.CurrencySpecification
     21 import net.taler.merchantpos.config.Category
     22 import net.taler.merchantpos.config.ConfigProduct
     23 
     24 data class Order(
     25     val id: Int,
     26     val currency: String,
     27     val currencySpec: CurrencySpecification?,
     28     val availableCategories: Map<Int, Category>,
     29     val products: List<ConfigProduct> = emptyList(),
     30 ) {
     31     val title: String = id.toString()
     32     val summary: String
     33         get() {
     34             if (products.size == 1) return products[0].description
     35             return getCategoryQuantities().map { (category: Category, quantity: Int) ->
     36                 "$quantity x ${category.localizedName}"
     37             }.joinToString()
     38         }
     39     val total: Amount
     40         get() {
     41             var total = Amount.zero(currency).withSpec(currencySpec)
     42             products.forEach { product ->
     43                 total += product.price * product.quantity
     44             }
     45             return total.withSpec(currencySpec)
     46         }
     47 
     48     operator fun plus(product: ConfigProduct): Order {
     49         val updatedProducts = products.toMutableList()
     50         val i = updatedProducts.indexOfFirst { it.id == product.id }
     51         if (i == -1) {
     52             updatedProducts.add(product.copy(quantity = 1))
     53         } else {
     54             val quantity = updatedProducts[i].quantity
     55             updatedProducts[i] = updatedProducts[i].copy(quantity = quantity + 1)
     56         }
     57         return copy(products = updatedProducts)
     58     }
     59 
     60     operator fun minus(product: ConfigProduct): Order {
     61         val updatedProducts = products.toMutableList()
     62         val i = updatedProducts.indexOfFirst { it.id == product.id }
     63         if (i == -1) return this
     64         val quantity = updatedProducts[i].quantity
     65         if (quantity <= 1) {
     66             updatedProducts.removeAt(i)
     67         } else {
     68             updatedProducts[i] = updatedProducts[i].copy(quantity = quantity - 1)
     69         }
     70         return copy(products = updatedProducts)
     71     }
     72 
     73     private fun getCategoryQuantities(): HashMap<Category, Int> {
     74         val categories = HashMap<Category, Int>()
     75         products.forEach { product ->
     76             val categoryId = product.categories[0]
     77             val category = availableCategories[categoryId] ?: return@forEach // custom products
     78             val oldQuantity = categories[category] ?: 0
     79             categories[category] = oldQuantity + product.quantity
     80         }
     81         return categories
     82     }
     83 
     84     /**
     85      * Returns a map of i18n summaries for each locale present in *all* given [Category]s
     86      * or null if there's no locale that fulfills this criteria.
     87      */
     88     private val summaryI18n: Map<String, String>?
     89         get() {
     90             if (products.size == 1) return products[0].descriptionI18n
     91             val categoryQuantities = getCategoryQuantities()
     92             // get all available locales
     93             val availableLocales = categoryQuantities.mapNotNull { (category, _) ->
     94                 val nameI18n = category.nameI18n
     95                 // if one category doesn't have locales, we can return null here already
     96                 nameI18n?.keys ?: return null
     97             }.flatten().toHashSet()
     98             // remove all locales not supported by all categories
     99             categoryQuantities.forEach { (category, _) ->
    100                 // category.nameI18n should be non-null now
    101                 availableLocales.retainAll(category.nameI18n!!.keys)
    102                 if (availableLocales.isEmpty()) return null
    103             }
    104             return availableLocales.associateWith { locale ->
    105                 categoryQuantities.map { (category, quantity) ->
    106                     // category.nameI18n should be non-null now
    107                     "$quantity x ${category.nameI18n!![locale]}"
    108                 }.joinToString()
    109             }
    110         }
    111 
    112     fun toContractTerms(includeProducts: Boolean = true): net.taler.common.Order {
    113         return net.taler.common.Order(
    114             summary = summary,
    115             summaryI18n = summaryI18n,
    116             amount = total,
    117             products = if (includeProducts) products.map { it.toContractProduct() } else null,
    118         )
    119     }
    120 
    121 }