From 011fa90246e1dba7cb0bbc78ee4c04677df2e41d Mon Sep 17 00:00:00 2001 From: Mahdi Abolfazli Date: Tue, 16 Jul 2024 17:37:20 +0200 Subject: [PATCH] feat(bank-sdk): Add SkontoValidator to parse compound extractions for skonto - add a flag to enable/disable skonto feature PP-480 --- .../bank/sdk/capture/CaptureFlowFragment.kt | 46 +++++++--- .../android/bank/sdk/capture/Configuration.kt | 7 +- .../sdk/capture/skonto/SkontoException.kt | 57 +++++++++++++ .../sdk/capture/skonto/SkontoValidator.kt | 85 +++++++++++++++++++ .../src/main/res/navigation/gbs_nav_graph.xml | 2 +- 5 files changed, 182 insertions(+), 15 deletions(-) create mode 100644 bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoException.kt create mode 100644 bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoValidator.kt diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt index 8e22bb78c9..fef9df9113 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt @@ -15,6 +15,7 @@ import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceException import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceFragment import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceFragmentListener import net.gini.android.bank.sdk.capture.digitalinvoice.LineItemsValidator +import net.gini.android.bank.sdk.capture.skonto.SkontoValidator import net.gini.android.bank.sdk.util.disallowScreenshots import net.gini.android.capture.CaptureSDKResult import net.gini.android.capture.Document @@ -115,24 +116,16 @@ class CaptureFlowFragment(private val openWithDocument: Document? = null) : } override fun onFinishedWithResult(result: CaptureSDKResult) { - when(result) { + when (result) { is CaptureSDKResult.Success -> { if (GiniBank.getCaptureConfiguration()?.returnAssistantEnabled == true) { try { - LineItemsValidator.validate(result.compoundExtractions) - navController.navigate(GiniCaptureFragmentDirections.toDigitalInvoiceFragment( - DigitalInvoiceFragment.getExtractionsBundle(result.specificExtractions), - DigitalInvoiceFragment.getCompoundExtractionsBundle(result.compoundExtractions), - result.returnReasons.toTypedArray(), - DigitalInvoiceFragment.getAmountsAreConsistentExtraction(result.specificExtractions) - )) - } catch (notUsed: DigitalInvoiceException) { - didFinishWithResult = true - captureFlowFragmentListener.onFinishedWithResult(interceptSuccessResult(result).toCaptureResult()) + tryShowingReturnAssistant(result) + } catch (digitalInvoiceException: DigitalInvoiceException) { + tryShowingSkontoScreen(result) } } else { - didFinishWithResult = true - captureFlowFragmentListener.onFinishedWithResult(interceptSuccessResult(result).toCaptureResult()) + finishWithResult(result) } } else -> { @@ -142,6 +135,33 @@ class CaptureFlowFragment(private val openWithDocument: Document? = null) : } } + private fun tryShowingSkontoScreen(result: CaptureSDKResult.Success) { + if (GiniBank.getCaptureConfiguration()?.skontoEnabled == true) { + try { + SkontoValidator.validate(result.compoundExtractions) + navController.navigate(GiniCaptureFragmentDirections.toSkontoFragment()) + } catch (e: Exception) { + finishWithResult(result) + } + } + } + private fun tryShowingReturnAssistant(result: CaptureSDKResult.Success) { + LineItemsValidator.validate(result.compoundExtractions) + navController.navigate( + GiniCaptureFragmentDirections.toDigitalInvoiceFragment( + DigitalInvoiceFragment.getExtractionsBundle(result.specificExtractions), + DigitalInvoiceFragment.getCompoundExtractionsBundle(result.compoundExtractions), + result.returnReasons.toTypedArray(), + DigitalInvoiceFragment.getAmountsAreConsistentExtraction(result.specificExtractions) + ) + ) + } + + private fun finishWithResult(result: CaptureSDKResult.Success) { + didFinishWithResult = true + captureFlowFragmentListener.onFinishedWithResult(interceptSuccessResult(result).toCaptureResult()) + } + private fun interceptSuccessResult(result: CaptureSDKResult.Success): CaptureSDKResult { return if (result.specificExtractions.isEmpty() || !pay5ExtractionsAvailable(result.specificExtractions) && diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt index 5c7e068888..f5f3821e12 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt @@ -201,7 +201,12 @@ data class CaptureConfiguration( * IMPORTANT: If you disallow screenshots and use the [CaptureFlowFragment] for launching the SDK in your activity, please clear the [android.view.WindowManager.LayoutParams.FLAG_SECURE] * on your activity's window after the SDK has finished to allow users to take screenshots of your app again. */ - val allowScreenshots: Boolean = true + val allowScreenshots: Boolean = true, + + /** + * Enable/disable the skonto feature. + */ + val skontoEnabled: Boolean = true, ) internal fun GiniCapture.Builder.applyConfiguration(configuration: CaptureConfiguration): GiniCapture.Builder { diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoException.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoException.kt new file mode 100644 index 0000000000..44be5843c4 --- /dev/null +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoException.kt @@ -0,0 +1,57 @@ +package net.gini.android.bank.sdk.capture.skonto + +/** + * Exceptions related to the Skonto feature. + */ +sealed class SkontoException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) { + + /** + * Internal use only. + * + * @suppress + */ + class SkontoMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + /** + * Internal use only. + * + * @suppress + */ + class DueDateMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + /** + * Internal use only. + * + * @suppress + */ + class AmountDiscountedMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + /** + * Internal use only. + * + * @suppress + */ + class AmountToPayMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + /** + * Internal use only. + * + * @suppress + */ + class RemainingDaysMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + /** + * Internal use only. + * + * @suppress + */ + class PaymentMethodMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) + + + /** + * Internal use only. + * + * @suppress + */ + class PercentageDiscountedMissingException(message: String? = null, cause: Throwable? = null) : SkontoException(message, cause) +} \ No newline at end of file diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoValidator.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoValidator.kt new file mode 100644 index 0000000000..bae197155a --- /dev/null +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoValidator.kt @@ -0,0 +1,85 @@ +package net.gini.android.bank.sdk.capture.skonto + +import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceException.* +import net.gini.android.bank.sdk.capture.skonto.SkontoException.* +import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction + +internal typealias Validate = (compoundExtractions: Map) -> Unit + +/** + * It checks that the compound extractions contain valid skonto objects which can be used to show the Skonto screen. + */ +class SkontoValidator { + + companion object { + + /** + * Checks that the compound extractions contain valid line items. + * + * In case it's not valid an appropriate [SkontoException] subclass will be thrown. + * + * @param compoundExtractions a map of [GiniCaptureCompoundExtraction]s + * @throws SkontoMissingException if skontoDiscounts are missing from the compound extractions + * @throws DueDateMissingException if skontoDueDate is missing from skontoDiscaounts object + * @throws AmountToPayMissingException if skontoAmountToPay is missing from skontoDiscaounts object + * @throws RemainingDaysMissingException if skontoRemainingDays is missing from skontoDiscaounts object + * @throws PercentageDiscountedMissingException if skontoPercentageDiscounted is missing from skontoDiscaounts object + */ + @JvmStatic + @Throws(Exception::class) + fun validate(compoundExtractions: Map) = listOf( + skontoDiscountsAvailable, + skontoDueDateAvailable, + skontoAmountToPayAvailable, + skontoRemainingDaysAvailable, + skontoPercentageDiscountedAvailable + ).forEach { it(compoundExtractions) } + } +} + +//mandatory +internal val skontoDiscountsAvailable: Validate = { compoundExtractions -> + if (!compoundExtractions.containsKey("skontoDiscounts")) { + throw SkontoMissingException() + } +} +//mandatory +internal val skontoDueDateAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoDueDate") || it.containsKey("skontoDueDateCalculated") }) != true) { + throw DueDateMissingException() + } +} +//not mandatory +internal val skontoAmountDiscountedAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoAmountDiscounted") || it.containsKey("skontoAmountDiscountedCalculated") }) != true) { + throw AmountDiscountedMissingException() + } +} +//mandatory +internal val skontoAmountToPayAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoAmountToPay") || it.containsKey("skontoAmountToPayCalculated") }) != true) { + throw AmountToPayMissingException() + } +} + +//mandatory +internal val skontoRemainingDaysAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoRemainingDays") || it.containsKey("skontoRemainingDaysCalculated") }) != true) { + throw RemainingDaysMissingException() + } +} + +//not mandatory +internal val skontoPaymentMethodAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoPaymentMethod") }) != true) { + throw PaymentMethodMissingException() + } +} + +//mandatory +internal val skontoPercentageDiscountedAvailable: Validate = { compoundExtractions -> + if ((compoundExtractions["skontoDiscounts"]?.specificExtractionMaps?.all { it.containsKey("skontoPercentageDiscounted") || it.containsKey("skontoPercentageDiscountedCalculated") }) != true) { + throw PercentageDiscountedMissingException() + } +} + diff --git a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml index bcbc55f120..3b45eb77c4 100644 --- a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml +++ b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/capture_flow_nav_graph" - app:startDestination="@id/gbs_destination_capture_fragment">> + app:startDestination="@id/gbs_destination_capture_fragment">