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 }