qbfc 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/.gitignore +11 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +84 -0
  5. data/VERSION +1 -0
  6. data/lib/qbfc.rb +41 -0
  7. data/lib/qbfc/base.rb +82 -0
  8. data/lib/qbfc/element.rb +243 -0
  9. data/lib/qbfc/entities/generated.rb +8 -0
  10. data/lib/qbfc/entity.rb +11 -0
  11. data/lib/qbfc/info.rb +42 -0
  12. data/lib/qbfc/infos/generated.rb +9 -0
  13. data/lib/qbfc/item.rb +29 -0
  14. data/lib/qbfc/items/generated.rb +11 -0
  15. data/lib/qbfc/list.rb +84 -0
  16. data/lib/qbfc/lists/account.rb +24 -0
  17. data/lib/qbfc/lists/generated.rb +15 -0
  18. data/lib/qbfc/lists/qb_class.rb +25 -0
  19. data/lib/qbfc/modifiable.rb +31 -0
  20. data/lib/qbfc/ole_wrapper.rb +201 -0
  21. data/lib/qbfc/qb_collection.rb +26 -0
  22. data/lib/qbfc/qb_types.rb +18 -0
  23. data/lib/qbfc/qbfc_const.rb +14 -0
  24. data/lib/qbfc/report.rb +95 -0
  25. data/lib/qbfc/reports/aging.rb +13 -0
  26. data/lib/qbfc/reports/budget_summary.rb +13 -0
  27. data/lib/qbfc/reports/custom_detail.rb +9 -0
  28. data/lib/qbfc/reports/custom_summary.rb +9 -0
  29. data/lib/qbfc/reports/general_detail.rb +44 -0
  30. data/lib/qbfc/reports/general_summary.rb +33 -0
  31. data/lib/qbfc/reports/job.rb +14 -0
  32. data/lib/qbfc/reports/payroll_detail.rb +13 -0
  33. data/lib/qbfc/reports/payroll_summary.rb +13 -0
  34. data/lib/qbfc/reports/rows.rb +51 -0
  35. data/lib/qbfc/reports/time.rb +12 -0
  36. data/lib/qbfc/request.rb +295 -0
  37. data/lib/qbfc/session.rb +147 -0
  38. data/lib/qbfc/terms.rb +10 -0
  39. data/lib/qbfc/terms/generated.rb +10 -0
  40. data/lib/qbfc/transaction.rb +110 -0
  41. data/lib/qbfc/transactions/generated.rb +25 -0
  42. data/lib/qbfc/voidable.rb +11 -0
  43. data/qbfc.gemspec +166 -0
  44. data/spec/fixtures/test.lgb +0 -0
  45. data/spec/fixtures/test.qbw +0 -0
  46. data/spec/fixtures/test.qbw.TLG +0 -0
  47. data/spec/integration/add_spec.rb +31 -0
  48. data/spec/integration/base_spec.rb +18 -0
  49. data/spec/integration/belongs_to_spec.rb +64 -0
  50. data/spec/integration/company_spec.rb +30 -0
  51. data/spec/integration/conditions_spec.rb +59 -0
  52. data/spec/integration/customer_spec.rb +46 -0
  53. data/spec/integration/element_finders_spec.rb +20 -0
  54. data/spec/integration/quick_test.rb +31 -0
  55. data/spec/integration/request_options_spec.rb +68 -0
  56. data/spec/rcov.opts +1 -0
  57. data/spec/spec.opts +6 -0
  58. data/spec/spec_helper.rb +62 -0
  59. data/spec/unit/base_spec.rb +138 -0
  60. data/spec/unit/element_finder_spec.rb +185 -0
  61. data/spec/unit/element_spec.rb +108 -0
  62. data/spec/unit/entities/generated_spec.rb +18 -0
  63. data/spec/unit/entity_spec.rb +18 -0
  64. data/spec/unit/info/generated_spec.rb +12 -0
  65. data/spec/unit/info_spec.rb +48 -0
  66. data/spec/unit/item_spec.rb +33 -0
  67. data/spec/unit/items/generated_spec.rb +16 -0
  68. data/spec/unit/list_finders_spec.rb +129 -0
  69. data/spec/unit/list_spec.rb +86 -0
  70. data/spec/unit/lists/account_spec.rb +20 -0
  71. data/spec/unit/lists/generated_spec.rb +15 -0
  72. data/spec/unit/lists/qb_class_spec.rb +9 -0
  73. data/spec/unit/modifiable_spec.rb +84 -0
  74. data/spec/unit/ole_wrapper_spec.rb +337 -0
  75. data/spec/unit/qb_collection_spec.rb +13 -0
  76. data/spec/unit/qbfc_const_spec.rb +10 -0
  77. data/spec/unit/qbfc_spec.rb +10 -0
  78. data/spec/unit/report_spec.rb +12 -0
  79. data/spec/unit/request_query_survey.txt +48 -0
  80. data/spec/unit/request_spec.rb +486 -0
  81. data/spec/unit/session_spec.rb +144 -0
  82. data/spec/unit/terms/generated_spec.rb +14 -0
  83. data/spec/unit/terms_spec.rb +18 -0
  84. data/spec/unit/transaction_finders_spec.rb +125 -0
  85. data/spec/unit/transaction_spec.rb +94 -0
  86. data/spec/unit/transactions/generated_spec.rb +20 -0
  87. data/spec/unit/voidable_spec.rb +32 -0
  88. data/tasks/qbfc_tasks.rake +4 -0
  89. metadata +182 -0
