qbfc 0.1.0-x86-mswin32-60 → 0.2.0-x86-mswin32-60
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/README +45 -1
- data/Rakefile +1 -1
- data/lib/qbfc/element.rb +72 -5
- data/lib/qbfc/item.rb +19 -0
- data/lib/qbfc/lists/account.rb +24 -0
- data/lib/qbfc/lists/generated.rb +7 -7
- data/lib/qbfc/ole_wrapper.rb +1 -0
- data/lib/qbfc/qb_types.rb +7 -20
- data/lib/qbfc/request.rb +146 -28
- data/lib/qbfc/session.rb +6 -2
- data/lib/qbfc/transactions/generated.rb +14 -7
- data/spec/spec_helper.rb +2 -1
- data/spec/unit/element_finder_spec.rb +15 -12
- data/spec/unit/item_spec.rb +15 -0
- data/spec/unit/list_finders_spec.rb +1 -1
- data/spec/unit/lists/account_spec.rb +20 -0
- data/spec/unit/ole_wrapper_spec.rb +7 -0
- data/spec/unit/request_query_survey.txt +1 -1
- data/spec/unit/request_spec.rb +251 -1
- data/spec/unit/session_spec.rb +6 -0
- data/spec/unit/transaction_finders_spec.rb +1 -1
- metadata +5 -2
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
|
-
==
|
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.
|
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/"
|
data/lib/qbfc/element.rb
CHANGED
@@ -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.
|
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|
|
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
|
|
data/lib/qbfc/item.rb
CHANGED
@@ -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
|
data/lib/qbfc/lists/generated.rb
CHANGED
@@ -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{
|
5
|
-
PayrollItemNonWage PayrollItemWage PriceLevel SalesRep SalesTaxCode
|
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
|
9
|
-
|
10
|
-
|
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 =>
|
13
|
+
generate(LIST_TYPES, List, {Modifiable => LIST_MOD_TYPES})
|
14
14
|
|
15
15
|
end
|
data/lib/qbfc/ole_wrapper.rb
CHANGED
@@ -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)
|
data/lib/qbfc/qb_types.rb
CHANGED
@@ -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
|
8
|
-
|
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
|
-
#
|
14
|
-
# I leave them here as a reminder.
|
15
|
-
|
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 +
|
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
|
data/lib/qbfc/request.rb
CHANGED
@@ -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
|
80
|
-
# (
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
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)
|
data/lib/qbfc/session.rb
CHANGED
@@ -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
|
-
|
118
|
-
|
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
|
5
|
-
|
6
|
-
|
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
|
10
|
-
|
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
|
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,
|
21
|
+
generate(TXN_TYPES, Transaction,
|
22
|
+
{ Modifiable => (TXN_TYPES - TXN_NO_MOD_TYPES),
|
23
|
+
Voidable => TXN_VOIDABLE_TYPES })
|
17
24
|
|
18
25
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
@
|
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(:
|
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(:
|
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 "
|
101
|
-
|
102
|
-
|
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
|
-
@
|
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(
|
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(
|
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
|
data/spec/unit/item_spec.rb
CHANGED
@@ -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
|
-
@
|
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)
|
data/spec/unit/request_spec.rb
CHANGED
@@ -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 "
|
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
|
data/spec/unit/session_spec.rb
CHANGED
@@ -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
|
-
@
|
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.
|
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-
|
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
|