effective_qb_sync 1.0.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +21 -0
- data/app/controllers/admin/qb_syncs_controller.rb +60 -0
- data/app/controllers/effective/qb_sync_controller.rb +40 -0
- data/app/models/effective/access_denied.rb +17 -0
- data/app/models/effective/datatables/qb_syncs.rb +30 -0
- data/app/models/effective/qb_log.rb +13 -0
- data/app/models/effective/qb_machine.rb +281 -0
- data/app/models/effective/qb_order_item.rb +13 -0
- data/app/models/effective/qb_order_items_form.rb +55 -0
- data/app/models/effective/qb_request.rb +262 -0
- data/app/models/effective/qb_ticket.rb +55 -0
- data/app/models/effective/qbwc_supervisor.rb +89 -0
- data/app/views/admin/qb_syncs/_actions.html.haml +2 -0
- data/app/views/admin/qb_syncs/_qb_item_names.html.haml +9 -0
- data/app/views/admin/qb_syncs/index.html.haml +24 -0
- data/app/views/admin/qb_syncs/instructions.html.haml +136 -0
- data/app/views/admin/qb_syncs/show.html.haml +52 -0
- data/app/views/effective/orders_mailer/qb_sync_error.html.haml +56 -0
- data/app/views/effective/qb_sync/authenticate.erb +12 -0
- data/app/views/effective/qb_sync/clientVersion.erb +8 -0
- data/app/views/effective/qb_sync/closeConnection.erb +8 -0
- data/app/views/effective/qb_sync/connectionError.erb +9 -0
- data/app/views/effective/qb_sync/getLastError.erb +9 -0
- data/app/views/effective/qb_sync/receiveResponseXML.erb +8 -0
- data/app/views/effective/qb_sync/sendRequestXML.erb +8 -0
- data/app/views/effective/qb_sync/serverVersion.erb +8 -0
- data/app/views/effective/qb_web_connector/quickbooks.qwc.erb +12 -0
- data/config/routes.rb +16 -0
- data/db/migrate/01_create_effective_qb_sync.rb.erb +68 -0
- data/lib/effective_qb_sync/engine.rb +42 -0
- data/lib/effective_qb_sync/version.rb +3 -0
- data/lib/effective_qb_sync.rb +42 -0
- data/lib/generators/effective_qb_sync/install_generator.rb +42 -0
- data/lib/generators/templates/effective_qb_sync.rb +61 -0
- data/lib/generators/templates/effective_qb_sync_mailer_preview.rb +39 -0
- data/spec/dummy/README.rdoc +8 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/product.rb +14 -0
- data/spec/dummy/app/models/product_with_float_price.rb +13 -0
- data/spec/dummy/app/models/user.rb +14 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +32 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +254 -0
- data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
- data/spec/dummy/config/initializers/effective_orders.rb +154 -0
- data/spec/dummy/config/initializers/effective_qb_sync.rb +41 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/simple_form.rb +189 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +208 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +90 -0
- data/spec/dummy/log/test.log +1 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/fixtures/qbxml_response_error.xml +6 -0
- data/spec/fixtures/qbxml_response_success.xml +621 -0
- data/spec/models/acts_as_purchasable_spec.rb +131 -0
- data/spec/models/factories_spec.rb +32 -0
- data/spec/models/qb_machine_spec.rb +554 -0
- data/spec/models/qb_request_spec.rb +327 -0
- data/spec/models/qb_ticket_spec.rb +62 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/factories.rb +97 -0
- metadata +397 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Effective::QbRequestSpecHelper
|
|
4
|
+
|
|
5
|
+
# valid attributes for qb requests
|
|
6
|
+
def valid_request_attributes
|
|
7
|
+
{
|
|
8
|
+
:state=>'Processing',
|
|
9
|
+
:qb_ticket => Effective::QbTicket.new,
|
|
10
|
+
:order => Effective::Order.new,
|
|
11
|
+
:request_type=>'OrderItemSynchronization'
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe Effective::QbRequest, "Generating Request QbXML" do
|
|
18
|
+
|
|
19
|
+
include Effective::QbRequestSpecHelper
|
|
20
|
+
|
|
21
|
+
before :each do
|
|
22
|
+
# let's generate an order that the request will need to use.
|
|
23
|
+
@order = FactoryGirl.create(:purchased_order)
|
|
24
|
+
@user = @order.user
|
|
25
|
+
|
|
26
|
+
@qb_request = Effective::QbRequest.new(valid_request_attributes)
|
|
27
|
+
@qb_request.order = @order
|
|
28
|
+
@qb_request.save!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# safety check before we get crazy
|
|
32
|
+
it "should be valid" do
|
|
33
|
+
@qb_request.should be_valid
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should generate valid qb_xml for the CustomerQuery state" do
|
|
37
|
+
@qb_request.state = 'CustomerQuery'
|
|
38
|
+
@qb_request.should be_valid
|
|
39
|
+
qb_xml = @qb_request.generate_request_xml
|
|
40
|
+
|
|
41
|
+
@doc = Nokogiri::XML(qb_xml)
|
|
42
|
+
|
|
43
|
+
@doc.xpath("//CustomerQueryRq").first["requestID"].should == (@qb_request.id.to_s)
|
|
44
|
+
|
|
45
|
+
@doc.at_xpath("//CustomerQueryRq//FullName").content.should eq(@order.billing_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should generate valid qb_xml for the CreateCustomer state" do
|
|
49
|
+
@qb_request.state = 'CreateCustomer'
|
|
50
|
+
@qb_request.should be_valid
|
|
51
|
+
qb_xml = @qb_request.generate_request_xml
|
|
52
|
+
|
|
53
|
+
@doc = Nokogiri::XML(qb_xml)
|
|
54
|
+
|
|
55
|
+
@doc.xpath("//CustomerAddRq").first["requestID"].should == (@qb_request.id.to_s)
|
|
56
|
+
|
|
57
|
+
@doc.xpath("//CustomerAddRq//CustomerAdd").present?.should == true
|
|
58
|
+
|
|
59
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//Name").content.should eq(@order.billing_name)
|
|
60
|
+
|
|
61
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//FirstName").content.present?.should eq true
|
|
62
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//LastName").content.present?.should eq true
|
|
63
|
+
|
|
64
|
+
@doc.xpath("//CustomerAddRq//CustomerAdd//BillAddress").present?.should == true
|
|
65
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//BillAddress//Addr2").content.should eq(@order.billing_address.address1)
|
|
66
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//BillAddress//City").content.should eq(@order.billing_address.city)
|
|
67
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//BillAddress//PostalCode").content.should eq(@order.billing_address.postal_code)
|
|
68
|
+
|
|
69
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//Phone").content.should eq(@user.phone)
|
|
70
|
+
@doc.at_xpath("//CustomerAddRq//CustomerAdd//Email").content.should eq(@user.email)
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should generate valid qb_xml for the OrderSync state" do
|
|
75
|
+
@qb_request.state = 'OrderSync'
|
|
76
|
+
|
|
77
|
+
@qb_request.should be_valid
|
|
78
|
+
qb_xml = @qb_request.generate_request_xml
|
|
79
|
+
|
|
80
|
+
@doc = Nokogiri::XML(qb_xml)
|
|
81
|
+
|
|
82
|
+
@doc.xpath("//SalesReceiptAddRq").first["requestID"].should == (@qb_request.id.to_s)
|
|
83
|
+
|
|
84
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd").present?.should == true
|
|
85
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//CustomerRef").present?.should == true
|
|
86
|
+
|
|
87
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//CustomerRef//FullName").content.should eq(@order.billing_name)
|
|
88
|
+
|
|
89
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//TxnDate").present?.should == true
|
|
90
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//Memo").present?.should == true
|
|
91
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//IsToBePrinted").content.should == 'false'
|
|
92
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//IsToBeEmailed").content.should == 'false'
|
|
93
|
+
|
|
94
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd").present?.should == true
|
|
95
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//ItemRef").present?.should == true
|
|
96
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//ItemRef//FullName").content.should eq(@order.order_items.first.qb_item_name)
|
|
97
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//Desc").content.should eq(@order.order_items.first.title)
|
|
98
|
+
@doc.at_xpath("//SalesReceiptAddRq//SalesReceiptAdd//Amount").content.to_f.should eq(@order.order_items.first.subtotal / 100.0)
|
|
99
|
+
|
|
100
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//SalesReceiptLineAdd").count.should == @order.order_items.length + 1
|
|
101
|
+
|
|
102
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//SalesReceiptLineAdd//Desc").count.should == @order.order_items.length + 1
|
|
103
|
+
|
|
104
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//SalesReceiptLineAdd//Desc").last.content.should eq(EffectiveQbSync.quickbooks_tax_name)
|
|
105
|
+
@doc.xpath("//SalesReceiptAddRq//SalesReceiptAdd//SalesReceiptLineAdd//Amount").last.content.to_f.should eq(@order.tax / 100.0)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "should raise an exception if there is no qb_item_name on the order_item" do
|
|
109
|
+
@qb_request.state = 'OrderSync'
|
|
110
|
+
allow(@qb_request.order.order_items.first).to receive(:qb_item_name).and_return(nil)
|
|
111
|
+
|
|
112
|
+
# This should raise an error
|
|
113
|
+
(@qb_request.generate_request_xml rescue :error).should eq :error
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should raise an exception if there is no attached order to the request" do
|
|
117
|
+
@qb_request.order = nil
|
|
118
|
+
Effective::QbRequest::PROCESSING_STATES.each do |state|
|
|
119
|
+
@qb_request.state = state
|
|
120
|
+
(@qb_request.generate_request_xml rescue :failed).should eq :failed
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "handle_response_xml should transition to the CreateCustomer state if passed a 500 status Code" do
|
|
125
|
+
@qb_request.state = 'CustomerQuery'
|
|
126
|
+
@qb_request.should be_valid
|
|
127
|
+
|
|
128
|
+
@xml = "<root><CustomerQueryRs statusCode=\"500\"></CustomerQueryRs></root>"
|
|
129
|
+
|
|
130
|
+
@qb_request.handle_response_xml(@xml)
|
|
131
|
+
|
|
132
|
+
@qb_request.state.should == 'CreateCustomer'
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "handle_response_xml should transition to the OrderSync state if passed a non-500 status Code" do
|
|
136
|
+
@qb_request.state = 'CustomerQuery'
|
|
137
|
+
@qb_request.should be_valid
|
|
138
|
+
|
|
139
|
+
@xml = "<root><CustomerQueryRs statusCode=\"499\"></CustomerQueryRs></root>"
|
|
140
|
+
|
|
141
|
+
@qb_request.handle_response_xml(@xml)
|
|
142
|
+
|
|
143
|
+
@qb_request.state.should == 'OrderSync'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should be able to find the first response with a requestID" do
|
|
147
|
+
@xml = "<root><Something request=\"0\"></Something><AThing requestID=\"500\"></AThing><AThing requestID=\"300\"></AThing></root>"
|
|
148
|
+
|
|
149
|
+
Effective::QbRequest.find_first_response_having_a_request_id(@xml).attr('requestID').should eq('500')
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
it "handle_create_customer_response_xml should return true if passed a valid status code" do
|
|
155
|
+
customer_response_xml = "<root><CustomerAddRs statusCode=\"0\"></CustomerAddRs></root>"
|
|
156
|
+
|
|
157
|
+
@qb_request.handle_create_customer_response_xml(customer_response_xml).should eq(true)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should raise an error if malformed xml is passed" do
|
|
161
|
+
@customer_response_xml = "<root><noxml></noxml></root>"
|
|
162
|
+
|
|
163
|
+
(@qb_request.handle_create_customer_response_xml(@customer_response_xml) rescue :error).should eq :error
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe Effective::QbRequest do
|
|
169
|
+
|
|
170
|
+
include Effective::QbRequestSpecHelper
|
|
171
|
+
|
|
172
|
+
before(:each) do
|
|
173
|
+
@qbxml_success = File.read(Rails.root.to_s + '/../fixtures/qbxml_response_success.xml')
|
|
174
|
+
|
|
175
|
+
# let's generate an order that the request will need to use.
|
|
176
|
+
@order = FactoryGirl.create(:purchased_order)
|
|
177
|
+
@user = @order.user
|
|
178
|
+
|
|
179
|
+
@qb_request = Effective::QbRequest.new(valid_request_attributes)
|
|
180
|
+
@qb_request.order = @order
|
|
181
|
+
@qb_request.save!
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "should be valid" do
|
|
185
|
+
@qb_request.should be_valid
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "should show an error on missing QbTicket" do
|
|
189
|
+
@qb_request.qb_ticket = nil
|
|
190
|
+
@qb_request.save
|
|
191
|
+
@qb_request.errors[:qb_ticket].present?.should eq true
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should show an error if request type is OrderItemSynchronization and there is no order" do
|
|
195
|
+
@qb_request.attributes = valid_request_attributes
|
|
196
|
+
@qb_request.request_type = 'OrderItemSynchronization'
|
|
197
|
+
@qb_request.order = nil
|
|
198
|
+
@qb_request.save
|
|
199
|
+
@qb_request.errors[:order].present?.should eq true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "should return Processing when state is empty" do
|
|
203
|
+
@qb_request.state = nil
|
|
204
|
+
@qb_request.state.should eq 'Processing'
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "should show an error on an invalid state" do
|
|
208
|
+
@attributes = valid_request_attributes
|
|
209
|
+
@attributes[:state] = 'InvalidState'
|
|
210
|
+
|
|
211
|
+
@qb_request.attributes = @attributes
|
|
212
|
+
@qb_request.save
|
|
213
|
+
@qb_request.errors[:state].present?.should eq true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it "should return a found record using response qb xml" do
|
|
217
|
+
allow(Effective::QbRequest).to receive(:find_by_id).and_return(@qb_request)
|
|
218
|
+
request = Effective::QbRequest.find_using_response_qbxml(@qbxml_success)
|
|
219
|
+
request.should eql(@qb_request)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "should return nil if it cannot find the corresponding request using the response qb xml" do
|
|
223
|
+
response_qbxml = '<QBXML><QBXMLMsgsRs></QBXMLMsgsRs></QBXML>'
|
|
224
|
+
allow(Effective::QbRequest).to receive(:find_by_id).and_return(@qb_request)
|
|
225
|
+
request = Effective::QbRequest.find_using_response_qbxml(response_qbxml)
|
|
226
|
+
request.should be_nil
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
describe Effective::QbRequest, "Working with Synchronizing Orders" do
|
|
231
|
+
|
|
232
|
+
include Effective::QbRequestSpecHelper
|
|
233
|
+
|
|
234
|
+
# we will create an order with five order items attached to it
|
|
235
|
+
before :each do
|
|
236
|
+
@qb_machine = Effective::QbMachine.new
|
|
237
|
+
|
|
238
|
+
# let's generate an order that the request will need to use.
|
|
239
|
+
@order = FactoryGirl.create(:purchased_order)
|
|
240
|
+
@user = @order.user
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it "Order should be valid" do
|
|
244
|
+
@order.should be_valid
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "test should verify that the order items are attached to the order" do
|
|
248
|
+
(Effective::Order.first.order_items.size > 1).should eq true
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "should return an empty array if there are no order items to be synchronized" do
|
|
252
|
+
Effective::Order.delete_all
|
|
253
|
+
requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
254
|
+
requests.should_not be_nil
|
|
255
|
+
requests.size.should eql(0)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "should create a request for each order that has no corresponding QbRequest attached to it" do
|
|
259
|
+
requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
260
|
+
requests.size.should eql(1)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "should not return a request for an OrderItem if its Order has a 'Failed' status" do
|
|
264
|
+
@order.purchase_state = 'declined'
|
|
265
|
+
@order.save(validate: false)
|
|
266
|
+
|
|
267
|
+
# return 0 requests because they all belong to the same Failed order
|
|
268
|
+
Effective::QbRequest.new_requests_for_unsynced_items.size.should eql(0)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "should not return a request for an Order if a request has already been created for it" do
|
|
272
|
+
requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
273
|
+
request = requests.first
|
|
274
|
+
|
|
275
|
+
# save and persist this request
|
|
276
|
+
request.qb_ticket = @qb_machine.ticket
|
|
277
|
+
request.save!
|
|
278
|
+
request.transition_state('Finished') # This was changed for effective_qb_sync
|
|
279
|
+
|
|
280
|
+
new_requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
281
|
+
new_requests.size.should eql(requests.size-1)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it "should return an array of type QbRequest" do
|
|
285
|
+
Effective::QbRequest.new_requests_for_unsynced_items.each do |req|
|
|
286
|
+
req.class.should eql(Effective::QbRequest)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it "should return each request as having a member OrderItem" do
|
|
291
|
+
Effective::QbRequest.new_requests_for_unsynced_items.each do |req|
|
|
292
|
+
req.order.should_not be_nil
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it "should return each OrderItem having the correct fields filled in" do
|
|
297
|
+
Effective::QbRequest.new_requests_for_unsynced_items.each do |req|
|
|
298
|
+
order_item = req.order.order_items.first
|
|
299
|
+
|
|
300
|
+
order_item.purchasable.kind_of?(Product).should eq true
|
|
301
|
+
order_item.qb_item_name.should eq 'Product'
|
|
302
|
+
order_item.price.should eq 1000
|
|
303
|
+
order_item.quantity.should eq 1
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'should create one qb_order_item for each order_item' do
|
|
308
|
+
Effective::QbRequest.new_requests_for_unsynced_items.each do |req|
|
|
309
|
+
req.qb_ticket = Effective::QbTicket.new()
|
|
310
|
+
req.transition_to_finished
|
|
311
|
+
|
|
312
|
+
# One QbOrderItem per OrderItem
|
|
313
|
+
Effective::QbOrderItem.count.should eq req.order.order_items.length
|
|
314
|
+
|
|
315
|
+
Effective::OrderItem.all.each { |oi| oi.qb_order_item.present?.should eq true }
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# it "should not return any OrderItem that has no QuickBooks item name" do
|
|
320
|
+
# requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
321
|
+
# Effective::OrderItem.update_all(:qb_item_name => '')
|
|
322
|
+
# new_requests = Effective::QbRequest.new_requests_for_unsynced_items
|
|
323
|
+
|
|
324
|
+
# requests.size.should_not eq new_requests.size
|
|
325
|
+
# end
|
|
326
|
+
|
|
327
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Effective::QbTicket do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@qb_ticket = Effective::QbTicket.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should be valid when initialized without any values" do
|
|
10
|
+
@qb_ticket.should be_valid
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should report a zero percent by default" do
|
|
14
|
+
@qb_ticket.percent.should eql(0)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should report an error on state" do
|
|
18
|
+
@qb_ticket.state = nil
|
|
19
|
+
@qb_ticket.save
|
|
20
|
+
@qb_ticket.errors[:state].present?.should eq true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should start out in the Ready state" do
|
|
24
|
+
@qb_ticket.state.should eql('Ready')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should load the same one when finding by username" do
|
|
28
|
+
@qb_ticket.save
|
|
29
|
+
@ticket = Effective::QbTicket.find_by_username(@qb_ticket.username)
|
|
30
|
+
@ticket.id.should eql(@qb_ticket.id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should be able to contain other QbRequests" do
|
|
34
|
+
@qb_ticket.save
|
|
35
|
+
@qb_request = Effective::QbRequest.new(order: Effective::Order.new, request_type: 'OrderItemSynchronization')
|
|
36
|
+
@qb_ticket.qb_requests.push @qb_request
|
|
37
|
+
@qb_ticket.save!
|
|
38
|
+
|
|
39
|
+
@qb_ticket.qb_requests.should_not be_empty
|
|
40
|
+
@qb_request.id.should_not be_nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should return a recorded log message when #log is called" do
|
|
44
|
+
@qb_ticket.save
|
|
45
|
+
log = @qb_ticket.log('This is a log message')
|
|
46
|
+
log.should_not be_nil
|
|
47
|
+
log.should_not be_new_record # make sure it's saved
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should increase the number of log messages when #log is called" do
|
|
51
|
+
@qb_ticket.save
|
|
52
|
+
|
|
53
|
+
@before = Effective::QbLog.count
|
|
54
|
+
@qb_ticket.log('This is a message')
|
|
55
|
+
@after = Effective::QbLog.count
|
|
56
|
+
|
|
57
|
+
@before.should eql(0)
|
|
58
|
+
@after.should eql(1)
|
|
59
|
+
@qb_ticket.qb_logs.count.should eql(1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
ENV["RAILS_ENV"] ||= 'test'
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../dummy/config/environment", __FILE__)
|
|
4
|
+
|
|
5
|
+
require 'rspec/rails'
|
|
6
|
+
require 'factory_girl_rails'
|
|
7
|
+
require 'pry'
|
|
8
|
+
|
|
9
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
|
10
|
+
# in spec/support/ and its subdirectories.
|
|
11
|
+
Dir[Rails.root.join("../../spec/support/**/*.rb")].each {|f| require f }
|
|
12
|
+
|
|
13
|
+
RSpec.configure do |config|
|
|
14
|
+
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
|
15
|
+
|
|
16
|
+
config.include Devise::TestHelpers, :type => :controller
|
|
17
|
+
|
|
18
|
+
Rails.logger.level = 4 # Output only minimal stuff to test.log
|
|
19
|
+
|
|
20
|
+
config.expect_with(:rspec) { |c| c.syntax = :should }
|
|
21
|
+
|
|
22
|
+
config.use_transactional_fixtures = true # Make this false to once again use DatabaseCleaner
|
|
23
|
+
config.order = 'random'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class ActiveRecord::Base
|
|
27
|
+
mattr_accessor :shared_connection
|
|
28
|
+
@@shared_connection = nil
|
|
29
|
+
|
|
30
|
+
def self.connection
|
|
31
|
+
@@shared_connection || retrieve_connection
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Forces all threads to share the same connection. This works on
|
|
36
|
+
# Capybara because it starts the web server in a thread.
|
|
37
|
+
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# To set up this gem for testing:
|
|
41
|
+
# spec/dummy> ln -s ../../spec spec
|
|
42
|
+
#
|
|
43
|
+
# spec/dummy> rails generate effective_qb_sync:install
|
|
44
|
+
# spec/dummy> rake db:migrate
|
|
45
|
+
# spec/dummy> rake db:test:prepare
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'factory_girl'
|
|
2
|
+
|
|
3
|
+
FactoryGirl.define do
|
|
4
|
+
factory :address, class: Effective::Address do
|
|
5
|
+
category 'billing'
|
|
6
|
+
full_name 'Peter Pan'
|
|
7
|
+
sequence(:address1) { |n| "1234#{n} Fake Street" }
|
|
8
|
+
city 'A canadian city'
|
|
9
|
+
state_code ['AB', 'BC', 'MB', 'NB', 'NL', 'NT', 'NS', 'ON', 'PE', 'QC', 'SK', 'YT', 'NU'].sample
|
|
10
|
+
country_code 'CA'
|
|
11
|
+
postal_code 'T5T2T1'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
factory :product do # This only exists in the dummy/ app
|
|
15
|
+
sequence(:title) { |n| "Product #{n}" }
|
|
16
|
+
|
|
17
|
+
price 1000
|
|
18
|
+
tax_exempt false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
factory :product_with_float_price do # This only exists in the dummy/ app
|
|
22
|
+
sequence(:title) { |n| "Product #{n}" }
|
|
23
|
+
|
|
24
|
+
price 10.00
|
|
25
|
+
tax_exempt false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
factory :user do # This only exists in the dummy/ app
|
|
29
|
+
sequence(:email) { |n| "user_#{n}@effective_qb_sync.test"}
|
|
30
|
+
|
|
31
|
+
password '12345678'
|
|
32
|
+
|
|
33
|
+
after(:build) { |user| user.skip_confirmation! if user.respond_to?(:skip_confirmation!) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
factory :cart, class: Effective::Cart do
|
|
37
|
+
association :user
|
|
38
|
+
|
|
39
|
+
before(:create) do |cart|
|
|
40
|
+
3.times { cart.cart_items << FactoryGirl.create(:cart_item, cart: cart) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
factory :cart_item, class: Effective::CartItem do
|
|
45
|
+
association :purchasable, factory: :product
|
|
46
|
+
association :cart, factory: :cart
|
|
47
|
+
|
|
48
|
+
quantity 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
factory :cart_with_items, class: Effective::Cart do
|
|
52
|
+
association :user
|
|
53
|
+
|
|
54
|
+
before(:create) do |cart|
|
|
55
|
+
3.times { cart.cart_items << FactoryGirl.create(:cart_item, cart: cart) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
factory :order, class: Effective::Order do
|
|
60
|
+
association :user
|
|
61
|
+
|
|
62
|
+
before(:create) do |order|
|
|
63
|
+
order.billing_address = FactoryGirl.build(:address, addressable: order)
|
|
64
|
+
order.shipping_address = FactoryGirl.build(:address, addressable: order)
|
|
65
|
+
|
|
66
|
+
3.times { order.order_items << FactoryGirl.create(:order_item, order: order) }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
factory :order_item, class: Effective::OrderItem do
|
|
71
|
+
association :purchasable, factory: :product
|
|
72
|
+
association :order, factory: :order
|
|
73
|
+
|
|
74
|
+
sequence(:title) { |n| "Order Item #{n}" }
|
|
75
|
+
quantity 1
|
|
76
|
+
price 1000
|
|
77
|
+
tax_exempt false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
factory :purchased_order, parent: :order do
|
|
81
|
+
payment_provider 'admin'
|
|
82
|
+
payment_card 'unknown'
|
|
83
|
+
|
|
84
|
+
after(:create) { |order| order.purchase! }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
factory :declined_order, parent: :order do
|
|
88
|
+
payment_provider 'admin'
|
|
89
|
+
|
|
90
|
+
after(:create) { |order| order.decline! }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
factory :pending_order, parent: :order do
|
|
94
|
+
purchase_state 'pending'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|