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.
Files changed (91) hide show
  1. data/.gitignore +11 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +81 -0
  5. data/VERSION +1 -0
  6. data/init.rb +3 -0
  7. data/install.rb +1 -0
  8. data/lib/qbfc.rb +41 -0
  9. data/lib/qbfc/base.rb +82 -0
  10. data/lib/qbfc/element.rb +243 -0
  11. data/lib/qbfc/entities/generated.rb +8 -0
  12. data/lib/qbfc/entity.rb +11 -0
  13. data/lib/qbfc/info.rb +42 -0
  14. data/lib/qbfc/infos/generated.rb +9 -0
  15. data/lib/qbfc/item.rb +29 -0
  16. data/lib/qbfc/items/generated.rb +11 -0
  17. data/lib/qbfc/list.rb +84 -0
  18. data/lib/qbfc/lists/account.rb +24 -0
  19. data/lib/qbfc/lists/generated.rb +15 -0
  20. data/lib/qbfc/lists/qb_class.rb +25 -0
  21. data/lib/qbfc/modifiable.rb +31 -0
  22. data/lib/qbfc/ole_wrapper.rb +201 -0
  23. data/lib/qbfc/qb_collection.rb +26 -0
  24. data/lib/qbfc/qb_types.rb +18 -0
  25. data/lib/qbfc/qbfc_const.rb +14 -0
  26. data/lib/qbfc/report.rb +95 -0
  27. data/lib/qbfc/reports/aging.rb +13 -0
  28. data/lib/qbfc/reports/budget_summary.rb +13 -0
  29. data/lib/qbfc/reports/custom_detail.rb +9 -0
  30. data/lib/qbfc/reports/custom_summary.rb +9 -0
  31. data/lib/qbfc/reports/general_detail.rb +44 -0
  32. data/lib/qbfc/reports/general_summary.rb +33 -0
  33. data/lib/qbfc/reports/job.rb +14 -0
  34. data/lib/qbfc/reports/payroll_detail.rb +13 -0
  35. data/lib/qbfc/reports/payroll_summary.rb +13 -0
  36. data/lib/qbfc/reports/rows.rb +51 -0
  37. data/lib/qbfc/reports/time.rb +12 -0
  38. data/lib/qbfc/request.rb +295 -0
  39. data/lib/qbfc/session.rb +147 -0
  40. data/lib/qbfc/terms.rb +10 -0
  41. data/lib/qbfc/terms/generated.rb +10 -0
  42. data/lib/qbfc/transaction.rb +110 -0
  43. data/lib/qbfc/transactions/generated.rb +25 -0
  44. data/lib/qbfc/voidable.rb +11 -0
  45. data/spec/fixtures/test.lgb +0 -0
  46. data/spec/fixtures/test.qbw +0 -0
  47. data/spec/fixtures/test.qbw.TLG +0 -0
  48. data/spec/integration/add_spec.rb +31 -0
  49. data/spec/integration/base_spec.rb +18 -0
  50. data/spec/integration/belongs_to_spec.rb +64 -0
  51. data/spec/integration/company_spec.rb +30 -0
  52. data/spec/integration/conditions_spec.rb +59 -0
  53. data/spec/integration/customer_spec.rb +46 -0
  54. data/spec/integration/element_finders_spec.rb +20 -0
  55. data/spec/integration/quick_test.rb +31 -0
  56. data/spec/integration/request_options_spec.rb +68 -0
  57. data/spec/rcov.opts +1 -0
  58. data/spec/spec.opts +6 -0
  59. data/spec/spec_helper.rb +62 -0
  60. data/spec/unit/base_spec.rb +138 -0
  61. data/spec/unit/element_finder_spec.rb +185 -0
  62. data/spec/unit/element_spec.rb +108 -0
  63. data/spec/unit/entities/generated_spec.rb +18 -0
  64. data/spec/unit/entity_spec.rb +18 -0
  65. data/spec/unit/info/generated_spec.rb +12 -0
  66. data/spec/unit/info_spec.rb +48 -0
  67. data/spec/unit/item_spec.rb +33 -0
  68. data/spec/unit/items/generated_spec.rb +16 -0
  69. data/spec/unit/list_finders_spec.rb +129 -0
  70. data/spec/unit/list_spec.rb +86 -0
  71. data/spec/unit/lists/account_spec.rb +20 -0
  72. data/spec/unit/lists/generated_spec.rb +15 -0
  73. data/spec/unit/lists/qb_class_spec.rb +9 -0
  74. data/spec/unit/modifiable_spec.rb +84 -0
  75. data/spec/unit/ole_wrapper_spec.rb +337 -0
  76. data/spec/unit/qb_collection_spec.rb +13 -0
  77. data/spec/unit/qbfc_const_spec.rb +10 -0
  78. data/spec/unit/qbfc_spec.rb +10 -0
  79. data/spec/unit/report_spec.rb +12 -0
  80. data/spec/unit/request_query_survey.txt +48 -0
  81. data/spec/unit/request_spec.rb +486 -0
  82. data/spec/unit/session_spec.rb +144 -0
  83. data/spec/unit/terms/generated_spec.rb +14 -0
  84. data/spec/unit/terms_spec.rb +18 -0
  85. data/spec/unit/transaction_finders_spec.rb +125 -0
  86. data/spec/unit/transaction_spec.rb +94 -0
  87. data/spec/unit/transactions/generated_spec.rb +20 -0
  88. data/spec/unit/voidable_spec.rb +32 -0
  89. data/tasks/qbfc_tasks.rake +4 -0
  90. data/uninstall.rb +1 -0
  91. metadata +180 -0
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ .project
7
+ .loadpath
8
+ *qt_temp*
9
+ *~
10
+ *.qbw.nd
11
+ spec/tmp
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
@@ -0,0 +1,3 @@
1
+ # Include hook code here
2
+
3
+ require 'qbfc'
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
+
@@ -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