qbfc 0.1.0-x86-mswin32-60 → 0.2.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -5,7 +5,29 @@ allowing more or less direct access to the actual COM object.
5
5
 
6
6
  Obviously, test before using on your production data...
7
7
 
8
- ==Examples
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
9
31
 
10
32
  # A very simple example, finding a single Customer by name
11
33
  QBFC::session do | qb |
@@ -37,5 +59,27 @@ Obviously, test before using on your production data...
37
59
  first_customer = customer_set.Detail[0]
38
60
  puts first_customer.full_name
39
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.
40
84
 
41
85
  Copyright (c) 2008 Jared E. Morgan, released under the MIT license
data/Rakefile CHANGED
@@ -63,7 +63,7 @@ require 'rake/gempackagetask'
63
63
 
64
64
  spec = Gem::Specification.new do |s|
65
65
  s.name = "qbfc"
66
- s.version = "0.1.0"
66
+ s.version = "0.2.0"
67
67
  s.author = "Jared Morgan"
68
68
  s.email = "jmorgan@morgancreative.net"
69
69
  s.homepage = "http://rubyforge.org/projects/qbfc/"
@@ -50,10 +50,74 @@ module QBFC
50
50
  # The options hash accepts the following:
51
51
  # - <tt>:owner_id</tt>: One or more OwnerIDs, used in accessing
52
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:
53
57
  #
54
58
  # Additional options are planned, but not really supported in this version.
55
59
  # Passing a Request object is the current recommended way of applying
56
- # Filters or other options to the Query Request.
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
+ #
57
121
  def find(sess, what, *args)
58
122
 
59
123
  if what.kind_of?(String) # Single FullName or ListID
@@ -63,12 +127,13 @@ module QBFC
63
127
  # Setup q, options and base_options arguments
64
128
  q, options, base_options = parse_find_args(*args)
65
129
  q ||= create_query(sess)
130
+
66
131
  q.apply_options(options)
67
132
 
68
133
  # QuickBooks only needs to return one record if .find is
69
134
  # only returning a single record.
70
135
  if what == :first && q.filter_available?
71
- q.filter.max_returned = 1
136
+ q.add_limit(1)
72
137
  end
73
138
 
74
139
  # Send the query so far to base_class_find if this is
@@ -106,9 +171,11 @@ module QBFC
106
171
  else
107
172
  ary = (0..(list.Count - 1)).collect { |i|
108
173
  element = list.GetAt(i)
109
- ret_name = element.ole_methods.detect{ |m| m.to_s =~ /(.*)Ret\Z/ }.to_s
174
+ ret_name = element.ole_methods.detect { |m|
175
+ m.to_s =~ /(.*)Ret\Z/ && element.send(m.to_s)
176
+ }.to_s
110
177
  ret_class = QBFC::const_get($1)
111
- ret_class.find(sess, element.send(ret_name).send(ret_class::ID_NAME), options.dup)
178
+ ret_class.find(sess, element.send(ret_name).send(ret_class::ID_NAME).GetValue, options.dup)
112
179
  }
113
180
 
114
181
  if what == :all
@@ -119,7 +186,7 @@ module QBFC
119
186
  end
120
187
  end
121
188
 
122
- private :base_class_find
189
+ private :base_class_find, :is_base_class
123
190
 
124
191
  end
125
192
 
@@ -1,6 +1,25 @@
1
1
  module QBFC
2
2
  class Item < List
3
3
  is_base_class
4
+
5
+ class << self
6
+
7
+ # Adds a SpecialItem.
8
+ #
9
+ # +item_type+ should be a constent, for example:
10
+ #
11
+ # Account.add_special(sess, QBFC_CONST::SitFinanceCharge)
12
+ #
13
+ # See SDK docs for SpecialItemAdd for more details.
14
+ def add_special(sess, item_type)
15
+ rq = QBFC::Request.new(sess, "SpecialItemAdd")
16
+ rq.special_item_type = item_type
17
+
18
+ # Insofar as I never actually plan to use this method, just return
19
+ # response.
20
+ return rq.response
21
+ end
22
+ end
4
23
  end
5
24
  end
6
25
 
@@ -0,0 +1,24 @@
1
+ module QBFC
2
+ class Account < List
3
+ include Modifiable
4
+
5
+ class << self
6
+
7
+ # Adds a SpecialAccount, per SDK documentation:
8
+ # "An account normally created automatically as needed within the
9
+ # QuickBooks UI, or in the SDK via the SpecialAccountAdd request."
10
+ #
11
+ # +account_type+ should be a constent, for example:
12
+ #
13
+ # Account.add_special(sess, QBFC_CONST::SatAccountsPayable)
14
+ #
15
+ # See SDK docs for SpecialAccountAdd for more details.
16
+ def add_special(sess, account_type)
17
+ rq = QBFC::Request.new(sess, "SpecialAccountAdd")
18
+ rq.special_account_type = account_type
19
+
20
+ new(sess, rq.response)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,15 +1,15 @@
1
1
  module QBFC
2
2
 
3
3
  # Generated List types that will inherit directly from QBFC::List
4
- LIST_TYPES = %w{Account BillingRate CustomerMsg CustomerType JobType PaymentMethod
5
- PayrollItemNonWage PayrollItemWage PriceLevel SalesRep SalesTaxCode ShipMethod ToDo
6
- Vehicle VendorType}
4
+ LIST_TYPES = %w{ BillingRate CustomerMsg CustomerType JobType PaymentMethod
5
+ PayrollItemNonWage PayrollItemWage PriceLevel SalesRep SalesTaxCode
6
+ ShipMethod ToDo Vehicle VendorType}
7
7
 
