Troubleshooting‎ > ‎

Null, AJAX, and Importing Data

posted Oct 10, 2011, 10:26 AM by Eric Patrick
The Short Sale system was exhibiting a bug where inserting a Ledger via AJAX resulted in duplicate ledgers being created. The bug was fixed with a minor tweak to the data-options tag of the Ledger DIV tag. Understanding the minor tweak is important in understanding how QBO 2.0 AJAX works.

When using AJAX to update data on the server, most calls are routed to an Edit method (e.g. LedgerService.asmx/Edit). This edit method used the QBO Import Framework to update the database. The Import Framework allows you to transmit just a few columns of data to update, rather than all columns related to an object. For example, you can update Ledger.Status by transmitting XML like this:

<LedgerItem>
  <LedgerID>12</LedgerID>
  <Status>Paid</Status>
</LedgerItem>

Presumably, the Ledger has many more columns populated than just LedgerID and Status. The Import Framework will grab existing values from the database, overwrite those existing values with the transmitted XML values, and save the Ledger.

However, it's conceivable that we might want to intentionally NULL some values in the Ledger; Ledger.AccountNumber perhaps. This can be done though the Import Framework with:

<LedgerItem>
  <LedgerID>12</LedgerID>
  <Status>Paid</Status>
  <AccountNumber></AccountNumber> <!-- or <AccountNumber/> -->
</LedgerItem>

In the case of the Short Sale system Ledger, there is a custom XSLT that renders a qbo.LedgerObject via a DIV tag. In some cases, the user would be in an insert mode, in other cases in an update mode. Thus, the DIV tag looked like this:

<div id="IncomeLedger" class="panel hidden" type="qbo.LedgerObject" data-options="{{ledger: 'Monthly Household Income', object: 'ShortSale', objectid: '{//ShortSaleID}',ledgerid: '{//SomeLedgerID}',  save: false }}">.</div>

When in an insert mode, the LedgerID would be NULL, and appear in the div tag as ledgerid: ''. This cause the AJAX to transmit:

<LedgerItem>
  <LedgerID></LedgerID>
  <Ledger>Monthly Household Income</Ledger>
  ...
</LedgerItem>

Note that this XML is essentially telling the server that the application intends the LedgerID to be NULL. Not quite what was meant. To fix this, the data-options tag can include a bindNull: false option, telling the qbo.LedgerObject to ignore empty and null values when building XML to send to the server.

<div id="IncomeLedger" class="panel hidden" type="qbo.LedgerObject" data-options="{{ledger: 'Monthly Household Income', object: 'ShortSale', objectid: '{//ShortSaleID}',ledgerid: '{//SomeLedgerID}',  save: false, bindNull: false }}">.</div>

Why did this cause duplicate Ledger rows?

The Ledger.Import method behaves a bit differently than most classes, because it is designed to handle LedgerItems as well as Ledgers in a single XML payload. Basically, this happens:

if (LedgerID.IsNull) Insert();
...
else Update();
...
SetProperties(importRow);
SaveAll();

In this example, Insert() was correctly being called, at which point a LedgerID was created and set. Then, in preparation for saving any defined LedgerItems, SetProperties(importRow) was NULLing out the LedgerID because of the <LedgerID></LedgerID> node.  SaveAll() would then insert the same ledger again.


Comments