Release Notes

qbo.Mortgage Updates

posted Jul 14, 2017, 8:58 AM by Kevin Cassidy   [ updated Jul 14, 2017, 9:00 AM ]

The qbo.Mortgage project has been updated to include Home and Summary pages for additional mortgage based tables.  In addition, changes to existing tables were made to be more standardized for qbo.

There are breaking changes for the Valuation and BidAtSale tables.  To avoid data loss, the qbo.Db solution has been updated to include pre and post deployment scripts to handle these changes.

High level changes made to the qbo.Mortgage project are:
  • Added Property Home/Summary pages
  • Added missing property based tables to Property/Summary
  • Added missing loan based tables to Loan/Summary
  • Added REO Home/Summary pages
  • Added Offer management to REO Summary page
  • Added DeedInLieu Home/Summary pages
  • Added CashForKeys Home/Summary pages
  • Valuation table is now process based
  • Added PropertyID field to BidAtSale table
  • Removed LoanID and ForeclosureID fields from BidAtSale table

Document Classification

posted Jul 13, 2017, 1:57 PM by Eric Patrick

The qbo.Attachment.Quandis plugins now includes a Classification IService plugin to field document classification. This combines OCR, text parsing via a score, and a matrix to classify documents.  From our website:

QBO includes an interface for Optical Character Recognition (OCR), which will extract text from common document formats (including Word, PDF, TIFF, JPEG, and more), making the text searchable. Rather than invent our own technology, we leverage third party software that excels at OCR; our default plugin leverages Google’s OCR engine. If you have an existing OCR provider that provides an API, Quandis’ Document module can leverage a plugin to use it instead of Google.

Once the text of a document is available in the QBO database, power users can extract data from the text using a simple Text Parsing plugin. Examples of values that can be extracted include:
  • Money: extract a loan amount from a text pattern like:
    “I promise to pay U.S. ${Value} (this amount is called ‘Principle’)”
  • Percentages: extract an interest rate from a text pattern like:
    “I will pay interest at a yearly rate of {Value}%.”
  • Dates: extract dates from a text pattern like:
    “Payments start on {Value}, with late charges starting”
  • Strings: extract names from a text pattern like:
    “Mortgage is issued to {Value}, known as the mortgagor.”
Next, documents can be classified with the combination of OCR, the text parsing engine, and a set of rules (matrix) to set the document type, template, status, or other values. For example, in the image below, a simple text parsing model looks for text specific to loan modifications, subprime mortgage, and mortgage notes, and then leverages a weighted rule set to determine the document type.

This enables you to purchase a portfolio of loans, drop the collateral documents into QBO, and having a workflow OCR, parses, and classify each document received. Processors would be assigned to review only documents classified as ‘unknown’, instead of putting eyeballs on each and every document, and loans missing key documents can automatically be flagged for follow-up.

Check out the demo video on our website as well.

Excel Imports: Hidden Rows

posted Jul 12, 2017, 4:14 AM by Eric Patrick

Frequently, power users observe that Excel-based imports result in more rows that present in Excel. This is generally a result of explicit or implicit ranges being created in Excel, which in turn are exposed to the OldDb drives provided by Microsoft. This is a problem Quandis cannot fix; the workaround is to save the spreadsheet as a CSV file, and the re-save it as an Excel spreadsheet.

For some of these use cases, issuing a SELECT DISTINCT * FROM {Sheet} will ignore the extra rows.

The ExcelEngine and OldDbEngine classes have been modified to optionally do this, and the default behavior is set to True. This means that if you intentionally want to import duplicate rows, you must explicitly tell the Import File Template to ignore this option:


Breaking Change: Async methods moved

posted Jun 22, 2017, 8:17 AM by Eric Patrick

qbo.Core has been updated with a large footprint change:
  • qbo.Application has been refactored to contain *.Async classes for all async methods instead of using extension classes
  • qbo.Decision has been extended to contain async methods to process workflows and all extensibility has now been ported to async
  • qbo.Import has been extended to contain async methods to process imports
  • Web projects for the above classes .ashx files have been ported to async
To deploy you must recompile ALL dlls that reference qbo.Application

Any cs files that reference:

using qbo.Application.AsyncExtensions;

should remove this reference as it's now obsolete. All core/web projects have been modified, and most plug-ins, have been modified in SVN.

Queue Service - qbo.QueueService.3.2.0 to the Installation folder and must be used for this build.

Bug Fix: Importing Configuration Data

posted May 24, 2017, 12:53 PM by Eric Patrick

A configuration change to Abstract.config has been made to fix a bug that occurs when importing configuration data generated by exporting template data from QBO.

The bug occurred when exporting template data such as Decision Templates, Attachment Templates, and other modules that include method signatures. When method signatures are encountered, if the method invokes a customized statement, the custom statement is included in the export:

  <AttachmentTemplate>My Custom Document</AttachmentTemplate>

When encountering the ConfigurationEntryCollection, AbstractObject/ImportXml does not parse the node correctly, remaining in an infinite loop on the closing tag.