8
- # Generated List types that do not accept Mod Requests
9
- LIST_NO_MOD_TYPES = %w{ BillingRate CustomerMsg CustomerType JobType PaymentMethod
10
- PayrollItemWage SalesTaxCode ShipMethod ToDo VendorType}
8
+ # Generated List types that do accept Mod Requests
9
+ # (most direct subclasses of List do not)
10
+ LIST_MOD_TYPES = %w{ PriceLevel SalesRep Vehicle }
11
11
 
12
12
  # Generate List subclasses
13
- generate(LIST_TYPES, List, {Modifiable => LIST_TYPES - LIST_NO_MOD_TYPES})
13
+ generate(LIST_TYPES, List, {Modifiable => LIST_MOD_TYPES})
14
14
 
15
15
  end
@@ -103,6 +103,7 @@ module QBFC
103
103
  obj = @ole_object.send(ole_method_name)
104
104
 
105
105
  if detect_ole_method?(obj, "SetValue")
106
+ params[0] = params[0].strftime("%Y-%m-%d") if params[0].kind_of?(Date)
106
107
  obj.SetValue(*params)
107
108
  if @setter && detect_ole_method?(@setter, ole_method_name)
108
109
  @setter.send(ole_method_name).SetValue(*params)
@@ -1,34 +1,21 @@
1
1
  # This file sets up the classes for QuickBooks entities, transactions and reports.
2
2
 
3
- # Types that allow Query and Delete only
4
- QBFC_DELETE_ONLY = %w{PayrollItemNonWage DataEventRecoveryInfo}
5
-
6
3
  # Report types return an IReportRet
7
- QBFC_REPORT_TYPES = %w{AgingReport BudgetSummaryReport CustomDetailReport CustomSummaryReport
8
- GeneralDetailReport GeneralSummaryReport JobReport PayrollDetailReport PayrollSummaryReport TimeReport }
9
-
10
- # Types that allow Special adds (Pre-defined and normally added automatically by QuickBooks)
11
- QBFC_HAS_SPECIAL_ADD = %w{Account Item}
4
+ QBFC_REPORT_TYPES = %w{AgingReport BudgetSummaryReport CustomDetailReport
5
+ CustomSummaryReport GeneralDetailReport GeneralSummaryReport JobReport
6
+ PayrollDetailReport PayrollSummaryReport TimeReport }
12
7
 
13
- # TODO: Here and below arrays I haven't yet formed any approach to dealing with.
14
- # I leave them here as a reminder.
15
- ELEMENTS_ADD_MOD = %w{ DataExt }
16
-
17
- ELEMENTS_ADD_MOD_QUERY = %w{ DataExtDef }
18
-
19
- # Types that have their own DelRq
20
- ELEMENT_DEL_TYPES = %w{DataEventRecoveryInfo DataExt DataExtDef}
8
+ # Very non-standard elements. I haven't yet formed an approach to dealing
9
+ # with these; I leave them here as a reminder.
10
+ QBFC_NON_STANDARD_TYPES = %w{ DataExt DataExtDef DataEventRecoveryInfo ItemAssembliesCanBuild}
21
11
 
22
12
  # Query types support Query requests only and return an itemized list of some sort;
23
13
  # most of these may be integrated as special finders for their types.
24
14
  QBFC_QUERY_TYPES = %w{BillToPay ListDeleted ReceivePaymentToDeposit Template TxnDeleted SalesTaxPaymentCheck}
25
15
 
26
- QBFC_ANOTHER_TO_INTEGRATE_SOMEWHERE = %w{ ItemAssembliesCanBuild }
27
-
28
-
29
16
  module QBFC
30
17
  # Create QBElement classes
31
- (QBFC_REPORT_TYPES + QBFC_DELETE_ONLY + %w{DataExt DataExtDef}).uniq.each do | qb_element_name |
18
+ (QBFC_REPORT_TYPES + QBFC_NON_STANDARD_TYPES + QBFC_QUERY_TYPES).uniq.each do | qb_element_name |
32
19
  const_set(qb_element_name, Class.new(Base))
33
20
  end
34
21
  end
@@ -75,45 +75,58 @@ module QBFC
75
75
  self.query.ole_object.ortype == -1 ||
76
76
  self.query.ole_object.ortype == 2
77
77
  end
78
-
79
- # Applies options from a Hash. This method is primarily experimental
80
- # (and proof of concept) at this time.
78
+
79
+ # Applies options from a Hash. This method is generally used by find methods
80
+ # (see Element.find for details)
81
81
  def apply_options(options)
82
82
  if options.kind_of? Hash
83
- filters = options[:conditions]
84
- if filters
85
- if filters[:txn_date]
86
- txn_date_filter = q.ORTxnQuery.TxnFilter.ORDateRangeFilter.TxnDateRangeFilter.ORTxnDateRangeFilter.TxnDateFilter
87
- txn_date_filter.FromTxnDate.SetValue( filters[:txn_date][0] ) if filters[:txn_date][0]
88
- txn_date_filter.ToTxnDate.SetValue( filters[:txn_date][1] ) if filters[:txn_date][1]
89
- filters.delete(:txn_date)
90
- end
83
+ conditions = options.delete(:conditions) || {}
84
+
85
+ conditions.each do | c_name, c_value |
86
+ c_name = c_name.to_s
91
87
 
