From a4796ec47d89a851b260b6fc195494547208a025 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 18 Mar 2020 14:24:41 -0300 Subject: Merge all three apps into one repository --- .../net/taler/merchantpos/order/Definitions.kt | 205 +++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 merchant-terminal/src/main/java/net/taler/merchantpos/order/Definitions.kt (limited to 'merchant-terminal/src/main/java/net/taler/merchantpos/order/Definitions.kt') diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/Definitions.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Definitions.kt new file mode 100644 index 0000000..63eda17 --- /dev/null +++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/Definitions.kt @@ -0,0 +1,205 @@ +/* + * 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 + */ + +package net.taler.merchantpos.order + +import androidx.core.os.LocaleListCompat +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL +import com.fasterxml.jackson.annotation.JsonProperty +import net.taler.merchantpos.Amount +import java.util.* +import java.util.Locale.LanguageRange +import kotlin.collections.ArrayList +import kotlin.collections.HashMap + +data class Category( + val id: Int, + val name: String, + @JsonProperty("name_i18n") + val nameI18n: Map? +) { + var selected: Boolean = false + val localizedName: String get() = getLocalizedString(nameI18n, name) +} + +@JsonInclude(NON_NULL) +abstract class Product { + @get:JsonProperty("product_id") + abstract val productId: String? + abstract val description: String + @get:JsonProperty("description_i18n") + abstract val descriptionI18n: Map? + abstract val price: String + @get:JsonProperty("delivery_location") + abstract val location: String? + abstract val image: String? + @get:JsonIgnore + val localizedDescription: String + get() = getLocalizedString(descriptionI18n, description) +} + +data class ConfigProduct( + @JsonIgnore + val id: String = UUID.randomUUID().toString(), + override val productId: String?, + override val description: String, + override val descriptionI18n: Map?, + override val price: String, + override val location: String?, + override val image: String?, + val categories: List, + @JsonIgnore + val quantity: Int = 0 +) : Product() { + val priceAsDouble by lazy { Amount.fromString(price).amount.toDouble() } + + override fun equals(other: Any?) = other is ConfigProduct && id == other.id + override fun hashCode() = id.hashCode() +} + +data class ContractProduct( + override val productId: String?, + override val description: String, + override val descriptionI18n: Map?, + override val price: String, + override val location: String?, + override val image: String?, + val quantity: Int +) : Product() { + constructor(product: ConfigProduct) : this( + product.productId, + product.description, + product.descriptionI18n, + product.price, + product.location, + product.image, + product.quantity + ) +} + +private fun getLocalizedString(map: Map?, default: String): String { + // just return the default, if it is the only element + if (map == null) return default + // create a priority list of language ranges from system locales + val locales = LocaleListCompat.getDefault() + val priorityList = ArrayList(locales.size()) + for (i in 0 until locales.size()) { + priorityList.add(LanguageRange(locales[i].toLanguageTag())) + } + // create a list of locales available in the given map + val availableLocales = map.keys.mapNotNull { + if (it == "_") return@mapNotNull null + val list = it.split("_") + when (list.size) { + 1 -> Locale(list[0]) + 2 -> Locale(list[0], list[1]) + 3 -> Locale(list[0], list[1], list[2]) + else -> null + } + } + val match = Locale.lookup(priorityList, availableLocales) + return match?.toString()?.let { map[it] } ?: default +} + +data class Order(val id: Int, val availableCategories: Map) { + val products = ArrayList() + val title: String = id.toString() + val summary: String + get() { + if (products.size == 1) return products[0].description + return getCategoryQuantities().map { (category: Category, quantity: Int) -> + "$quantity x ${category.localizedName}" + }.joinToString() + } + val total: Double + get() { + var total = 0.0 + products.forEach { product -> + val price = product.priceAsDouble + total += price * product.quantity + } + return total + } + val totalAsString: String + get() = String.format("%.2f", total) + + operator fun plus(product: ConfigProduct): Order { + val i = products.indexOf(product) + if (i == -1) { + products.add(product.copy(quantity = 1)) + } else { + val quantity = products[i].quantity + products[i] = products[i].copy(quantity = quantity + 1) + } + return this + } + + operator fun minus(product: ConfigProduct): Order { + val i = products.indexOf(product) + if (i == -1) return this + val quantity = products[i].quantity + if (quantity <= 1) { + products.remove(product) + } else { + products[i] = products[i].copy(quantity = quantity - 1) + } + return this + } + + private fun getCategoryQuantities(): HashMap { + val categories = HashMap() + products.forEach { product -> + val categoryId = product.categories[0] + val category = availableCategories.getValue(categoryId) + val oldQuantity = categories[category] ?: 0 + categories[category] = oldQuantity + product.quantity + } + return categories + } + + /** + * Returns a map of i18n summaries for each locale present in *all* given [Category]s + * or null if there's no locale that fulfills this criteria. + */ + val summaryI18n: Map? + get() { + if (products.size == 1) return products[0].descriptionI18n + val categoryQuantities = getCategoryQuantities() + // get all available locales + val availableLocales = categoryQuantities.mapNotNull { (category, _) -> + val nameI18n = category.nameI18n + // if one category doesn't have locales, we can return null here already + nameI18n?.keys ?: return null + }.flatten().toHashSet() + // remove all locales not supported by all categories + categoryQuantities.forEach { (category, _) -> + // category.nameI18n should be non-null now + availableLocales.retainAll(category.nameI18n!!.keys) + if (availableLocales.isEmpty()) return null + } + return availableLocales.map { locale -> + Pair( + locale, categoryQuantities.map { (category, quantity) -> + // category.nameI18n should be non-null now + "$quantity x ${category.nameI18n!![locale]}" + }.joinToString() + ) + }.toMap() + } + +} -- cgit v1.2.3