Quantity times Unit price equals total amount check

🚧

The following section is obsolete - use Python TxScript

We now recommend to prefer Python to implement Serverless Functions. We also provide the Rossum Transaction Script runtime to make it easy to manipulate business transaction and document data from the Python code without extra boilerplate and helpers.

The basic equation for checking the integrity of line item values is to check that the quantity of the item multiplied by the unit price equals the total amount in the given row. If the values are captured and the equation is not satisfied, a warning message will be shown to the operator.

This code example assumes you have the following field IDs in your extraction schema:

  • "item_quantity" for capturing the quantity for each line item
  • "item_amount" for capturing unit price with tax for each line item
  • "item_amount_total" for capturing the total amount for the given line item

Moreover, the serverless function should be configured to get notifications on "user_update" action and the function should be assigned to some queue.

// This serverless function example can be used for annotation_content events
// (e.g. user_update action). annotation_content events provide annotation
// content tree as the input.
//
// The function below shows a warning to the operator if a row in line items
// does not fullfil the quantity*item_amount == item_amount_total equation.

// --- ROSSUM HOOK REQUEST HANDLER ---

// The rossum_hook_request_handler is an obligatory main function that accepts
// input and produces output of the rossum serverless function hook. Currently,
// the only available programming language is Javascript executed on Node.js 12 environment.
// @param {Object} annotation - see https://api.elis.rossum.ai/docs/#annotation-content-event-data-format
// @returns {Object} - the messages and operations that update the annotation content

exports.rossum_hook_request_handler = ({
  annotation: {
    content
  }
}) => {
  try {


    const quantityColumnDatapoints = findBySchemaId(
      content,
      'item_quantity',
    );

    // List of all datapoints of item_amount_base schema id
    const itemAmountColumnDatapoints = findBySchemaId(
      content,
      'item_amount',
    );
    
    // List of all datapoints of item_amount_base schema id
    const itemAmountTotalColumnDatapoints = findBySchemaId(
      content,
      'item_amount_total',
    );
    
    messages = [];
    
    for(var i = 0; i < quantityColumnDatapoints.length; i++){
      
        quantity = parseFloat(quantityColumnDatapoints[i].content.normalized_value);
        itemAmount = parseFloat(itemAmountColumnDatapoints[i].content.normalized_value); 
        itemAmountTotal = parseFloat(itemAmountTotalColumnDatapoints[i].content.normalized_value);
        
        if(quantity && itemAmount && itemAmountTotal){
        
          if(quantity*itemAmount != itemAmountTotal){
            
            const message =  `Quantity (${quantityColumnDatapoints[i].content.value}) * Item amount (${itemAmountColumnDatapoints[i].content.value}) is not equal to Item amount total (${itemAmountTotalColumnDatapoints[i].content.value})`;
            
            messages.push(
              createMessage(
                'warning',
                message,
                quantityColumnDatapoints[i].id
              )
            );
            
            messages.push(
              createMessage(
                'warning',
                message,
                itemAmountColumnDatapoints[i].id
              )
            );
            
            messages.push(
              createMessage(
                'warning',
                message,
                itemAmountTotalColumnDatapoints[i].id
              )
            );
          
          }
        }
    }

    // Return messages and operations to be used to update current annotation data
    return {
      messages
    };
  } catch (e) {
    // In case of exception, create and return error message. This may be useful for debugging.
    const messages = [
      createMessage('error', 'Serverless Function: ' + e.message)
    ];
    return {
      messages,
    };
  }
};

// --- HELPER FUNCTIONS ---

// Return datapoints matching a schema id.
// @param {Object} content - the annotation content tree (see https://api.elis.rossum.ai/docs/#annotation-data)
// @param {string} schemaId - the field's ID as defined in the extraction schema(see https://api.elis.rossum.ai/docs/#document-schema)
// @returns {Array} - the list of datapoints matching the schema ID

const findBySchemaId = (content, schemaId) =>
  content.reduce(
    (results, dp) =>
    dp.schema_id === schemaId ?
    [...results, dp] :
    dp.children ?
    [...results, ...findBySchemaId(dp.children, schemaId)] :
    results,
    [],
  );

// Create a message which will be shown to the user
// @param {number} datapointId - the id of the datapoint where the message will appear (null for "global" messages).
// @param {String} messageType - the type of the message, any of {info|warning|error}. Errors prevent confirmation in the UI.
// @param {String} messageContent - the message shown to the user
// @returns {Object} - the JSON message definition (see https://api.elis.rossum.ai/docs/#annotation-content-event-response-format)

const createMessage = (type, content, datapointId = null) => ({
  content: content,
  type: type,
  id: datapointId,
});

// Replace the value of the datapoint with a new value.
// @param {Object} datapoint - the content of the datapoint
// @param {string} - the new value of the datapoint
// @return {Object} - the JSON replace operation definition (see https://api.elis.rossum.ai/docs/#annotation-content-event-response-format)

const createReplaceOperation = (datapoint, newValue) => ({
  op: 'replace',
  id: datapoint.id,
  value: {
    content: {
      value: newValue,
    },
  },
});

Of course, the code above could be modified to check the amounts without tax by having schema IDs "amount_amount_base" and "item_total_base" and using them in the equation.