92
- if filters[:ref_number]
93
- ref_num_filter = q.send("OR#{self.qb_name}Query").send("#{self.qb_name}Filter").
94
- ORRefNumberFilter.RefNumberRangeFilter
95
- ref_num_filter.FromRefNumber.SetValue( filters[:ref_number][0] ) if filters[:ref_number][0]
96
- ref_num_filter.ToRefNumber.SetValue( filters[:ref_number][1] ) if filters[:ref_number][1]
97
- filters.delete(:ref_number)
98
- end
99
-
100
- filters.each do |filter, value|
101
- q.send("OR#{self.qb_name}Query").
102
- send("#{self.qb_name}Filter").
103
- send("#{filter}=", QBFC_CONST::PsNotPaidOnly)
88
+ case c_name
89
+ when /list\Z/i
90
+ # List filters
91
+ list = query.__send__(c_name.camelize)
92
+ c_value = [c_value] unless c_value.kind_of?(Array)
93
+ c_value.each { |i| list.Add(i) }
94
+ when /range\Z/i
95
+ # Range filters
96
+ c_value = parse_range_value(c_value)
97
+ range_filter = filter_for(c_name)
98
+ range_name = c_name.match(/(.*)_range\Z/i)[1]
99
+ if range_name == 'modified_date'
100
+ # Modified Date Range use the IQBDateTimeType which requires a\
101
+ # boolean 'asDateOnly' value.
102
+ range_filter.__send__("from_#{range_name}=", c_value.first, true) if c_value.first
103
+ range_filter.__send__("to_#{range_name}=", c_value.last, true) if c_value.last
104
+ else
105
+ range_filter.__send__("from_#{range_name}=", c_value.first) if c_value.first
106
+ range_filter.__send__("to_#{range_name}=", c_value.last) if c_value.last
107
+ end
108
+ when /status\Z/i
109
+ # Status filters
110
+ filter.__send__("#{c_name}=", c_value)
111
+ else
112
+ # Reference filters - Only using FullNameList for now
113
+ ref_filter = filter_for(c_name)
114
+ c_value = [c_value] unless c_value.respond_to?(:each)
115
+ c_value.each do | val |
116
+ ref_filter.FullNameList.Add(val)
117
+ end
104
118
  end
105
-
106
- options.delete(:conditions)
107
119
  end
108
120
 
109
121
  add_owner_ids(options.delete(:owner_id))
122
+ add_limit(options.delete(:limit))
123
+ add_includes(options.delete(:include))
110
124
 
111
125
  options.each do |key, value|
112
- q.send(key.to_s.camelize).SetValue(value)
126
+ self.send(key.to_s.camelize).SetValue(value)
113
127
  end
114
128
  end
115
- end
116
-
129
+ end
117
130
 
118
131
  # Add one or more OwnerIDs to the Request. Used in retrieving
119
132
  # custom fields (aka private data extensions).
@@ -127,6 +140,111 @@ module QBFC
127
140
  end
128
141
  end
129
142
 
143
+ # Set MaxReturned to limit the number of records returned.
144
+ def add_limit(limit)
145
+ filter.max_returned = limit if limit
146
+ end
147
+
148
+ # add_includes accepts an Array of elements to include in the return of the
149
+ # Request. The array may include either or both of elements that are
150
+ # additional to normal returns (such as Line Items, Linked Transactions)
151
+ # or elements that are normally included (to be added to the
152
+ # IncludeRetElementList).
153
+ #
154
+ # If elements are given that would be added to IncludeRetElementList, this
155
+ # limits the elements returned to *only* those included in the array.
156
+ #
157
+ # Another option is to give :all as the argument, which will always return
158
+ # as many elements as possible.
159
+ #
160
+ # add_includes is typically called by #apply_options, typically called
161
+ # from Element.find, as seen in the examples below:
162
+ #
163
+ # @sess.checks.find(:all, :include => [:linked_txns]) -> Include linked transactions
164
+ # @sess.checks.find(:all, :include => [:txn_id]) -> Include +only+ TxnID
165
+ # @sess.checks.find(:all, :include => :all) ->
166
+ # Includes all elements, including LinkedTxns and LineItems.
167
+ def add_includes(inc)
168
+ return if inc.nil?
169
+
170
+ inc = [inc] if (!inc.respond_to?(:each) || inc.kind_of?(String))
171
+
172
+ if inc.include?(:all)
173
+ ole_methods.each do |m|
174
+ m = m.to_s
175
+ if m =~ /\AInclude/ && m != 'IncludeRetElementList'
176
+ @request.__send__("#{m.underscore}=", true)
177
+ end
178
+ end
179
+ return
180
+ end
181
+
182
+ inc.each do |item|
183
+ cam_item = item.to_s.camelize.gsub(/Id/, "ID")
184
+ if @request.respond_to_ole?("Include#{cam_item}")
185
+ @request.__send__("include_#{item}=", true)
186
+ else
187
+ @request.IncludeRetElementList.Add(cam_item)
188
+ end
189
+ end
190
+ end
191
+
192
+ # Parse a value for a range filter. This can be a Range, a one or more
193
+ # element Array or a single value. For a single value or one-element array
194
+ # value#last should return nil. The calling method (#apply_options) will
195
+ # call value#first and value#last to set the from and to values
196
+ # respectively.
197
+ def parse_range_value(value)
198
+ value << nil if value.kind_of?(Array) && value.length == 1
199
+ value = [value, nil] if (value.kind_of?(String) || !value.respond_to?(:first))
200
+ value
201
+ end
202
+
203
+ # Determine and return the Filter object for the given filter name, dealing
204
+ # with OR's and other "weird" circumstances.
205
+ # NB: This method may well miss some situations. Hopefully, it will become
206
+ # more complete in time.
207
+ def filter_for(name)
208
+ name = name.camelize + "Filter"
209
+ f = nil
210
+
211
+ # List queries place the modified_date_range directly in the filter
212
+ if name == 'ModifiedDateRangeFilter'
213
+ return filter if filter.respond_to_ole?('FromModifiedDate')
214
+ end
215
+
216
+ # Try to get the filter directly
217
+ if filter.respond_to_ole?(name)
218
+ f = filter.send(name)
219
+ end
220
+
221
+ # Check if this is within an 'OR'
222
+ if filter.respond_to_ole?("OR#{name}")
223
+ f = filter.send("OR#{name}").send(name)
224
+ elsif filter.respond_to_ole?("OR#{name.gsub(/Range/, '')}")
225
+ f = filter.send("OR#{name.gsub(/Range/, '')}").send(name)
226
+ end
227
+
228
+ # DateRange OR's
229
+ if filter.respond_to_ole?("ORDateRangeFilter") && name =~ /DateRange/i
230
+ f = filter.send("ORDateRangeFilter").send(name)
231
+ end
232
+
233
+ # It might have a nested OR
234
+ if f && f.respond_to_ole?("OR#{name}")
235
+ f = f.send("OR#{name}")
236
+ end
237
+
238
+ # Ranges might have another step, with the 'Range' removed.
239
+ if f && f.respond_to_ole?(name.gsub(/Range/, ''))
240
+ f = f.send(name.gsub(/Range/, ''))
241
+ end
242
+
243
+ return f
244
+ end
245
+
246
+ private :parse_range_value, :filter_for
247
+
130
248
  # Send missing methods to @ole_object (OLEWrapper)
