commit cb8050d841a2e20271c8c94a114b0c351f182f8a
parent 47d63c9d8d9c77e361c4a455d5bc761a4350fbf0
Author: Iván Ávalos <avalos@disroot.org>
Date: Tue, 20 Aug 2024 16:06:57 +0200
[wallet] WIP: taxes
bug 0009081
Diffstat:
7 files changed, 123 insertions(+), 16 deletions(-)
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -65,6 +65,7 @@ data class ContractProduct(
override val location: String? = null,
override val image: String? = null,
val quantity: Int = 1,
+ val taxes: List<Tax>? = null,
) : Product() {
val totalPrice: Amount? by lazy {
price?.let { price * quantity }
@@ -72,6 +73,12 @@ data class ContractProduct(
}
@Serializable
+data class Tax(
+ val name: String,
+ val tax: Amount,
+)
+
+@Serializable
data class ContractMerchant(
val name: String
)
diff --git a/wallet/src/main/java/net/taler/wallet/payment/ProductAdapter.kt b/wallet/src/main/java/net/taler/wallet/payment/ProductAdapter.kt
@@ -16,6 +16,7 @@
package net.taler.wallet.payment
+import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory.decodeByteArray
import android.util.Base64
@@ -26,9 +27,11 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
+import androidx.compose.ui.util.fastDistinctBy
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import net.taler.common.Amount
import net.taler.common.ContractProduct
import net.taler.wallet.R
import net.taler.wallet.payment.ProductAdapter.ProductViewHolder
@@ -41,6 +44,7 @@ internal class ProductAdapter(private val listener: ProductImageClickListener) :
RecyclerView.Adapter<ProductViewHolder>() {
private var items = emptyList<ContractProduct>()
+ private var taxesEqual = true
override fun getItemCount() = items.size
@@ -62,12 +66,16 @@ internal class ProductAdapter(private val listener: ProductImageClickListener) :
diffResult.dispatchUpdatesTo(this)
items = newItems
+ taxesEqual = newItems.distinctBy { it.taxes }.size > 1
}
internal inner class ProductViewHolder(v: View) : ViewHolder(v) {
+ private val context: Context = v.context
+
private val quantity: TextView = v.findViewById(R.id.quantity)
private val image: ImageView = v.findViewById(R.id.image)
private val name: TextView = v.findViewById(R.id.name)
+ private val taxes: TextView = v.findViewById(R.id.taxes)
private val price: TextView = v.findViewById(R.id.price)
fun bind(product: ContractProduct) {
@@ -87,10 +95,24 @@ internal class ProductAdapter(private val listener: ProductImageClickListener) :
}
}
name.text = product.description
- price.visibility = product.totalPrice?.let {
- price.text = it.toString()
- VISIBLE
- } ?: GONE
+
+ if (product.totalPrice != null) {
+ price.visibility = VISIBLE
+ price.text = product.totalPrice.toString()
+ } else {
+ price.visibility = GONE
+ }
+
+ if (!taxesEqual && product.taxes != null) {
+ taxes.visibility = VISIBLE
+ taxes.text = product.taxes!!.filter {
+ !it.tax.isZero()
+ }.joinToString(separator = "\n") {
+ context.getString(R.string.payment_tax, it.name, it.tax)
+ }
+ } else {
+ taxes.visibility = GONE
+ }
}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -22,6 +22,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
+import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@@ -33,6 +34,7 @@ import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import kotlinx.coroutines.launch
import net.taler.common.Amount
import net.taler.common.ContractTerms
+import net.taler.common.Tax
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.common.showError
@@ -102,7 +104,8 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
is PayStatus.Prepared -> {
showLoading(false)
val fees = payStatus.amountEffective - payStatus.amountRaw
- showOrder(payStatus.contractTerms, payStatus.amountRaw, fees)
+ val taxes = compileTaxes(payStatus.contractTerms)
+ showOrder(payStatus.contractTerms, payStatus.amountRaw, fees, taxes)
ui.bottom.confirmButton.isEnabled = true
ui.bottom.confirmButton.setOnClickListener {
model.showProgressBar.value = true
@@ -153,17 +156,44 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
}
}
- private fun showOrder(contractTerms: ContractTerms, amount: Amount, totalFees: Amount? = null) {
+ private fun compileTaxes(contractTerms: ContractTerms): List<Tax>? {
+ val distinct = contractTerms.products.distinctBy { it.taxes }
+ return if (distinct.size == 1) {
+ distinct.first().taxes
+ } else {
+ null
+ }
+ }
+
+ private fun showOrder(
+ contractTerms: ContractTerms,
+ amount: Amount,
+ totalFees: Amount? = null,
+ taxes: List<Tax>? = null,
+ ) {
ui.details.orderView.text = contractTerms.summary
adapter.update(contractTerms.products)
ui.details.productsList.fadeIn()
ui.bottom.totalView.text = amount.toString()
+
if (totalFees != null && !totalFees.isZero()) {
ui.bottom.feeView.text = getString(R.string.payment_fee, totalFees)
ui.bottom.feeView.fadeIn()
} else {
ui.bottom.feeView.visibility = GONE
}
+
+ if (taxes != null) {
+ ui.bottom.feeView.visibility = VISIBLE
+ ui.bottom.feeView.text = taxes.filter {
+ !it.tax.isZero()
+ }.joinToString(separator = "\n") {
+ requireContext().getString(R.string.payment_tax, it.name, it.tax)
+ }
+ } else {
+ ui.bottom.feeView.visibility = GONE
+ }
+
ui.details.orderLabelView.fadeIn()
ui.details.orderView.fadeIn()
ui.bottom.totalLabelView.fadeIn()
diff --git a/wallet/src/main/res/layout-w550dp/payment_bottom_bar.xml b/wallet/src/main/res/layout-w550dp/payment_bottom_bar.xml
@@ -67,16 +67,32 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
- android:layout_marginBottom="8dp"
android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/confirmButton"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/taxView"
app:layout_constraintTop_toBottomOf="@+id/totalView"
tools:text="@string/payment_fee"
tools:visibility="visible" />
+ <TextView
+ android:id="@+id/taxView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="8dp"
+ android:visibility="gone"
+ app:layout_constraintEnd_toStartOf="@+id/confirmButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/feeView"
+ app:layout_constraintBottom_toBottomOf="parent"
+ tools:text="@string/payment_tax"
+ tools:visibility="visible" />
+
<Button
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
diff --git a/wallet/src/main/res/layout/list_item_product.xml b/wallet/src/main/res/layout/list_item_product.xml
@@ -30,7 +30,6 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
tools:text="31" />
<ImageView
@@ -55,21 +54,37 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/taxes"
app:layout_constraintEnd_toStartOf="@+id/price"
app:layout_constraintStart_toEndOf="@+id/image"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
tools:text="A product item that in some cases could have a very long name" />
<TextView
- android:id="@+id/price"
+ android:id="@+id/taxes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:paddingTop="3dp"
+ app:layout_constraintEnd_toStartOf="@id/price"
+ app:layout_constraintStart_toEndOf="@id/image"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ android:textStyle="italic"
+ android:visibility="gone"
+ tools:visibility="visible"
+ tools:text="incl. 0.2€ VAT" />
+
+ <TextView
+ android:id="@+id/price"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:textStyle="bold"
tools:text="23.42" />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/wallet/src/main/res/layout/payment_bottom_bar.xml b/wallet/src/main/res/layout/payment_bottom_bar.xml
@@ -71,7 +71,7 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
- app:layout_constraintBottom_toTopOf="@+id/confirmButton"
+ app:layout_constraintBottom_toTopOf="@+id/taxView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
@@ -79,6 +79,21 @@
tools:text="@string/payment_fee"
tools:visibility="visible" />
+ <TextView
+ android:id="@+id/taxView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@+id/confirmButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/feeView"
+ tools:text="@string/payment_tax"
+ tools:visibility="visible" />
+
<Button
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
@@ -91,7 +106,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/feeView"
+ app:layout_constraintTop_toBottomOf="@+id/taxView"
tools:enabled="true" />
<ProgressBar
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
@@ -188,6 +188,8 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<string name="payment_pay_template_title">Customize your order</string>
<string name="payment_pending">Payment not completed, it will be retried</string>
<string name="payment_prompt_title">Review Payment</string>
+ <!-- including <amount> <tax name> -->
+ <string name="payment_tax">incl. %1$s %2$s</string>
<string name="payment_template_error">Error creating order</string>
<string name="payment_title">Payment</string>