The fix for this is to define ConfigurationEntryCollection as a child class for all objects, by modifying Abstract.config:

  <Child Name="ConfigurationEntryCollection" Type="qbo.Application.ConfigurationEntryObject, qbo.Application"/>

Deploying the updated Abstract.config addresses this bug.

qbo.Decision: ImportForm MergeXmlData

posted Apr 14, 2017, 10:41 AM by Eric Patrick

The qbo.Decision module has been updated to enable tasks created from workflows to retain existing user-defined fields.

In some use cases (such as QLS), automation will create tasks (ImportForm rows) before a workflow referencing the task is launched. Prior to this change, when the workflow binds to the task, it would call ImportForm/Save without any user-defined fields (XmlData) specified, causing these fields to be removed.

With this change in place, when the workflow step creates an instance of the task (ImportFormTemplate/CreateInstance), it now includes a MergeXmlData parameter, causing the task to retain any existing user-defined fields.

This change includes a feature switch: ImportFormMergeXmlDataDefault set from Design > Configuration > Modules > Decision, Settings panel.

qbo.Attachment Folders

posted Apr 7, 2017, 6:06 AM by Eric Patrick   [ updated Apr 7, 2017, 7:41 AM by Philip Raath ]

The Attachment module has been extended to support user-added 'folders'. A Folder is merely an Attachment row where AttachmentType='Folder'. The Attachment Search panel has been extended to offer:
  • New Folder menu options
  • Renders 'Folders' with a folder icon
  • If a user drills down into a Folder, the panel will include:
    • a header row with the folder name and description
    • an 'Up' button to navigate up to the parent folder

qbo.Message Substitution

posted Jan 6, 2017, 5:48 PM by Eric Patrick

The qbo.Message module supports curly-brace substitution for Subject, BodyHTML and BodyText fields. For example:

Message/Save?Object=Contact&ObjectID=1&Subject=Hello {Suffix} {LastName}

will replace Suffix and LastName with the corresponding fields from the Contact row for ContactID = 1. Technically, each curly brace expression is evaluated against the Message's parent's summary data. If a full XPath expression is not used, we use //{expression} (e.g. //Suffix or //LastName in the example above).

There are some use cases where this can be inappropriate. For example, email body's may contains text that include curly-brace expressions that are not intended to be XPath expressions. In such situations there are two application settings that apply:
  • qbo.Message.Properties.Settings (maintained from Design > Configuration > Modules > Message, Settings panel)
    • MergeSubstitution: boolean, defaults to true
    • SuppressSubstitutionErrors: boolean, defaults to true
If SuppressSubstitutionErrors is true, Message/SetDefualts will ignore any errors encountered when trying to evaluate an XPath expression during substitution. For example:

Message/Save?Object=Contact&ObjectID=1&BodyText=This is not valid xpath: {Bad XPath Here}

In this case, this is the same as:

Message/Save?Object=Contact&ObjectID=1&BodyText=This is not valid xpath:

If SuppressSubstitutionErrors is false, the same call will raise an error.

In some cases, you may have content that uses curly-brace expressions that are not intended to be XPath expressions. For example, I might post the contents of this blog post as a Message. To prevent the Message module from performing the substitution, I can override the default MergeSubstitution setting with a parameter:

Message/Save?Object=Contact&ObjectID=1&BodyText=This is a lesson on {curly braces}!&MergeSubstitution=false

Note that if the message is template-based, MergeSubstitution parameters do nothing; substitution is always attempted. For example:

Message/Save?Object=Contact&ObjectID=1&Template=MyTemplate&BodyText=This is a lesson on {curly braces}!&MergeSubstitution=false

is the same as:

Message/Save?Object=Contact&ObjectID=1&Template=MyTemplate&BodyText=This is a lesson on {curly braces}!

qbo.Attachment Enhancements

posted Dec 22, 2016, 7:07 AM by Eric Patrick   [ updated Dec 22, 2016, 7:15 AM ]


The standard Document Search panel has been extended to support new features, including:
  • Toggle Description: a Description, if present, can be displayed. OCR'd documents populate the Description be default.
    • Description may also be toggled by double-clicking on the white space of a document row

  • Zip and Download: select multiple documents, zip them, and download the zip file. This does not create a new Attachment.

  • Zip and Save: same as Zip and Download, but creates a new Attachment

  • Zip and Email: same as Zip and Save, including a password to protected the zip file with, and a prompt for email addresses to email the zip file to

  • Convert to PDF: convert selected document(s) (such as TIFF images) to PDF

  • Merge to Single PDF: merges selected documents into a single PDF file

  • OCR: OCR the text of a document, saving the text to the Description field. This operation is queued, as it is typically long-running.
    • If there is no description present, the Descirption field (if visible) offers an OCR button

Technical Details

These new features require the installation of three plugins:
  1. qbo.Attachment.ABCPDF: handles Convert to PDF, Merge to Single PDF (see Plugins > ABCPDF > qbo.ABCPDF.sln)
  2. qbo.Attachment.GoogleDrive: handles OCR (see Plugins > Google > qbo.Google.sln)
  3. qbo.Attachment.DotNetZip: handles ZIP functionality (see Plugins > DotNetZip > qbo.DotNetZip.sln)