131
249
  def method_missing(symbol, *params) #:nodoc:
132
250
  @request.qbfc_method_missing(@sess, symbol, *params)
@@ -114,8 +114,12 @@ module QBFC
114
114
  def method_missing(symbol, *params) #:nodoc:
115
115
  if (('a'..'z') === symbol.to_s[0].chr && symbol.to_s[-1].chr != '=')
116
116
  camelized_method = symbol.to_s.camelize.to_sym
117
- single_camelized_method = symbol.to_s.singularize.camelize.to_sym
118
- if QBFC.const_defined?(camelized_method)
117
+ if camelized_method.to_s =~ /Terms\Z/
118
+ single_camelized_method = camelized_method
119
+ else
120
+ single_camelized_method = symbol.to_s.singularize.camelize.to_sym
121
+ end
122
+ if QBFC.const_defined?(camelized_method) && camelized_method.to_s !~ /Terms\Z/
119
123
  if params[0]
120
124
  return QBFC::const_get(camelized_method).find_by_unique_id(self, params[0])
121
125
  else
@@ -1,18 +1,25 @@
1
1
  module QBFC
2
2
 
3
3
  # Generated Transaction types
4
- TXN_TYPES = %w{ARRefundCreditCard Bill BillPaymentCheck BillPaymentCreditCard BuildAssembly Charge Check CreditCardCharge
5
- CreditCardCredit CreditMemo Deposit Estimate InventoryAdjustment Invoice ItemReceipt JournalEntry PurchaseOrder
6
- ReceivePayment SalesOrder SalesReceipt SalesTaxPaymentCheck TimeTracking VehicleMileage VendorCredit}
4
+ TXN_TYPES = %w{ARRefundCreditCard Bill BillPaymentCheck BillPaymentCreditCard
5
+ BuildAssembly Charge Check CreditCardCharge CreditCardCredit CreditMemo
6
+ Deposit Estimate InventoryAdjustment Invoice ItemReceipt JournalEntry
7
+ PurchaseOrder ReceivePayment SalesOrder SalesReceipt SalesTaxPaymentCheck
8
+ TimeTracking VehicleMileage VendorCredit}
7
9
 
8
10
  # Generated Transaction types that support TxnVoid Request
9
- TXN_VOIDABLE_TYPES = %w{ARRefundCreditCard Bill BillPaymentCheck BillPaymentCreditCard Charge Check CreditCardCharge CreditCardCredit
10
- CreditMemo Deposit InventoryAdjustment Invoice ItemReceipt JournalEntry SalesReceipt VendorCredit}
11
+ TXN_VOIDABLE_TYPES = %w{ARRefundCreditCard Bill BillPaymentCheck
12
+ BillPaymentCreditCard Charge Check CreditCardCharge CreditCardCredit
13
+ CreditMemo Deposit InventoryAdjustment Invoice ItemReceipt JournalEntry
14
+ SalesReceipt VendorCredit}
11
15
 
12
16
  # Generated Transaction types that don't support Mod Requests
13
- TXN_NO_MOD_TYPES = %w{ARRefundCreditCard BillPaymentCreditCard Deposit InventoryAdjustment VehicleMileage VendorCredit }
17
+ TXN_NO_MOD_TYPES = %w{ARRefundCreditCard BillPaymentCreditCard Deposit
18
+ InventoryAdjustment VehicleMileage VendorCredit }
14
19
 
15
20
  # Generate Transaction subclasses
16
- generate(TXN_TYPES, Transaction, {Modifiable => (TXN_TYPES - TXN_NO_MOD_TYPES), Voidable => TXN_VOIDABLE_TYPES})
21
+ generate(TXN_TYPES, Transaction,
22
+ { Modifiable => (TXN_TYPES - TXN_NO_MOD_TYPES),
23
+ Voidable => TXN_VOIDABLE_TYPES })
17
24
 
18
25
  end
@@ -52,8 +52,9 @@ module QBFC
52
52
 
53
53
  def close
54
54
  @sess.close
55
+ sleep(1)
55
56
  unless @is_reader
56
- sleep(5)
57
+ sleep(3)
57
58
  FileUtils.rm_rf(@dirname)
58
59
  end
59
60
  end
@@ -37,7 +37,7 @@ describe QBFC::Element do
37
37
  # Filter mock
38
38
  @filter = mock("QBFC::OLEWrapper#Filter")
