summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-08-04 09:46:38 -0300
committerTorsten Grote <t@grobox.de>2020-08-04 09:46:38 -0300
commit35f7ed512ed7445362d6caee1bf60441f4ce979e (patch)
tree08fb02c802ff36065dd85fb9dcb95a0adabc03c3
parentd3a035c59c508b7b0ef3c06a1b0f1f3c0a077bb8 (diff)
downloadtaler-android-35f7ed512ed7445362d6caee1bf60441f4ce979e.tar.gz
taler-android-35f7ed512ed7445362d6caee1bf60441f4ce979e.tar.bz2
taler-android-35f7ed512ed7445362d6caee1bf60441f4ce979e.zip
[pos] Implement new refund API (untested since there is no wallet support)
Also do a bit of code cleanup and minor refactorings This also removes the volley HTTP library which is not needed anymore
-rw-r--r--merchant-lib/src/main/AndroidManifest.xml1
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Config.kt (renamed from merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt)15
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt12
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt (renamed from merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt)0
-rw-r--r--merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt (renamed from merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt)23
-rw-r--r--merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt98
-rw-r--r--merchant-terminal/build.gradle3
-rw-r--r--merchant-terminal/src/main/AndroidManifest.xml1
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt7
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt15
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt (renamed from merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfigFragment.kt)4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt59
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt (renamed from merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt)6
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt134
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt40
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoryAdapter.kt62
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderAdapter.kt114
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt92
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt4
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt (renamed from merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt)23
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt91
-rw-r--r--merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundUriFragment.kt (renamed from merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt)2
-rw-r--r--merchant-terminal/src/main/res/layout/fragment_merchant_config.xml2
-rw-r--r--merchant-terminal/src/main/res/layout/fragment_refund.xml2
-rw-r--r--merchant-terminal/src/main/res/navigation/nav_graph.xml8
25 files changed, 431 insertions, 387 deletions
diff --git a/merchant-lib/src/main/AndroidManifest.xml b/merchant-lib/src/main/AndroidManifest.xml
index 7318c077..1408b335 100644
--- a/merchant-lib/src/main/AndroidManifest.xml
+++ b/merchant-lib/src/main/AndroidManifest.xml
@@ -17,7 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.taler.merchantlib">
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Config.kt
index a8d113e1..eb09485c 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantConfig.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Config.kt
@@ -20,6 +20,21 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
+data class ConfigResponse(
+ /**
+ * libtool-style representation of the Merchant protocol version, see
+ * https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ * The format is "current:revision:age".
+ */
+ val version: String,
+
+ /**
+ Currency supported by this backend.
+ */
+ val currency: String
+)
+
+@Serializable
data class MerchantConfig(
@SerialName("base_url")
val baseUrl: String,
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
index 96892f5f..c92d4d2b 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -72,6 +72,18 @@ class MerchantApi(private val httpClient: HttpClient) {
} as OrderHistory
}
+ suspend fun giveRefund(
+ merchantConfig: MerchantConfig,
+ orderId: String,
+ request: RefundRequest
+ ): Response<RefundResponse> = response {
+ httpClient.post(merchantConfig.urlFor("private/orders/$orderId/refund")) {
+ header(Authorization, "ApiKey ${merchantConfig.apiKey}")
+ contentType(Json)
+ body = request
+ } as RefundResponse
+ }
+
}
fun getDefaultHttpClient(): HttpClient = HttpClient(OkHttp) {
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
index 783dd19d..783dd19d 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/PostOrderRequest.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt b/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt
index 49164e67..61f0ab72 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/ConfigResponse.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Refunds.kt
@@ -16,19 +16,28 @@
package net.taler.merchantlib
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
+import net.taler.common.Amount
@Serializable
-data class ConfigResponse(
+data class RefundRequest(
/**
- * libtool-style representation of the Merchant protocol version, see
- * https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
- * The format is "current:revision:age".
+ * Amount to be refunded
*/
- val version: String,
+ val refund: Amount,
/**
- Currency supported by this backend.
+ * Human-readable refund justification
*/
- val currency: String
+ val reason: String
+)
+
+@Serializable
+data class RefundResponse(
+ /**
+ * URL (handled by the backend) that the wallet should access to trigger refund processing.
+ */
+ @SerialName("taler_refund_uri")
+ val talerRefundUri: String
)
diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
index 437697b6..f9f5e87b 100644
--- a/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
+++ b/merchant-lib/src/test/java/net/taler/merchantlib/MerchantApiTest.kt
@@ -16,11 +16,12 @@
package net.taler.merchantlib
-import io.ktor.http.HttpStatusCode
+import io.ktor.http.HttpStatusCode.Companion.NotFound
import kotlinx.coroutines.runBlocking
import net.taler.common.Amount
import net.taler.common.ContractProduct
import net.taler.common.ContractTerms
+import net.taler.common.Timestamp
import net.taler.merchantlib.MockHttpClient.giveJsonResponse
import net.taler.merchantlib.MockHttpClient.httpClient
import org.junit.Assert.assertEquals
@@ -35,6 +36,7 @@ class MerchantApiTest {
instance = "testInstance",
apiKey = "apiKeyFooBar"
)
+ private val orderId = "orderIdFoo"
@Test
fun testGetConfig() = runBlocking {
@@ -95,7 +97,7 @@ class MerchantApiTest {
httpClient.giveJsonResponse(
"http://example.net/instances/testInstance/private/orders",
- statusCode = HttpStatusCode.NotFound
+ statusCode = NotFound
) {
"""{
"code": 2000,
@@ -110,7 +112,6 @@ class MerchantApiTest {
@Test
fun testCheckOrder() = runBlocking {
- val orderId = "orderIdFoo"
val unpaidResponse = CheckPaymentResponse.Unpaid(false, "http://taler.net/foo")
httpClient.giveJsonResponse("http://example.net/instances/testInstance/private/orders/$orderId") {
"""{
@@ -125,7 +126,7 @@ class MerchantApiTest {
httpClient.giveJsonResponse(
"http://example.net/instances/testInstance/private/orders/$orderId",
- statusCode = HttpStatusCode.NotFound
+ statusCode = NotFound
) {
"""{
"code": 2909,
@@ -138,4 +139,93 @@ class MerchantApiTest {
}
}
+ @Test
+ fun testDeleteOrder() = runBlocking {
+ httpClient.giveJsonResponse("http://example.net/instances/testInstance/private/orders/$orderId") {
+ "{}"
+ }
+ api.deleteOrder(merchantConfig, orderId).assertSuccess {}
+
+ httpClient.giveJsonResponse(
+ "http://example.net/instances/testInstance/private/orders/$orderId",
+ statusCode = NotFound
+ ) {
+ """{
+ "code": 2511,
+ "hint": "Order unknown"
+ }
+ """.trimIndent()
+ }
+ api.deleteOrder(merchantConfig, orderId).assertFailure {
+ assertTrue(it.contains("2511"))
+ assertTrue(it.contains("Order unknown"))
+ }
+ }
+
+ @Test
+ fun testGetOrderHistory() = runBlocking {
+ httpClient.giveJsonResponse("http://example.net/instances/testInstance/private/orders") {
+ """{ "orders": [
+ {
+ "order_id": "2020.217-0281FGXCS25P2",
+ "row_id": 183,
+ "timestamp": {
+ "t_ms": 1596542338000
+ },
+ "amount": "TESTKUDOS:1",
+ "summary": "Chips",
+ "refundable": true,
+ "paid": true
+ },
+ {
+ "order_id": "2020.216-01G2ZPXSP6BYT",
+ "row_id": 154,
+ "timestamp": {
+ "t_ms": 1596468174000
+ },
+ "amount": "TESTKUDOS:0.8",
+ "summary": "Peanuts",
+ "refundable": false,
+ "paid": false
+ }
+ ]
+ }""".trimIndent()
+ }
+ api.getOrderHistory(merchantConfig).assertSuccess {
+ assertEquals(2, it.orders.size)
+
+ val order1 = it.orders[0]
+ assertEquals(Amount("TESTKUDOS", 1, 0), order1.amount)
+ assertEquals("2020.217-0281FGXCS25P2", order1.orderId)
+ assertEquals(true, order1.paid)
+ assertEquals(true, order1.refundable)
+ assertEquals("Chips", order1.summary)
+ assertEquals(Timestamp(1596542338000), order1.timestamp)
+
+ val order2 = it.orders[1]
+ assertEquals(Amount("TESTKUDOS", 0, 80000000), order2.amount)
+ assertEquals("2020.216-01G2ZPXSP6BYT", order2.orderId)
+ assertEquals(false, order2.paid)
+ assertEquals(false, order2.refundable)
+ assertEquals("Peanuts", order2.summary)
+ assertEquals(Timestamp(1596468174000), order2.timestamp)
+ }
+ }
+
+ @Test
+ fun testGiveRefund() = runBlocking {
+ httpClient.giveJsonResponse("http://example.net/instances/testInstance/private/orders/$orderId/refund") {
+ """{
+ "taler_refund_uri": "taler://refund/foo/bar"
+ }""".trimIndent()
+ }
+ val request = RefundRequest(
+ refund = Amount("TESTKUDOS", 5, 0),
+ reason = "Give me my money back now!!!"
+ )
+ api.giveRefund(merchantConfig, orderId, request).assertSuccess {
+ assertEquals("taler://refund/foo/bar", it.talerRefundUri)
+ }
+ }
+
}
diff --git a/merchant-terminal/build.gradle b/merchant-terminal/build.gradle
index 44998929..1bdc1385 100644
--- a/merchant-terminal/build.gradle
+++ b/merchant-terminal/build.gradle
@@ -68,9 +68,6 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
- // HTTP Requests
- implementation 'com.android.volley:volley:1.1.1'
-
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
testImplementation 'androidx.test.ext:junit:1.1.1'
diff --git a/merchant-terminal/src/main/AndroidManifest.xml b/merchant-terminal/src/main/AndroidManifest.xml
index 3d89fee5..15182932 100644
--- a/merchant-terminal/src/main/AndroidManifest.xml
+++ b/merchant-terminal/src/main/AndroidManifest.xml
@@ -19,7 +19,6 @@
package="net.taler.merchantpos">
<uses-permission android:name="android.permission.NFC" />
- <uses-permission android:name="android.permission.INTERNET" />
<uses-feature
android:name="android.hardware.nfc"
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
index 905738b6..5f5d534a 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/MainViewModel.kt
@@ -19,20 +19,18 @@ package net.taler.merchantpos
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
-import com.android.volley.toolbox.Volley
import net.taler.merchantlib.MerchantApi
import net.taler.merchantlib.getDefaultHttpClient
import net.taler.merchantpos.config.ConfigManager
import net.taler.merchantpos.history.HistoryManager
-import net.taler.merchantpos.history.RefundManager
import net.taler.merchantpos.order.OrderManager
import net.taler.merchantpos.payment.PaymentManager
+import net.taler.merchantpos.refund.RefundManager
class MainViewModel(app: Application) : AndroidViewModel(app) {
private val httpClient = getDefaultHttpClient()
private val api = MerchantApi(httpClient)
- private val queue = Volley.newRequestQueue(app)
val orderManager = OrderManager(app)
val configManager = ConfigManager(app, viewModelScope, httpClient, api).apply {
@@ -40,11 +38,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
}
val paymentManager = PaymentManager(app, configManager, viewModelScope, api)
val historyManager = HistoryManager(configManager, viewModelScope, api)
- val refundManager = RefundManager(configManager, queue)
+ val refundManager = RefundManager(configManager, viewModelScope, api)
override fun onCleared() {
httpClient.close()
- queue.cancelAll { !it.isCanceled }
}
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
index 9deb042c..578debf8 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/Utils.kt
@@ -16,15 +16,11 @@
package net.taler.merchantpos
-import android.util.Log
import android.view.View
import androidx.annotation.StringRes
-import com.android.volley.Response
-import com.android.volley.VolleyError
import com.google.android.material.snackbar.BaseTransientBottomBar.ANIMATION_MODE_FADE
import com.google.android.material.snackbar.BaseTransientBottomBar.Duration
import com.google.android.material.snackbar.Snackbar.make
-import net.taler.merchantpos.MainActivity.Companion.TAG
fun topSnackbar(view: View, text: CharSequence, @Duration duration: Int) {
make(view, text, duration)
@@ -36,14 +32,3 @@ fun topSnackbar(view: View, text: CharSequence, @Duration duration: Int) {
fun topSnackbar(view: View, @StringRes resId: Int, @Duration duration: Int) {
topSnackbar(view, view.resources.getText(resId), duration)
}
-
-class LogErrorListener(private val onError: (error: VolleyError) -> Any) :
- Response.ErrorListener {
-
- override fun onErrorResponse(error: VolleyError) {
- val body = error.networkResponse.data?.let { String(it) }
- Log.e(TAG, "$error $body")
- onError.invoke(error)
- }
-
-}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfigFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt
index 77a87fb7..daddbff8 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantConfigFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigFragment.kt
@@ -34,13 +34,13 @@ import kotlinx.android.synthetic.main.fragment_merchant_config.*
import net.taler.common.navigate
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
-import net.taler.merchantpos.config.MerchantConfigFragmentDirections.Companion.actionSettingsToOrder
+import net.taler.merchantpos.config.ConfigFragmentDirections.Companion.actionSettingsToOrder
import net.taler.merchantpos.topSnackbar
/**
* Fragment that displays merchant settings.
*/
-class MerchantConfigFragment : Fragment() {
+class ConfigFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val configManager by lazy { model.configManager }
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
deleted file mode 100644
index 5d411962..00000000
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/config/MerchantRequest.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>
- */
-
-package net.taler.merchantpos.config
-
-import android.net.Uri
-import android.util.ArrayMap
-import com.android.volley.Response
-import com.android.volley.toolbox.JsonObjectRequest
-import net.taler.merchantlib.MerchantConfig
-import net.taler.merchantpos.LogErrorListener
-import org.json.JSONObject
-
-class MerchantRequest(
- method: Int,
- private val merchantConfig: MerchantConfig,
- endpoint: String,
- params: Map<String, String>?,
- jsonRequest: JSONObject?,
- listener: Response.Listener<JSONObject>,
- errorListener: LogErrorListener
-) :
- JsonObjectRequest(
- method,
- merchantConfig.legacyUrl(endpoint, params),
- jsonRequest,
- listener,
- errorListener
- ) {
-
- override fun getHeaders(): MutableMap<String, String> {
- val headerMap = ArrayMap<String, String>()
- headerMap["Authorization"] = "ApiKey " + merchantConfig.apiKey
- return headerMap
- }
-
-}
-
-private fun MerchantConfig.legacyUrl(endpoint: String, params: Map<String, String>?): String {
- val uriBuilder = Uri.parse(baseUrl).buildUpon()
- uriBuilder.appendPath(endpoint)
- params?.forEach {
- uriBuilder.appendQueryParameter(it.key, it.value)
- }
- return uriBuilder.toString()
-}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt
index 596b8b02..8cc435aa 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/MerchantHistoryFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/history/HistoryFragment.kt
@@ -35,8 +35,8 @@ import net.taler.common.navigate
import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
-import net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionGlobalMerchantSettings
-import net.taler.merchantpos.history.MerchantHistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment
+import net.taler.merchantpos.history.HistoryFragmentDirections.Companion.actionGlobalMerchantSettings
+import net.taler.merchantpos.history.HistoryFragmentDirections.Companion.actionNavHistoryToRefundFragment
internal interface RefundClickListener {
fun onRefundClicked(item: OrderHistoryEntry)
@@ -45,7 +45,7 @@ internal interface RefundClickListener {
/**
* Fragment to display the merchant's payment history, received from the backend.
*/
-class MerchantHistoryFragment : Fragment(), RefundClickListener {
+class HistoryFragment : Fragment(), RefundClickListener {
companion object {
const val TAG = "taler-merchant"
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
deleted file mode 100644
index 7f9b4c5a..00000000
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundManager.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>
- */
-
-package net.taler.merchantpos.history
-
-import android.util.Log
-import androidx.annotation.UiThread
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.android.volley.Request.Method.POST
-import com.android.volley.RequestQueue
-import com.android.volley.Response.Listener
-import com.android.volley.VolleyError
-import net.taler.common.Amount
-import net.taler.merchantlib.OrderHistoryEntry
-import net.taler.merchantpos.LogErrorListener
-import net.taler.merchantpos.config.ConfigManager
-import net.taler.merchantpos.config.MerchantRequest
-import org.json.JSONObject
-
-sealed class RefundResult {
- object Error : RefundResult()
- object PastDeadline : RefundResult()
- object AlreadyRefunded : RefundResult()
- class Success(
- val refundUri: String,
- val item: OrderHistoryEntry,
- val amount: Amount,
- val reason: String
- ) : RefundResult()
-}
-
-class RefundManager(
- private val configManager: ConfigManager,
- private val queue: RequestQueue
-) {
-
- companion object {
- val TAG = RefundManager::class.java.simpleName
- }
-
- var toBeRefunded: OrderHistoryEntry? = null
- private set
-
- private val mRefundResult = MutableLiveData<RefundResult>()
- internal val refundResult: LiveData<RefundResult> = mRefundResult
-
- @UiThread
- internal fun startRefund(item: OrderHistoryEntry) {
- toBeRefunded = item
- mRefundResult.value = null
- }
-
- @UiThread
- internal fun abortRefund() {
- toBeRefunded = null
- mRefundResult.value = null
- }
-
- @UiThread
- internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) {
- val merchantConfig = configManager.merchantConfig!!
- val refundRequest = mapOf(
- "order_id" to item.orderId,
- "refund" to amount.toJSONString(),
- "reason" to reason
- )
- val body = JSONObject(refundRequest)
- Log.d(TAG, body.toString(4))
- val req = MerchantRequest(POST, merchantConfig, "refund", null, body,
- Listener { onRefundResponse(it, item, amount, reason) },
- LogErrorListener { onRefundError(it) }
- )
- queue.add(req)
- }
-
- @UiThread
- private fun onRefundResponse(
- json: JSONObject,
- item: OrderHistoryEntry,
- amount: Amount,
- reason: String
- ) {
- if (!json.has("contract_terms")) {
- Log.e(TAG, "Contract terms missing: $json")
- onRefundError()
- return
- }
-
- val contractTerms = json.getJSONObject("contract_terms")
- val refundDeadline = if (contractTerms.has("refund_deadline")) {
- contractTerms.getJSONObject("refund_deadline").getLong("t_ms")
- } else null
- val autoRefund = contractTerms.has("auto_refund")
- val refundUri = json.getString("taler_refund_uri")
-
- Log.e("TEST", "refundDeadline: $refundDeadline")
- if (refundDeadline != null) Log.e(
- "TEST",
- "refundDeadline passed: ${System.currentTimeMillis() > refundDeadline}"
- )
- Log.e("TEST", "autoRefund: $autoRefund")
- Log.e("TEST", "refundUri: $refundUri")
-
- mRefundResult.value = RefundResult.Success(refundUri, item, amount, reason)
- }
-
- @UiThread
- private fun onRefundError(error: VolleyError? = null) {
- val data = error?.networkResponse?.data
- if (data != null) {
- val json = JSONObject(String(data))
- if (json.has("code") && json.getInt("code") == 2602) {
- mRefundResult.value = RefundResult.AlreadyRefunded
- return
- }
- }
- mRefundResult.value = RefundResult.Error
- }
-
-}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt
index e935d4f4..4f8e5af0 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt
@@ -21,18 +21,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.View.INVISIBLE
import android.view.ViewGroup
-import android.widget.Button
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.Adapter
import kotlinx.android.synthetic.main.fragment_categories.*
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
import net.taler.merchantpos.config.Category
-import net.taler.merchantpos.order.CategoryAdapter.CategoryViewHolder
interface CategorySelectionListener {
fun onCategorySelected(category: Category)
@@ -69,39 +65,3 @@ class CategoriesFragment : Fragment(), CategorySelectionListener {
}
}
-
-private class CategoryAdapter(
- private val listener: CategorySelectionListener
-) : Adapter<CategoryViewHolder>() {
-
- private val categories = ArrayList<Category>()
-
- override fun getItemCount() = categories.size
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
- val view =
- LayoutInflater.from(parent.context).inflate(R.layout.list_item_category, parent, false)
- return CategoryViewHolder(view)
- }
-
- override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
- holder.bind(categories[position])
- }
-
- fun setItems(items: List<Category>) {
- categories.clear()
- categories.addAll(items)
- notifyDataSetChanged()
- }
-
- private inner class CategoryViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- private val button: Button = v.findViewById(R.id.button)
-
- fun bind(category: Category) {
- button.text = category.localizedName
- button.isPressed = category.selected
- button.setOnClickListener { listener.onCategorySelected(category) }
- }
- }
-
-}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoryAdapter.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoryAdapter.kt
new file mode 100644
index 00000000..c690ec5c
--- /dev/null
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/CategoryAdapter.kt
@@ -0,0 +1,62 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.merchantpos.order
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import net.taler.merchantpos.R
+import net.taler.merchantpos.config.Category
+import net.taler.merchantpos.order.CategoryAdapter.CategoryViewHolder
+
+internal class CategoryAdapter(private val listener: CategorySelectionListener) :
+ Adapter<CategoryViewHolder>() {
+
+ private val categories = ArrayList<Category>()
+
+ override fun getItemCount() = categories.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.list_item_category, parent, false)
+ return CategoryViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
+ holder.bind(categories[position])
+ }
+
+ fun setItems(items: List<Category>) {
+ categories.clear()
+ categories.addAll(items)
+ notifyDataSetChanged()
+ }
+
+ internal inner class CategoryViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ private val button: Button = v.findViewById(R.id.button)
+
+ fun bind(category: Category) {
+ button.text = category.localizedName
+ button.isPressed = category.selected
+ button.setOnClickListener { listener.onCategorySelected(category) }
+ }
+ }
+
+}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderAdapter.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderAdapter.kt
new file mode 100644
index 00000000..2180ccbd
--- /dev/null
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderAdapter.kt
@@ -0,0 +1,114 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.merchantpos.order
+
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.selection.ItemDetailsLookup
+import androidx.recyclerview.selection.ItemKeyProvider
+import androidx.recyclerview.selection.SelectionTracker
+import androidx.recyclerview.widget.AsyncListDiffer
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import net.taler.merchantpos.R
+import net.taler.merchantpos.config.ConfigProduct
+import net.taler.merchantpos.order.OrderAdapter.OrderViewHolder
+
+internal class OrderAdapter : Adapter<OrderViewHolder>() {
+
+ lateinit var tracker: SelectionTracker<String>
+ val keyProvider = OrderKeyProvider()
+ private val itemCallback = object : ItemCallback<ConfigProduct>() {
+ override fun areItemsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean {
+ return oldItem.quantity == newItem.quantity
+ }
+ }
+ private val differ = AsyncListDiffer(this, itemCallback)
+
+ override fun getItemCount() = differ.currentList.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderViewHolder {
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.list_item_order, parent, false)
+ return OrderViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: OrderViewHolder, position: Int) {
+ val item = getItem(position)!!
+ holder.bind(item, tracker.isSelected(item.id))
+ }
+
+ fun setItems(items: List<ConfigProduct>, commitCallback: () -> Unit) {
+ // toMutableList() is needed for some reason, otherwise doesn't update adapter
+ differ.submitList(items.toMutableList(), commitCallback)
+ }
+
+ fun getItem(position: Int): ConfigProduct? = differ.currentList[position]
+
+ fun getItemByKey(key: String): ConfigProduct? {
+ return differ.currentList.find { it.id == key }
+ }
+
+ fun findPosition(product: ConfigProduct): Int {
+ return differ.currentList.indexOf(product)
+ }
+
+ internal inner class OrderViewHolder(private val v: View) : RecyclerView.ViewHolder(v) {
+ private val quantity: TextView = v.findViewById(R.id.quantity)
+ private val name: TextView = v.findViewById(R.id.name)
+ private val price: TextView = v.findViewById(R.id.price)
+
+ fun bind(product: ConfigProduct, selected: Boolean) {
+ v.isActivated = selected
+ quantity.text = product.quantity.toString()
+ name.text = product.localizedDescription
+ price.text = product.totalPrice.amountStr
+ }
+ }
+
+ internal inner class OrderKeyProvider : ItemKeyProvider<String>(SCOPE_MAPPED) {
+ override fun getKey(position: Int) = getItem(position)!!.id
+ override fun getPosition(key: String): Int {
+ return differ.currentList.indexOfFirst { it.id == key }
+ }
+ }
+
+ internal class OrderLineLookup(private val list: RecyclerView) : ItemDetailsLookup<String>() {
+ override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
+ list.findChildViewUnder(e.x, e.y)?.let { view ->
+ val holder = list.getChildViewHolder(view)
+ val adapter = list.adapter as OrderAdapter
+ val position = holder.adapterPosition
+ return object : ItemDetails<String>() {
+ override fun getPosition(): Int = position
+ override fun getSelectionKey(): String = adapter.keyProvider.getKey(position)
+ override fun inSelectionHotspot(e: MotionEvent) = true
+ }
+ }
+ return null
+ }
+ }
+
+} \ No newline at end of file
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
index f792d7ad..b60f3a58 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
@@ -18,32 +18,21 @@ package net.taler.merchantpos.order
import android.os.Bundle
import android.view.LayoutInflater
-import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
-import androidx.recyclerview.selection.ItemDetailsLookup
-import androidx.recyclerview.selection.ItemKeyProvider
import androidx.recyclerview.selection.SelectionPredicates
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StorageStrategy
-import androidx.recyclerview.widget.AsyncListDiffer
-import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.Adapter
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
import kotlinx.android.synthetic.main.fragment_order_state.*
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
-import net.taler.merchantpos.config.ConfigProduct
import net.taler.merchantpos.order.OrderAdapter.OrderLineLookup
-import net.taler.merchantpos.order.OrderAdapter.OrderViewHolder
class OrderStateFragment : Fragment() {
@@ -130,84 +119,3 @@ class OrderStateFragment : Fragment() {
}
}
-
-private class OrderAdapter : Adapter<OrderViewHolder>() {
-
- lateinit var tracker: SelectionTracker<String>
- val keyProvider = OrderKeyProvider()
- private val itemCallback = object : DiffUtil.ItemCallback<ConfigProduct>() {
- override fun areItemsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean {
- return oldItem == newItem
- }
-
- override fun areContentsTheSame(oldItem: ConfigProduct, newItem: ConfigProduct): Boolean {
- return oldItem.quantity == newItem.quantity
- }
- }
- private val differ = AsyncListDiffer(this, itemCallback)
-
- override fun getItemCount() = differ.currentList.size
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderViewHolder {
- val view =
- LayoutInflater.from(parent.context).inflate(R.layout.list_item_order, parent, false)
- return OrderViewHolder(view)
- }
-
- override fun onBindViewHolder(holder: OrderViewHolder, position: Int) {
- val item = getItem(position)!!
- holder.bind(item, tracker.isSelected(item.id))
- }
-
- fun setItems(items: List<ConfigProduct>, commitCallback: () -> Unit) {
- // toMutableList() is needed for some reason, otherwise doesn't update adapter
- differ.submitList(items.toMutableList(), commitCallback)
- }
-
- fun getItem(position: Int): ConfigProduct? = differ.currentList[position]
-
- fun getItemByKey(key: String): ConfigProduct? {
- return differ.currentList.find { it.id == key }
- }
-
- fun findPosition(product: ConfigProduct): Int {
- return differ.currentList.indexOf(product)
- }
-
- private inner class OrderViewHolder(private val v: View) : ViewHolder(v) {
- private val quantity: TextView = v.findViewById(R.id.quantity)
- private val name: TextView = v.findViewById(R.id.name)
- private val price: TextView = v.findViewById(R.id.price)
-
- fun bind(product: ConfigProduct, selected: Boolean) {
- v.isActivated = selected
- quantity.text = product.quantity.toString()
- name.text = product.localizedDescription
- price.text = product.totalPrice.amountStr
- }
- }
-
- private inner class OrderKeyProvider : ItemKeyProvider<String>(SCOPE_MAPPED) {
- override fun getKey(position: Int) = getItem(position)!!.id
- override fun getPosition(key: String): Int {
- return differ.currentList.indexOfFirst { it.id == key }
- }
- }
-
- internal class OrderLineLookup(private val list: RecyclerView) : ItemDetailsLookup<String>() {
- override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
- list.findChildViewUnder(e.x, e.y)?.let { view ->
- val holder = list.getChildViewHolder(view)
- val adapter = list.adapter as OrderAdapter
- val position = holder.adapterPosition
- return object : ItemDetails<String>() {
- override fun getPosition(): Int = position
- override fun getSelectionKey(): String = adapter.keyProvider.getKey(position)
- override fun inSelectionHotspot(e: MotionEvent) = true
- }
- }
- return null
- }
- }
-
-}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
index bc1e35fc..6bab0e62 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/PaymentManager.kt
@@ -110,8 +110,8 @@ class PaymentManager(
// delete unpaid order
val merchantConfig = configManager.merchantConfig!!
mPayment.value?.let { payment ->
- if (!payment.paid) payment.orderId?.let { orderId ->
- Log.e(TAG, "Deleting cancelled and unpaid order $orderId")
+ if (!payment.paid && payment.error != null) payment.orderId?.let { orderId ->
+ Log.d(TAG, "Deleting cancelled and unpaid order $orderId")
scope.launch(Dispatchers.IO) {
api.deleteOrder(merchantConfig, orderId)
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt
index 17d78f61..edb27583 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundFragment.kt
@@ -14,13 +14,12 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.history
+package net.taler.merchantpos.refund
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
@@ -36,11 +35,11 @@ import net.taler.common.navigate
import net.taler.merchantlib.OrderHistoryEntry
import net.taler.merchantpos.MainViewModel
import net.taler.merchantpos.R
-import net.taler.merchantpos.history.RefundFragmentDirections.Companion.actionRefundFragmentToRefundUriFragment
-import net.taler.merchantpos.history.RefundResult.AlreadyRefunded
-import net.taler.merchantpos.history.RefundResult.Error
-import net.taler.merchantpos.history.RefundResult.PastDeadline
-import net.taler.merchantpos.history.RefundResult.Success
+import net.taler.merchantpos.refund.RefundFragmentDirections.Companion.actionRefundFragmentToRefundUriFragment
+import net.taler.merchantpos.refund.RefundResult.AlreadyRefunded
+import net.taler.merchantpos.refund.RefundResult.Error
+import net.taler.merchantpos.refund.RefundResult.PastDeadline
+import net.taler.merchantpos.refund.RefundResult.Success
class RefundFragment : Fragment() {
@@ -88,9 +87,9 @@ class RefundFragment : Fragment() {
}
private fun onRefundResultChanged(result: RefundResult?): Any = when (result) {
- Error -> onError(R.string.refund_error_backend)
- PastDeadline -> onError(R.string.refund_error_deadline)
- AlreadyRefunded -> onError(R.string.refund_error_already_refunded)
+ is Error -> onError(result.msg)
+ PastDeadline -> onError(getString(R.string.refund_error_deadline))
+ AlreadyRefunded -> onError(getString(R.string.refund_error_already_refunded))
is Success -> {
progressBar.fadeOut()
refundButton.fadeIn()
@@ -100,8 +99,8 @@ class RefundFragment : Fragment() {
}
}
- private fun onError(@StringRes res: Int) {
- Snackbar.make(requireView(), res, LENGTH_LONG).show()
+ private fun onError(msg: String) {
+ Snackbar.make(requireView(), msg, LENGTH_LONG).show()
progressBar.fadeOut()
refundButton.fadeIn()
}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt
new file mode 100644
index 00000000..ea2d3981
--- /dev/null
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundManager.kt
@@ -0,0 +1,91 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.merchantpos.refund
+
+import androidx.annotation.UiThread
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import net.taler.common.Amount
+import net.taler.merchantlib.MerchantApi
+import net.taler.merchantlib.OrderHistoryEntry
+import net.taler.merchantlib.RefundRequest
+import net.taler.merchantpos.config.ConfigManager
+
+sealed class RefundResult {
+ class Error(val msg: String) : RefundResult()
+ object PastDeadline : RefundResult()
+ object AlreadyRefunded : RefundResult()
+ class Success(
+ val refundUri: String,
+ val item: OrderHistoryEntry,
+ val amount: Amount,
+ val reason: String
+ ) : RefundResult()
+}
+
+class RefundManager(
+ private val configManager: ConfigManager,
+ private val scope: CoroutineScope,
+ private val api: MerchantApi
+) {
+
+ var toBeRefunded: OrderHistoryEntry? = null
+ private set
+
+ private val mRefundResult = MutableLiveData<RefundResult>()
+ internal val refundResult: LiveData<RefundResult> = mRefundResult
+
+ @UiThread
+ internal fun startRefund(item: OrderHistoryEntry) {
+ toBeRefunded = item
+ mRefundResult.value = null
+ }
+
+ @UiThread
+ internal fun abortRefund() {
+ toBeRefunded = null
+ mRefundResult.value = null
+ }
+
+ @UiThread
+ internal fun refund(item: OrderHistoryEntry, amount: Amount, reason: String) {
+ val merchantConfig = configManager.merchantConfig!!
+ val request = RefundRequest(amount, reason)
+ scope.launch(Dispatchers.IO) {
+ api.giveRefund(merchantConfig, item.orderId, request).handle(::onRefundError) {
+ val result = RefundResult.Success(
+ refundUri = it.talerRefundUri,
+ item = item,
+ amount = amount,
+ reason = reason
+ )
+ mRefundResult.postValue(result)
+ }
+ }
+ }
+
+ @UiThread
+ private fun onRefundError(msg: String) {
+ if (msg.contains("2602")) {
+ mRefundResult.postValue(RefundResult.AlreadyRefunded)
+ } else mRefundResult.postValue(RefundResult.Error(msg))
+ }
+
+}
diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundUriFragment.kt
index 1ea0959b..b8e8997d 100644
--- a/merchant-terminal/src/main/java/net/taler/merchantpos/history/RefundUriFragment.kt
+++ b/merchant-terminal/src/main/java/net/taler/merchantpos/refund/RefundUriFragment.kt
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.merchantpos.history
+package net.taler.merchantpos.refund
import android.os.Bundle
import android.view.LayoutInflater
diff --git a/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml b/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
index b19f14c8..0061a1c4 100644
--- a/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
+++ b/merchant-terminal/src/main/res/layout/fragment_merchant_config.xml
@@ -24,7 +24,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:context=".config.MerchantConfigFragment">
+ tools:context=".config.ConfigFragment">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/configUrlView"
diff --git a/merchant-terminal/src/main/res/layout/fragment_refund.xml b/merchant-terminal/src/main/res/layout/fragment_refund.xml
index 944da55b..a13cd5a4 100644
--- a/merchant-terminal/src/main/res/layout/fragment_refund.xml
+++ b/merchant-terminal/src/main/res/layout/fragment_refund.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".history.RefundFragment">
+ tools:context=".refund.RefundFragment">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/amountView"
diff --git a/merchant-terminal/src/main/res/navigation/nav_graph.xml b/merchant-terminal/src/main/res/navigation/nav_graph.xml
index 606f2de1..2c9ef2c6 100644
--- a/merchant-terminal/src/main/res/navigation/nav_graph.xml
+++ b/merchant-terminal/src/main/res/navigation/nav_graph.xml
@@ -54,7 +54,7 @@
<fragment
android:id="@+id/nav_history"
- android:name="net.taler.merchantpos.history.MerchantHistoryFragment"
+ android:name="net.taler.merchantpos.history.HistoryFragment"
android:label="@string/history_label"
tools:layout="@layout/fragment_merchant_history">
<action
@@ -64,7 +64,7 @@
<fragment
android:id="@+id/refundFragment"
- android:name="net.taler.merchantpos.history.RefundFragment"
+ android:name="net.taler.merchantpos.refund.RefundFragment"
android:label="@string/history_refund"
tools:layout="@layout/fragment_refund">
<action
@@ -75,13 +75,13 @@
<fragment
android:id="@+id/refundUriFragment"
- android:name="net.taler.merchantpos.history.RefundUriFragment"
+ android:name="net.taler.merchantpos.refund.RefundUriFragment"
android:label="@string/history_refund"
tools:layout="@layout/fragment_refund_uri" />
<fragment
android:id="@+id/nav_settings"
- android:name="net.taler.merchantpos.config.MerchantConfigFragment"
+ android:name="net.taler.merchantpos.config.ConfigFragment"
android:label="@string/config_label"
tools:layout="@layout/fragment_merchant_config">
<action