PosConfig.kt (6477B)
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.config 18 19 import android.os.Build.VERSION.SDK_INT 20 import kotlinx.serialization.SerialName 21 import kotlinx.serialization.Serializable 22 import net.taler.common.Amount 23 import net.taler.common.ContractProduct 24 import net.taler.common.OrderProduct 25 import net.taler.common.TalerUtils 26 import net.taler.common.Tax 27 import net.taler.merchantlib.MerchantConfig 28 import java.math.RoundingMode 29 import java.util.UUID 30 31 sealed class Config { 32 abstract fun isValid(): Boolean 33 abstract fun hasPassword(): Boolean 34 abstract fun savePassword(): Boolean 35 36 /** 37 * JSON config URL + user/password 38 * 39 * @Deprecated("Use New instead") 40 */ 41 /* 42 data class Old( 43 val configUrl: String, 44 val username: String, 45 val password: String, 46 val savePassword: Boolean, 47 ): Config() { 48 override fun isValid() = configUrl.isNotBlank() 49 override fun hasPassword() = password.isNotBlank() 50 override fun savePassword() = savePassword 51 } 52 */ 53 54 /** 55 * Merchant URL + access token 56 */ 57 data class New( 58 val merchantUrl: String, 59 val accessToken: String, 60 val savePassword: Boolean, 61 ): Config() { 62 override fun isValid() = merchantUrl.isNotBlank() 63 override fun hasPassword() = accessToken.isNotBlank() 64 override fun savePassword() = savePassword 65 } 66 } 67 68 @Serializable 69 data class PosConfig( 70 @SerialName("config") 71 val merchantConfig: MerchantConfig? = null , 72 val categories: List<Category>, 73 val products: List<ConfigProduct> 74 ) 75 76 @Serializable 77 data class Category( 78 val id: Int, 79 val name: String, 80 @SerialName("name_i18n") 81 val nameI18n: Map<String, String>? = null 82 ) { 83 var selected: Boolean = false 84 val localizedName: String 85 get() = if (SDK_INT >= 26) TalerUtils.getLocalizedString(nameI18n, name) else name 86 } 87 88 @Serializable 89 data class ConfigProduct( 90 val id: String = UUID.randomUUID().toString(), 91 @SerialName("product_id") 92 override val productId: String? = null, 93 @SerialName("product_name") 94 override val productName: String? = null, 95 override val description: String, 96 @SerialName("description_i18n") 97 override val descriptionI18n: Map<String, String>? = null, 98 override val price: Amount, 99 @SerialName("prices_are_net") 100 val pricesAreNet: Boolean = false, 101 @SerialName("delivery_location") 102 override val location: String? = null, 103 override val image: String? = null, 104 override val taxes: Set<Tax>? = null, 105 val categories: List<Int>, 106 val quantity: Int = 0, 107 @SerialName("total_stock") 108 val totalStock: Int? = null, 109 @SerialName("unit_total_stock") 110 val unitTotalStock: String? = null, 111 @SerialName("total_sold") 112 val totalSold: Int? = null, 113 @SerialName("unit_total_sold") 114 val unitTotalSold: String? = null, 115 @SerialName("total_lost") 116 val totalLost: Int? = null, 117 @SerialName("unit_total_lost") 118 val unitTotalLost: String? = null, 119 val availableToSell: Boolean = true, 120 val remainingStock: Int? = null, 121 val currencyMismatch: Boolean = false, 122 ) : OrderProduct() { 123 val totalPrice: Amount 124 get() = (price * quantity).withSpec(price.spec) 125 private val normalizedProductName: String? 126 get() = productName?.sanitizeVisibleText()?.takeIf { it.isNotEmpty() } 127 private val normalizedDescription: String 128 get() = localizedDescription.sanitizeVisibleText() 129 val displayName: String 130 get() = normalizedProductName ?: normalizedDescription 131 val displayDescription: String? 132 get() = normalizedProductName 133 ?.takeIf { it != normalizedDescription } 134 ?.let { normalizedDescription.takeIf(String::isNotEmpty) } 135 val displayPrice: String 136 get() = price.toString() 137 val stableKey: String 138 get() = listOf( 139 productId?.trim().orEmpty(), 140 productName?.trim().orEmpty(), 141 description.trim(), 142 price.currency, 143 price.value.toString(), 144 price.fraction.toString(), 145 categories.joinToString(","), 146 ).joinToString("|") 147 val stockLimit: Int? 148 get() { 149 if (totalStock == -1 || unitTotalStock == "-1") return null 150 val total = totalStock 151 ?: unitTotalStock?.toBigDecimalOrNull() 152 ?.setScale(0, RoundingMode.DOWN) 153 ?.toInt() 154 ?: return null 155 val sold = totalSold 156 ?: unitTotalSold?.toBigDecimalOrNull() 157 ?.setScale(0, RoundingMode.DOWN) 158 ?.toInt() 159 ?: 0 160 val lost = totalLost 161 ?: unitTotalLost?.toBigDecimalOrNull() 162 ?.setScale(0, RoundingMode.DOWN) 163 ?.toInt() 164 ?: 0 165 return (total - sold - lost).coerceAtLeast(0) 166 } 167 168 fun toContractProduct() = ContractProduct( 169 productId = productId, 170 productName = productName ?: description, 171 description = description, 172 descriptionI18n = descriptionI18n?.takeIf { it.isNotEmpty() }, 173 price = price, 174 pricesAreNet = pricesAreNet, 175 location = null, 176 image = image?.takeIf { it.isNotBlank() }, 177 taxes = taxes?.takeIf { it.isNotEmpty() }, 178 quantity = quantity 179 ) 180 } 181 182 private fun String.sanitizeVisibleText(): String { 183 return filterNot { char -> 184 when (Character.getType(char)) { 185 Character.FORMAT.toInt(), 186 Character.CONTROL.toInt(), 187 Character.SURROGATE.toInt(), 188 Character.PRIVATE_USE.toInt(), 189 Character.UNASSIGNED.toInt() -> true 190 else -> false 191 } 192 }.trim() 193 }