39
39
  @request.stub!(:filter).and_return(@filter)
40
- @filter.stub!(:max_returned=)
40
+ @request.stub!(:add_limit)
41
41
  @request.stub!(:filter_available?).and_return(true)
42
42
  @request.stub!(:apply_options)
43
43
  end
@@ -66,16 +66,15 @@ describe QBFC::Element do
66
66
 
67
67
  it "should set request#max_returned to 1 if :first" do
68
68
  setup_request
69
- @request.should_receive(:filter).and_return(@filter)
69
+ @request.should_receive(:add_limit).with(1)
70
70
  @request.stub!(:filter_available?).and_return(true)
71
- @filter.should_receive(:max_returned=).with(1)
72
71
  QBFC::Test::ElementFind::find(@sess, :first)
73
72
  end
74
73
 
75
74
  it "should not set request#max_returned if not request.filter_available?" do
76
75
  setup_request
77
76
  @request.stub!(:filter_available?).and_return(false)
78
- @request.should_not_receive(:filter)
77
+ @request.should_not_receive(:add_limit)
79
78
  QBFC::Test::ElementFind::find(@sess, :first)
80
79
  end
81
80
 
@@ -97,9 +96,11 @@ describe QBFC::Element do
97
96
  QBFC::Test::ElementFind::find(@sess, :first).should be_nil
98
97
  end
99
98
 
100
- it "can accept a Request object"
101
- it "generates a Request object if not given one"
102
- it "accepts conditions"
99
+ it "should call Base.parse_find_args" do
100
+ setup_request
101
+ QBFC::Test::ElementFind::should_receive(:parse_find_args).with({:option => true})
102
+ QBFC::Test::ElementFind::find(@sess, :all, {:option => true})
103
+ end
103
104
 
104
105
  it "applies options to request" do
105
106
  setup_request
@@ -107,8 +108,6 @@ describe QBFC::Element do
107
108
  QBFC::Test::ElementFind::find(@sess, :first, :owner_id => 0)
108
109
  end
109
110
 
110
- it "passes additional arguments to Request"
111
-
112
111
  it "should get request#response" do
113
112
  setup_request
114
113
  @request.should_receive(:response).and_return(@response)
@@ -138,9 +137,12 @@ describe QBFC::Element do
138
137
  @element = mock(QBFC::Test::ElementFind)
139
138
  @base_element = mock(QBFC::Test::BaseFind)
140
139
  @customer_ret = mock("CustomerRet")
141
- @base_element.stub!(:ole_methods).and_return(["CustomerRet"])
140
+ @list_id = mock("ListID")
141
+ @base_element.stub!(:ole_methods).and_return(["VendorRet", "CustomerRet"])
142
+ @base_element.stub!(:VendorRet).and_return(nil)
142
143
  @base_element.stub!(:CustomerRet).and_return(@customer_ret)
143
- @customer_ret.stub!(:ListID).and_return("123-456")
144
+ @customer_ret.stub!(:ListID).and_return(@list_id)
145
+ @list_id.stub!(:GetValue).and_return("123-456")
144
146
  QBFC::Customer.stub!(:find_by_id).and_return(@element)
145
147
 
146
148
  @response.stub!(:GetAt).and_return(@base_element)
@@ -154,7 +156,8 @@ describe QBFC::Element do
154
156
 
155
157
  it "should send class ChildList::find_by_id with ListID and find options for each" do
156
158
  @base_element.should_receive(:CustomerRet).at_least(:once).and_return(@customer_ret)
157
- @customer_ret.should_receive(:ListID).at_least(:once).and_return("789-012")
159
+ @customer_ret.should_receive(:ListID).at_least(:once).and_return(@list_id)
160
+ @list_id.should_receive(:GetValue).at_least(:once).and_return("789-012")
158
161
  QBFC::Customer.should_receive(:find_by_id).at_least(:once).with(@sess, "789-012", {}).and_return(@element)
159
162
  QBFC::Test::BaseFind.find(@sess, :first, @request, {}).should be(@element)
160
163
  end
@@ -15,4 +15,19 @@ describe QBFC::Item do
15
15
  it "should return subclass objects"
16
16
  end
17
17
 
18
+ describe ".add_special" do
19
+ before(:each) do
20
+ @request = mock("QBFC::Request")
21
+ @response = mock("QBFC::Request#response")
22
+ end
23
+
24
+ it "should add a Special Account" do
25
+ QBFC::Request.should_receive(:new).with(@sess, "SpecialItemAdd").and_return(@request)
26
+ @request.should_receive(:special_item_type=).with(QBFC_CONST::SitFinanceCharge)
27
+ @request.should_receive(:response).and_return(@response)
28
+
29
+ QBFC::Item.add_special(@sess, QBFC_CONST::SitFinanceCharge)
30
+ end
31
+ end
32
+
18
33
  end
@@ -23,7 +23,7 @@ describe QBFC::List do
23
23
  # Filter mock
24
24
  @filter = mock("QBFC::OLEWrapper#Filter")
25
25
  @request.stub!(:filter).and_return(@filter)
26
- @filter.stub!(:max_returned=)
26
+ @request.stub!(:add_limit)
27
27
  @request.stub!(:filter_available?).and_return(true)
28
28
  @request.stub!(:apply_options)
29
29
  end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe QBFC::Account do
