Invoice Capture Features

Features from the Gini Capture SDK

The capture feature uses our Gini Capture SDK. All features listed in its documentation can be used here as well.

An important difference is in how you configure the capture features. In the Gini Bank SDK you need to use the CaptureConfiguration instead of the Gini Capture SDK’s GiniCapture.Builder. The configuration names are the same so you can easily map them to the CaptureConfiguration.

File Import (Open With)

Another difference is related to the file import (or “open with”) feature which allows importing of files from other apps via Android’s “open with” or “share” functionality.

Screen API

To handle imported files using the Gini Bank SDK you need to register an activity result handler with the CaptureFlowImportContract() and then pass the incoming intent to GiniBank.startCaptureFlowForIntent():

// Use the androidx's Activity Result API to register a handler for the capture result.
val captureImportLauncher = registerForActivityResult(CaptureFlowImportContract()) { result: CaptureResult ->
    when (result) {
        is CaptureResult.Success -> {
            handleExtractions(result.specificExtractions)
        }
        is CaptureResult.Error -> {
            when (result.value) {
                is ResultError.Capture -> {
                    val captureError: GiniCaptureError = (result.value as ResultError.Capture).giniCaptureError
                    handleCaptureError(captureError)
                }
                is ResultError.FileImport -> {
                    val fileImportError = result.value as ResultError.FileImport
                    handleFileImportError(fileImportError)
                }
            }
        }
        CaptureResult.Empty -> {
            handleNoExtractions()
        }
        CaptureResult.Cancel -> {
            handleCancellation()
        }
    }
}

fun handleFileImportError(exception: ImportedFileValidationException) {
    var message = ...
    exception.validationError?.let { validationError ->
        // Get the default message
        message = getString(validationError.textResource)
        // Or use custom messages
        message = when (validationError) {
            FileImportValidator.Error.TYPE_NOT_SUPPORTED -> ...
            FileImportValidator.Error.SIZE_TOO_LARGE -> ...
            FileImportValidator.Error.TOO_MANY_PDF_PAGES -> ...
            FileImportValidator.Error.PASSWORD_PROTECTED_PDF -> ...
            FileImportValidator.Error.TOO_MANY_DOCUMENT_PAGES -> ...
        }
    }
    AlertDialog.Builder(this)
        .setMessage(message)
        .setPositiveButton("OK") { _, _ -> finish() }
        .show()
}

fun startGiniBankSDKForImportedFile(importedFileIntent: Intent) {
    // Configure capture first
    configureCapture();

    fileImportCancellationToken =
        GiniBank.startCaptureFlowForIntent(captureImportLauncher, this, importedFileIntent)
}

Component API

When using the Component API you need to create a Document from the intent using GiniBank.createDocumentForImportedFiles() and then continue either to the ReviewFragmentCompat, MultiPageReviewFragment or AnalysisFragmentCompat:

fun startGiniBankSDKForImportedFile(importedFileIntent: Intent) {
    GiniBank.createDocumentForImportedFiles(importedFileIntent, this, object : AsyncCallback<Document, ImportedFileValidationException> {
        override fun onSuccess(result: Document) {
            if (result.isReviewable) {
                // If you have enabled capturing documents with multiple pages:
                launchMultiPageReviewScreen()
                // OR if you didn't:
                launchSinglePageReviewScreen()
            } else {
                launchAnalysisScreen(result)
            }
            finish()
        }

        override fun onError(exception: ImportedFileValidationException) {
            handleFileImportError(exception)
        }

        override fun onCancelled() {
            // Handle cancellation.
        }
    })
}

Return Assistant

The return assistant feature allows your users to view and edit payable items in an invoice. The total amount is updated to be the sum of only those items which the user opts to pay.

To enable this feature simply set returnAssistantEnabled to true in the CaptureConfiguration:

CaptureConfiguration(returnAssistantEnabled = true)

Screen API

When integrating using the Screen API it is enough to enable the return assistant feature. The Gini Bank SDK will show the return assistant automatically if the invoice contained payable items and will update the extractions returned to your app according to the user’s changes.

The amountToPay extraction is updated to be the sum of items the user decided to pay. It includes discounts and additional charges that might be present on the invoice.

The extractions related to the return assistant are stored in the compoundExtractions field of the CaptureResult. See the Gini Bank API’s documentation to learn about the return assistant’s compound extractions.

Component API

Using the Component API is more challenging. You need to manage three additional fragments: DigitalInvoiceFragment, DigitalInvoiceOnboardingFragment and LineItemDetailsFragment.

Note

See the Component API example app’s digitalinvoice package for a sample integration.

The following diagram extends the one found in the Gini Capture SDK’s Component API guide. It shows the possible flows through the SDK based on the listener method invocations. For brevity each fragment’s listener is shown next to it. In your integration you will provide the listener implementations and handle the listener method calls. You should navigate to the appropriate fragment based on this diagram.