@@ -0,0 +1,337 @@
1
+ require 'spec_helper'
2
+
3
+ describe QBFC::OLEWrapper do
4
+
5
+ before(:each) do
6
+ @sess = mock(QBFC::Session)
7
+ @ole_object = mock(WIN32OLE)
8
+ @wrapper = QBFC::OLEWrapper.new(@ole_object)
9
+ end
10
+
11
+ it "should initialize with a WIN32OLE object" do
12
+ lambda{ QBFC::OLEWrapper.new(@ole_object) }.should_not raise_error
13
+ end
14
+
15
+ it "should initialize with a String name a WIN32OLE server" do
16
+ WIN32OLE.should_receive(:new).with("ServerName")
17
+ QBFC::OLEWrapper.new("ServerName")
18
+ end
19
+
20
+ it "should return ole_methods" do
21
+ @ole_object.should_receive("ole_methods").and_return([])
22
+ @wrapper.ole_methods.should == []
23
+ end
24
+
25
+ it "should check if the OLE object responds to a given ole_method" do
26
+ @ole_object.should_receive("ole_methods").twice.and_return(["FullName", "ListID"])
27
+ @wrapper.respond_to_ole?(:FullName).should_not be_false
28
+ @wrapper.respond_to_ole?(:NonMethod).should be_nil
29
+ end
30
+
31
+ it "should make a list responding to GetAt act as an Array" do
32
+ @get_at_object = mock(WIN32OLE)
33
+ @get_at_wrapper = QBFC::OLEWrapper.new(@get_at_object)
34
+
35
+ @ole_object.should_receive("GetAt").with(1).and_return(@get_at_object)
36
+ QBFC::OLEWrapper.should_receive(:new).with(@get_at_object).and_return(@get_at_wrapper)
37
+
38
+ @wrapper[1].should == @get_at_wrapper
39
+ end
40
+
41
+ it "should pass non-integer values to [] on to ole_object" do
42
+ @ole_object.should_receive("[]").with('1').and_return("Second Object")
43
+ @wrapper['1'].should == "Second Object"
44
+ end
45
+
46
+ describe "QBFC::OLEWrapper#qbfc_method_missing" do
47
+
48
+ before(:each) do
49
+ @sess = mock(QBFC::Session)
50
+ @ole_object = mock(WIN32OLE)
51
+ @wrapper = QBFC::OLEWrapper.new(@ole_object)
52
+
53
+ @full_name = mock('WIN32OLE.FullName')
54
+ @full_name.stub!(:ole_methods).and_return(['GetValue', 'SetValue'])
55
+
56
+ @ole_object.stub!(:FullName).and_return(@full_name)
57
+ @ole_object.stub!(:ole_methods).and_return(['FullName', 'LineRetList', 'PayeeEntityRef', 'AccountRef', 'TimeModified', 'ListID', 'ORInvoiceLineRetList', 'ColDataList', 'ORReportDataList', 'colID'])
58
+ end
59
+
60
+ it "should call a capitalized method directly" do
61
+ @ole_object.should_receive(:TestValue).and_return(0)
62
+ QBFC::OLEWrapper.should_not_receive(:new)
63
+
64
+ @wrapper.qbfc_method_missing(@sess, :TestValue).should == 0
65
+ end
66
+
67
+ it "should call a capitalized method directly and wrap in OLEWrapper if it returns a WIN32OLE" do
68
+ @full_name = WIN32OLE.new("QBFC6.QBSessionManager")
69
+ @ole_object.should_receive(:FullName).and_return(@full_name)
70
+ @full_name.should_not_receive(:GetValue)
71
+
72
+ @full_name_wrapper = QBFC::OLEWrapper.new(@ole_object)
73
+ QBFC::OLEWrapper.should_receive(:new).with(@full_name).and_return(@full_name_wrapper)
74
+
75
+ @wrapper.qbfc_method_missing(@sess, :FullName).should == @full_name_wrapper
76
+ end
77
+
78
+ it "should act as a getter method" do
79
+ @ole_object.should_receive(:FullName).and_return(@full_name)
80
+ @full_name.should_receive(:GetValue).and_return('Full Name')
81
+
82
+ @wrapper.qbfc_method_missing(@sess, :full_name).should == 'Full Name'
83
+ end
84
+
85
+ it "should convert 'Id' to 'ID' in getter" do
86
+ @ole_object.should_receive(:ListID).and_return(@full_name)
87
+ @full_name.should_receive(:GetValue).and_return('{123-456}')
88
+
89
+ @wrapper.qbfc_method_missing(@sess, :list_id).should == '{123-456}'
90
+ end
91
+
92
+ it "should convert return of date/time getter methods to Time" do
93
+ time_modified = @full_name
94
+ @ole_object.should_receive(:TimeModified).and_return(time_modified)
95
+ time_modified.should_receive(:GetValue).and_return('2007-01-01 10:00:00')
96
+
97
+ ret = @wrapper.qbfc_method_missing(@sess, :time_modified)
98
+ ret.should be_kind_of(Time)
99
+ ret.strftime("%Y-%m-%d %H:%M:%S").should == '2007-01-01 10:00:00'
100
+ end
101
+
102
+ it "should wrap WIN32OLE objects returned by getter that don't respond to GetValue" do
103
+ @full_name = WIN32OLE.new("QBFC6.QBSessionManager")
104
+ @ole_object.should_receive(:FullName).and_return(@full_name)
105
+ @full_name.should_receive(:ole_methods).and_return(['SetValue'])
106
+ @full_name.should_not_receive(:GetValue)
107
+
108
+ @full_name_wrapper = QBFC::OLEWrapper.new(@ole_object)
109
+ QBFC::OLEWrapper.should_receive(:new).with(@full_name).and_return(@full_name_wrapper)
110
+
111
+ @wrapper.qbfc_method_missing(@sess, :full_name).should == @full_name_wrapper
112
+ end
113
+
114
+ it "should wrap @setter if applicable when wrapping WIN32OLE objects returned by getter " do
115
+ @setter = mock(WIN32OLE)
116
+ @setter.should_receive("ole_methods").and_return(["FullName", "ListID"])
117
+ @wrapper = QBFC::OLEWrapper.new(@ole_object, @setter)
118
+
119
+ @full_name_getter = WIN32OLE.new("QBFC6.QBSessionManager")
120
+ @full_name_setter = WIN32OLE.new("QBFC6.QBSessionManager")
121
+ @ole_object.should_receive(:FullName).and_return(@full_name_getter)
122
+ @full_name_getter.should_receive(:ole_methods).and_return(['SetValue'])
123
+ @setter.should_receive(:FullName).and_return(@full_name_setter)
124
+
125
+ @full_name_wrapper = QBFC::OLEWrapper.new(@full_name_getter, @full_name_setter)
126
+ QBFC::OLEWrapper.should_receive(:new).with(@full_name_getter, @full_name_setter).and_return(@full_name_wrapper)
127
+
128
+ @wrapper.qbfc_method_missing(@sess, :full_name).should == @full_name_wrapper
129
+ end
130
+
131
+ it "should return non-WIN32OLE returned by getter that don't respond to GetValue" do
132
+ @ole_object.should_receive(:FullName).and_return('Full Name')
133
+ @full_name.should_not_receive(:GetValue)
134
+ @wrapper.qbfc_method_missing(@sess, :full_name).should == 'Full Name'
135
+ end
136
+
137
+ it "should act as a setter method" do
138
+ @ole_object.should_receive(:FullName).and_return(@full_name)
139
+ @full_name.should_receive(:SetValue).with('Full Name')
140
+
141
+ @wrapper.qbfc_method_missing(@sess, :full_name=, 'Full Name')
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
150
+
151
+ it "should set @setter also when acting as a setter method, if applicable" do
152
+ @setter = mock(WIN32OLE)
153
+ @setter.should_receive("ole_methods").and_return(["FullName", "ListID"])
154
+ @wrapper = QBFC::OLEWrapper.new(@ole_object, @setter)
155
+
156
+ @full_name_setter = WIN32OLE.new("QBFC6.QBSessionManager")
157
+ @setter.should_receive(:FullName).and_return(@full_name_setter)
158
+ @full_name.should_receive(:SetValue).with('Full Name')
159
+ @full_name_setter.should_receive(:SetValue).with('Full Name')
160
+
161
+ @wrapper.qbfc_method_missing(@sess, :full_name=, 'Full Name')
162
+ end
163
+
164
+ it "should convert 'Id' to 'ID' in setter" do
165
+ @ole_object.should_receive(:ListID).and_return(@full_name)
166
+ @full_name.should_receive(:SetValue).and_return('{123-456}')
167
+
168
+ @wrapper.qbfc_method_missing(@sess, :list_id=, '{123-456}')
169
+ end
170
+
171
+ it "should raise SetValueMissing error on a setter call for a method without SetValue" do
172
+ @ole_object.should_receive(:FullName).and_return(@full_name)
173
+ @full_name.should_receive(:ole_methods).and_return(['GetValue'])
174
+
175
+ lambda { @wrapper.qbfc_method_missing(@sess, :full_name=, 'Full Name') }.
176
+ should raise_error(QBFC::SetValueMissing)
177
+ end
178
+
179
+ it "should wrap *RetList objects in an Array" do
180
+ ret_list = mock('WIN32OLE.RetList')
181
+ ret_list.stub!(:ole_methods).and_return(['GetAt', 'Count'])
182
+ ret_list.should_receive(:Count).and_return(2)
183
+ ret_list.should_receive(:GetAt).with(0).and_return(@full_name)
184
+ ret_list.should_receive(:GetAt).with(1).and_return(@full_name)
185
+
186
+ @ole_object.should_receive(:LineRetList).and_return(ret_list)
187
+
188
+ @full_name_wrapper = QBFC::OLEWrapper.new(@full_name)
189
+ QBFC::OLEWrapper.should_receive(:new).with(@full_name).twice.and_return(@full_name_wrapper)
190
+
191
+ @wrapper.qbfc_method_missing(@sess, :lines).should ==
192
+ [@full_name_wrapper, @full_name_wrapper]
193
+ end
194
+
195
+ it "should wrap OR*RetList objects in an Array" do
196
+ ret_list = mock('WIN32OLE.ORInvoiceLineRetList')
197
+ list_item_wrapper = mock('WIN32OLE.InvoiceLineRetWrapper')
198
+ list_item = mock('WIN32OLE.InvoiceLineRet')
199
+ ret_list.stub!(:ole_methods).and_return(['GetAt', 'Count'])
200
+ ret_list.should_receive(:Count).and_return(2)
201
+ ret_list.should_receive(:GetAt).with(0).and_return(list_item_wrapper)
202
+ ret_list.should_receive(:GetAt).with(1).and_return(list_item_wrapper)
203
+ list_item_wrapper.should_receive(:InvoiceLineRet).twice.and_return(list_item)
204
+
205
+ @ole_object.should_receive(:ORInvoiceLineRetList).and_return(ret_list)
206
+
207
+ @wrapper.qbfc_method_missing(@sess, :invoice_lines).should ==
208
+ [list_item, list_item]
209
+ end
210
+
211
+ it "should wrap *List objects in an Array" do
212
+ ret_list = mock('WIN32OLE.List')
213
+ ret_list.stub!(:ole_methods).and_return(['GetAt', 'Count'])
214
+ ret_list.should_receive(:Count).and_return(2)
215
+ ret_list.should_receive(:GetAt).with(0).and_return(@full_name)
216
+ ret_list.should_receive(:GetAt).with(1).and_return(@full_name)
217
+
218
+ @ole_object.should_receive(:ColDataList).and_return(ret_list)
219
+
220
+ @full_name_wrapper = QBFC::OLEWrapper.new(@full_name)
221
+ QBFC::OLEWrapper.should_receive(:new).with(@full_name).twice.and_return(@full_name_wrapper)
222
+
223
+ @wrapper.qbfc_method_missing(@sess, :col_datas).should ==
224
+ [@full_name_wrapper, @full_name_wrapper]
225
+ end
226
+
227
+ it "should wrap OR*List objects in an Array" do
228
+ ret_list = mock('WIN32OLE.ORInvoiceLineList')
229
+ list_item_wrapper = mock('WIN32OLE.InvoiceLineWrapper')
230
+ list_item = mock('WIN32OLE.InvoiceLine')
231
+ ret_list.stub!(:ole_methods).and_return(['GetAt', 'Count'])
232
+ ret_list.should_receive(:Count).and_return(2)
233
+ ret_list.should_receive(:GetAt).with(0).and_return(list_item_wrapper)
234
+ ret_list.should_receive(:GetAt).with(1).and_return(list_item_wrapper)
235
+ list_item_wrapper.should_receive(:ReportData).twice.and_return(list_item)
236
+
237
+ @ole_object.should_receive(:ORReportDataList).and_return(ret_list)
238
+
239
+ @wrapper.qbfc_method_missing(@sess, :report_datas).should ==
240
+ [list_item, list_item]
241
+ end
242
+
243
+ it "should call a lower-case method defined by QBFC" do
244
+ @ole_object.should_receive(:colID).and_return('Id')
245
+ @wrapper.qbfc_method_missing(@sess, :colID).should == 'Id'
246
+ end
247
+
248
+ it "should have *_full_name for *Ref" do
249
+ ref = mock('WIN32OLE.PayeeRef')
250
+ @ole_object.should_receive(:AccountRef).and_return(ref)
251
+
252
+ full_name = "Full Name"
253
+ full_name_obj = mock('WIN32OLE.FullName')
254
+ ref.should_receive(:FullName).and_return(full_name_obj)
255
+ full_name_obj.should_receive(:GetValue).and_return(full_name)
256
+
257
+ @wrapper.qbfc_method_missing(@sess, :account_full_name).should == full_name
258
+ end
259
+
260
+ it "should have *_id for *Ref" do
261
+ ref = mock('WIN32OLE.PayeeRef')
262
+ @ole_object.should_receive(:AccountRef).and_return(ref)
263
+
264
+ list_id = "1"
265
+ list_id_obj = mock('WIN32OLE.ListID')
266
+ ref.should_receive(:ListID).and_return(list_id_obj)
267
+ list_id_obj.should_receive(:GetValue).and_return(list_id)
268
+
269
+ @wrapper.qbfc_method_missing(@sess, :account_id).should == list_id
270
+ end
271
+
272
+ it "should have *_full_name for *EntityRef" do
273
+ ref = mock('WIN32OLE.PayeeEntityRef')
274
+ @ole_object.should_receive(:PayeeEntityRef).and_return(ref)
275
+
276
+ full_name = "Full Name"
277
+ full_name_obj = mock('WIN32OLE.FullName')
278
+ ref.should_receive(:FullName).and_return(full_name_obj)
279
+ full_name_obj.should_receive(:GetValue).and_return(full_name)
280
+
281
+ @wrapper.qbfc_method_missing(@sess, :payee_full_name).should == full_name
282
+ end
283
+
284
+ it "should have *_id for *EntityRef" do
285
+ ref = mock('WIN32OLE.PayeeEntityRef')
286
+ @ole_object.should_receive(:PayeeEntityRef).and_return(ref)
287
+
288
+ list_id = "1"
289
+ list_id_obj = mock('WIN32OLE.ListID')
290
+ ref.should_receive(:ListID).and_return(list_id_obj)
291
+ list_id_obj.should_receive(:GetValue).and_return(list_id)
292
+
293
+ @wrapper.qbfc_method_missing(@sess, :payee_id).should == list_id
294
+ end
295
+
296
+ it "should create a Base-inherited object from a *EntityRef" do
297
+ entity_ref = mock('WIN32OLE.PayeeEntityRef')
298
+ @ole_object.should_receive(:PayeeEntityRef).and_return(entity_ref)
299
+
300
+ list_id = "1"
301
+ list_id_obj = mock('WIN32OLE.ListID')
302
+ entity_ref.should_receive(:ListID).and_return(list_id_obj)
303
+ list_id_obj.should_receive(:GetValue).and_return(list_id)
304
+
305
+ entity = mock(QBFC::Entity)
306
+
307
+ QBFC::Entity.should_receive(:find_by_id).with(@sess, list_id).and_return(entity)
308
+
309
+ @wrapper.qbfc_method_missing(@sess, :payee).should == entity
310
+ end
311
+
312
+ it "should return nil for a *Ref if the ole_method calling *Ref returns nil" do
313
+ @ole_object.should_receive(:PayeeEntityRef).and_return(nil)
314
+ @wrapper.qbfc_method_missing(@sess, :payee).should be_nil
315
+ end
316
+
317
+ it "should create a Base-inherited object from a *Ref" do
318
+ account_ref = mock('WIN32OLE.AccountRef')
319
+ @ole_object.should_receive(:AccountRef).and_return(account_ref)
320
+
321
+ list_id = "1"
322
+ list_id_obj = mock('WIN32OLE.ListID')
323
+ account_ref.should_receive(:ListID).and_return(list_id_obj)
324
+ list_id_obj.should_receive(:GetValue).and_return(list_id)
325
+
326
+ account = mock(QBFC::Account)
327
+
328
+ QBFC::Account.should_receive(:find_by_id).with(@sess, list_id).and_return(account)
329
+
330
+ @wrapper.qbfc_method_missing(@sess, :account).should == account
331
+ end
332
+
333
+ it "should raise NoMethodError if none of the above apply" do
334
+ lambda { @wrapper.qbfc_method_missing(@sess, :no_method) }.should raise_error(NoMethodError, 'no_method')
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe QBFC::QBCollection do
4
+
5
+ before(:each) do
6
+ @sess = mock(QBFC::Session)
7
+ end
8
+
9
+ it "should send missing methods to the Class specified, with the Session" do
10
+ QBFC::Customer.should_receive(:find).with(@sess, :all)
11
+ QBFC::QBCollection.new(@sess, 'Customer').find(:all)
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe QBFC_CONST do
4
+
5
+ it "should load QBFC constants" do
6
+ QBFC_CONST::DmToday.should == 1
7
+ QBFC_CONST::OmDontCare.should == 2
8
+ end
9
+
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe QBFC do
4
+
5
+ it "should have a session method which calls QBFC::Session.open" do
6
+ QBFC::Session.should_receive(:open).with({:filename => "file"})
7
+ QBFC::session({:filename => "file"})
8
+ end
9
+
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe QBFC::Report do
4
+
5
+ describe ".get_class" do
6
+ it "should get the class for this report" do
7
+ QBFC::Report.get_class("AuditTrail").should be(QBFC::Reports::GeneralDetail)
8
+ QBFC::Report.get_class("PayrollSummary").should be(QBFC::Reports::PayrollSummary)
9
+ QBFC::Report.get_class("TimeByJobDetail").should be(QBFC::Reports::Time)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ These are notes from a "survey" through possible options for Query Requests.
2
+ This is intended to inform what specs are added to Request (particularly
3
+ #apply_options and the methods it calls).
4
+
5
+ lists
6
+ - list_id_list
7
+ - full_name_list
8
+ - txn_id_list
9
+ X ref_number_list
10
+ - ref_number_case_sensitive_list
11
+
12
+ filters
13
+ - max_returned (alias limit)
14
+ - active_status (alias active)
15
+
16
+ - from_modified_date (for lists)
17
+ - to_modified_date (for lists)
18
+
19
+ - ORDateRangeFilter
20
+ - modified_date_range_filter (from and to)
21
+ - txn_date_range_filter (from and to OR macro)
22
+
23
+ - entity_filter / account_filter / item_filter (all ORs)
24
+ - ListIDList, FullNameList, ListIDWithChildren, FullNameWithChildren
25
+
26
+ - ORNameFilter / ORRefNumFilter
27
+ - name_filter (has criterion and name)
28
+ - name_range_filter (from and to)
29
+
30
+ - time_tracking_entity_filter
31
+
32
+ - ItemRef
33
+
34
+ - account_type_list
35
+
36
+ - txn_filter_no_account (SalesOrder)
37
+
38
+ - pending_status
39
+ - paid_status
40
+ - done_status
41
+
42
+ TransactionQuery has a bunch of extras
43
+
44
+ includes
45
+ - include_ret_element_list
46
+ - include_line_items
47
+ - include_linked_txns
48
+ - IncludeComponentLineItems
@@ -0,0 +1,486 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe QBFC::Request do
4
+
5
+ before(:each) do
6
+ @sess = mock(QBFC::Session)
7
+ @request_set = mock(QBFC::OLEWrapper)
8
+ @ole_request = mock(QBFC::OLEWrapper)
9
+
10
+ @sess.stub!(:CreateMsgSetRequest).and_return(@request_set)
11
+ @request_set.stub!(:AppendCustomerQueryRq).and_return(@ole_request)
12
+ end
13
+
14
+ it "sends CreateMsgSetRequest to Quickbooks Session" do
15
+ @sess.should_receive(:CreateMsgSetRequest).with('US', 6, 0).and_return(@request_set)
16
+ QBFC::Request.new(@sess, 'CustomerQuery')
17
+ end
18
+
19
+ it "appends a query to MsgSetRequest" do
20
+ @request_set.should_receive(:AppendCustomerQueryRq).and_return @ole_request
21
+ QBFC::Request.new(@sess, 'CustomerQuery')
22
+ end
23
+
24
+ it "accepts version information" do
25
+ @sess.should_receive(:CreateMsgSetRequest).with('CA', 5, 5).and_return(@request_set)
26
+ QBFC::Request.new(@sess, 'CustomerQuery', 'CA', 5, 5)
27
+ end
28
+
29
+ it "should raise a QBFC::QBXMLVersionError if the version is not supported" do
30
+ @sess.should_receive(:CreateMsgSetRequest).and_raise(WIN32OLERuntimeError.new('error code:8004030A'))
31
+ lambda { QBFC::Request.new(@sess, 'CustomerQuery')}.should raise_error(QBFC::QBXMLVersionError)
32
+ end
33
+
34
+ it "should re-raise errors other than QBXMLVersionError" do
35
+ @sess.should_receive(:CreateMsgSetRequest).and_raise(WIN32OLERuntimeError.new('error'))
36
+ lambda { QBFC::Request.new(@sess, 'CustomerQuery')}.should raise_error(WIN32OLERuntimeError)
37
+ end
38
+
39
+ it "should raise a QBFC::UnknownRequestError if the request is not supported" do
40
+ @request_set.should_receive(:AppendCustomerQueryRq).and_raise(WIN32OLERuntimeError.new('error code:0x80020006'))
41
+ lambda { QBFC::Request.new(@sess, 'CustomerQuery')}.should raise_error(QBFC::UnknownRequestError)
42
+ end
43
+
44
+ it "should re-raise errors other than UnknownRequestError" do
45
+ @request_set.should_receive(:AppendCustomerQueryRq).and_raise(WIN32OLERuntimeError.new('error'))
46
+ lambda { QBFC::Request.new(@sess, 'CustomerQuery')}.should raise_error(WIN32OLERuntimeError)
47
+ end
48
+
49
+ it "should show ole_methods" do
50
+ @ole_request.should_receive(:ole_methods)
51
+ QBFC::Request.new(@sess, 'CustomerQuery').ole_methods
52
+ end
53
+
54
+ it "gives direct access to the request's ole_object" do
55
+ @ole_request.should_receive(:ole_object).and_return("OLEObject")
56
+ QBFC::Request.new(@sess, 'CustomerQuery').ole_object.should == "OLEObject"
57
+ end
58
+
59
+ it "should have the OLEWrapper object handle missing methods" do
60
+ @ole_request.should_receive(:qbfc_method_missing).with(@sess, :no_method)
61
+ QBFC::Request.new(@sess, 'CustomerQuery').no_method
62
+
63
+ @ole_request.should_receive(:qbfc_method_missing).with(@sess, :NoMethod)
64
+ QBFC::Request.new(@sess, 'CustomerQuery').NoMethod
65
+ end
66
+
67
+ it "should return xml of the request" do
68
+ @request_set.should_receive(:ToXMLString)
69
+ QBFC::Request.new(@sess, 'CustomerQuery').to_xml
70
+ end
71
+
72
+ describe "#response" do
73
+ before(:each) do
74
+ @sess = mock(QBFC::Session)
75
+ @request_set = mock(QBFC::OLEWrapper)
76
+ @ole_request = mock(QBFC::OLEWrapper)
77
+
78
+ @sess.stub!(:CreateMsgSetRequest).and_return(@request_set)
79
+ @request_set.stub!(:AppendCustomerQueryRq).and_return(@ole_request)
80
+
81
+ @response_set = mock("DoRequestsRespost")
82
+ @response_list = mock("ResponseList")
83
+ @response = mock("GetAt")
84
+ @detail = mock("Detail")
85
+
86
+ @sess.stub!(:DoRequests).and_return @response_set
87
+ @response_set.stub!(:ResponseList).and_return @response_list
88
+ @response_list.stub!(:GetAt).and_return @response
89
+ @response.stub!(:Detail).and_return @detail
90
+ end
91
+
92
+ it "gets a response" do
93
+ @sess.should_receive(:DoRequests).and_return @response_set
94
+ @response_set.should_receive(:ResponseList).and_return @response_list
95
+ @response_list.should_receive(:GetAt).with(0).and_return @response
96
+ @response.should_receive(:Detail).and_return @detail
97
+
98
+ request = QBFC::Request.new(@sess, 'CustomerQuery')
99
+
100
+ request.response.should == @detail
101
+ end
102
+
103
+ it "returns a nil response if the response has no Detail" do
104
+ @response.should_receive(:Detail).and_return nil
105
+
106
+ request = QBFC::Request.new(@sess, 'CustomerQuery')
107
+
108
+ QBFC::OLEWrapper.should_not_receive(:new)
109
+ request.response.should be_nil
110
+ end
111
+
112
+
113
+ it "should return xml of the response" do
114
+ request = QBFC::Request.new(@sess, 'CustomerQuery')
115
+ @response_set.should_receive(:ToXMLString)
116
+ request.response_xml
117
+ end
118
+ end
119
+
120
+ describe "#query" do
121
+ before(:each) do
122
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
123
+ @request.instance_variable_set(:@request, @ole_request)
124
+
125
+ @or_query = mock("OLEWrapper#or_query")
126
+ end
127
+
128
+ it "gets the OR*Query for the given Request" do
129
+ @ole_request.should_receive(:ole_methods).and_return(["TxnID", "RefNumber", "ORTransactionQuery", "OwnerIDList"])
130
+ @ole_request.should_receive(:ORTransactionQuery).and_return(@or_query)
131
+ @request.query.should be(@or_query)
132
+ end
133
+
134
+ it "should return nil if no query name is detected" do
135
+ @ole_request.should_receive(:ole_methods).and_return(["TxnID", "RefNumber", "OwnerIDList"])
136
+ @request.query.should be_nil
137
+ end
138
+ end
139
+
140
+ describe "#filter" do
141
+ before(:each) do
142
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
143
+ @request.instance_variable_set(:@request, @ole_request)
144
+
145
+ @or_query = mock("OLEWrapper#or_query")
146
+ @filter = mock("OLEWrapper#filter")
147
+ @ole_request.stub!(:ole_methods).and_return(["TxnID", "RefNumber", "ORTransactionQuery", "OwnerIDList"])
148
+ @ole_request.stub!(:ORTransactionQuery).and_return(@or_query)
149
+ end
150
+
151
+ it "gets the *Filter for the given Request" do
152
+ @or_query.should_receive(:ole_methods).and_return(["TxnIDList", "RefNumberList", "TransactionFilter"])
153
+ @or_query.should_receive(:TransactionFilter).and_return(@filter)
154
+ @request.filter.should be(@filter)
155
+ end
156
+
157
+ it "should return nil if no filter name is detected" do
158
+ @or_query.should_receive(:ole_methods).and_return(["TxnIDList", "RefNumberList"])
159
+ @request.filter.should be_nil
160
+ end
161
+
162
+ it "should return nil if the #query is nil" do
163
+ @ole_request.should_receive(:ole_methods).and_return([])
164
+ @request.filter.should be_nil
165
+ end
166
+ end
167
+
168
+ describe "#filter_available?" do
169
+ before(:each) do
170
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
171
+
172
+ @or_query = mock("OLEWrapper#or_query")
173
+ @filter = mock("OLEWrapper#filter")
174
+ @ole_request.stub!(:ole_methods).and_return(["TxnID", "RefNumber", "ORTransactionQuery", "OwnerIDList", "ortype"])
175
+ @ole_request.stub!(:ORTransactionQuery).and_return(@or_query)
176
+
177
+ @ole_object = mock(WIN32OLE)
178
+ @or_query.stub!(:ole_object).and_return(@ole_object)
179
+ @ole_object.stub!(:ole_object).and_return(["TxnID", "RefNumber", "ORTransactionQuery", "OwnerIDList", "ortype"])
180
+ end
181
+
182
+ it "should be true if no query options have been set" do
183
+ @ole_object.should_receive(:ortype).at_least(:once).and_return(-1)
184
+ @request.filter_available?.should be_true
185
+ end
186
+
187
+ it "should be true if Filter option has been set" do
188
+ @ole_object.should_receive(:ortype).at_least(:once).and_return(2)
189
+ @request.filter_available?.should be_true
190
+ end
191
+
192
+ it "should be false if a *List option has been set" do
193
+ @ole_object.should_receive(:ortype).at_least(:once).and_return(1)
194
+ @request.filter_available?.should be_false
195
+ end
196
+
197
+ end
198
+
199
+ describe "#apply_options" do
200
+ before(:each) do
201
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
202
+ @query = mock('Request#query')
203
+ @filter = mock('Request#filter')
204
+ @request.stub!(:query).and_return(@query)
205
+ end
206
+
207
+ it "should apply an :owner_id option" do
208
+ @request.should_receive(:add_owner_ids).with(1)
209
+ @request.apply_options(:owner_id => 1)
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
445
+ end
446
+
447
+ describe "#add_owner_ids" do
448
+ before(:each) do
449
+ @request = QBFC::Request.new(@sess, 'CustomerQuery')
450
+ @owner_list = mock(QBFC::OLEWrapper)
451
+ end
452
+
453
+ it "can add a single owner id to the Request" do
454
+ @ole_request.should_receive(:OwnerIDList).and_return(@owner_list)
455
+ @owner_list.should_receive(:Add).with(0)
456
+ @request.add_owner_ids(0)
457
+ end
458
+
459
+ it "can add multiple owner ids to the Request" do
460
+ ids = ["{6B063959-81B0-4622-85D6-F548C8CCB517}", 0]
461
+ @ole_request.should_receive(:OwnerIDList).twice.and_return(@owner_list)
462
+ @owner_list.should_receive(:Add).with(ids[0])
463
+ @owner_list.should_receive(:Add).with(ids[1])
464
+ @request.add_owner_ids(ids)
465
+ end
466
+
467
+ it "can accept nil and will do nothing" do
468
+ @ole_request.should_not_receive(:OwnerIDList)
469
+ @owner_list.should_not_receive(:Add)
470
+ @request.add_owner_ids(nil)
471
+ end
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
486
+ end