qbo_rails 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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +23 -0
  4. data/lib/generators/qbo_rails/install/install_generator.rb +32 -0
  5. data/lib/generators/qbo_rails/install/templates/config/qbo_rails.rb +17 -0
  6. data/lib/generators/qbo_rails/install/templates/db/migrate/create_qbo_errors.rb +12 -0
  7. data/lib/generators/qbo_rails/install/templates/models/qbo_error.rb +3 -0
  8. data/lib/generators/qbo_rails/install/usage.md +3 -0
  9. data/lib/qbo_rails.rb +106 -0
  10. data/lib/qbo_rails/error_handler.rb +4 -0
  11. data/lib/qbo_rails/version.rb +3 -0
  12. data/lib/tasks/qbo_rails_tasks.rake +4 -0
  13. data/spec/dummy/README.rdoc +28 -0
  14. data/spec/dummy/Rakefile +6 -0
  15. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  16. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  18. data/spec/dummy/app/controllers/settings_controller.rb +33 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/models/account.rb +2 -0
  21. data/spec/dummy/app/models/customer.rb +2 -0
  22. data/spec/dummy/app/models/qbo_error.rb +3 -0
  23. data/spec/dummy/app/services/qbo_rails.rb +15 -0
  24. data/spec/dummy/app/services/qbo_rails/error_handler.rb +32 -0
  25. data/spec/dummy/app/views/layouts/application.html.erb +26 -0
  26. data/spec/dummy/app/views/settings/close_and_redirect.html.erb +14 -0
  27. data/spec/dummy/app/views/settings/index.html.erb +29 -0
  28. data/spec/dummy/bin/bundle +3 -0
  29. data/spec/dummy/bin/rails +8 -0
  30. data/spec/dummy/bin/rake +8 -0
  31. data/spec/dummy/bin/setup +29 -0
  32. data/spec/dummy/config.ru +4 -0
  33. data/spec/dummy/config/application.rb +41 -0
  34. data/spec/dummy/config/boot.rb +5 -0
  35. data/spec/dummy/config/database.yml +25 -0
  36. data/spec/dummy/config/environment.rb +5 -0
  37. data/spec/dummy/config/environments/development.rb +41 -0
  38. data/spec/dummy/config/environments/production.rb +79 -0
  39. data/spec/dummy/config/environments/test.rb +42 -0
  40. data/spec/dummy/config/initializers/assets.rb +11 -0
  41. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  43. data/spec/dummy/config/initializers/dotenv.rb +3 -0
  44. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  45. data/spec/dummy/config/initializers/inflections.rb +16 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  47. data/spec/dummy/config/initializers/qbo_rails.rb +17 -0
  48. data/spec/dummy/config/initializers/session_store.rb +3 -0
  49. data/spec/dummy/config/initializers/webmock.rb +2 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +23 -0
  52. data/spec/dummy/config/routes.rb +5 -0
  53. data/spec/dummy/config/secrets.yml +22 -0
  54. data/spec/dummy/db/development.sqlite3 +0 -0
  55. data/spec/dummy/db/migrate/20150319132530_create_accounts.rb +12 -0
  56. data/spec/dummy/db/migrate/20150319132650_create_customers.rb +17 -0
  57. data/spec/dummy/db/migrate/20150328125013_add_qbo_id_to_customer.rb +5 -0
  58. data/spec/dummy/db/migrate/20150418134516_create_qbo_errors.rb +12 -0
  59. data/spec/dummy/db/production.sqlite3 +0 -0
  60. data/spec/dummy/db/schema.rb +50 -0
  61. data/spec/dummy/db/test.sqlite3 +0 -0
  62. data/spec/dummy/log/development.log +584 -0
  63. data/spec/dummy/log/production.log +0 -0
  64. data/spec/dummy/log/test.log +6502 -0
  65. data/spec/dummy/public/404.html +67 -0
  66. data/spec/dummy/public/422.html +67 -0
  67. data/spec/dummy/public/500.html +66 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/dummy/tmp/cache/222/7E0/token +2 -0
  70. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  71. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  72. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  73. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  74. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  75. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  76. data/spec/factories/accounts.rb +8 -0
  77. data/spec/factories/customers.rb +15 -0
  78. data/spec/rails_helper.rb +92 -0
  79. data/spec/requests/qbo_rails_spec.rb +118 -0
  80. data/spec/spec_helper.rb +86 -0
  81. data/spec/vcr/qbo_rails/auth/forbidden.yml +119 -0
  82. data/spec/vcr/qbo_rails/customer/create.yml +75 -0
  83. data/spec/vcr/qbo_rails/customer/create_and_then_delete.yml +225 -0
  84. data/spec/vcr/qbo_rails/customer/delete.yml +167 -0
  85. data/spec/vcr/qbo_rails/customer/error_customer_in_use.yml +257 -0
  86. data/spec/vcr/qbo_rails/customer/update.yml +138 -0
  87. metadata +358 -0
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,2 @@
1
+ o: ActiveSupport::Cache::Entry: @valueo:OAuth::RequestToken : @tokenI"5qyprdsbsHPZNViCb0vqEioyKMfQLD65wB9koW9KLjPysIphH:ET: @secretI"-BLa09K3SI9qESOgWJJCi22mIwboXQoso7MtjtRbW; T:@consumerU:OAuth::Consumer{:keyI"#qyprdhobqczMYO48EHGKyw40TRExem; T: secretI"-dPeYy2UNbMzyaRnNKpFX6bw6PIl6scQU06M07k0I; T: options{:signature_methodI"HMAC-SHA1; T:request_token_pathI" /oauth/v1/get_request_token; T:authorize_pathI"/oauth/authorize; T:access_token_pathI"/oauth/v1/get_access_token; T:
2
+ proxy0: scheme: header:http_method: post:oauth_versionI"1.0; T: siteI"https://oauth.intuit.com; T:authorize_urlI"/https://appcenter.intuit.com/Connect/Begin; T: @params{ :oauth_token_secret@I"oauth_token_secret; T@:oauth_callback_confirmedI" true; TI"oauth_callback_confirmed; T@:oauth_token@I"oauth_token; T@:@created_atf1438085447.409708:@expires_in0
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :account do
3
+ company_name 'Nelson Hockey Club'
4
+ qb_token Rails.application.secrets.qbo_sandbox_access_token
5
+ qb_secret Rails.application.secrets.qbo_sandbox_access_token_secret
6
+ qb_company_id Rails.application.secrets.qbo_sandbox_company_id
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # Read about factories at https://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :customer do
5
+ firstname "Emer"
6
+ lastname "Rackowski"
7
+ email "erack@mail.com"
8
+ phone "333-444-5555"
9
+ mobile "666-777-8888"
10
+ address_1 "1 East Haskins Road"
11
+ city "Nelson"
12
+ state "NH"
13
+ zip "03457-4555"
14
+ end
15
+ end
@@ -0,0 +1,92 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require 'spec_helper'
4
+ require File.expand_path("../dummy/config/environment", __FILE__)
5
+ require 'rspec/rails'
6
+ require 'factory_girl_rails'
7
+ require 'vcr'
8
+
9
+ VCR.configure do |config|
10
+ config.cassette_library_dir = File.expand_path("../vcr", __FILE__)
11
+ config.hook_into :webmock
12
+ s = Rails.application.secrets
13
+ config.filter_sensitive_data('<ACCESS_TOKEN>') { URI.encode_www_form_component(s.qbo_sandbox_access_token) }
14
+ config.filter_sensitive_data('<CONSUMER_KEY>') { URI.encode_www_form_component(s.qbo_app_consumer_key) }
15
+ config.filter_sensitive_data('<COMPANY_ID>') { URI.encode_www_form_component(s.qbo_sandbox_company_id) }
16
+
17
+ uri_matcher = VCR.request_matchers[:uri]
18
+ # Don't check sandbox company id or trailing URL id
19
+ # This enables multiple different sandboxes to be used
20
+ # in testing
21
+ config.register_request_matcher(:for_intuit) do |req_1, req_2|
22
+ uri1, uri2 = req_1.uri, req_2.uri
23
+ if uri1 =~ /intuit\.com/ && uri2 =~ /intuit\.com/
24
+ strip_url_company_id(req_1.uri, req_2.uri)
25
+ regexp_trail_id = %r(/\d+/?\z)
26
+ if uri1.match(regexp_trail_id)
27
+ r1_without_id = uri1.gsub(regexp_trail_id, "")
28
+ r2_without_id = uri2.gsub(regexp_trail_id, "")
29
+ uri1.match(regexp_trail_id) && uri2.match(regexp_trail_id) && r1_without_id == r2_without_id
30
+ else
31
+ uri_matcher.matches?(req_1, req_2)
32
+ end
33
+ else
34
+ uri_matcher.matches?(req_1, req_2)
35
+ end
36
+ end
37
+
38
+ def strip_url_company_id(*args)
39
+ regexp_company_id = %r((company)/\d+/?)
40
+ args.each do |ref|
41
+ ref.sub!(regexp_company_id, '')
42
+ end
43
+ end
44
+ config.default_cassette_options = { match_requests_on: [:method, :for_intuit] }
45
+ end
46
+
47
+ # Add additional requires below this line. Rails is not loaded until this point!
48
+
49
+ # Requires supporting ruby files with custom matchers and macros, etc, in
50
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
51
+ # run as spec files by default. This means that files in spec/support that end
52
+ # in _spec.rb will both be required and run as specs, causing the specs to be
53
+ # run twice. It is recommended that you do not name files matching this glob to
54
+ # end with _spec.rb. You can configure this pattern with the --pattern
55
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
56
+ #
57
+ # The following line is provided for convenience purposes. It has the downside
58
+ # of increasing the boot-up time by auto-requiring all files in the support
59
+ # directory. Alternatively, in the individual `*_spec.rb` files, manually
60
+ # require only the support files necessary.
61
+ #
62
+ # Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
63
+
64
+ # Checks for pending migrations before tests are run.
65
+ # If you are not using ActiveRecord, you can remove this line.
66
+ ActiveRecord::Migration.maintain_test_schema!
67
+
68
+ RSpec.configure do |config|
69
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
70
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
71
+ config.include FactoryGirl::Syntax::Methods
72
+
73
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
74
+ # examples within a transaction, remove the following line or assign false
75
+ # instead of true.
76
+ config.use_transactional_fixtures = true
77
+
78
+ # RSpec Rails can automatically mix in different behaviours to your tests
79
+ # based on their file location, for example enabling you to call `get` and
80
+ # `post` in specs under `spec/controllers`.
81
+ #
82
+ # You can disable this behaviour by removing the line below, and instead
83
+ # explicitly tag your specs with their type, e.g.:
84
+ #
85
+ # RSpec.describe UsersController, :type => :controller do
86
+ # # ...
87
+ # end
88
+ #
89
+ # The different available types are documented in the features, such as in
90
+ # https://relishapp.com/rspec/rspec-rails/docs
91
+ config.infer_spec_type_from_file_location!
92
+ end
@@ -0,0 +1,118 @@
1
+ require 'rails_helper'
2
+
3
+ describe 'QBO request' do
4
+
5
+ describe '.create_or_update' do
6
+ it 'for a basic create' do
7
+ account = create(:account)
8
+ record = create(:customer, firstname: 'Edward')
9
+ qbo_rails, qb_customer = map_to_qbo(account, record)
10
+ expect(record.qbo_id).to be_nil
11
+ VCR.use_cassette("qbo_rails/customer/create", record: :none) do
12
+ qbo_rails.create_or_update(record, qb_customer)
13
+ record.reload
14
+ expect(record.qbo_id).to_not be_nil
15
+ expect(qbo_rails.result.sync_token).to eq 0
16
+ end
17
+ end
18
+
19
+ it 'for a basic update' do
20
+ account = create(:account)
21
+ # Get the qbo_id from spec above
22
+ record = create(:customer, firstname: 'Edward', qbo_id: 60)
23
+ qbo_rails, qb_customer = map_to_qbo(account, record)
24
+ #Quickbooks.log = true
25
+ VCR.use_cassette("qbo_rails/customer/update", record: :none) do
26
+ result = qbo_rails.create_or_update(record, qb_customer)
27
+ expect(result.sync_token).to be > 0
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ describe '.delete' do
34
+ # At this time you can't delete (or really make inactive) builtin entities
35
+ # in the the sandbox. So it returns a 400, bad request
36
+ it 'a sandbox customer record, Paulsen Medical Supplies, id 18' do
37
+ account = create(:account)
38
+ qbo_rails = QboRails.new(account, :customer)
39
+ #Quickbooks.log = true
40
+ VCR.use_cassette("qbo_rails/customer/delete", record: :none) do
41
+ expect{ result = qbo_rails.delete(18) }.to change{ QboError.count }.by(1)
42
+ end
43
+ end
44
+
45
+ it 'deletes a newly created customer' do
46
+ account = create(:account)
47
+ record = create(:customer, firstname: 'Mathis')
48
+ qbo_rails, qb_customer = map_to_qbo(account, record)
49
+ #Quickbooks.log = true
50
+ VCR.use_cassette("qbo_rails/customer/create_and_then_delete", record: :none) do
51
+ qbo_rails.create_or_update(record, qb_customer)
52
+ qbo_rails.delete(record.reload)
53
+ expect(Nokogiri::XML(qbo_rails.result.to_xml.to_s).at('Active').content).to eq 'false'
54
+ end
55
+ end
56
+ end
57
+
58
+ describe QboRails::ErrorHandler do
59
+
60
+ # You can see this is error in spec/dummy/app/service/qbo_rails/error_handler.rb
61
+ it 'initial request is an add request for existing customer that fails. Catch error
62
+ and resend request as an update' do
63
+ account = create(:account)
64
+ record = create(:customer)
65
+ qbo_rails, qb_customer = map_to_qbo(account, record)
66
+ expect(record.qbo_id).to be_nil
67
+ #Quickbooks.log = true
68
+ VCR.use_cassette("qbo_rails/customer/error_customer_in_use", record: :none) do
69
+ expect { qbo_rails.create_or_update(record, qb_customer) }.to change{ QboError.count }.by(0)
70
+ record.reload
71
+ expect(record.qbo_id).to_not be_nil
72
+ expect(qbo_rails.result.id).to eq record.qbo_id.to_s
73
+ expect(qbo_rails.error).to be_nil
74
+ end
75
+ end
76
+
77
+ it 'does not match a custom error handler and records error' do
78
+ account = create(:account)
79
+ record = create(:customer)
80
+ qbo_rails, qb_customer = map_to_qbo(account, record)
81
+ qb_customer.display_name = 'Emer:Rack'
82
+ #Quickbooks.log = true
83
+ VCR.use_cassette("qbo_rails/customer/error_customer_bad_display_name", record: :none) do
84
+ expect{ qbo_rails.create_or_update(record, qb_customer) }.to change{ QboError.count }.by(1)
85
+ expect(qbo_rails.error).to_not be_nil
86
+ end
87
+ end
88
+
89
+ # You can see this is error in spec/dummy/app/service/qbo_rails/error_handler.rb
90
+ it 'runs a custom error handler and verifies that @only_run_once prevents endless looping' do
91
+ account = create(:account)
92
+ account.qb_token = nil
93
+ account.qb_secret = nil
94
+ record = create(:customer)
95
+ qbo_rails, qb_customer = map_to_qbo(account, record)
96
+ expect(qbo_rails.only_run_once).to be nil
97
+ #Quickbooks.log = true
98
+ VCR.use_cassette("qbo_rails/auth/forbidden", record: :none) do
99
+ qbo_rails.create_or_update(record, qb_customer)
100
+ expect(qbo_rails.only_run_once).to be true
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ def map_to_qbo(account, record)
107
+ qbo_rails = QboRails.new(account, :customer)
108
+ qb_customer = qbo_rails.base.qr_model(:customer)
109
+ qb_customer.display_name = "#{record.firstname} #{record.lastname}"
110
+ address = qbo_rails.base.qr_model :physical_address
111
+ address.line1 = record.address_1
112
+ address.city = record.city
113
+ address.country_sub_division_code = record.state
114
+ address.postal_code = record.zip
115
+ qb_customer.billing_address = address
116
+ [qbo_rails, qb_customer]
117
+ end
118
+ end