The part related to the return assistant is in the lower right corner:

Diagram of possible flows through the SDK with the Component API fragments and their listeners including the return assistant.

DigitalInvoiceFragment

This is the entry point for the return assistant. It displays the line items extracted from an invoice and their total price. The user can deselect line items which should not be paid for and also edit the quantity, price or description of each line item. The total price is always updated to include only the selected line items.

The returned extractions in the DigitalInvoiceFragmentListener.onPayInvoice() are updated to include the user’s modifications:

  • amountToPay in the specific extractions is updated to contain the sum of the selected line items’ prices.
  • The line items in the compound extractions are also updated according to the user’s modifications.

Before showing the DigitalInvoiceFragment you should validate the compound extractions using the LineItemsValidator. These extractions are returned in the AnalysisFragmentListener.onExtractionsAvailable()] listener method:

override fun onExtractionsAvailable(
    extractions: Map<String, GiniCaptureSpecificExtraction>,
    compoundExtractions: Map<String, GiniCaptureCompoundExtraction>,
    returnReasons: List<GiniCaptureReturnReason>
) {
    try {
        // Check whether the compound extractions contain valid line items or not
        LineItemsValidator.validate(compoundExtractions)

        // At this point there are valid line items and you can start the return assistant
        val fragment = DigitalInvoiceFragment.createInstance(extractions, compoundExtractions, returnReasons)
        startReturnAssistant(fragment)

    } catch (notUsed: DigitalInvoiceException) {
        // There were no valid line items and you can proceed directly to handling the extractions
        // without the return assistant
        handleExtractions(extractions)
    }
}

A DigitalInvoiceFragmentListener instance must be available before the DigitalInvoiceFragment is attached to an Activity. Failing to do so will throw an exception. The listener instance can be provided either implicitly by making the hosting Activity implement the DigitalInvoiceFragmentListener interface or explicitly by setting the listener using the DigitalInvoiceFragment.listener property.

DigitalInvoiceOnboardingFragment

This fragment shows the onboarding screen related to the return assistant. It displays information about the return assistant to the user.

You should show the DigitalInvoiceOnboardingFragment when the DigitalInvoiceFragmentListener.showOnboarding() is called.

A DigitalInvoiceOnboardingFragmentListener instance must be available before the DigitalInvoiceOnboardingFragment is attached to an activity. Failing to do so will throw an exception. The listener instance can be provided either implicitly by making the host Activity implement the DigitalInvoiceOnboardingFragmentListener interface or explicitly by setting the listener using the DigitalInvoiceOnboardingFragment.listener property.

LineItemDetailsFragment

This fragment allows the user to edit the details of a line item: description, quantity and price. It also allows the user to deselect the line item.

The returned line item in the LineItemDetailsFragmentListener.onSave() listener method is updated to contain the user’s modifications.

You should show the LineItemDetailsFragment when the DigitalInvoiceFragmentListener.onEditLineItem() is called.

A LineItemDetailsFragmentListener instance must be available before the LineItemDetailsFragment is attached to an activity. Failing to do so will throw an exception. The listener instance can be provided either implicitly by making the host Activity implement the LineItemDetailsFragmentListener interface or explicitly by setting the listener using the LineItemDetailsFragment.listener property.

Sending Feedback

Your app should send feedback for the extractions related to the return assistant. These extractions are found in the compoundExtractions field of the CaptureResult if you are using the Screen API and in the compoundExtractions parameter of the DigitalInvoiceFragmentListener.onPayInvoice() listener method if you use the Component API.

Default Networking Implementation

If you use the GiniCaptureDefaultNetworkService and the GiniCaptureDefaultNetworkApi then sending feedback for the return assistant extractions is done by the GiniCaptureDefaultNetworkApi when you send feedback for the payment data extractions as described in the Sending Feedback section.

Custom Networking Implementation

If you use your own networking implementation and directly communicate with the Gini Bank API then see this section in its documentation on how to send feedback for the compound extractions.

In case you use the Gini Bank API Library then sending compound extraction feedback is very similar to how it’s shown in this section in its documentation. The only difference is that you need to also pass in the CompoundExtraction map to DocumentTaskManager.sendFeebackForExtractions():

// Extractions seen and accepted by the user (including user modifications)
Map<String, SpecificExtraction> specificExtractionFeedback;

// Return assistant extractions as returned by the CaptureResult or DigitalInvoiceFragmentListener
Map<String, CompoundExtraction> compoundExtractionFeedback;

final Task<Document> sendFeedback = documentTaskManager.sendFeedbackForExtractions(document,
        specificExtractionFeedback, compoundExtractionFeedback);
sendFeedback.waitForCompletion();