ABBYY Invoice Cloud

A quick introduction how to integrate the ABBYY Invoice Cloud. https://abbyy.cloud/


 

Overview

ABBYY Invoice Cloud is a new webservice available from Abbyy where invoices can be sent to for extraction and the result is returned. The following article explains how the integration can be used within Straatos including a sample script for the various steps.


The Straatos workflow below explains the steps required for processing an Invoice with Abbyy. The integration is handled withing the 'Send to ABBYY Extraction' and 'Get Result'. Those steps can be used together with the other Straatos functions to create an invoice processing solution. The step 'Validation' in the flow is optional to see the results. This is the standart Straatos validation and not specific to the ABBYY Integration.

Fields and Values from ABBYY

ABBYY supports the Extraction of Invoice Header and Table Line items. The list of countries supported will be updated by ABBYY, as of the time writing this documentation, only Australian/NZ invoices are supported.

Fields extracted by Abbyy:

Invoice Number

 

Table line items:

 

 

Script for 'Send to ABBYY Extraction'

 

The following sample code sends the document to ABBYY and triggers the invoice extraction:

// Send Documents to Abbyy for processing and start the invoice Process.
// The processing is done in 2 steps. 1) Create a new task by uploading a file and 2) Start the process with the parameters.
// API to create a new task: https://api.abbyy.cloud/v1/file
// API to start processing: https://api.abbyy.cloud/v1/capture/invoices

// Initate Status
// Below the AbbyyResult is set as 'false' as a default value so that the routing will be possible in case no other value is assigned in the 'Get Result' step.
AbbyyResult = 'false';

var taskAPI = 'https://api.abbyy.cloud/v1/file';
var processinAPI = 'https://api.abbyy.cloud/v1/capture/invoices';
var authorizationKey = 'Basic <your authorisation key>';

var webServiceKey = '<your Straatos webServiceKey>';
var adapterURLPrefix = 'https://effektif-adapter.cumuluspro.net/AdapterService.svc/json/';
var effektifURLPrefix = 'http://effektif.cumuluspro.net:8080/effektif-web/cp/';

var documentInfoInput = {
    DocumentId: _documentId,
    WebServiceKey: webServiceKey
};

straatos.ajax({
    url: adapterURLPrefix + 'GetDocumentInfo',
    data: JSON.stringify(documentInfoInput),
    method: 'POST'
}).done(function (documentInfoString) {
    console.log(documentInfoString);
    var documentInfo = JSON.parse(documentInfoString);
    console.log(documentInfo.originalURL);
    straatos.ajax({
        url: documentInfo.originalURL,
        dataType: 'binary'
    }).done(function (invoice){
        var boundaryName = 'Straatos-' + straatos.uuid(); //boundary requires something unique
        var boundary = '--' + boundaryName + 'rn';
        var multipart = straatos.newFormData();

        multipart.append(boundary);
        multipart.append('Content-Disposition: form-data; name="'+_documentId+'"; filename="'+_documentId+'.pdf"rn');
        multipart.append('Content-Type: application/pdfrn');
        multipart.append('rn');
        multipart.append(invoice);
        multipart.append('rn');
        multipart.append('--' + boundaryName + '--rn');
    
        straatos.ajax({
            url: taskAPI,
            method: 'POST',
            data: multipart,
            contentType: 'multipart/form-data; boundary="' + boundaryName + '"',   //requires the boundaryName in double quotes.
            headers: {
                authorization: authorizationKey
            }
        }).done(function (fileResponse) {
            console.log(fileResponse);
            var fileRes = JSON.parse(fileResponse);
            var processingReq = {
                export_format: 'xml',
                files: [{
                    id: fileRes[0].id,
                    token: fileRes[0].token
                }]
            };
            straatos.ajax({
                url: processinAPI,
                method: 'POST',
                data: JSON.stringify(processingReq),
                headers: {
                    authorization: authorizationKey
                }
            }).done(function (processResponse){
                console.log(processResponse);
                var resp = JSON.parse(processResponse);
                console.log('InvoiceId:'+resp.id);
                InvoiceId = resp.id;
            }).fail(function(processEr, erMsg){
                console.log(erMsg);
                straatos.setError('Invoices: ' + erMsg);
            });
        }).fail(function(fileEr, errMsg){
            console.log(errMsg);
            straatos.setError('File: ' + errMsg);
        });
    });
}).fail(function (jqXHR, error) {
    straatos.setError('Info: ' + error);
});

 