4
+
5
+ before(:each) do
6
+ @sess = mock(QBFC::Session)
7
+ @request = mock("QBFC::Request")
8
+ @response = mock("QBFC::Request#response")
9
+ end
10
+
11
+ it "should add a Special Account" do
12
+ QBFC::Request.should_receive(:new).with(@sess, "SpecialAccountAdd").and_return(@request)
13
+ @request.should_receive(:special_account_type=).with(QBFC_CONST::SatAccountsReceivable)
14
+ @request.should_receive(:response).and_return(@response)
15
+ QBFC::Account.should_receive(:new).with(@sess, @response)
16
+
17
+ QBFC::Account.add_special(@sess, QBFC_CONST::SatAccountsReceivable)
18
+ end
19
+
20
+ end
@@ -140,6 +140,13 @@ describe QBFC::OLEWrapper do
140
140
 
141
141
  @wrapper.qbfc_method_missing(@sess, :full_name=, 'Full Name')
142
142
  end
143
+
144
+ it "should convert Dates in setter method" do
145
+ @ole_object.should_receive(:TimeModified).and_return(@full_name)
146
+ @full_name.should_receive(:SetValue).with('2008-01-01')
147
+
148
+ @wrapper.qbfc_method_missing(@sess, :time_modified=, Date.parse('2008-01-01'))
149
+ end
143
150
 
144
151
  it "should set @setter also when acting as a setter method, if applicable" do
145
152
  @setter = mock(WIN32OLE)
@@ -6,7 +6,7 @@ lists
6
6
  - list_id_list
7
7
  - full_name_list
8
8
  - txn_id_list
9
- - ref_number_list
9
+ X ref_number_list
10
10
  - ref_number_case_sensitive_list
11
11
 
12
12
  filters
@@ -199,12 +199,249 @@ describe QBFC::Request do
199
199
  describe "#apply_options" do
200
200
  before(:each) do
201
201
  @request = QBFC::Request.new(@sess, 'CustomerQuery')
202
+ @query = mock('Request#query')
203
+ @filter = mock('Request#filter')
204
+ @request.stub!(:query).and_return(@query)
202
205
  end
203
206
 
204
- it "accepts an :owner_id option" do
207
+ it "should apply an :owner_id option" do
205
208
  @request.should_receive(:add_owner_ids).with(1)
206
209
  @request.apply_options(:owner_id => 1)
207
210
  end
