effective_qb_sync 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +94 -0
  4. data/Rakefile +21 -0
  5. data/app/controllers/admin/qb_syncs_controller.rb +60 -0
  6. data/app/controllers/effective/qb_sync_controller.rb +40 -0
  7. data/app/models/effective/access_denied.rb +17 -0
  8. data/app/models/effective/datatables/qb_syncs.rb +30 -0
  9. data/app/models/effective/qb_log.rb +13 -0
  10. data/app/models/effective/qb_machine.rb +281 -0
  11. data/app/models/effective/qb_order_item.rb +13 -0
  12. data/app/models/effective/qb_order_items_form.rb +55 -0
  13. data/app/models/effective/qb_request.rb +262 -0
  14. data/app/models/effective/qb_ticket.rb +55 -0
  15. data/app/models/effective/qbwc_supervisor.rb +89 -0
  16. data/app/views/admin/qb_syncs/_actions.html.haml +2 -0
  17. data/app/views/admin/qb_syncs/_qb_item_names.html.haml +9 -0
  18. data/app/views/admin/qb_syncs/index.html.haml +24 -0
  19. data/app/views/admin/qb_syncs/instructions.html.haml +136 -0
  20. data/app/views/admin/qb_syncs/show.html.haml +52 -0
  21. data/app/views/effective/orders_mailer/qb_sync_error.html.haml +56 -0
  22. data/app/views/effective/qb_sync/authenticate.erb +12 -0
  23. data/app/views/effective/qb_sync/clientVersion.erb +8 -0
  24. data/app/views/effective/qb_sync/closeConnection.erb +8 -0
  25. data/app/views/effective/qb_sync/connectionError.erb +9 -0
  26. data/app/views/effective/qb_sync/getLastError.erb +9 -0
  27. data/app/views/effective/qb_sync/receiveResponseXML.erb +8 -0
  28. data/app/views/effective/qb_sync/sendRequestXML.erb +8 -0
  29. data/app/views/effective/qb_sync/serverVersion.erb +8 -0
  30. data/app/views/effective/qb_web_connector/quickbooks.qwc.erb +12 -0
  31. data/config/routes.rb +16 -0
  32. data/db/migrate/01_create_effective_qb_sync.rb.erb +68 -0
  33. data/lib/effective_qb_sync/engine.rb +42 -0
  34. data/lib/effective_qb_sync/version.rb +3 -0
  35. data/lib/effective_qb_sync.rb +42 -0
  36. data/lib/generators/effective_qb_sync/install_generator.rb +42 -0
  37. data/lib/generators/templates/effective_qb_sync.rb +61 -0
  38. data/lib/generators/templates/effective_qb_sync_mailer_preview.rb +39 -0
  39. data/spec/dummy/README.rdoc +8 -0
  40. data/spec/dummy/Rakefile +6 -0
  41. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  42. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/models/product.rb +14 -0
  46. data/spec/dummy/app/models/product_with_float_price.rb +13 -0
  47. data/spec/dummy/app/models/user.rb +14 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  49. data/spec/dummy/bin/bundle +3 -0
  50. data/spec/dummy/bin/rails +4 -0
  51. data/spec/dummy/bin/rake +4 -0
  52. data/spec/dummy/config/application.rb +32 -0
  53. data/spec/dummy/config/boot.rb +5 -0
  54. data/spec/dummy/config/database.yml +25 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +37 -0
  57. data/spec/dummy/config/environments/production.rb +80 -0
  58. data/spec/dummy/config/environments/test.rb +36 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  61. data/spec/dummy/config/initializers/devise.rb +254 -0
  62. data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
  63. data/spec/dummy/config/initializers/effective_orders.rb +154 -0
  64. data/spec/dummy/config/initializers/effective_qb_sync.rb +41 -0
  65. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  66. data/spec/dummy/config/initializers/inflections.rb +16 -0
  67. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  68. data/spec/dummy/config/initializers/session_store.rb +3 -0
  69. data/spec/dummy/config/initializers/simple_form.rb +189 -0
  70. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  71. data/spec/dummy/config/locales/en.yml +23 -0
  72. data/spec/dummy/config/routes.rb +3 -0
  73. data/spec/dummy/config/secrets.yml +22 -0
  74. data/spec/dummy/config.ru +4 -0
  75. data/spec/dummy/db/schema.rb +208 -0
  76. data/spec/dummy/db/test.sqlite3 +0 -0
  77. data/spec/dummy/log/development.log +90 -0
  78. data/spec/dummy/log/test.log +1 -0
  79. data/spec/dummy/public/404.html +67 -0
  80. data/spec/dummy/public/422.html +67 -0
  81. data/spec/dummy/public/500.html +66 -0
  82. data/spec/dummy/public/favicon.ico +0 -0
  83. data/spec/fixtures/qbxml_response_error.xml +6 -0
  84. data/spec/fixtures/qbxml_response_success.xml +621 -0
  85. data/spec/models/acts_as_purchasable_spec.rb +131 -0
  86. data/spec/models/factories_spec.rb +32 -0
  87. data/spec/models/qb_machine_spec.rb +554 -0
  88. data/spec/models/qb_request_spec.rb +327 -0
  89. data/spec/models/qb_ticket_spec.rb +62 -0
  90. data/spec/spec_helper.rb +45 -0
  91. data/spec/support/factories.rb +97 -0
  92. metadata +397 -0
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ # # Attributes
4
+ describe Product do
5
+ let(:user) { FactoryGirl.create(:user) }
6
+ let(:order) { FactoryGirl.create(:order) }
7
+ let(:product) { order.order_items.first.purchasable }
8
+ let(:product_with_float_price) { FactoryGirl.create(:product_with_float_price) }
9
+
10
+ describe 'assumptions' do
11
+ it 'should be effectively purchasable' do
12
+ product.kind_of?(ActsAsPurchasable).should eq true
13
+ end
14
+ end
15
+
16
+ describe 'purchased' do
17
+ it 'is purchased? when in a purchased Order' do
18
+ order.purchase!
19
+
20
+ product.purchased?.should eq true
21
+ product.purchased_orders.include?(order).should eq true
22
+ end
23
+
24
+ it 'is purchased? in the after_purchase callback' do
25
+ instance_order = nil
26
+ instance_product = nil
27
+ instance_purchased = nil
28
+
29
+ Product.instance_eval do
30
+ after_purchase do |order, order_item|
31
+ if defined?(:instance_order)
32
+ instance_order = order
33
+ instance_product = self
34
+ instance_purchased = self.purchased?
35
+ end
36
+ end
37
+ end
38
+
39
+ order.purchase!
40
+
41
+ instance_order.purchased?.should eq true
42
+ instance_product.purchased?.should eq true
43
+ instance_purchased.should eq true
44
+ end
45
+
46
+ it 'is returned by the purchased scopes' do
47
+ order.purchase!
48
+
49
+ Product.purchased.to_a.include?(product).should eq true
50
+ Product.purchased_by(order.user).to_a.include?(product).should eq true
51
+
52
+ Product.sold.to_a.include?(product).should eq true
53
+
54
+ Product.not_purchased.to_a.include?(product).should eq false
55
+ end
56
+ end
57
+
58
+ describe 'float prices' do
59
+ it 'should automatically convert float prices to integer' do
60
+ product_with_float_price.price = 20.00
61
+ product_with_float_price.tax_exempt = true
62
+
63
+ order = Effective::Order.new(product_with_float_price, user: user)
64
+ order.billing_address = FactoryGirl.create(:address, state_code: 'AB')
65
+
66
+ order.subtotal.should eq 2000
67
+ order.tax.should eq 0
68
+ order.total.should eq 2000
69
+ end
70
+
71
+ it 'should automatically convert tax floats to integers' do
72
+ product_with_float_price.price = 20.00
73
+ product_with_float_price.tax_exempt = false
74
+
75
+ order = Effective::Order.new(product_with_float_price, user: user)
76
+ order.billing_address = FactoryGirl.create(:address, state_code: 'AB')
77
+
78
+ order.subtotal.should eq 2000
79
+ order.tax.should eq 100
80
+ order.total.should eq 2100
81
+ end
82
+ end
83
+
84
+ describe 'price=' do
85
+ it 'should accept an integer price' do
86
+ product = Product.new()
87
+ product.price = 1250
88
+
89
+ product.price.should eq 1250
90
+ end
91
+
92
+ it 'should convert a String that looks like an Integer' do
93
+ product = Product.new()
94
+ product.price = '1250'
95
+
96
+ product.price.should eq 1250
97
+ end
98
+
99
+ it 'should convert a String that looks like a Float' do
100
+ product = Product.new()
101
+ product.price = '12.50'
102
+
103
+ product.price.should eq 1250
104
+ end
105
+
106
+ it 'should convert from a Float' do
107
+ product = Product.new()
108
+ product.price = 12.50
109
+ product.price.should eq 1250
110
+
111
+ product.price = Float(12.50)
112
+ product.price.should eq 1250
113
+ end
114
+
115
+ it 'should convert from a BigDecimal' do
116
+ product = Product.new()
117
+ product.price = BigDecimal.new(12.5, 4)
118
+
119
+ product.price.should eq 1250
120
+ end
121
+
122
+ it 'should treat nil as a zero' do
123
+ product = Product.new()
124
+ product.price = nil
125
+
126
+ product.price.should eq 0
127
+ end
128
+
129
+ end
130
+
131
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ # Attributes
4
+ describe 'Factories' do
5
+ let(:factories) { [:user, :address, :product, :cart, :order_item, :order, :purchased_order, :declined_order] }
6
+
7
+ it 'should have all valid factories' do
8
+ factories.each do |factory|
9
+ obj = FactoryGirl.create(factory)
10
+
11
+ puts "Invalid factory #{factory}: #{obj.errors.inspect}" unless obj.valid?
12
+
13
+ obj.valid?.should eq true
14
+ end
15
+ end
16
+
17
+ it 'should have created an Order with a billing_address and shipping_address' do
18
+ order = FactoryGirl.create(:order)
19
+
20
+ order.billing_address.present?.should eq true
21
+ order.shipping_address.present?.should eq true
22
+
23
+ order.billing_address.valid?.should eq true
24
+ order.shipping_address.valid?.should eq true
25
+
26
+ order.billing_address.full_name.present?.should eq true
27
+ order.shipping_address.full_name.present?.should eq true
28
+
29
+ order.save.should eq true
30
+ end
31
+
32
+ end
@@ -0,0 +1,554 @@
1
+ require 'spec_helper'
2
+
3
+ describe Effective::QbMachine, "Basic Functionality" do
4
+
5
+ before :each do
6
+ end
7
+
8
+ it "should be valid" do
9
+ @qb_machine = Effective::QbMachine.new
10
+ @qb_machine.should be_valid
11
+ end
12
+
13
+ it "should initialize a ticket in the Ready state when constructed" do
14
+ @qb_machine = Effective::QbMachine.new
15
+ @qb_machine.ticket.state.should eql('Ready')
16
+ end
17
+
18
+ it "should not create a ticket if the ticket could not be found" do
19
+ @qb_machine = Effective::QbMachine.new(1976)
20
+ @qb_machine.ticket.should be_nil
21
+ end
22
+
23
+ it "should not be valid if a ticket could not be found" do
24
+ @qb_machine = Effective::QbMachine.new(1976)
25
+ @qb_machine.should_not be_valid
26
+ end
27
+
28
+ it "should be valid upon finding an existing ticket" do
29
+ @qb_ticket = Effective::QbTicket.create
30
+ @qb_machine = Effective::QbMachine.new(@qb_ticket.id)
31
+ @qb_machine.should be_valid
32
+ end
33
+
34
+ it "should delegate logging functionality to the ticket" do
35
+ before = Effective::QbLog.count
36
+ @qb_machine = Effective::QbMachine.new
37
+ @qb_machine.log('Message')
38
+ assert Effective::QbLog.count > before
39
+ end
40
+
41
+ it "should not record an empty log message" do
42
+ @qb_machine = Effective::QbMachine.new
43
+ Effective::QbLog.should_not_receive(:create)
44
+ @qb_machine.log('')
45
+ end
46
+
47
+ it "should record the last log message in an instance member" do
48
+ @qb_machine = Effective::QbMachine.new
49
+ message = 'A message'
50
+ @qb_machine.log(message)
51
+ @qb_machine.last_log_message.should eql(message)
52
+ end
53
+
54
+ end
55
+
56
+ describe Effective::QbMachine, "Authentication Behavior (op_authenticate)" do
57
+
58
+ before :each do
59
+ @qb_machine = Effective::QbMachine.new
60
+ end
61
+
62
+ it "should be valid" do
63
+ @qb_machine.should be_valid
64
+ end
65
+
66
+ it "should authenticate successfully with the correct password" do
67
+ result = @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username, EffectiveQbSync.quickbooks_password)
68
+ result.should_not eql('nvu')
69
+ end
70
+
71
+ it "should not authenticate successfully with a different password" do
72
+ result = @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username, '12345')
73
+ result.should eql('nvu')
74
+ end
75
+
76
+ it "should transition ticket to Finished state upon unsuccessful authentication" do
77
+ @qb_machine.should_receive(:authentication_valid?).and_return(false)
78
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,'incorrectpassword')
79
+ @qb_machine.ticket.state.should eql('Finished')
80
+ end
81
+
82
+ it "should populate last_error with authentication failure message upon unsuccessful authentication" do
83
+ @qb_machine.should_receive(:authentication_valid?).and_return(false)
84
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,'incorrectpassword')
85
+ @qb_machine.ticket.last_error.should_not be_blank
86
+ end
87
+
88
+ it "should keep ticket in the Authenticated state after authentication if there is work to be done" do
89
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
90
+ @qb_machine.should_receive(:has_work?).and_return(true)
91
+
92
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
93
+ @qb_machine.ticket.state.should eql('Authenticated')
94
+ end
95
+
96
+ it "should transition ticket to Finished state if there is not any work to be done" do
97
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
98
+ @qb_machine.should_receive(:has_work?).and_return(false)
99
+
100
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
101
+ @qb_machine.ticket.state.should eql('Finished')
102
+ end
103
+
104
+ it "should return 'nvu' from op_authentication if the user login is invalid" do
105
+ @qb_machine.should_receive(:authentication_valid?).and_return(false)
106
+ result = @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,'incorrectpassword')
107
+ result.should eql('nvu')
108
+ end
109
+
110
+ it "should return 'none' from op_authentication if the login is valid but no work to be done" do
111
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
112
+ @qb_machine.should_receive(:has_work?).and_return(false)
113
+ result = @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
114
+ result.should eql('none')
115
+ end
116
+
117
+ it "should return '' from op_authentication if the login is valid and there is work to be done" do
118
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
119
+ @qb_machine.should_receive(:has_work?).and_return(true)
120
+ result = @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
121
+ result.should eql('')
122
+ end
123
+
124
+ it "should record the ticket username field on successful authentication" do
125
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
126
+ username = 'successful user'
127
+ @qb_machine.op_authenticate(username,'password')
128
+ @qb_machine.ticket.username.should eql(username)
129
+ end
130
+
131
+ it "should record the ticket username field on unsuccessful authentication" do
132
+ @qb_machine.should_receive(:authentication_valid?).and_return(false)
133
+ username = 'unsuccessful user'
134
+ @qb_machine.op_authenticate(username,'password')
135
+ @qb_machine.ticket.username.should eql(username)
136
+ end
137
+
138
+ it "should not have a current request after successful authentication" do
139
+ @qb_machine.should_receive(:authentication_valid?).and_return(true)
140
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
141
+ @qb_machine.ticket.qb_request.should be_nil
142
+ end
143
+
144
+ end
145
+
146
+ describe Effective::QbMachine, "Sending Request qbXML to QuickBooks (op_send_request_xml)" do
147
+
148
+ before :each do
149
+ @qb_machine = Effective::QbMachine.new
150
+ allow(@qb_machine).to receive(:authentication_valid?).and_return(true)
151
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
152
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username,EffectiveQbSync.quickbooks_password)
153
+
154
+ # Here the machine should be in the Authenticated State
155
+
156
+ # - signature: string sendRequestXML(ticket, hcpresponse, company, country, major_ver, minor_ver)
157
+
158
+ @hcpresponse = 'some response'
159
+ @company = 'some company'
160
+ @country = 'US'
161
+ @major_ver = '8'
162
+ @minor_ver = '2'
163
+
164
+ @default_request_params = {
165
+ :hcpresponse=>@hcpresponse,
166
+ :company=>@company,
167
+ :country=>@country,
168
+ :major_ver=>@major_ver,
169
+ :minor_ver=>@minor_ver
170
+ }
171
+
172
+ # the default order item we'll be using when mocking out QbMachine#create_request
173
+ @order = Effective::Order.new
174
+
175
+ # the default request we'll use when mocking out QbMachine#create_request
176
+ @qb_request = Effective::QbRequest.new(request_type: 'OrderItemSynchronization', order: @order)
177
+ allow(@qb_request).to receive(:to_qb_xml).and_return('<qbXML></qbXML>') # we are not worried about qbXML correctness here
178
+ allow(@qb_machine).to receive(:create_request).and_return(@qb_request)
179
+ end
180
+
181
+ it "should populate ticket fields [hpc_response,company_file_name, etc] when non-blank" do
182
+ allow(@qb_machine).to receive(:has_work?).and_return(false)
183
+ @qb_machine.op_send_request_xml(@default_request_params)
184
+
185
+ @qb_machine.ticket.hpc_response.should eql(@hcpresponse)
186
+ @qb_machine.ticket.company_file_name.should eql(@company)
187
+ @qb_machine.ticket.country.should eql(@country)
188
+ @qb_machine.ticket.qbxml_major_version.should eql(@major_ver)
189
+ @qb_machine.ticket.qbxml_minor_version.should eql(@minor_ver)
190
+ end
191
+
192
+ it "should not overwrite ticket fields [hpc_response,company_file_name, etc] on subsequent request XML calls if those fields are blank" do
193
+ allow(@qb_machine).to receive(:has_work?).and_return(false)
194
+ @qb_machine.op_send_request_xml(@default_request_params)
195
+
196
+ @qb_machine.ticket.hpc_response.should eql(@hcpresponse)
197
+ @qb_machine.ticket.company_file_name.should eql(@company)
198
+ @qb_machine.ticket.country.should eql(@country)
199
+ @qb_machine.ticket.qbxml_major_version.should eql(@major_ver)
200
+
201
+ # now blank out the fields and call again
202
+
203
+ @qb_machine.op_send_request_xml(@default_request_params.except(:hcpresponse,:company,:country,:major_ver,:minor_ver))
204
+
205
+ @qb_machine.ticket.hpc_response.should eql(@hcpresponse)
206
+ @qb_machine.ticket.company_file_name.should eql(@company)
207
+ @qb_machine.ticket.country.should eql(@country)
208
+ @qb_machine.ticket.qbxml_major_version.should eql(@major_ver)
209
+ end
210
+
211
+ it "should transition ticket to the RequestError state if the ticket is not in the Authenticated or Processing states" do
212
+ @qb_machine.ticket.update_attributes!(state: 'Finished')
213
+ @qb_machine.op_send_request_xml(@default_request_params)
214
+ @qb_machine.ticket.state.should eql('RequestError')
215
+ end
216
+
217
+ it "should return a non-empty string if there is work to be done" do
218
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
219
+
220
+ result = @qb_machine.op_send_request_xml(@default_request_params)
221
+ result.should_not be_blank
222
+ end
223
+
224
+ it "should create a QbRequest model and attach to the ticket for the corresponding qbXML" do
225
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
226
+ @qb_machine.op_send_request_xml(@default_request_params)
227
+ @qb_request.id.should_not be_nil
228
+ @qb_machine.ticket.qb_requests.should_not be_empty
229
+ end
230
+
231
+ it "should only create one QbRequest model when sending request xml" do
232
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
233
+ @qb_machine.op_send_request_xml(@default_request_params)
234
+ @qb_machine.ticket.qb_requests.size.should eql(1)
235
+ end
236
+
237
+ it "should put the QbRequest model into the Processing state after sending work to QuickBooks" do
238
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
239
+ @qb_machine.op_send_request_xml(@default_request_params)
240
+ request = @qb_machine.ticket.qb_requests.first
241
+ request.state.should eql('Processing')
242
+ end
243
+
244
+ it "should set the QbTicket model into the Processing state after sending work to QuickBooks" do
245
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
246
+ @qb_machine.op_send_request_xml(@default_request_params)
247
+ @qb_machine.ticket.state.should eql('Processing')
248
+ end
249
+
250
+ it "should store the qbXML into the QbRequest model" do
251
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
252
+ qbXML = @qb_machine.op_send_request_xml(@default_request_params)
253
+ request = @qb_machine.ticket.qb_requests.first
254
+ request.request_qbxml.should eql(qbXML)
255
+ end
256
+
257
+ it "should set the request_sent_at field in the QbRequest model" do
258
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
259
+ qbXML = @qb_machine.op_send_request_xml(@default_request_params)
260
+ request = @qb_machine.ticket.qb_requests.first
261
+ request.request_sent_at.should_not be_nil
262
+ end
263
+
264
+ it "should set the current request in the ticket" do
265
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
266
+ @qb_machine.op_send_request_xml(@default_request_params)
267
+ @qb_machine.ticket.qb_request.should_not be_nil
268
+ @qb_machine.ticket.qb_request.should eql(@qb_request)
269
+ end
270
+
271
+ end
272
+
273
+
274
+ describe Effective::QbMachine, "Receiving response qbXML from QuickBooks (op_receive_response_xml)" do
275
+
276
+ before :each do
277
+ @qb_machine = Effective::QbMachine.new
278
+
279
+ # fake out authentication to pass successfully
280
+ allow(@qb_machine).to receive(:authentication_valid?).and_return(true)
281
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
282
+
283
+ @qb_machine.op_authenticate(EffectiveQbSync.quickbooks_username, EffectiveQbSync.quickbooks_password)
284
+
285
+ @default_request_params = {
286
+ :hcpresponse=>'some response',
287
+ :company=>'some company',
288
+ :country=>'US',
289
+ :major_ver=>'8',
290
+ :minor_ver=>'2'
291
+ }
292
+
293
+ # the default order item we'll be using when mocking out QbMachine#create_request
294
+ @order = Effective::Order.new
295
+ @qb_request = Effective::QbRequest.new(id: 1, request_type: 'OrderItemSynchronization', order: @order, state: 'CustomerQuery')
296
+ @qb_request_xml = '<qbXML></qbXML>'
297
+ allow(@qb_request).to receive(:to_qb_xml).and_return(@qb_request_xml) # we are not worried about qbXML correctness here
298
+
299
+ # set the machine to send some request qbXML to quickbooks
300
+ allow(@qb_machine).to receive(:has_work?).and_return(true)
301
+ allow(@qb_machine).to receive(:create_request).and_return(@qb_request)
302
+ @qb_machine.op_send_request_xml(@default_request_params)
303
+
304
+ @qb_response_xml = '<qbXML><QBXMLMsgsRs><AccountQueryRs requestID="1" statusCode="0"></AccountQueryRs></QBXMLMsgsRs></qbXML>'
305
+ # int receiveResponseXML(ticket, response, hresult, message)
306
+ @default_response_params = {
307
+ :response=>@qb_response_xml,
308
+ :hresult=>'',
309
+ :message=>''
310
+ }
311
+
312
+ # by default always report no more work to be done
313
+ allow(@qb_machine).to receive(:how_much_more_work).and_return(0)
314
+ end
315
+
316
+ it "should return -1 to indicate error if the ticket state is not in the Processing state" do
317
+ @qb_machine.ticket.update_attributes! :state=>'Finished'
318
+ result = @qb_machine.op_receive_response_xml(@default_response_params)
319
+ result.should eql(-1)
320
+ end
321
+
322
+ it "should set the ticket state to RequestError if the ticket state was previously Authenticated" do
323
+ @qb_machine.ticket.update_attributes! :state=>'Authenticated'
324
+ @qb_machine.op_receive_response_xml(@default_response_params)
325
+ @qb_machine.ticket.state.should eql('RequestError')
326
+ end
327
+
328
+ it "should set the ticket state to RequestError if the ticket state was previously Ready" do
329
+ @qb_machine.ticket.update_attributes! :state=>'Ready'
330
+ @qb_machine.op_receive_response_xml(@default_response_params)
331
+ @qb_machine.ticket.state.should eql('RequestError')
332
+ end
333
+
334
+ it "should set the ticket state to RequestError if the ticket state was previously Finished" do
335
+ @qb_machine.ticket.update_attributes! :state=>'Finished'
336
+ @qb_machine.op_receive_response_xml(@default_response_params)
337
+ @qb_machine.ticket.state.should eql('RequestError')
338
+ end
339
+
340
+ it "should send the ticket into a RequestError state if there is no matching Processing request" do
341
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(nil)
342
+ @qb_machine.op_receive_response_xml(@default_response_params)
343
+ @qb_machine.ticket.state.should eql('RequestError')
344
+ end
345
+
346
+ it "should set the request state to Error if the response indicates error" do
347
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
348
+ allow(@qb_request).to receive(:consume_response_xml).and_return(false)
349
+ @qb_machine.op_receive_response_xml(@default_response_params)
350
+ @qb_request.state.should eql('Error')
351
+ end
352
+
353
+ it "should set the ticket last_error field if the response indicates error" do
354
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
355
+ allow(@qb_request).to receive(:consume_response_xml).and_return(false)
356
+ @qb_machine.op_receive_response_xml(@default_response_params)
357
+ @qb_machine.ticket.last_error.should_not be_blank
358
+ end
359
+
360
+ it "should set the request state to Error if there was a connection error with QuickBooks" do
361
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
362
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
363
+
364
+ @params = @default_response_params
365
+ @params[:hresult] = 'error_result'
366
+ @params[:message] = 'error_message'
367
+
368
+ @qb_machine.op_receive_response_xml(@params)
369
+ @qb_request.state.should eql('Error')
370
+ end
371
+
372
+ it "should set the set the ticket state to RequestError if there was a connection error with QuickBooks" do
373
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
374
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
375
+
376
+ @params = @default_response_params
377
+ @params[:hresult] = 'error_result'
378
+ @params[:message] = 'error_message'
379
+
380
+ @qb_machine.op_receive_response_xml(@params)
381
+ @qb_machine.ticket.state.should eql('RequestError')
382
+ end
383
+
384
+ it "should set the ticket last_error field if there was a connection error with QuickBooks" do
385
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
386
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
387
+
388
+ @params = @default_response_params
389
+ @params[:hresult] = 'error_result'
390
+ @params[:message] = 'error_message'
391
+
392
+ @qb_machine.op_receive_response_xml(@params)
393
+ @qb_machine.ticket.last_error.should_not be_blank
394
+ end
395
+
396
+ it "should return -1 if there was a connection error with QuickBooks" do
397
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
398
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
399
+
400
+ @params = @default_response_params
401
+ @params[:hresult] = 'error_result'
402
+ @params[:message] = 'error_message'
403
+
404
+ response = @qb_machine.op_receive_response_xml(@params)
405
+ response.should eql(-1)
406
+ end
407
+
408
+ it "should set the ticket connection_error_hresult and connection_error_message fields upon a connection error with QuickBooks" do
409
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
410
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
411
+
412
+ @params = @default_response_params
413
+ @params[:hresult] = 'error_result'
414
+ @params[:message] = 'error_message'
415
+
416
+ @qb_machine.op_receive_response_xml(@params)
417
+ @qb_machine.ticket.connection_error_hresult.should_not be_blank
418
+ @qb_machine.ticket.connection_error_message.should_not be_blank
419
+ end
420
+
421
+ it "should return a number between 1 and 99, inclusive, if there is more work to be done" do
422
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
423
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
424
+ allow(@qb_machine).to receive(:how_much_more_work).and_return(15)
425
+
426
+ result = @qb_machine.op_receive_response_xml(@default_response_params)
427
+ result.should be > 0
428
+ result.should be < 100
429
+ end
430
+
431
+ it "should set the response_qbxml field in the QbRequest model" do
432
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
433
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
434
+
435
+ @qb_request.request_type = nil
436
+
437
+ @qb_machine.op_receive_response_xml(@default_response_params)
438
+
439
+ @qb_request.response_qbxml.should eql(@qb_response_xml)
440
+ end
441
+
442
+ it "should release the ticket's current request if that request signals that it is done" do
443
+ allow(@qb_machine).to receive(:find_outstanding_request).and_return(@qb_request)
444
+ @qb_request.state = 'Finished'
445
+ allow(@qb_request).to receive(:consume_response_xml).and_return(true)
446
+
447
+ # @qb_request.stub!(:consume_response_xml).and_return {
448
+ # @qb_request.state = 'Finished'
449
+ # # return true to indicate success
450
+ # true
451
+ # }
452
+ @qb_machine.op_receive_response_xml(@default_response_params)
453
+
454
+ # it should be nil because the request is in a finished state
455
+ @qb_machine.ticket.qb_request.should be_nil
456
+ end
457
+
458
+ end
459
+
460
+
461
+ describe Effective::QbMachine, "Receiving notification that the QBWC cannot connect to QuickBooks (op_connection_error)" do
462
+
463
+ before :each do
464
+ @qb_machine = Effective::QbMachine.new
465
+ @hresult = 'hresult'
466
+ @message = 'message'
467
+ @result = @qb_machine.op_connection_error(@hresult, @message)
468
+ end
469
+
470
+ it "should always return 'done'" do
471
+ @result.should eql('done')
472
+ end
473
+
474
+ it "should store the hresult and message in the ticket" do
475
+ @qb_machine.ticket.connection_error_hresult.should eql(@hresult)
476
+ @qb_machine.ticket.connection_error_message.should eql(@message)
477
+ end
478
+
479
+ it "should set the ticket state to ConnectionError" do
480
+ @qb_machine.ticket.state.should eql('ConnectionError')
481
+ end
482
+
483
+ end
484
+
485
+ describe Effective::QbMachine, "Receiving a request from the QBWC to provide the last error message (op_last_error)" do
486
+
487
+ before :each do
488
+ @qb_machine = Effective::QbMachine.new
489
+ @last_error = 'What?'
490
+ @qb_machine.ticket.update_attributes! :last_error=>@last_error
491
+ end
492
+
493
+ it "should return the last error" do
494
+ error = @qb_machine.op_last_error
495
+ end
496
+
497
+ it "should return '' if the last error is blank" do
498
+ @qb_machine.ticket.update_attributes! :last_error=>nil
499
+ error = @qb_machine.op_last_error
500
+ error.should eql('')
501
+
502
+ @qb_machine.ticket.update_attributes! :last_error=>''
503
+ error = @qb_machine.op_last_error
504
+ error.should eql('')
505
+ end
506
+
507
+ end
508
+
509
+ describe Effective::QbMachine, "Closing the connection (op_close_connection)" do
510
+
511
+ before :each do
512
+ @qb_machine = Effective::QbMachine.new
513
+ end
514
+
515
+ it "should not transition ticket state on close_connection if state is Finished" do
516
+ @qb_machine.ticket.update_attributes! :state=>'Finished'
517
+ @qb_machine.op_close_connection
518
+ @qb_machine.ticket.state.should eql('Finished')
519
+ end
520
+
521
+ it "should not transition ticket state on close_connection if state is ConnectionError" do
522
+ @qb_machine.ticket.update_attributes! :state=>'ConnectionError'
523
+ @qb_machine.op_close_connection
524
+ @qb_machine.ticket.state.should eql('ConnectionError')
525
+ end
526
+
527
+ it "should not transition ticket state on close_connection if state is RequestError" do
528
+ @qb_machine.ticket.update_attributes! :state=>'RequestError'
529
+ @qb_machine.op_close_connection
530
+ @qb_machine.ticket.state.should eql('RequestError')
531
+ end
532
+
533
+ it "should transition ticket state to Finished if state is Ready " do
534
+ @qb_machine.ticket.update_attributes! :state=>'Ready'
535
+ @qb_machine.op_close_connection
536
+ @qb_machine.ticket.state.should eql('Finished')
537
+ end
538
+
539
+ it "should transition ticket state to Finished if state is Authenticated" do
540
+ @qb_machine.ticket.update_attributes! :state=>'Authenticated'
541
+ @qb_machine.op_close_connection
542
+ @qb_machine.ticket.state.should eql('Finished')
543
+ end
544
+
545
+ it "should transition ticket state to Finished if state is Processing" do
546
+ @qb_machine.ticket.update_attributes! :state=>'Processing'
547
+ @qb_machine.op_close_connection
548
+ @qb_machine.ticket.state.should eql('Finished')
549
+ end
550
+
551
+
552
+ end
553
+
554
+