Script for step 'Get Result'

// This is to retrieve the values from Abbyy once sent.
// There are 3 steps. 1) Checking the status (and see if it is ready) 2) Getting the result and saving it into the index fields 3) Deleting the task in Abbyy
// API to check the status: https://api.abbyy.cloud/v1/task/{token}
// API to get the result: https://api.abbyy.cloud/v1/file/
// API to delete the task: https://api.abbyy.cloud/v1/task/
// Note: the Abbyy processing takes a while (approximately 1 minute) for a document. Hence this step needs to check if the result is ready. If not, set the workflowfield 'AbbyyResult' to 'false'.
// Once a result is available, set the AbbyyResult to 'true' and assign the results to the various Workflow fields. In case of 'AbbyyResult' is 'false' (means no result is yet available) finish this
// script. The workflow will then automatically wait for 1 minute and then send the document into this workflow step again.

var taskAPI = 'https://api.abbyy.cloud/v1/task/'+InvoiceId;
var resultAPI = 'https://api.abbyy.cloud/v1/file/';
var authorizationKey = 'Basic <your Abbyy authorisation Key>';
AbbyyResult = 'false';

// Defining the various potential values that Abbyy can return
var VendorIdElementName = '_VendorId';
var bankCodeElementName = '_BankCode';
var bankAccountElementName = '_BankAccount';
var supplierStreetElementName = '_Street';
var supplierZIPElementName = '_ZIP';
var supplierCityElementName = '_City';
var supplierCountryElementName = '_Country';
var supplierNameElementName = '_Name';
var supplierStateElementName = '_State';
var headerTextElementName = '_HeaderText';
var footerTextElementName = '_FooterText';
var rejectReasonElementName = '_Reason';
var invoiceNumberElementName = '_InvoiceNumber';
var invoiceDateElementName = '_InvoiceDate';
var totalElementName = '_Total';
var currencyElementName = '_Currency';
var invoiceTypeElementName = '_InvoiceType';
var statusElementName = '_Status';
var abnElementName = '_VATID';
var nationalVATElementName = '_NationalVAT';
var descriptionElementName = '_Description';
var unitPriceElementName = '_UnitPrice';
var quantityElementName = '_Quantity';
var totalPriceElementName = '_TotalPriceNetto';
var vatPercentageElementName = '_VATPercentage';
var vatValueElementName = '_VATValue';
var vatCodeElementName = '_VATCode';
var totalIncVATElementName = '_TotalPriceBrutto';
var itemCodeElementName = '_ItemCode';

_table = {
	body: {
		lines: []
	}
};