211
+
212
+ it "should apply an :limit option" do
213
+ @request.should_receive(:add_limit).with(1)
214
+ @request.apply_options(:limit => 1)
215
+ end
216
+
217
+ it "should apply an :include option" do
218
+ include_ary = [1,2,3]
219
+ @request.should_receive(:add_includes).with(include_ary)
220
+ @request.apply_options(:include => include_ary)
221
+ end
222
+
223
+ it "should apply lists to query" do
224
+ ref_number_list = mock('OLEWrapper#ref_number_list')
225
+ @query.should_receive(:RefNumberList).and_return(ref_number_list)
226
+ ref_number_list.should_receive(:Add).with('82')
227
+ ref_number_list.should_receive(:Add).with('1234')
228
+
229
+ @request.apply_options(:conditions => {:ref_number_list => %w{82 1234}})
230
+ end
231
+
232
+ it "should apply lists to query when given a single item" do
233
+ ref_number_list = mock('OLEWrapper#ref_number_list')
234
+ @query.should_receive(:RefNumberList).and_return(ref_number_list)
235
+ ref_number_list.should_receive(:Add).with('20')
236
+
237
+ @request.apply_options(:conditions => {:ref_number_list => '20'})
238
+ end
239
+
240
+ describe "(range)" do
241
+ before(:each) do
242
+ @range_filter = mock('Request#TxnDateRangeFilter')
243
+ @request.stub!(:filter_for).with('txn_date_range').and_return(@range_filter)
244
+ @request.stub!(:filter_for).with('modified_date_range').and_return(@range_filter)
245
+ @range_filter.stub!(:from_txn_date=)
246
+ @range_filter.stub!(:to_txn_date=)
247
+ end
248
+
249
+ it "should #parse_range_value" do
250
+ @request.should_receive(:parse_range_value).with([0,2]).and_return([0,2])
251
+ @request.apply_options(:conditions => {:txn_date_range => [0,2]})
252
+ end
253
+
254
+ it "should get appropriate filter" do
255
+ @request.should_receive(:filter_for).with('txn_date_range').and_return(@range_filter)
256
+ @request.apply_options(:conditions => {:txn_date_range => [0,2]})
257
+ end
258
+
259
+ it "should apply date range to filter" do
260
+ @range_filter.should_receive(:from_txn_date=).with(0)
261
+ @range_filter.should_receive(:to_txn_date=).with(2)
262
+ @request.apply_options(:conditions => {:txn_date_range => [0,2]})
263
+ end
264
+
265
+ it "should add 'true' argument (asDateOnly) for modified_date ranges" do
266
+ @range_filter.should_receive(:from_modified_date=).with(0, true)
267
+ @range_filter.should_receive(:to_modified_date=).with(2, true)
268
+ @request.apply_options(:conditions => {:modified_date_range => [0,2]})
269
+ end
270
+ end
271
+
272
+ describe "(status)" do
273
+ it "should set status" do
274
+ @filter = mock('Request#filter')
275
+ @request.stub!(:filter).and_return(@filter)
276
+ @filter.should_receive(:paid_status=).with(QBFC_CONST::PsPaidOnly)
277
+ @request.apply_options(:conditions => {:paid_status => QBFC_CONST::PsPaidOnly})
278
+ end
279
+ end
280
+
281
+ describe "(reference)" do
282
+ before(:each) do
283
+ @ref_filter = mock('Request#RefFilter')
284
+ @full_name_list = mock('OLEWrapper#FullNameList')
285
+ @request.stub!(:filter_for).with('entity').and_return(@ref_filter)
286
+ @ref_filter.should_receive(:FullNameList).at_least(:once).and_return(@full_name_list)
287
+ @full_name_list.stub!(:Add)
288
+ end
289
+
290
+ it "should get appropriate filter" do
291
+ @request.should_receive(:filter_for).with('entity').and_return(@ref_filter)
292
+ @request.apply_options(:conditions => {:entity => 'ABC Supplies'})
293
+ end
294
+
295
+ it "should set a single full name" do
296
+ @full_name_list.should_receive(:Add).with('ABC Supplies')
297
+ @request.apply_options(:conditions => {:entity => 'ABC Supplies'})
298
+ end
299
+
300
+ it "should set a single full name (non-string)" do
301
+ @full_name_list.should_receive(:Add).with(1)
302
+ @request.apply_options(:conditions => {:entity => 1})
303
+ end
304
+
305
+ it "should set a multiple full names" do
306
+ @full_name_list.should_receive(:Add).with('ABC Supplies')
307
+ @full_name_list.should_receive(:Add).with('CompuStuff')
308
+ @request.apply_options(:conditions => {:entity => %w{ABC\ Supplies CompuStuff}})
309
+ end
310
+ end
311
+ end
312
+
313
+ describe "#add_includes" do
314
+ before(:each) do
315
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
316
+ @ret_list = mock('RetElementList')
317
+
318
+ @ole_request.stub!(:respond_to_ole?).with('IncludeLineItems').and_return(true)
319
+ @ole_request.stub!(:respond_to_ole?).with('IncludeLinkedTxns').and_return(true)
320
+ @ole_request.stub!(:respond_to_ole?).with('IncludeTxnID').and_return(false)
321
+ @ole_request.stub!(:respond_to_ole?).with('IncludeCustomerRef').and_return(false)
322
+ end
323
+
324
+ it "should add specific include requests" do
325
+ @ole_request.should_receive(:include_line_items=).with(true)
326
+ @request.__send__(:add_includes, [:line_items])
327
+ end
328
+
329
+ it "should apply additional includes to IncludeRetElementList" do
330
+ @ole_request.should_receive(:IncludeRetElementList).twice.and_return(@ret_list)
331
+ @ret_list.should_receive(:Add).with('TxnID')
332
+ @ret_list.should_receive(:Add).with('CustomerRef')
333
+ @request.__send__(:add_includes, [:txn_id, :customer_ref])
334
+ end
335
+
336
+ it "should include :all" do
337
+ @ole_request.should_receive(:ole_methods).and_return(%w{ORTxnQuery IncludeLineItems IncludeLinkedTxns iterator})
338
+ @ole_request.should_receive(:include_line_items=).with(true)
339
+ @ole_request.should_receive(:include_linked_txns=).with(true)
340
+ @request.__send__(:add_includes, :all)
341
+ end
342
+ end
343
+
344
+ describe "#parse_range_value" do
345
+ before(:each) do
346
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
347
+ end
348
+
349
+ it "should add a nil element to a one-element Array" do
350
+ ary = [0]
351
+ @request.__send__(:parse_range_value, ary).should == [0, nil]
352
+ end
353
+
354
+ it "should return unchanged a multiple element Array" do
355
+ ary = [0, 1]
356
+ @request.__send__(:parse_range_value, ary).should be(ary)
357
+ end
358
+
359
+ it "should return unchanged a Range" do
360
+ rng = 0..1
361
+ @request.__send__(:parse_range_value, rng).should be(rng)
362
+ end
363
+
364
+ it "should take a scalar and return an array with the second element nil" do
365
+ val = 0
366
+ @request.__send__(:parse_range_value, val).should == [val, nil]
367
+ end
368
+
369
+ it "should take a String scalar and return an array with the second element nil" do
370
+ val = '0'
371
+ @request.__send__(:parse_range_value, val).should == [val, nil]
372
+ end
373
+
374
+ end
375
+
376
+ describe "#filter_for" do
377
+ before(:each) do
378
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
379
+ @query = mock('Request#query')
380
+ @filter = mock('Request#filter')
381
+ @request.stub!(:query).and_return(@query)
382
+ @request.stub!(:filter).and_return(@filter)
383
+
384
+ @or_date_range_filter = mock('Request#ORDateRangeFilter')
385
+ @or_ref_number_filter = mock('Request#ORRefNumberFilter')
386
+ @txn_date_range_filter = mock('Request#TxnDateRangeFilter')
387
+ @final_filter = mock('Request#FinalFilter')
388
+
389
+ @filter.stub!(:respond_to_ole?).and_return(false)
390
+ @or_date_range_filter.stub!(:respond_to_ole?).and_return(false)
391
+ @final_filter.stub!(:respond_to_ole?).and_return(false)
392
+ end
393
+
394
+ it "should follow ORDateRangeFilter for date_ranges" do
395
+ @filter.should_receive(:respond_to_ole?).with('ORDateRangeFilter').and_return(true)
396
+ @filter.should_receive(:ORDateRangeFilter).and_return(@or_date_range_filter)
397
+ @or_date_range_filter.should_receive(:ModifiedDateRangeFilter).and_return(@final_filter)
398
+
399
+ @request.__send__(:filter_for, 'modified_date_range').should be(@final_filter)
400
+ end
401
+
402
+ it "should follow OR{name}Filter" do
403
+ @filter.should_receive(:respond_to_ole?).with('ORRefNumberFilter').and_return(true)
404
+ @filter.should_receive(:ORRefNumberFilter).and_return(@or_ref_number_filter)
405
+ @or_ref_number_filter.should_receive(:RefNumberFilter).and_return(@final_filter)
406
+
407
+ @request.__send__(:filter_for, 'ref_number').should be(@final_filter)
408
+ end
409
+
410
+ it "should follow OR{name}Filter, with 'Range' removed" do
411
+ @filter.should_receive(:respond_to_ole?).with('ORRefNumberFilter').and_return(true)
412
+ @filter.should_receive(:ORRefNumberFilter).and_return(@or_ref_number_filter)
413
+ @or_ref_number_filter.should_receive(:RefNumberRangeFilter).and_return(@final_filter)
414
+
415
+ @request.__send__(:filter_for, 'ref_number_range').should be(@final_filter)
416
+ end
417
+
418
+ it "should return #filter if *ModifiedDate in #filter" do
419
+ @filter.should_receive(:respond_to_ole?).with('FromModifiedDate').and_return(true)
420
+ @request.__send__(:filter_for, 'modified_date_range').should == @filter
421
+ end
422
+
423
+ it "should follow OR below the Filter" do
424
+ @filter.should_receive(:respond_to_ole?).with('TxnDateRangeFilter').and_return(true)
425
+ @filter.should_receive(:TxnDateRangeFilter).and_return(@txn_date_range_filter)
426
+
427
+ @txn_date_range_filter.should_receive(:respond_to_ole?).with('ORTxnDateRangeFilter').and_return(true)
428
+ @txn_date_range_filter.should_receive(:ORTxnDateRangeFilter).and_return(@or_date_range_filter)
429
+
430
+ @request.__send__(:filter_for, 'txn_date_range').should be(@or_date_range_filter)
431
+ end
432
+
433
+ it "should get a nested Filter with 'Range' removed" do
434
+ @filter.should_receive(:respond_to_ole?).with('TxnDateRangeFilter').and_return(true)
435
+ @filter.should_receive(:TxnDateRangeFilter).and_return(@txn_date_range_filter)
436
+
437
+ @txn_date_range_filter.should_receive(:respond_to_ole?).with('ORTxnDateRangeFilter').and_return(true)
438
+ @txn_date_range_filter.should_receive(:ORTxnDateRangeFilter).and_return(@or_date_range_filter)
439
+
440
+ @or_date_range_filter.should_receive(:respond_to_ole?).with('TxnDateFilter').and_return(true)
441
+ @or_date_range_filter.should_receive(:TxnDateFilter).and_return(@final_filter)
442
+
443
+ @request.__send__(:filter_for, 'txn_date_range').should be(@final_filter)
444
+ end
208
445
  end
