effective_qb_online 0.9.11 → 0.10.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 +4 -4
- data/app/controllers/effective/qb_oauth_controller.rb +58 -24
- data/app/mailers/effective/qb_online_mailer.rb +10 -0
- data/app/models/effective/qb_api.rb +40 -10
- data/app/models/effective/qb_realm.rb +23 -0
- data/app/models/effective/qb_receipt.rb +3 -3
- data/app/views/admin/qb_online/_company.html.haml +9 -2
- data/app/views/admin/qb_online/index.html.haml +4 -3
- data/app/views/effective/qb_online_mailer/reconnection_required.html.haml +7 -0
- data/db/migrate/101_create_effective_qb_online.rb +3 -0
- data/db/migrate/20261013140000_upgrade_effective_qb_online_zero_nine_twelve.rb +11 -0
- data/lib/effective_qb_online/version.rb +1 -1
- data/lib/effective_qb_online.rb +17 -1
- metadata +61 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 78a04b8a3964143fa09e0cae9d54c29619a9b55d8f28b8079bb0b9c900301199
|
|
4
|
+
data.tar.gz: 9e52ef89f8402f6648b21063804deb9b9a8818facf74f877c0bff40992a3ab15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 633be21ff66eecb59f7fbc422145f163e311ed65e9e16ed8b9b215e40fdf278c6b6449bd72cfee7172d0d2f967aac871f8ffdbae1332652cd1a14dff9152a493
|
|
7
|
+
data.tar.gz: '0843e178990e9c31a99e6d4d2b4875fdd0a202bfffcf4240c7e7fbd05e711513c1301739611bf4ad0de26a30f2b1603c92d68c97e29733cfa510e3c7d835641a'
|
|
@@ -10,10 +10,11 @@ module Effective
|
|
|
10
10
|
before_action { EffectiveResources.authorize!(self, :admin, :effective_qb_online) }
|
|
11
11
|
|
|
12
12
|
def authorize
|
|
13
|
+
session[:qb_oauth_state] = SecureRandom.hex(24)
|
|
13
14
|
grant_url = client.auth_code.authorize_url(
|
|
14
15
|
redirect_uri: redirect_uri,
|
|
15
16
|
response_type: 'code',
|
|
16
|
-
state:
|
|
17
|
+
state: session[:qb_oauth_state],
|
|
17
18
|
scope: 'com.intuit.quickbooks.accounting'
|
|
18
19
|
)
|
|
19
20
|
|
|
@@ -22,44 +23,77 @@ module Effective
|
|
|
22
23
|
|
|
23
24
|
# This matches the QuickBooks Redirect URI and we have to set it up ahead of time.
|
|
24
25
|
def callback
|
|
25
|
-
|
|
26
|
+
expected_state = session.delete(:qb_oauth_state)
|
|
27
|
+
unless expected_state.present? && params[:state].present? && ActiveSupport::SecurityUtils.secure_compare(expected_state, params[:state].to_s)
|
|
28
|
+
flash[:danger] = 'Invalid QuickBooks authorization response. Please start the connection again.'
|
|
29
|
+
return redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
30
|
+
end
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
unless params[:code].present? && params[:realmId].present?
|
|
33
|
+
flash[:danger] = 'QuickBooks authorization was not completed. Please try again.'
|
|
34
|
+
return redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
35
|
+
end
|
|
29
36
|
|
|
30
37
|
realm = Effective::QbRealm.all.first_or_initialize
|
|
38
|
+
if realm.persisted? && realm.realm_id != params[:realmId].to_s
|
|
39
|
+
flash[:danger] = 'Please reconnect the existing QuickBooks company. Selecting another company would invalidate your account settings and receipt history.'
|
|
40
|
+
return redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
41
|
+
end
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
token = client.auth_code.get_token(params[:code], redirect_uri: redirect_uri)
|
|
44
|
+
|
|
45
|
+
realm.with_lock do
|
|
46
|
+
realm.update!(
|
|
47
|
+
realm_id: params[:realmId],
|
|
48
|
+
access_token: token.token,
|
|
49
|
+
refresh_token: token.refresh_token,
|
|
50
|
+
access_token_expires_at: Time.at(token.expires_at),
|
|
51
|
+
refresh_token_expires_at: Time.current + token.params.fetch('x_refresh_token_expires_in', 100.days).to_i.seconds,
|
|
52
|
+
reconnection_required_at: nil,
|
|
53
|
+
reconnection_reason: nil
|
|
54
|
+
)
|
|
55
|
+
end
|
|
39
56
|
|
|
40
57
|
flash[:success] = 'Successfully connected with QuickBooks Online'
|
|
41
58
|
|
|
42
59
|
redirect_to(effective_qb_online.admin_quickbooks_path, allow_other_host: true)
|
|
60
|
+
rescue OAuth2::Error, Faraday::Error, ActiveRecord::RecordNotFound
|
|
61
|
+
flash[:danger] = 'QuickBooks authorization could not be completed. Please start the connection again.'
|
|
62
|
+
redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
43
63
|
end
|
|
44
64
|
|
|
45
65
|
def revoke
|
|
46
|
-
realm =
|
|
47
|
-
return unless realm
|
|
48
|
-
|
|
49
|
-
# Instantiate the token
|
|
50
|
-
token = OAuth2::AccessToken.new(client, realm.access_token, refresh_token: realm.refresh_token)
|
|
66
|
+
realm = Effective::QbRealm.first
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if response.status == 200
|
|
56
|
-
flash[:success] = 'Successfully revoked from QuickBooks Online'
|
|
57
|
-
realm.destroy!
|
|
68
|
+
if realm.blank?
|
|
69
|
+
flash[:success] = 'Already disconnected from QuickBooks Online'
|
|
58
70
|
else
|
|
59
|
-
|
|
71
|
+
revoked_refresh_token = realm.refresh_token
|
|
72
|
+
unless EffectiveQbOnline.revoke_token!(realm)
|
|
73
|
+
flash[:danger] = 'Unable to revoke QuickBooks access. Please try again or reconnect using a current company administrator.'
|
|
74
|
+
return redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
realm.with_lock do
|
|
78
|
+
if realm.refresh_token == revoked_refresh_token
|
|
79
|
+
realm.destroy!
|
|
80
|
+
flash[:success] = 'Successfully revoked from QuickBooks Online'
|
|
81
|
+
else
|
|
82
|
+
flash[:danger] = 'The QuickBooks connection changed while revoking access. Your newer connection has been preserved.'
|
|
83
|
+
end
|
|
84
|
+
end
|
|
60
85
|
end
|
|
61
86
|
|
|
62
|
-
redirect_to(effective_qb_online.admin_quickbooks_path
|
|
87
|
+
redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
88
|
+
rescue ActiveRecord::RecordNotFound
|
|
89
|
+
flash[:success] = 'Already disconnected from QuickBooks Online'
|
|
90
|
+
redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
91
|
+
rescue OAuth2::Error
|
|
92
|
+
flash[:danger] = 'QuickBooks could not revoke access. Your connection settings have been preserved. Please reconnect or try again.'
|
|
93
|
+
redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
94
|
+
rescue Faraday::Error
|
|
95
|
+
flash[:danger] = 'Unable to reach QuickBooks Online. Please try again.'
|
|
96
|
+
redirect_to(effective_qb_online.admin_quickbooks_path)
|
|
63
97
|
end
|
|
64
98
|
|
|
65
99
|
private
|
|
@@ -2,6 +2,16 @@ module Effective
|
|
|
2
2
|
class QbOnlineMailer < EffectiveQbOnline.parent_mailer_class
|
|
3
3
|
include EffectiveMailer
|
|
4
4
|
|
|
5
|
+
def reconnection_required(resource, opts = {})
|
|
6
|
+
raise('expected an Effective::QbRealm') unless resource.kind_of?(Effective::QbRealm)
|
|
7
|
+
@qb_realm = resource
|
|
8
|
+
|
|
9
|
+
to = EffectiveOrders.qb_online_sync_error_recipients.presence || EffectiveOrders.mailer_admin
|
|
10
|
+
cc = EffectiveQbOnline.sync_error_cc_recipients.presence
|
|
11
|
+
subject = subject_for(__method__, 'QuickBooks Online reconnection required', resource, opts)
|
|
12
|
+
mail(to: to, cc: cc, subject: subject, **headers_for(resource, opts))
|
|
13
|
+
end
|
|
14
|
+
|
|
5
15
|
def sync_error(resource, opts = {})
|
|
6
16
|
raise('expected an Effective::QbReceipt') unless resource.kind_of?(Effective::QbReceipt)
|
|
7
17
|
|
|
@@ -223,31 +223,61 @@ module Effective
|
|
|
223
223
|
end
|
|
224
224
|
end
|
|
225
225
|
|
|
226
|
-
def with_authenticated_request(max_attempts:
|
|
226
|
+
def with_authenticated_request(max_attempts: 2)
|
|
227
227
|
attempts = 0
|
|
228
228
|
|
|
229
229
|
begin
|
|
230
|
+
realm.reload
|
|
231
|
+
if realm.reconnection_required?
|
|
232
|
+
raise EffectiveQbOnline::ReconnectionRequired, 'QuickBooks Online requires reconnection by a current company administrator. Retry this receipt manually after reconnecting.'
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
if @access_token && (@access_token.token != realm.access_token || @access_token.refresh_token != realm.refresh_token)
|
|
236
|
+
@access_token = nil
|
|
237
|
+
end
|
|
238
|
+
|
|
230
239
|
@access_token ||= OAuth2::AccessToken.new(EffectiveQbOnline.oauth2_client, realm.access_token, refresh_token: realm.refresh_token)
|
|
231
240
|
yield(@access_token)
|
|
232
|
-
rescue OAuth2::Error, Quickbooks::AuthorizationFailure =>
|
|
233
|
-
|
|
241
|
+
rescue OAuth2::Error, Quickbooks::AuthorizationFailure => error
|
|
242
|
+
# Only authentication failures justify refreshing. Other OAuth/API errors
|
|
243
|
+
# retain their original meaning and must not cause a token rotation.
|
|
244
|
+
raise unless error.kind_of?(Quickbooks::AuthorizationFailure) || error.response.status == 401
|
|
234
245
|
|
|
235
246
|
attempts += 1
|
|
236
|
-
raise
|
|
247
|
+
raise if attempts >= max_attempts
|
|
237
248
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
refresh_access_token!
|
|
250
|
+
retry
|
|
251
|
+
end
|
|
252
|
+
end
|
|
241
253
|
|
|
254
|
+
def refresh_access_token!
|
|
255
|
+
attempted_refresh_token = @access_token.refresh_token
|
|
256
|
+
|
|
257
|
+
# Refresh tokens rotate. Reload under a lock so another worker's successful
|
|
258
|
+
# refresh is reused instead of overwritten with stale credentials.
|
|
259
|
+
realm.with_lock do
|
|
260
|
+
return if realm.reconnection_required?
|
|
261
|
+
return if realm.access_token != @access_token.token || realm.refresh_token != attempted_refresh_token
|
|
262
|
+
|
|
263
|
+
refreshed = @access_token.refresh!
|
|
242
264
|
realm.update!(
|
|
243
265
|
access_token: refreshed.token,
|
|
244
266
|
refresh_token: refreshed.refresh_token,
|
|
245
267
|
access_token_expires_at: Time.at(refreshed.expires_at),
|
|
246
|
-
refresh_token_expires_at:
|
|
268
|
+
refresh_token_expires_at: Time.current + refreshed.params.fetch('x_refresh_token_expires_in', 100.days).to_i.seconds
|
|
247
269
|
)
|
|
248
|
-
|
|
249
|
-
retry
|
|
250
270
|
end
|
|
271
|
+
rescue OAuth2::Error => error
|
|
272
|
+
raise unless error.code == 'invalid_grant'
|
|
273
|
+
|
|
274
|
+
reason = error.response.parsed['x_error_reason'].presence || 'invalid_grant'
|
|
275
|
+
realm.require_reconnection!(reason: reason, refresh_token: attempted_refresh_token)
|
|
276
|
+
return unless realm.reconnection_required?
|
|
277
|
+
|
|
278
|
+
raise EffectiveQbOnline::ReconnectionRequired, 'QuickBooks Online authorization is no longer valid. Reconnect using a current company administrator, then retry errored receipts manually.'
|
|
279
|
+
ensure
|
|
280
|
+
@access_token = nil
|
|
251
281
|
end
|
|
252
282
|
|
|
253
283
|
def scrub(value, sql: false)
|
|
@@ -18,6 +18,9 @@ module Effective
|
|
|
18
18
|
payment_method_id :string
|
|
19
19
|
order_number_as_transaction_number :boolean, default: false
|
|
20
20
|
|
|
21
|
+
reconnection_required_at :datetime
|
|
22
|
+
reconnection_reason :string
|
|
23
|
+
|
|
21
24
|
timestamps
|
|
22
25
|
end
|
|
23
26
|
|
|
@@ -34,6 +37,26 @@ module Effective
|
|
|
34
37
|
'QuickBooks Online Settings'
|
|
35
38
|
end
|
|
36
39
|
|
|
40
|
+
def reconnection_required?
|
|
41
|
+
reconnection_required_at.present?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Only the first failure for the current grant sends a notification. A failed
|
|
45
|
+
# request using old credentials must not disable a newly reconnected grant.
|
|
46
|
+
def require_reconnection!(reason:, refresh_token:)
|
|
47
|
+
changed = false
|
|
48
|
+
|
|
49
|
+
with_lock do
|
|
50
|
+
if !reconnection_required? && self.refresh_token == refresh_token
|
|
51
|
+
update!(reconnection_required_at: Time.current, reconnection_reason: reason)
|
|
52
|
+
changed = true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
EffectiveQbOnline.send_email(:reconnection_required, self) if changed
|
|
57
|
+
changed
|
|
58
|
+
end
|
|
59
|
+
|
|
37
60
|
def configured?
|
|
38
61
|
realm_id.present? && access_token.present? && deposit_to_account_id.present? && payment_method_id.present?
|
|
39
62
|
end
|
|
@@ -96,7 +96,7 @@ module Effective
|
|
|
96
96
|
rescue => e
|
|
97
97
|
result = [e.message, ("(intuit_tid: #{e.intuit_tid})" if e.try(:intuit_tid).present?)].compact.join("\n\n")
|
|
98
98
|
assign_attributes(result: result)
|
|
99
|
-
error!
|
|
99
|
+
error!(notify: !e.kind_of?(EffectiveQbOnline::ReconnectionRequired))
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
end
|
|
@@ -110,10 +110,10 @@ module Effective
|
|
|
110
110
|
completed!
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
def error!
|
|
113
|
+
def error!(notify: true)
|
|
114
114
|
errored!
|
|
115
115
|
EffectiveLogger.error(result, associated: self) if defined?(EffectiveLogger)
|
|
116
|
-
EffectiveQbOnline.send_email(:sync_error, self) if EffectiveOrders.send_qb_online_sync_error
|
|
116
|
+
EffectiveQbOnline.send_email(:sync_error, self) if notify && EffectiveOrders.send_qb_online_sync_error
|
|
117
117
|
|
|
118
118
|
false
|
|
119
119
|
end
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
- company_info = (api.company_info rescue nil)
|
|
1
|
+
- company_info = (api.company_info rescue nil) unless api.realm.reconnection_required? || local_assigns[:api_error].present?
|
|
2
|
+
|
|
3
|
+
- if api.realm.reconnection_required?
|
|
4
|
+
.alert.alert-warning
|
|
5
|
+
%p QuickBooks Online authorization is no longer valid. Reconnect using a current company administrator.
|
|
6
|
+
%p Your company settings and receipts have been preserved. After reconnecting, retry errored receipts manually.
|
|
7
|
+
= link_to 'Reconnect QuickBooks', effective_qb_online.quickbooks_oauth_path, class: 'btn btn-primary'
|
|
8
|
+
|
|
2
9
|
|
|
3
10
|
- if company_info.present?
|
|
4
11
|
%table.table.table-sm
|
|
@@ -50,7 +57,7 @@
|
|
|
50
57
|
%tbody
|
|
51
58
|
%tr
|
|
52
59
|
%td QuickBooks API Error
|
|
53
|
-
%td= api.api_error
|
|
60
|
+
%td= api.realm.reconnection_required? ? 'Reconnection required' : (local_assigns[:api_error] || api.api_error)
|
|
54
61
|
|
|
55
62
|
%tr
|
|
56
63
|
%td QuickBooks Online
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
%h1.effective-admin-heading= @page_title
|
|
2
2
|
|
|
3
3
|
- api = EffectiveQbOnline.api
|
|
4
|
+
- api_error = api.api_error if api.present? && !api.realm.reconnection_required?
|
|
4
5
|
|
|
5
6
|
- if api.blank?
|
|
6
7
|
= card('Connect to QuickBooks Online') do
|
|
@@ -11,13 +12,13 @@
|
|
|
11
12
|
= card('Test Credentials') do
|
|
12
13
|
= render('test_credentials', api: api)
|
|
13
14
|
|
|
14
|
-
- if api.realm&.configured?
|
|
15
|
+
- if api.realm&.configured? && !api.realm.reconnection_required?
|
|
15
16
|
.mb-2
|
|
16
17
|
= collapse('Show Company Settings') do
|
|
17
|
-
= render('company', api: api)
|
|
18
|
+
= render('company', api: api, api_error: api_error)
|
|
18
19
|
- else
|
|
19
20
|
= card('Company') do
|
|
20
|
-
= render('company', api: api)
|
|
21
|
+
= render('company', api: api, api_error: api_error)
|
|
21
22
|
|
|
22
23
|
= card('Sales Receipts') do
|
|
23
24
|
.text-right.mb-2
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
%p QuickBooks Online authorization is no longer valid for company #{@qb_realm.realm_id}.
|
|
2
|
+
%p Sign in as a website administrator and reconnect using a current QuickBooks company administrator.
|
|
3
|
+
%p Background QuickBooks requests are paused until reconnection. Company settings and receipts have been preserved.
|
|
4
|
+
%p After reconnecting, review errored receipts and retry them manually.
|
|
5
|
+
%p= link_to 'Reconnect QuickBooks Online', effective_qb_online.admin_quickbooks_url
|
|
6
|
+
|
|
7
|
+
%p Have a great day!
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class UpgradeEffectiveQbOnlineZeroNineTwelve < ActiveRecord::Migration[6.0]
|
|
2
|
+
def up
|
|
3
|
+
add_column :qb_realms, :reconnection_required_at, :datetime unless column_exists?(:qb_realms, :reconnection_required_at)
|
|
4
|
+
add_column :qb_realms, :reconnection_reason, :string unless column_exists?(:qb_realms, :reconnection_reason)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def down
|
|
8
|
+
remove_column :qb_realms, :reconnection_reason if column_exists?(:qb_realms, :reconnection_reason)
|
|
9
|
+
remove_column :qb_realms, :reconnection_required_at if column_exists?(:qb_realms, :reconnection_required_at)
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/effective_qb_online.rb
CHANGED
|
@@ -21,19 +21,35 @@ module EffectiveQbOnline
|
|
|
21
21
|
mailer&.constantize || Effective::QbOnlineMailer
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
class ReconnectionRequired < StandardError; end
|
|
25
|
+
|
|
24
26
|
def self.oauth2_client
|
|
25
|
-
|
|
27
|
+
OAuth2::Client.new(
|
|
26
28
|
oauth_client_id,
|
|
27
29
|
oauth_client_secret,
|
|
28
30
|
site: 'https://appcenter.intuit.com/connect/oauth2',
|
|
29
31
|
authorize_url: 'https://appcenter.intuit.com/connect/oauth2',
|
|
30
32
|
token_url: 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer',
|
|
33
|
+
revoke_url: 'https://developer.api.intuit.com/v2/oauth2/tokens/revoke',
|
|
31
34
|
connection_opts: {
|
|
32
35
|
request: { open_timeout: 10, timeout: 30 }
|
|
33
36
|
}
|
|
34
37
|
)
|
|
35
38
|
end
|
|
36
39
|
|
|
40
|
+
# Intuit requires client authentication rather than an access-token bearer header.
|
|
41
|
+
# https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
|
|
42
|
+
def self.revoke_token!(realm)
|
|
43
|
+
response = oauth2_client.revoke_token(
|
|
44
|
+
realm.refresh_token,
|
|
45
|
+
nil,
|
|
46
|
+
token_method: :post,
|
|
47
|
+
headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
response.status == 200
|
|
51
|
+
end
|
|
52
|
+
|
|
37
53
|
def self.api(realm: nil)
|
|
38
54
|
realm ||= Effective::QbRealm.first
|
|
39
55
|
return nil if realm.blank?
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: effective_qb_online
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Code and Effect
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -28,30 +27,82 @@ dependencies:
|
|
|
28
27
|
name: quickbooks-ruby
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
31
33
|
- - ">="
|
|
32
34
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
35
|
+
version: 2.0.7
|
|
34
36
|
type: :runtime
|
|
35
37
|
prerelease: false
|
|
36
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
39
|
requirements:
|
|
40
|
+
- - "~>"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '2.0'
|
|
38
43
|
- - ">="
|
|
39
44
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
45
|
+
version: 2.0.7
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: oauth2
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '2.0'
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: 2.0.25
|
|
56
|
+
type: :runtime
|
|
57
|
+
prerelease: false
|
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '2.0'
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: 2.0.25
|
|
66
|
+
- !ruby/object:Gem::Dependency
|
|
67
|
+
name: faraday
|
|
68
|
+
requirement: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - "~>"
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '2.14'
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 2.14.4
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.14'
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: 2.14.4
|
|
41
86
|
- !ruby/object:Gem::Dependency
|
|
42
87
|
name: faraday-net_http_persistent
|
|
43
88
|
requirement: !ruby/object:Gem::Requirement
|
|
44
89
|
requirements:
|
|
90
|
+
- - "~>"
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '2.3'
|
|
45
93
|
- - ">="
|
|
46
94
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
95
|
+
version: 2.3.1
|
|
48
96
|
type: :runtime
|
|
49
97
|
prerelease: false
|
|
50
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
99
|
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '2.3'
|
|
52
103
|
- - ">="
|
|
53
104
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
105
|
+
version: 2.3.1
|
|
55
106
|
- !ruby/object:Gem::Dependency
|
|
56
107
|
name: effective_bootstrap
|
|
57
108
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -258,10 +309,12 @@ files:
|
|
|
258
309
|
- app/views/admin/qb_receipts/_form.html.haml
|
|
259
310
|
- app/views/admin/qb_receipts/edit.html.haml
|
|
260
311
|
- app/views/admin/qb_receipts/new.html.haml
|
|
312
|
+
- app/views/effective/qb_online_mailer/reconnection_required.html.haml
|
|
261
313
|
- app/views/effective/qb_online_mailer/sync_error.html.haml
|
|
262
314
|
- config/effective_qb_online.rb
|
|
263
315
|
- config/routes.rb
|
|
264
316
|
- db/migrate/101_create_effective_qb_online.rb
|
|
317
|
+
- db/migrate/20261013140000_upgrade_effective_qb_online_zero_nine_twelve.rb
|
|
265
318
|
- db/seeds.rb
|
|
266
319
|
- lib/effective_qb_online.rb
|
|
267
320
|
- lib/effective_qb_online/engine.rb
|
|
@@ -273,7 +326,6 @@ homepage: https://github.com/code-and-effect/effective_qb_online
|
|
|
273
326
|
licenses:
|
|
274
327
|
- MIT
|
|
275
328
|
metadata: {}
|
|
276
|
-
post_install_message:
|
|
277
329
|
rdoc_options: []
|
|
278
330
|
require_paths:
|
|
279
331
|
- lib
|
|
@@ -288,8 +340,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
288
340
|
- !ruby/object:Gem::Version
|
|
289
341
|
version: '0'
|
|
290
342
|
requirements: []
|
|
291
|
-
rubygems_version:
|
|
292
|
-
signing_key:
|
|
343
|
+
rubygems_version: 4.0.17
|
|
293
344
|
specification_version: 4
|
|
294
345
|
summary: Create QuickBooks Online SalesReceipts for purchased effective orders
|
|
295
346
|
test_files: []
|