Go back to the main page

Easily turn a Ruby Hash to QuickBooks XML

 

 

I recently released a new Gem called to_qbxml. I created it in response to the current de facto Ruby Gem, Qbxml. Let me state right off the bat that I used much of that Gem's code for to_qbxml and want to thank the author, Alexsey, for all his OSS QuickBooks contributions.

Here are some of the issues I had with the Qbxml Gem:

  1. QBXML requires repeatable nodes such as the InvoiceLineAdd node in the sample below but I had no idea how to do this with the Qbxml Gem.
  2. <?xml version="1.0" encoding="utf-8"?>
    <?qbxml version="7.0"?>
    <QBXML>
      <QBXMLMsgsRq onError="stopOnError">
        <InvoiceAddRq requestID="1">
          <InvoiceAdd>
            <InvoiceLineAdd>
              <!-- omitted -->
            </InvoiceLineAdd>
            <InvoiceLineAdd>
              <!-- omitted -->
            </InvoiceLineAdd>
          </InvoiceAdd>
        </InvoiceAddRq>
      </QBXMLMsgsRq>
    </QBXML>
    
    I am sure that Qbxml Gem does support this as it is a crucial part of creating valid QBXML but instead I created a solution that uses a reserved hash key, repeat, that can be passed an array of hashes to create the above XML.
    # ----
    # to_qbxml gem example of creating repeatable nodes
    # ----
    def line_item(line_number)
      {
        invoice_line_add: {
          item_ref: {
            list_id: '3243'
          },
          desc: "Line #{line_number}",
          amount: 10.99,
          is_taxable: true,
          quantity: 3,
          rate_percent: 0,
          repeat: [{
            line: {
              desc: 'inside'
            }
          }]
        }
      }
    end
    hash = { repeat: [ line_item(1), line_item(2) ] }
    xml = ToQbxml.new(hash).make(:invoice)
    
  3. Reliance on ActiveSupport::Inflector to handle QBXML case conversions
  4. Since Inflector is used within Rails autoload this can adversely effect autoloading when included into an existing Rails app. Instead, to_qbxml removes that dependency and uses a simple regular expression to achieve the same result.
  5. Built-in validation is not needed.
  6. In my experience, it is best to use the OCR docs and the QBXML validator and SDKTest utilities that come bundled with the standard QBSDK installation.
  7. Turning QBXML into a Ruby hash is not useful when it comes to reading QBXML
  8. Instead use the rich parsing and searching of Nokogiri. With to_qbxml you can return a Nokogiri document back to by passing doc: true into the initialization options. Below is an example of using Nokogiri's rich CSS selector support to parse QBXML.
    n = ToQbxml.new(hash, doc: true).make(:invoice, action: :query)
    expect(n.at('InvoiceQueryRq > IncludeLineItems').content).to eq 'true'
    expect(n.at('InvoiceQueryRq > ModifiedDateRangeFilter > FromModifiedDate').content).to eq hash[:modified_date_range_filter][:from_modified_date]
    expect(n.at('InvoiceQueryRq > ModifiedDateRangeFilter > ToModifiedDate').content).to eq hash[:modified_date_range_filter][:to_modified_date]
    end
      
    

Hence a new Gem is born

Therefore, I ripped out these and some other "features" to create a lightweight and flexible but yet powerful Ruby Hash to QBXML library. Enjoy, and check out the screencast for more commentary.