I discovered how to use QBFC with Ruby. QBFC is part of the Quickbooks SDK and is a fairly basic wrapper around QBXML. To me, it’s not the simplest interface, but it’s a nice not to bother with the XML, as I’ve been doing. Whereas when using QBXML, you would call WIN32OLE.new(“QBXMLRP2.RequestProcessor”), use WIN32OLE.new(“QBFC6.QBSessionManager”) instead.
Here’s quick example:
require 'win32ole'
qb = WIN32OLE.new("QBFC6.QBSessionManager")
qb.OpenConnection2 "", "Ruby Test", 1 # 1 is ctLocalQBD
qb.BeginSession("", 2) # 2 is omDontCare
request_set = qb.CreateMsgSetRequest("US", 6, 0)
customer_query = request_set.AppendCustomerQueryRq
response = qb.DoRequests(request_set)
customer_set = response.ResponseList.GetAt(0)
first_customer = customer_set.Detail.GetAt(0)
puts first_customer.FullName.GetValue
qb.EndSession
qb.CloseConnection
qb = nil
Now, I like to wrap all that opening and closing connection stuff in a block, so here’s a version I like a bit better:
require 'win32ole'
OPEN_MODES = {
:single => 0,
:multi => 1,
:either => 2
}
def session(app_name, options = {})
com_obj = WIN32OLE.new("QBFC6.QBSessionManager")
com_obj.OpenConnection2(options[:app_id].to_s, app_name, 1) # 1 is ctLocalQBD
open_mode = OPEN_MODES[options[:open_mode].to_s] || 2 # :either is default
com_obj.BeginSession(options[:filename].to_s, open_mode)
begin
yield(com_obj)
ensure
com_obj.EndSession
com_obj.CloseConnection
com_obj = nil
end
end
session("Test Application") do | qb |
request_set = qb.CreateMsgSetRequest("US", 6, 0)
customer_query = request_set.AppendCustomerQueryRq
response = qb.DoRequests(request_set)
customer_set = response.ResponseList.GetAt(0)
first_customer = customer_set.Detail.GetAt(0)
puts first_customer.FullName.GetValue
end
And, finally, a bit of playing with WIN32OLE (risky, most likely):
require 'win32ole'
unless Object.const_defined?(:ActiveSupport)
gem 'activesupport'
require 'active_support'
end
class WIN32OLE
alias_method :old_array_selector, :[]
def [](idx)
if idx.kind_of? Integer
self.GetAt(idx)
else
old_array_selector(idx)
end
end
alias_method :old_method_missing, :method_missing
def method_missing(symbol, *params)
if (('a'..'z') === symbol.to_s[0].chr)
obj = old_method_missing(symbol.to_s.camelize.to_sym, *params)
if obj.ole_methods.detect{|m| m.to_s == 'GetValue'}
obj.GetValue
end
else
old_method_missing(symbol, *params)
end
end
end
The new definition of [] checks if an integer was passed. If so, and if self responds to “GetAt”, go ahead and send the integer value to self#GetAt . This allows me to use response.ResponseList[0] instead response.ResponseList.GetAt(0).
The method_missing stuff allows does a similar shortcut for GetValue . Instead of first_customer.FullName.GetValue, I can call first_customer.full_name.
This is not at all tested, so I can’t say what these changes might mess up.