jm81-qbfc 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +11 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +85 -0
- data/Rakefile +81 -0
- data/VERSION +1 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/qbfc.rb +41 -0
- data/lib/qbfc/base.rb +82 -0
- data/lib/qbfc/element.rb +243 -0
- data/lib/qbfc/entities/generated.rb +8 -0
- data/lib/qbfc/entity.rb +11 -0
- data/lib/qbfc/info.rb +42 -0
- data/lib/qbfc/infos/generated.rb +9 -0
- data/lib/qbfc/item.rb +29 -0
- data/lib/qbfc/items/generated.rb +11 -0
- data/lib/qbfc/list.rb +84 -0
- data/lib/qbfc/lists/account.rb +24 -0
- data/lib/qbfc/lists/generated.rb +15 -0
- data/lib/qbfc/lists/qb_class.rb +25 -0
- data/lib/qbfc/modifiable.rb +31 -0
- data/lib/qbfc/ole_wrapper.rb +201 -0
- data/lib/qbfc/qb_collection.rb +26 -0
- data/lib/qbfc/qb_types.rb +18 -0
- data/lib/qbfc/qbfc_const.rb +14 -0
- data/lib/qbfc/report.rb +95 -0
- data/lib/qbfc/reports/aging.rb +13 -0
- data/lib/qbfc/reports/budget_summary.rb +13 -0
- data/lib/qbfc/reports/custom_detail.rb +9 -0
- data/lib/qbfc/reports/custom_summary.rb +9 -0
- data/lib/qbfc/reports/general_detail.rb +44 -0
- data/lib/qbfc/reports/general_summary.rb +33 -0
- data/lib/qbfc/reports/job.rb +14 -0
- data/lib/qbfc/reports/payroll_detail.rb +13 -0
- data/lib/qbfc/reports/payroll_summary.rb +13 -0
- data/lib/qbfc/reports/rows.rb +51 -0
- data/lib/qbfc/reports/time.rb +12 -0
- data/lib/qbfc/request.rb +295 -0
- data/lib/qbfc/session.rb +147 -0
- data/lib/qbfc/terms.rb +10 -0
- data/lib/qbfc/terms/generated.rb +10 -0
- data/lib/qbfc/transaction.rb +110 -0
- data/lib/qbfc/transactions/generated.rb +25 -0
- data/lib/qbfc/voidable.rb +11 -0
- data/spec/fixtures/test.lgb +0 -0
- data/spec/fixtures/test.qbw +0 -0
- data/spec/fixtures/test.qbw.TLG +0 -0
- data/spec/integration/add_spec.rb +31 -0
- data/spec/integration/base_spec.rb +18 -0
- data/spec/integration/belongs_to_spec.rb +64 -0
- data/spec/integration/company_spec.rb +30 -0
- data/spec/integration/conditions_spec.rb +59 -0
- data/spec/integration/customer_spec.rb +46 -0
- data/spec/integration/element_finders_spec.rb +20 -0
- data/spec/integration/quick_test.rb +31 -0
- data/spec/integration/request_options_spec.rb +68 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/unit/base_spec.rb +138 -0
- data/spec/unit/element_finder_spec.rb +185 -0
- data/spec/unit/element_spec.rb +108 -0
- data/spec/unit/entities/generated_spec.rb +18 -0
- data/spec/unit/entity_spec.rb +18 -0
- data/spec/unit/info/generated_spec.rb +12 -0
- data/spec/unit/info_spec.rb +48 -0
- data/spec/unit/item_spec.rb +33 -0
- data/spec/unit/items/generated_spec.rb +16 -0
- data/spec/unit/list_finders_spec.rb +129 -0
- data/spec/unit/list_spec.rb +86 -0
- data/spec/unit/lists/account_spec.rb +20 -0
- data/spec/unit/lists/generated_spec.rb +15 -0
- data/spec/unit/lists/qb_class_spec.rb +9 -0
- data/spec/unit/modifiable_spec.rb +84 -0
- data/spec/unit/ole_wrapper_spec.rb +337 -0
- data/spec/unit/qb_collection_spec.rb +13 -0
- data/spec/unit/qbfc_const_spec.rb +10 -0
- data/spec/unit/qbfc_spec.rb +10 -0
- data/spec/unit/report_spec.rb +12 -0
- data/spec/unit/request_query_survey.txt +48 -0
- data/spec/unit/request_spec.rb +486 -0
- data/spec/unit/session_spec.rb +144 -0
- data/spec/unit/terms/generated_spec.rb +14 -0
- data/spec/unit/terms_spec.rb +18 -0
- data/spec/unit/transaction_finders_spec.rb +125 -0
- data/spec/unit/transaction_spec.rb +94 -0
- data/spec/unit/transactions/generated_spec.rb +20 -0
- data/spec/unit/voidable_spec.rb +32 -0
- data/tasks/qbfc_tasks.rake +4 -0
- data/uninstall.rb +1 -0
- metadata +180 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Jared E Morgan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
==QBFC-Ruby
|
2
|
+
|
3
|
+
QBFC-Ruby provides a wrapper around QuickBooks' QBFC COM object, while
|
4
|
+
allowing more or less direct access to the actual COM object.
|
5
|
+
|
6
|
+
Obviously, test before using on your production data...
|
7
|
+
|
8
|
+
==Find
|
9
|
+
|
10
|
+
QBFC-Ruby supports +find+ queries with options, which utitilize QBFC's Query
|
11
|
+
Requests. See QBFC::Element.find for details and options.
|
12
|
+
|
13
|
+
QBFC::session do | qb |
|
14
|
+
checks = qb.checks.find(:first, :conditions => {:entity => 'ABC Supplies'})
|
15
|
+
end
|
16
|
+
|
17
|
+
==Relationships
|
18
|
+
|
19
|
+
QBFC-Ruby supports loading of related records. These are records
|
20
|
+
represented by "*Ref" in the QBFC documentation. For example, a Check
|
21
|
+
has, among others, a PayeeEntityRef and an AccountRef. These can be
|
22
|
+
accessed via, respectively, <tt>check.payee</tt> and <tt>check.account</tt>.
|
23
|
+
|
24
|
+
<tt>check.payee.name</tt> returns the name of the payee.
|
25
|
+
|
26
|
+
You can also access the *ID and Name fields of referenced records.
|
27
|
+
Example: for the payee of a Check, <tt>check.payee_id</tt> and
|
28
|
+
<tt>check.payee_name</tt>.
|
29
|
+
|
30
|
+
==General Examples
|
31
|
+
|
32
|
+
# A very simple example, finding a single Customer by name
|
33
|
+
QBFC::session do | qb |
|
34
|
+
puts qb.customer('Customer Name').full_name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Find all Customer, then return the first in the Array
|
38
|
+
# Next, find the first Customer only
|
39
|
+
QBFC::session do | qb |
|
40
|
+
customers = qb.customers.find(:all)
|
41
|
+
puts customers[0].full_name
|
42
|
+
puts qb.customers.find(:first).full_name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Same as previous, but not using a block
|
46
|
+
sess = QBFC::Session.new
|
47
|
+
customers = QBFC::Customer.find(sess, :all)
|
48
|
+
puts customers[0].full_name
|
49
|
+
puts QBFC::Customer.find(sess, :first).full_name
|
50
|
+
sess.close
|
51
|
+
|
52
|
+
# Use a QBFC::Session object, but access the COM object
|
53
|
+
# more directly.
|
54
|
+
QBFC::session do | qb |
|
55
|
+
request_set = qb.CreateMsgSetRequest("US", 6, 0)
|
56
|
+
customer_query = request_set.AppendCustomerQueryRq
|
57
|
+
response = qb.DoRequests(request_set)
|
58
|
+
customer_set = response.ResponseList[0]
|
59
|
+
first_customer = customer_set.Detail[0]
|
60
|
+
puts first_customer.full_name
|
61
|
+
end
|
62
|
+
|
63
|
+
==Alternatives
|
64
|
+
|
65
|
+
[QuickBooks for Ruby]
|
66
|
+
(docs: http://quickbooks.rubyforge.org,
|
67
|
+
homepage: http://rubyforge.org/projects/quickbooks)
|
68
|
+
|
69
|
+
This is a project with similar goals to QBFC-Ruby. I believe the approach
|
70
|
+
is creating Ruby classes that mirror the QuickBooks types and generate / parse
|
71
|
+
qbXML. In my opinion, this approach is more stable and flexible than what I'm
|
72
|
+
doing with QBFC-Ruby, but at the cost of slower development. (As in, QBFC-Ruby
|
73
|
+
was intended as a collection of quick and dirty shortcuts; QuickBooks for Ruby
|
74
|
+
seems to be intended as a much more *complete* project)
|
75
|
+
|
76
|
+
[QBFC/qbXML COM Objects]
|
77
|
+
Using the SDK directly is an option. Unless you need to use QBWC (QuickBooks
|
78
|
+
Web Connector) or have some other reason for *wanting* to us qbXML, I suggest
|
79
|
+
using QBFC. The SDK documents (see http://developer.intuit.com/ ) are pretty
|
80
|
+
easy to use and navigate.
|
81
|
+
|
82
|
+
There are some other libraries on rubyforge in early stages which I haven't
|
83
|
+
really explored.
|
84
|
+
|
85
|
+
Copyright (c) 2008 Jared E. Morgan, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "qbfc"
|
8
|
+
gem.rubyforge_project = "qbfc"
|
9
|
+
gem.summary = "A wrapper around the QBFC COM object of the Quickbooks SDK"
|
10
|
+
gem.email = "jmorgan@morgancreative.net"
|
11
|
+
gem.homepage = "http://rubyforge.org/projects/qbfc/"
|
12
|
+
gem.authors = ["Jared Morgan"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
|
16
|
+
rf = Jeweler::RubyforgeTasks.new
|
17
|
+
rf.remote_doc_path = ''
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'spec/rake/spectask'
|
24
|
+
|
25
|
+
QBFC_ROOT = File.dirname(__FILE__)
|
26
|
+
|
27
|
+
desc "Run all specs in spec/unit directory"
|
28
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/unit/**/*_spec.rb'
|
31
|
+
spec.spec_opts = ['--options', "\"#{QBFC_ROOT}/spec/spec.opts\""]
|
32
|
+
end
|
33
|
+
|
34
|
+
namespace :spec do
|
35
|
+
desc "Run all specs in spec/unit directory with RCov"
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/unit/**/*_spec.rb'
|
39
|
+
spec.spec_opts = ['--options', "\"#{QBFC_ROOT}/spec/spec.opts\""]
|
40
|
+
spec.rcov = true
|
41
|
+
spec.rcov_opts = lambda do
|
42
|
+
IO.readlines("#{QBFC_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Run all specs in spec/integration directory"
|
47
|
+
Spec::Rake::SpecTask.new(:integration) do |spec|
|
48
|
+
spec.libs << 'lib' << 'spec'
|
49
|
+
spec.pattern = 'spec/integration/**/*_spec.rb'
|
50
|
+
spec.spec_opts = ['--options', "\"#{QBFC_ROOT}/spec/spec.opts\""]
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Run all specs in spec/integration directory with RCov"
|
54
|
+
Spec::Rake::SpecTask.new(:integration_rcov) do |spec|
|
55
|
+
spec.libs << 'lib' << 'spec'
|
56
|
+
spec.pattern = 'spec/unit/**/*_spec.rb'
|
57
|
+
spec.spec_opts = ['--options', "\"#{QBFC_ROOT}/spec/spec.opts\""]
|
58
|
+
spec.rcov = true
|
59
|
+
spec.rcov_opts = lambda do
|
60
|
+
IO.readlines("#{QBFC_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
task :default => :spec
|
67
|
+
|
68
|
+
require 'rake/rdoctask'
|
69
|
+
Rake::RDocTask.new do |rdoc|
|
70
|
+
if File.exist?('VERSION')
|
71
|
+
version = File.read('VERSION')
|
72
|
+
else
|
73
|
+
version = ""
|
74
|
+
end
|
75
|
+
|
76
|
+
rdoc.rdoc_dir = 'rdoc'
|
77
|
+
rdoc.title = "QBFC-Ruby #{version}"
|
78
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
79
|
+
rdoc.rdoc_files.include('README*')
|
80
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
81
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
data/lib/qbfc.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
# ActiveSupport is used for camelize, singularize and similar String inflectors.
|
5
|
+
unless Object.const_defined?(:ActiveSupport)
|
6
|
+
gem 'activesupport'
|
7
|
+
require 'active_support'
|
8
|
+
end
|
9
|
+
|
10
|
+
module QBFC
|
11
|
+
VERSION = '0.3.0'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Opens and yields a QBFC::Session
|
16
|
+
def session(*args, &block)
|
17
|
+
QBFC::Session::open(*args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generate classes.
|
21
|
+
# - +names+: Array of class names.
|
22
|
+
# - +superclass+: Superclass of classes to be generated.
|
23
|
+
# - +includes+: hash of Module => names of classes to include this module.
|
24
|
+
def generate(names, superclass, include_modules = {})
|
25
|
+
names.each do | class_name |
|
26
|
+
const_set(class_name, Class.new(superclass))
|
27
|
+
end
|
28
|
+
|
29
|
+
include_modules.each do | mod, classes |
|
30
|
+
classes.each do | class_name |
|
31
|
+
const_get(class_name).__send__(:include, mod)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
%w{ ole_wrapper qbfc_const session request base element info report qb_collection qb_types }.each do |file|
|
40
|
+
require 'qbfc/' + file
|
41
|
+
end
|
data/lib/qbfc/base.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module QBFC
|
2
|
+
# Base is the...um..."base" class from which Element, Info, and
|
3
|
+
# Report inherit. It defines methods that the three share.
|
4
|
+
class Base
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# is_base_class? is used by Element and subclasses. It is included
|
8
|
+
# in Base because some Base methods may check for it.
|
9
|
+
def is_base_class? #:nodoc:
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Element::find and Info::get receive optional arguments which can include
|
14
|
+
# a Request object and/or an options Hash. <tt>parse_find_args</tt>
|
15
|
+
# gets these arguments into a set that is easier to deal with.
|
16
|
+
def parse_find_args(*args)
|
17
|
+
request = args[0].kind_of?(QBFC::Request) ? args[0].dup : nil
|
18
|
+
options = args[-1].kind_of?(Hash) ? args[-1].dup : {}
|
19
|
+
|
20
|
+
# base classes will need to pass a subset of options to
|
21
|
+
# the ChildClass.find . Also, the actually options to the
|
22
|
+
# BaseClass.find Request cannot include owner_id.
|
23
|
+
if is_base_class?
|
24
|
+
base_options = options.dup
|
25
|
+
base_options.delete(:conditions)
|
26
|
+
options.delete(:owner_id)
|
27
|
+
else
|
28
|
+
base_options = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
return request, options, base_options
|
32
|
+
end
|
33
|
+
|
34
|
+
# A convenience method for creating and returning
|
35
|
+
# a Query Request for this class.
|
36
|
+
def create_query(sess)
|
37
|
+
QBFC::Request.new(sess, "#{qb_name}Query")
|
38
|
+
end
|
39
|
+
|
40
|
+
protected :parse_find_args, :create_query
|
41
|
+
|
42
|
+
# The QuickBooks name for this Element or Report.
|
43
|
+
# It typically matches the last part of class name.
|
44
|
+
# Used in determining names of Requests and other
|
45
|
+
# OLE methods.
|
46
|
+
def qb_name
|
47
|
+
self.name.split('::').last
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create an instance of this Element or Report.
|
52
|
+
# - <tt>sess</tt>: An open QBFC::Session object that will recieve all requests
|
53
|
+
# - <tt>ole</tt>: An optional QBFC::OLEWrapper object representing
|
54
|
+
# a response to a QueryRq. It is unlikely that this will be used directly.
|
55
|
+
def initialize(sess, ole = nil)
|
56
|
+
@sess, @ole = sess, ole
|
57
|
+
@ole = QBFC::OLEWrapper.new(@ole) if @ole.kind_of?(WIN32OLE)
|
58
|
+
end
|
59
|
+
|
60
|
+
# List the methods of the OLE object
|
61
|
+
def ole_methods
|
62
|
+
@ole.ole_methods
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if the OLE object responds to a given method
|
66
|
+
def respond_to_ole?(symbol)
|
67
|
+
@ole.respond_to_ole?(symbol)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Pass missing methods to OLEWrapper#qbfc_method_missing
|
71
|
+
# to handle checking if there is a related OLE method to run.
|
72
|
+
def method_missing(symbol, *params)
|
73
|
+
@ole.qbfc_method_missing(@sess, symbol, *params)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Name of the QuickBooks Element or Query represented by this class.
|
77
|
+
def qb_name
|
78
|
+
self.class.qb_name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
data/lib/qbfc/element.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'qbfc/modifiable'
|
2
|
+
|
3
|
+
module QBFC
|
4
|
+
|
5
|
+
# An Element is a Transaction or a List; that is any QuickBooks objects that can
|
6
|
+
# be created, edited (possibly), deleted and read. Contrast to a Report or Info
|
7
|
+
# which are read-only.
|
8
|
+
#
|
9
|
+
# Inheritance from Element implies a set of shared methods, such as find, but the
|
10
|
+
# only shared implementation defined here is #custom, for getting custom field information.
|
11
|
+
class Element < Base
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Set that this is a "base class", one which is inherited,
|
15
|
+
# such as List, Transaction, Entity, or Terms.
|
16
|
+
# Base classes do not accept Add Requests, and their finders
|
17
|
+
# will return an instance of an inherited class.
|
18
|
+
def is_base_class
|
19
|
+
@is_base_class = true
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if this is a "base class" (see is_base_class)
|
23
|
+
def is_base_class?
|
24
|
+
@is_base_class ? true : false
|
25
|
+
end
|
26
|
+
|
27
|
+
# Find a QuickBooks record for the given session.
|
28
|
+
#
|
29
|
+
# session.[qb_name] aliases this functionality. The following
|
30
|
+
# pairs are equivalent:
|
31
|
+
#
|
32
|
+
# QBFC::Vendor.find(session, :first) <-> session.vendor
|
33
|
+
# QBFC::Vendor.find(session, "Supply Shop") <-> session.vendor("Supply Shop")
|
34
|
+
# QBFC::Vendor.find(session, :all) <-> session.vendors.find(:all)
|
35
|
+
#
|
36
|
+
# Find requires a +session+ (representing a QBFC::Session) and
|
37
|
+
# a +what+ argument. +what+ can be one of:
|
38
|
+
# - <tt>:first</tt> - finds the first record fitting any given conditions.
|
39
|
+
# - <tt>:finds</tt> - finds all records fitting any given conditions.
|
40
|
+
# - An unique identifier, such as a ListID, FullName, RefNumber or TxnID
|
41
|
+
#
|
42
|
+
# .find can also receive a optional Request object followed
|
43
|
+
# by an optional options Hash. Valid argument sets are:
|
44
|
+
#
|
45
|
+
# QBFC::Vendor.find(session, :first)
|
46
|
+
# QBFC::Vendor.find(session, :first, request)
|
47
|
+
# QBFC::Vendor.find(session, :first, request, options)
|
48
|
+
# QBFC::Vendor.find(session, :first, options)
|
49
|
+
#
|
50
|
+
# The options hash accepts the following:
|
51
|
+
# - <tt>:owner_id</tt>: One or more OwnerIDs, used in accessing
|
52
|
+
# custom fields (aka private data extensions).
|
53
|
+
# - <tt>:limit</tt>: The maximum number of records to be returned.
|
54
|
+
# - <tt>:include</tt>: Elements to include (see below for details)
|
55
|
+
# - <tt>:conditions</tt>: A hash of conditions (generally 'Filters' in
|
56
|
+
# QuickBooks SDK. See below:
|
57
|
+
#
|
58
|
+
# Additional options are planned, but not really supported in this version.
|
59
|
+
# Passing a Request object is the current recommended way of applying
|
60
|
+
# unsupported conditions (Filters) or other options to the Query Request.
|
61
|
+
#
|
62
|
+
# ==Include
|
63
|
+
#
|
64
|
+
# The :include option accepts an Array of elements to include in the
|
65
|
+
# return of the Query. The array may include either or both of elements
|
66
|
+
# that are additional to normal returns (such as Line Items, Linked
|
67
|
+
# Transactions) or elements that are normally included (to be added to the
|
68
|
+
# IncludeRetElementList).
|
69
|
+
#
|
70
|
+
# If elements are given that would be added to IncludeRetElementList, this
|
71
|
+
# limits the elements returned to *only* those included in the array.
|
72
|
+
#
|
73
|
+
# Another option is to give :all as the argument, which will always return
|
74
|
+
# as many elements as possible.
|
75
|
+
#
|
76
|
+
# @sess.checks.find(:all, :include => [:linked_txns]) -> Include linked transactions
|
77
|
+
# @sess.checks.find(:all, :include => [:txn_id]) -> Include +only+ TxnID
|
78
|
+
# @sess.checks.find(:all, :include => :all) ->
|
79
|
+
# Includes all elements, including LinkedTxns and LineItems.
|
80
|
+
#
|
81
|
+
# ==Conditions
|
82
|
+
#
|
83
|
+
# Conditions are dependent on the particular Request. See the QuickBooks
|
84
|
+
# SDK documentation for applicable filters for each Query Request. Note
|
85
|
+
# that not all Filters are supported.
|
86
|
+
#
|
87
|
+
# Typically the condition is given as :filter_name => value where
|
88
|
+
# +filter_name+ is the name of the filter less the word 'Filter' (see
|
89
|
+
# examples below).
|
90
|
+
#
|
91
|
+
# Here are some general rules:
|
92
|
+
#
|
93
|
+
# [List filters]
|
94
|
+
# These are filters that end in List. They take an Array of values.
|
95
|
+
#
|
96
|
+
# :ref_number_list => ["1001", "1003", "1003a"]
|
97
|
+
# :txn_id_list => ["123-456"]
|
98
|
+
#
|
99
|
+
# [Range Filters]
|
100
|
+
# Filters which take a range of values. These accept any
|
101
|
+
# object which responds to +first+ and +last+, such as a Range or Array.
|
102
|
+
# +first+ is used to set the From value, +last+ sets the To value. If a
|
103
|
+
# scalar value is given (or a single element Array), To is set and From
|
104
|
+
# is left empty. nil can also be given for either value.
|
105
|
+
#
|
106
|
+
# :txn_date_range => ['2008-01', nil]
|
107
|
+
# :txn_date_range => ['2008-01']
|
108
|
+
# :txn_date_range => '2008-01'
|
109
|
+
#
|
110
|
+
# :ref_number_range => "1001".."1003"
|
111
|
+
# :ref_number_range => ["1001", "1003"]
|
112
|
+
#
|
113
|
+
# [Reference Filters]
|
114
|
+
# Filters which reference another object (belongs to
|
115
|
+
# filters). These current only accept Name arguments, as a single value
|
116
|
+
# or an Array.
|
117
|
+
#
|
118
|
+
# :account => 'Checking'
|
119
|
+
# :entity => ['ABC Supplies', 'CompuStuff']
|
120
|
+
#
|
121
|
+
def find(sess, what, *args)
|
122
|
+
if what.kind_of?(String) # Single FullName or ListID
|
123
|
+
return find_by_unique_id(sess, what, *args)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Setup q, options and base_options arguments
|
127
|
+
q, options, base_options = parse_find_args(*args)
|
128
|
+
q ||= create_query(sess)
|
129
|
+
ignore_base_class = options.kind_of?(Hash) ? options.delete(:ignore_base_class) : false
|
130
|
+
|
131
|
+
q.apply_options(options)
|
132
|
+
|
133
|
+
# QuickBooks only needs to return one record if .find is
|
134
|
+
# only returning a single record.
|
135
|
+
if what == :first && q.filter_available?
|
136
|
+
q.add_limit(1)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Send the query so far to base_class_find if this is
|
140
|
+
# a base class to handle special base class functionality.
|
141
|
+
if is_base_class? && !ignore_base_class
|
142
|
+
return base_class_find(sess, what, q, base_options)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Get response and return an Array, a QBFC::Element-inherited
|
146
|
+
# object, or nil, depending on <tt>what</tt> argument and whether
|
147
|
+
# the query returned any records.
|
148
|
+
list = q.response
|
149
|
+
|
150
|
+
if list.nil?
|
151
|
+
(what == :all) ? [] : nil
|
152
|
+
elsif what == :all
|
153
|
+
(0..(list.Count - 1)).collect { |i|
|
154
|
+
new(sess, list.GetAt(i))
|
155
|
+
}
|
156
|
+
else
|
157
|
+
new(sess, list.GetAt(0))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Base classes can be used to find members of any of their
|
162
|
+
# inherited classes. Since QuickBooks has limited options
|
163
|
+
# for these type of queries (in particular, no custom fields
|
164
|
+
# are returned), an initial query is run which retrieves
|
165
|
+
# only IDs and the class type and then individual subsequent
|
166
|
+
# queries are run for each returned object.
|
167
|
+
def base_class_find(sess, what, q, options)
|
168
|
+
q.IncludeRetElementList.Add(self::ID_NAME)
|
169
|
+
list = q.response
|
170
|
+
|
171
|
+
if list.nil?
|
172
|
+
(what == :all) ? [] : nil
|
173
|
+
else
|
174
|
+
ary = (0..(list.Count - 1)).collect { |i|
|
175
|
+
element = list.GetAt(i)
|
176
|
+
ret_name = element.ole_methods.detect { |m|
|
177
|
+
m.to_s =~ /(.*)Ret\Z/ && element.send(m.to_s)
|
178
|
+
}.to_s
|
179
|
+
ret_class = QBFC::const_get($1)
|
180
|
+
ret_class.find(sess, element.send(ret_name).send(ret_class::ID_NAME).GetValue, options.dup)
|
181
|
+
}
|
182
|
+
|
183
|
+
if what == :all
|
184
|
+
ary
|
185
|
+
else
|
186
|
+
ary[0]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
private :base_class_find, :is_base_class
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
is_base_class
|
196
|
+
|
197
|
+
# Extends Base#initialize to allow for an Add Request if
|
198
|
+
# .new is called directly (instead of by .find)
|
199
|
+
def initialize(sess, ole_object = nil)
|
200
|
+
super
|
201
|
+
|
202
|
+
if @ole.nil?
|
203
|
+
add_rq = QBFC::Request.new(sess, "#{qb_name}Add")
|
204
|
+
@ole = QBFC::OLEWrapper.new(add_rq.ole_object)
|
205
|
+
@new_record = true
|
206
|
+
@setter = add_rq
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Is this a new record, i.e. are we doing an Add Request?
|
211
|
+
def new_record?
|
212
|
+
@new_record ? true : false
|
213
|
+
end
|
214
|
+
|
215
|
+
# Access information from a custom field.
|
216
|
+
def custom(field_name, owner_id = '0')
|
217
|
+
if @ole.DataExtRetList
|
218
|
+
@ole.data_ext.each do |field|
|
219
|
+
if field.data_ext_name == field_name && field.owner_id == owner_id.to_s
|
220
|
+
return field.data_ext_value
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
return nil
|
226
|
+
end
|
227
|
+
|
228
|
+
# Save (create or update) this record
|
229
|
+
def save
|
230
|
+
if @setter
|
231
|
+
@setter.submit
|
232
|
+
else
|
233
|
+
raise NotSavableError, "This record cannot be saved (Probably because it does not support Mod Requests)."
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Require subclass files
|
241
|
+
%w{ list transaction }.each do |file|
|
242
|
+
require 'qbfc/' + file
|
243
|
+
end
|