If you do not want to install these plugins for any reason, you can disable these advanced UI features via the EnableStandardServicesFromUI application setting:
  • Design > Configuration > Modules > Attachment
  • Click on the Setting tab
  • Edit the 'EnableStandardServicesFromUI' setting, and set it to false

qbo.Import: ImportFile/Apply

posted Dec 21, 2016, 12:28 PM by Eric Patrick   [ updated Aug 16, 2017, 11:42 AM ]


The qbo.Import module has been extended to support bulk-applying a method signature to every row returned by a statement. Examples include:
  • Add a workflow to all loans matching some search criteria
  • Add a task to all foreclosures matching some search criteria
  • Send an email to all contacts matching some search criteria

User Interface

YouTube Video

The Loan.Search.xslt has been extended to support adding tasks and workflows. For example, to add a workflow to all loans in CA with a UPB greater than $500,000
  • Navigate to Loans, click the advanced search dropdown, and choose State: CA and UPB: 500,000-10,000,000 and search
  • From the result's Option menu, choose Add Workflow
  • From the Choose Workflow popup, choose apply to 'All Matching Records', choose your workflow, and click Save
The popup that invokes ImportFile/Apply is GenericTemplate.popup.xslt, and makes the following assumptions:
  • If you choose 'Selected Records', Apply will only be called for the selected rows, regardless of how many rows where returned
    • in this case, Apply is executed in real time, and the user must wait for the results to finish processing before navigating away from the page
    • the user will get an alert '{X} row affected', where X is the number of rows that the method signature was applied to
  • If you choose 'All Matching Records', Apply will be called for every row returned by the query, regardless of any rows that are checked (or not checked)
    • in this case, Apply is queued, so the user does not need to wait for the results
    • the user will get an alert that the request was queued, and a confirmation number (the Queue.MessageID)
A generic user interface for this method can be found at /Import/ImportFile.ashx/BatchApply.

API Details

The method signature for ImportFile/Apply is:

ImportFile/Apply?ClassName={ClassName}&Operation={Operation}&{Parameters to pass to ClassName/Operation}&Signature={Method Signature}

The Signature parameter may use substitution in it's query string of parameters. If substitution is specified, the parameters are substituted against the row of data returned by ClassName/Operation. For example:

ImportFile/Apply?ClassName=Loan&Operation=SmartSearch&SmartSearch=123123&Signature=Decision/Save?Object=Loan%26ObjectID={LoanID}%26Template=Hello World

does the following:
  • Executes Loan/SmartSearch?SmartSearch=123123
  • For each row returned, builds a method signature from Decision/Save?Object=Loan&ObjectID={LoanID}&Template=Hello World
    • this will replace {LoanID} with the LoanID column returned by Loan/SmartSearch
    • if the Hello World workflow already exists for a loan returned by the operation, normal repeatability rules apply
    • any column from Loan/SmartSearch may be used in this method signature
A more complex example is:

ImportFile/Apply?ClassName=Contact&Operation=Search&State=MA&Signature=Message/Send?ToAddress={Email}%26Subject=Hello {FirstName}%26BodyText=Welcome to QBO, {Suffix} {LastName}!

does the following:
  • Executes Contact/Search?State=MA
  • For each row returned, builds a method signature from Message/Send?ToAddress={Email}&Subject=Hello {FirstName}&BodyText=Welcome to QBO, {Suffix} {LastName}!
    • this will replace {Email}, {FirstName}, {Suffix} and {LastName} with the matching column returned by Contact/Search
Beware of the ampersand Gotcha!

The Signature parameter is itself a query string. If you're typing the full URL to leverage ImportFile.ashx/Apply, you must replace & with %26 manually. For example:
  • Signature=Decision/Save?Object=Loan&ObjectID={LoanID}: the value of Signature parsed on the server will be Decision/Save?Object=Loan
  • Signature=Decision/Save?Object=Loan%26ObjectID={LoanID}: the value of Signature parsed on the server will be Decision/Save?Object=Loan&ObjectID={LoanID}
If you are not typing the Signature query string directly, but use javascript to calculate the value being passed over the wire, javascript will take care of this substitution for you! For example:

  <input type="hidden" name="ClassName" value="Loan"/>
  <input type="hidden" name="Operation" value="Search"/>
  <input type="hidden" name="State" value="MA"/>
  <input type="hidden" name="Signature" value="Decision/Save?Object=Loan&ObjectID={LoanID}"/>

When parsing this data to pass to the server, the browser / javascript will recognize that the value being passed for the Signature parameter includes ampersands, and will automatically substitute & with %26 for you. See Templates/Application/GenericTemplate.Popup.xslt for an example.

Audit Trail: ImportFile/Batch

If you wish to include an audit trail with your use case, you can call ImportFile/Batch instead of ImportFile/Apply. ImportFile/Batch will:
  • Create an ImportFile record,
  • Create ImportFileQueue records for each row returned by the query, and
  • Queue ImportFile/Import
This allows you to have an audit trail (in the form of ImportFileQueue rows), and breaks the activity into smaller chunks (each row's method signature is queued as a separate job).

1-10 of 23