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