straatos.ajax({
    url: taskAPI,
    method: 'GET',
    headers: {
        authorization: authorizationKey
    }
}).done(function (statusResponse){
    console.log(statusResponse);
    var resp = JSON.parse(statusResponse);
    console.log('Status: ' + resp.status);
    var status = resp.status;
    if(status === 'Done')
    {
        AbbyyResult = 'true';
        var sourceFileUrl = resultAPI+resp.services[0].files.source.id + '/' +resp.services[0].files.source.token;
        var targetFileUrl = resultAPI+resp.services[0].files.target.id + '/' +resp.services[0].files.target.token;

        console.log(targetFileUrl);
        straatos.ajax({
        url: targetFileUrl,
        method: 'GET',
        headers: {
            authorization: authorizationKey
        }
        }).done(function (statusResponse){

            VendorId = getElementValue(statusResponse, VendorIdElementName);
            BankCode = getElementValue(statusResponse, bankCodeElementName);
            BankAccount = getElementValue(statusResponse, bankAccountElementName);
            Street = getElementValue(statusResponse, supplierStreetElementName);
            ZIP = getElementValue(statusResponse, supplierZIPElementName);
            City = getElementValue(statusResponse, supplierCityElementName);
            Country = getElementValue(statusResponse, supplierCountryElementName);
            Name = getElementValue(statusResponse, supplierNameElementName);
            State = getElementValue(statusResponse, supplierStateElementName);
            HeaderText = getElementValue(statusResponse, headerTextElementName);
            FooterText = getElementValue(statusResponse, footerTextElementName);
            Reason = getElementValue(statusResponse, rejectReasonElementName);           
            InvoiceNumber = getElementValue(statusResponse, invoiceNumberElementName);
            Total = getElementValue(statusResponse, totalElementName);
            InvoiceType = getElementValue(statusResponse, invoiceTypeElementName);
            Currency = getElementValue(statusResponse, currencyElementName);
            ABN = getElementValue(statusResponse, abnElementName);
            Status = getElementValue(statusResponse, statusElementName);
            NationalVAT = getElementValue(statusResponse, nationalVATElementName);
            
            var invoiceDateString = getElementValue(statusResponse, invoiceDateElementName);
            InvoiceDate = invoiceDateString;           
            var descriptionElement = findElements(statusResponse, descriptionElementName);
            var unitPriceElement = findElements(statusResponse, unitPriceElementName);
            var quantityElement = findElements(statusResponse, quantityElementName);
            var totalPriceElement = findElements(statusResponse, totalPriceElementName);
            var vatPercentageElement = findElements(statusResponse, vatPercentageElementName);
            var vatValueElement = findElements(statusResponse, vatValueElementName);
            var vatCodeElement = findElements(statusResponse, vatCodeElementName);
            var totalIncVATElement = findElements(statusResponse, totalIncVATElementName);
            console.log(descriptionElement.length);

            for(var i=0;i<descriptionElement.length;i++)
            {
                _table.body.lines.push({
                    Description: { value: getValue(descriptionElement[i], descriptionElementName)},
                    UnitPrice: { value: getValue(unitPriceElement[i], unitPriceElementName)},
                    Quantity: { value: getValue(quantityElement[i], quantityElementName)},
                    TotalPriceNetto: { value: getValue(totalPriceElement[i], totalPriceElementName)},
                    VatPercentage: { value: getValue(vatPercentageElement[i], vatPercentageElementName)},
                    VatValue: { value: getValue(vatValueElement[i], vatValueElementName)},
                    VatCode: { value: getValue(vatCodeElement[i], vatCodeElementName)},
                    TotalPriceBrutto: { value: getValue(totalIncVATElement[i], totalIncVATElementName)}
                });
            }

           //Cleaning up the Abbyy Task by deleting it.
            straatos.ajax({
            url: targetFileUrl,
            method: 'DELETE',
            headers: {
                authorization: authorizationKey
            }
            }).done(function (statusResponse){
                console.log('Target File deleted');
                straatos.ajax({
                    url: sourceFileUrl,
                    method: 'DELETE',
                    headers: {
                        authorization: authorizationKey
                    }
                }).done(function (statusResponse){
                    console.log('Source File deleted');
                });
            }); 
        }).fail(function (resp, err){
            console.log(err);
        });
    } 
}).fail(function(processEr, erMsg){
    console.log(erMsg);
    straatos.setError('status: ' + erMsg);
});


//Function below is to work with XML in Straatos
function getElementValue(xml, name){
    var element = findElements(xml, name);
    var elementValue = getValue(element[0], name);
    return elementValue;
}

function getValue(xml, name) {

    try{
        var matches = xml.match(new RegExp('<' + name + '.*>(.*?)<\/' + name + '>'));
        if (matches && matches.length > 1) {
        return matches[1];
        }
        console.log('no match');
        return '';
        }
    catch(error){
        return '';
    }

}

function findElements(xml, name, attribute, attributeValue) {
    var result = [];
    
    var attributeMatch = '';
    
    if (attribute && (attributeValue || attributeValue == '0')) {
        attributeMatch = '[ \t]+?' + attribute + '="' + attributeValue.replace(/\./g, '\.') + '"';
    } else {
        attributeMatch = '[ \t]';
    }
    
    var matches = xml.match(new RegExp('(<' + name + '>[\s\S]*?<\/' + name +'>)', 'g'));

    
    if (matches) {
        matches.forEach(function (match, i) {
            result.push(match);
        });
    }

    matches = xml.match(new RegExp('(<' + name + attributeMatch + '.*?>[\s\S]*?<\/' + name +'>)', 'g'));
        
    if (matches) {
        matches.forEach(function (match, i) {
            result.push(match);
        });
    }

    matches = xml.match(new RegExp('(<' + name + attributeMatch + '.*?\/>)', 'g'));
   

    if (matches) {
        matches.forEach(function (match, i) {
            result.push(match);
        });
    }
    
    return result;
}

 

 
Create your own Knowledge Base