209
446
 
210
447
  describe "#add_owner_ids" do
@@ -233,4 +470,17 @@ describe QBFC::Request do
233
470
  @request.add_owner_ids(nil)
234
471
  end
235
472
  end
473
+
474
+ describe "#add_limit" do
475
+ before(:each) do
476
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
477
+ @filter = mock('Request#filter')
478
+ @request.stub!(:filter).and_return(@filter)
479
+ end
480
+
481
+ it "can should update the filter's max_returned value" do
482
+ @filter.should_receive(:max_returned=).with(1)
483
+ @request.add_limit(1)
484
+ end
485
+ end
236
486
  end
@@ -124,6 +124,12 @@ describe QBFC::Session do
124
124
  QBFC::QBCollection.should_receive(:new).with(@qb_sess, :QBClass).and_return(@collection)
125
125
  @qb_sess.classes.should == @collection
126
126
  end
127
+
128
+ it "should create QBFC::QBCollection for QBFC::Terms from #terms" do
129
+ @collection = mock(QBFC::QBCollection)
130
+ QBFC::QBCollection.should_receive(:new).with(@qb_sess, :Terms).and_return(@collection)
131
+ @qb_sess.terms.should == @collection
132
+ end
127
133
 
128
134
  it "should let OLEWrapper handle other unknown methods" do
129
135
  @ole_wrapper.should_receive(:qbfc_method_missing).with(@qb_sess, :FullName).and_return(true)
@@ -23,7 +23,7 @@ describe QBFC::Transaction do
23
23
  # Filter mock
24
24
  @filter = mock("QBFC::OLEWrapper#Filter")
25
25
  @request.stub!(:filter).and_return(@filter)
26
- @filter.stub!(:max_returned=)
26
+ @request.stub!(:add_limit)
27
27
  @request.stub!(:filter_available?).and_return(true)
28
28
  @request.stub!(:apply_options)
29
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qbfc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: x86-mswin32-60
6
6
  authors:
7
7
  - Jared Morgan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-02-22 00:00:00 -06:00
12
+ date: 2008-03-24 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -42,6 +42,7 @@ files:
42
42
  - lib/qbfc/entities/generated.rb
43
43
  - lib/qbfc/infos/generated.rb
44
44
  - lib/qbfc/items/generated.rb
45
+ - lib/qbfc/lists/account.rb
45
46
  - lib/qbfc/lists/generated.rb
46
47
  - lib/qbfc/lists/qb_class.rb
47
48
  - lib/qbfc/terms/generated.rb
@@ -81,6 +82,7 @@ files:
81
82
  - spec/unit/entities/generated_spec.rb
82
83
  - spec/unit/info/generated_spec.rb
83
84
  - spec/unit/items/generated_spec.rb
85
+ - spec/unit/lists/account_spec.rb
84
86
  - spec/unit/lists/generated_spec.rb
85
87
  - spec/unit/lists/qb_class_spec.rb
86
88
  - spec/unit/terms/generated_spec.rb
@@ -134,6 +136,7 @@ test_files:
134
136
  - spec/unit/entities/generated_spec.rb
135
137
  - spec/unit/info/generated_spec.rb
136
138
  - spec/unit/items/generated_spec.rb
139
+ - spec/unit/lists/account_spec.rb
137
140
  - spec/unit/lists/generated_spec.rb
138
141
  - spec/unit/lists/qb_class_spec.rb
139
142
  - spec/unit/terms/generated_spec.rb