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 +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
|