module QBFC
class QuickbooksClosedError < RuntimeError #:nodoc:
end
class SetValueMissing < RuntimeError#:nodoc:
end
class QBXMLVersionError < RuntimeError#:nodoc:
end
class UnknownRequestError < RuntimeError#:nodoc:
end
class BaseClassNewError < RuntimeError#:nodoc:
end
class NotSavableError < RuntimeError#:nodoc:
end
# Encapsulates a QBFC session.
#
# QBFC::Session.open(:app_name => 'Test Application') do |qb|
# qb.customers.find(:all).each do |customer|
# puts customer.full_name
# end
# end
#
# qb = QBFC::Session.new(:app_name => 'Test Application')
# qb.customers.find(:all).each do |customer|
# puts customer.full_name
# end
# qb.close
#
# A QBFC::Session abstracts the ole_methods so that more conventional Ruby method names are used,
# e.g. full_name instead of FullName.GetValue().
#
# This also allows a shortcut for setting up Quickbooks objects:
#
# - session.customers.find(:all) instead of QBFC::Customer.find(session, :all)
# - session.customer('CustomerFullName') instead of QBFC::Customer.find(session, 'CustomerFullName')
# - session.customer instead of QBFC::Customer.find(session, :first)
class Session
class << self
# Open a QBFC session. Takes options as a hash, and an optional block. Options are:
#
# - +app_name+: Name that the application sends to Quickbooks (used for allowing/denying access)
# (defaults to 'Ruby QBFC Application'
# - +app_id+: Per the Quickbooks SDK (QBFC Language Reference):
# 'Normally not assigned. Use an empty string for appID.' An empty string is passed by default.
# - +conn_type+: QBFC_CONST::CtUnknown, CtLocalQBD, CtRemoteQBD, CtLocalQBDLaunchUI, or CtRemoteQBOE.
# Default is QBFC_CONST::CtLocalQBD (1)
# - +filename+: The full path to the Quickbooks file; leave blank to connect to the currently
# open company file. Default is an empty string (Quickbooks should be running).
# - +open_mode+: The desired access mode. It can be one of three values:
# - QBFC_CONST::OmSingleUser (specifies single-user mode)
# - QBFC_CONST::OmMultiUser (specifies multi-user mode)
# - QBFC_CONST::OmDontCare (accept whatever mode is currently in effect, or single-user mode if no other mode is in effect)
# Default is QBFC_CONST::OmDontCare
#
# If given a block, it yields the Session object and closes the Session and Connection
# when the block closes.
#
# Otherwise, it returns the new Session object.
def open(*options, &block)
qb = new(*options)
if block_given?
begin
yield qb
ensure
qb.close
end
else
qb
end
end
end
# See Session.open for initialization options.
def initialize(options = {})
ole_object = WIN32OLE.new("QBFC6.QBSessionManager")
ole_object.OpenConnection2(options[:app_id].to_s,
(options[:app_name] || "Ruby QBFC Application"),
(options[:conn_type] || QBFC_CONST::CtLocalQBD))
begin
ole_object.BeginSession(options[:filename].to_s,
(options[:open_mode] || QBFC_CONST::OmDontCare))
rescue WIN32OLERuntimeError
ole_object.CloseConnection
ole_object = nil
raise QBFC::QuickbooksClosedError, "BeginSession failed: Quickbooks must be open or a valid filename specified."
end
@ole_object = QBFC::OLEWrapper.new(ole_object)
end
# Close the session with Quickbooks. If this is ommitted, Quickbooks will not close.
# Using a block with Session.open ensures the session is closed.
def close
@ole_object.EndSession
@ole_object.CloseConnection
@ole_object = nil
end
# The classes method allows using session.classes.find instead of
# session.q_b_classes.find, for finds on QBClass.
def classes
QBFC::QBCollection.new(self, :QBClass)
end
# Responsible for the conversion of ole_method name to more convential Ruby method names.
# This specifies the methods for setting up an Entity, such as a Customer, directly, which is
# not included in OLEWrapper (setting up entities that are children of another entity is).
# Send other missing methods on to OLE Wrapper
def method_missing(symbol, *params) #:nodoc:
if (('a'..'z') === symbol.to_s[0].chr && symbol.to_s[-1].chr != '=')
camelized_method = symbol.to_s.camelize.to_sym
if camelized_method.to_s =~ /Terms\Z/
single_camelized_method = camelized_method
else
single_camelized_method = symbol.to_s.singularize.camelize.to_sym
end
if QBFC.const_defined?(camelized_method) && camelized_method.to_s !~ /Terms\Z/
if params[0]
return QBFC::const_get(camelized_method).find_by_unique_id(self, params[0])
else
return QBFC::const_get(camelized_method).find(self, :first)
end
elsif QBFC.const_defined?(single_camelized_method)
return QBFC::QBCollection.new(self, single_camelized_method)
end
end
# Don't want to pass an OLEWrapper to a WIN32OLE method.
params = params.collect{ |p| p.respond_to?(:ole_object) ? p.ole_object : p }
@ole_object.qbfc_method_missing(self, symbol, *params)
end
end
end