ledger_sync 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -3
- data/lib/ledger_sync.rb +12 -6
- data/lib/ledger_sync/adaptors/ledger_serializer.rb +124 -0
- data/lib/ledger_sync/adaptors/ledger_serializer_attribute.rb +148 -0
- data/lib/ledger_sync/adaptors/ledger_serializer_attribute_set.rb +51 -0
- data/lib/ledger_sync/adaptors/ledger_serializer_type/mapping_type.rb +41 -0
- data/lib/ledger_sync/adaptors/ledger_serializer_type/references_many_type.rb +24 -0
- data/lib/ledger_sync/adaptors/ledger_serializer_type/value_type.rb +17 -0
- data/lib/ledger_sync/adaptors/operation.rb +15 -9
- data/lib/ledger_sync/adaptors/quickbooks_online/account/ledger_serializer.rb +21 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/create.rb +8 -36
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/find.rb +1 -13
- data/lib/ledger_sync/adaptors/quickbooks_online/account/operations/update.rb +4 -35
- data/lib/ledger_sync/adaptors/quickbooks_online/account/searcher.rb +3 -44
- data/lib/ledger_sync/adaptors/quickbooks_online/adaptor.rb +75 -28
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/ledger_serializer.rb +40 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/create.rb +9 -48
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/find.rb +10 -21
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/update.rb +9 -52
- data/lib/ledger_sync/adaptors/quickbooks_online/bill_line_item/ledger_serializer.rb +27 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/ledger_serializer.rb +23 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/create.rb +2 -25
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/find.rb +2 -14
- data/lib/ledger_sync/adaptors/quickbooks_online/customer/operations/update.rb +3 -30
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/ledger_serializer.rb +35 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/create.rb +2 -39
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/find.rb +2 -14
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit/operations/update.rb +4 -43
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit_line_item/ledger_serializer.rb +27 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/ledger_serializer.rb +48 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/create.rb +10 -51
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/find.rb +10 -22
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/update.rb +10 -55
- data/lib/ledger_sync/adaptors/quickbooks_online/expense_line_item/ledger_serializer.rb +27 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/ledger_serializer.rb +32 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/create.rb +4 -37
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/find.rb +4 -15
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/operations/update.rb +5 -42
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry_line_item/ledger_serializer.rb +31 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer.rb +103 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/{account/mapping.rb → ledger_serializer_type/account_sub_type.rb} +128 -148
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/account_type.rb +51 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/amount_type.rb +23 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/classification_type.rb +23 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/date_type.rb +23 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/journal_entry_line_item_type.rb +20 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/ledger_serializer_type/payment_type.rb +21 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation.rb +2 -7
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/create.rb +29 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/find.rb +31 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/full_update.rb +43 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/sparse_update.rb +29 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/ledger_serializer.rb +23 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/create.rb +1 -24
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/find.rb +1 -13
- data/lib/ledger_sync/adaptors/quickbooks_online/payment/operations/update.rb +2 -29
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/ledger_serializer.rb +33 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/create.rb +1 -29
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/find.rb +1 -13
- data/lib/ledger_sync/adaptors/quickbooks_online/transfer/operations/update.rb +4 -35
- data/lib/ledger_sync/adaptors/quickbooks_online/util/adaptor_error_parser.rb +2 -1
- data/lib/ledger_sync/adaptors/quickbooks_online/util/operation_error_parser.rb +2 -1
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/ledger_serializer.rb +25 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/create.rb +1 -23
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/find.rb +1 -13
- data/lib/ledger_sync/adaptors/quickbooks_online/vendor/operations/update.rb +2 -29
- data/lib/ledger_sync/adaptors/test/account/operations/create.rb +5 -1
- data/lib/ledger_sync/adaptors/test/account/operations/find.rb +5 -1
- data/lib/ledger_sync/adaptors/test/account/operations/invalid.rb +1 -0
- data/lib/ledger_sync/adaptors/test/account/operations/update.rb +8 -2
- data/lib/ledger_sync/adaptors/test/account/operations/valid.rb +1 -0
- data/lib/ledger_sync/adaptors/test/customer/operations/create.rb +5 -1
- data/lib/ledger_sync/adaptors/test/customer/operations/find.rb +5 -1
- data/lib/ledger_sync/adaptors/test/customer/operations/invalid.rb +1 -0
- data/lib/ledger_sync/adaptors/test/customer/operations/update.rb +1 -24
- data/lib/ledger_sync/adaptors/test/customer/operations/valid.rb +3 -6
- data/lib/ledger_sync/adaptors/test/expense/operations/create.rb +8 -3
- data/lib/ledger_sync/adaptors/test/expense/operations/find.rb +4 -14
- data/lib/ledger_sync/adaptors/test/expense/operations/update.rb +2 -32
- data/lib/ledger_sync/adaptors/test/ledger_serializer.rb +64 -0
- data/lib/ledger_sync/adaptors/test/operation/create.rb +27 -0
- data/lib/ledger_sync/adaptors/test/operation/find.rb +29 -0
- data/lib/ledger_sync/adaptors/test/operation/update.rb +34 -0
- data/lib/ledger_sync/adaptors/test/payment/operations/create.rb +5 -1
- data/lib/ledger_sync/adaptors/test/payment/operations/find.rb +5 -1
- data/lib/ledger_sync/adaptors/test/payment/operations/update.rb +1 -23
- data/lib/ledger_sync/adaptors/test/transfer/operations/create.rb +1 -22
- data/lib/ledger_sync/adaptors/test/transfer/operations/find.rb +1 -13
- data/lib/ledger_sync/adaptors/test/transfer/operations/update.rb +1 -25
- data/lib/ledger_sync/adaptors/test/vendor/operations/create.rb +5 -1
- data/lib/ledger_sync/adaptors/test/vendor/operations/find.rb +7 -1
- data/lib/ledger_sync/adaptors/test/vendor/operations/invalid.rb +1 -0
- data/lib/ledger_sync/adaptors/test/vendor/operations/update.rb +1 -23
- data/lib/ledger_sync/adaptors/test/vendor/operations/valid.rb +1 -0
- data/lib/ledger_sync/error/adaptor_errors.rb +17 -5
- data/lib/ledger_sync/error/operation_errors.rb +12 -5
- data/lib/ledger_sync/error/resource_errors.rb +5 -1
- data/lib/ledger_sync/resource.rb +33 -6
- data/lib/ledger_sync/resource_attribute.rb +4 -0
- data/lib/ledger_sync/resource_attribute/dirty_mixin.rb +16 -0
- data/lib/ledger_sync/resource_attribute/mixin.rb +19 -7
- data/lib/ledger_sync/resource_attribute/reference/many.rb +55 -1
- data/lib/ledger_sync/resource_attribute_set.rb +6 -7
- data/lib/ledger_sync/resources/account.rb +1 -1
- data/lib/ledger_sync/resources/bill.rb +1 -0
- data/lib/ledger_sync/resources/bill_line_item.rb +2 -0
- data/lib/ledger_sync/resources/expense.rb +5 -1
- data/lib/ledger_sync/resources/expense_line_item.rb +2 -0
- data/lib/ledger_sync/resources/journal_entry.rb +1 -0
- data/lib/ledger_sync/result.rb +4 -3
- data/lib/ledger_sync/type/id.rb +34 -0
- data/lib/ledger_sync/type/reference_many.rb +8 -2
- data/lib/ledger_sync/type/reference_one.rb +5 -0
- data/lib/ledger_sync/type/value_mixin.rb +12 -0
- data/lib/ledger_sync/util/hash_helpers.rb +16 -0
- data/lib/ledger_sync/version.rb +1 -1
- data/qa.rb +142 -0
- metadata +38 -5
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/mapping.rb +0 -15
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry/mapping.rb +0 -14
- data/lib/ledger_sync/error/sync_errors.rb +0 -22
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LedgerSync
|
4
|
+
module Adaptors
|
5
|
+
module LedgerSerializerType
|
6
|
+
class ReferencesManyType < ValueType
|
7
|
+
def convert_from_ledger(resource_class: nil, serializer:, value:)
|
8
|
+
resource_class ||= serializer._inferred_resource_class
|
9
|
+
return [] if value.nil?
|
10
|
+
|
11
|
+
value.map do |one_value|
|
12
|
+
serializer.new(resource: resource_class.new).deserialize(hash: one_value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert_from_local(serializer:, value:)
|
17
|
+
value.map do |one_value|
|
18
|
+
serializer.new(resource: one_value).to_ledger_hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -48,6 +48,7 @@ module LedgerSync
|
|
48
48
|
:before_operations,
|
49
49
|
:operations,
|
50
50
|
:resource,
|
51
|
+
:resource_before_perform,
|
51
52
|
:root_operation,
|
52
53
|
:result,
|
53
54
|
:response,
|
@@ -64,6 +65,7 @@ module LedgerSync
|
|
64
65
|
@before_operations = []
|
65
66
|
@operations = []
|
66
67
|
@resource = resource
|
68
|
+
@resource_before_perform = resource.dup
|
67
69
|
@result = nil
|
68
70
|
@root_operation = nil
|
69
71
|
end
|
@@ -104,12 +106,21 @@ module LedgerSync
|
|
104
106
|
@performed == true
|
105
107
|
end
|
106
108
|
|
109
|
+
def ledger_serializer
|
110
|
+
@ledger_serializer ||= begin
|
111
|
+
modules = self.class.name.split('::Operations::').first
|
112
|
+
Object.const_get("#{modules}::LedgerSerializer").new(resource: resource)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
107
116
|
# Results
|
108
117
|
|
109
|
-
def failure(error)
|
118
|
+
def failure(error, resource: nil)
|
119
|
+
@response = error
|
110
120
|
@result = LedgerSync::OperationResult.Failure(
|
111
121
|
error,
|
112
122
|
operation: self,
|
123
|
+
resource: resource,
|
113
124
|
response: error
|
114
125
|
)
|
115
126
|
end
|
@@ -118,10 +129,12 @@ module LedgerSync
|
|
118
129
|
result.failure?
|
119
130
|
end
|
120
131
|
|
121
|
-
def success(response:)
|
132
|
+
def success(resource:, response:)
|
133
|
+
@response = response
|
122
134
|
@result = LedgerSync::OperationResult.Success(
|
123
135
|
self,
|
124
136
|
operation: self,
|
137
|
+
resource: resource,
|
125
138
|
response: response
|
126
139
|
)
|
127
140
|
end
|
@@ -176,13 +189,6 @@ module LedgerSync
|
|
176
189
|
end
|
177
190
|
end
|
178
191
|
|
179
|
-
def merge_into(from:, to:)
|
180
|
-
case to
|
181
|
-
when *(Resource::PRIMITIVES | [Array]) then from
|
182
|
-
else to.merge!(from) { |_key, new_value, old_value| merge_into(from: old_value, to: new_value) } if to && from
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
192
|
private
|
187
193
|
|
188
194
|
def operate
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LedgerSync
|
4
|
+
module Adaptors
|
5
|
+
module QuickBooksOnline
|
6
|
+
module Account
|
7
|
+
class LedgerSerializer < QuickBooksOnline::LedgerSerializer
|
8
|
+
attribute ledger_attribute: 'Id', resource_attribute: :ledger_id
|
9
|
+
attribute ledger_attribute: 'Name', resource_attribute: :name
|
10
|
+
attribute ledger_attribute: 'AccountType', resource_attribute: :account_type, type: LedgerSerializerType::AccountType
|
11
|
+
attribute ledger_attribute: 'AccountSubType', resource_attribute: :account_sub_type, type: LedgerSerializerType::AccountSubType
|
12
|
+
attribute ledger_attribute: 'AcctNum', resource_attribute: :number
|
13
|
+
attribute ledger_attribute: 'CurrencyRef.value', resource_attribute: :currency
|
14
|
+
attribute ledger_attribute: 'Classification', resource_attribute: :classification, type: LedgerSerializerType::ClassificationType
|
15
|
+
attribute ledger_attribute: 'Description', resource_attribute: :description
|
16
|
+
attribute ledger_attribute: 'Active', resource_attribute: :active
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,51 +1,23 @@
|
|
1
|
-
require 'ledger_sync/adaptors/quickbooks_online/account/mapping'
|
2
|
-
|
3
1
|
module LedgerSync
|
4
2
|
module Adaptors
|
5
3
|
module QuickBooksOnline
|
6
4
|
module Account
|
7
5
|
module Operations
|
8
|
-
class Create < Operation::Create
|
6
|
+
class Create < QuickBooksOnline::Operation::Create
|
9
7
|
class Contract < LedgerSync::Adaptors::Contract
|
10
8
|
params do
|
11
|
-
required(:
|
12
|
-
required(:name).filled(:string)
|
13
|
-
required(:classification).filled(:string)
|
14
|
-
required(:account_type).filled(:string)
|
9
|
+
required(:external_id).maybe(:string)
|
15
10
|
required(:account_sub_type).filled(:string)
|
16
|
-
required(:
|
11
|
+
required(:account_type).filled(:string)
|
12
|
+
required(:active).maybe(:bool)
|
13
|
+
required(:classification).filled(:string)
|
17
14
|
required(:currency).maybe(:string)
|
18
15
|
required(:description).maybe(:string)
|
19
|
-
required(:
|
16
|
+
required(:ledger_id).value(:nil)
|
17
|
+
required(:name).filled(:string)
|
18
|
+
required(:number).maybe(:integer)
|
20
19
|
end
|
21
20
|
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def operate
|
26
|
-
response = adaptor.post(
|
27
|
-
resource: 'account',
|
28
|
-
payload: local_resource_data
|
29
|
-
)
|
30
|
-
|
31
|
-
resource.ledger_id = response.dig('Id')
|
32
|
-
success(response: response)
|
33
|
-
end
|
34
|
-
|
35
|
-
def local_resource_data
|
36
|
-
{
|
37
|
-
'Name' => resource.name,
|
38
|
-
'Classification' => Mapping::ACCOUNT_CLASSIFICATIONS[resource.classification],
|
39
|
-
'AccountType' => Mapping::ACCOUNT_TYPES[resource.account_type],
|
40
|
-
'AccountSubType' => Mapping::ACCOUNT_SUB_TYPES[resource.account_sub_type],
|
41
|
-
'AcctNum' => resource.number,
|
42
|
-
'CurrencyRef' => {
|
43
|
-
'value' => resource.currency,
|
44
|
-
},
|
45
|
-
'Description' => resource.description,
|
46
|
-
'Active' => resource.active
|
47
|
-
}
|
48
|
-
end
|
49
21
|
end
|
50
22
|
end
|
51
23
|
end
|
@@ -6,6 +6,7 @@ module LedgerSync
|
|
6
6
|
class Find < Operation::Find
|
7
7
|
class Contract < LedgerSync::Adaptors::Contract
|
8
8
|
params do
|
9
|
+
required(:external_id).maybe(:string)
|
9
10
|
required(:ledger_id).filled(:string)
|
10
11
|
required(:name).maybe(:string)
|
11
12
|
required(:classification).maybe(:string)
|
@@ -17,19 +18,6 @@ module LedgerSync
|
|
17
18
|
required(:active).maybe(:bool)
|
18
19
|
end
|
19
20
|
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def operate
|
24
|
-
return failure(nil) if resource.ledger_id.nil?
|
25
|
-
|
26
|
-
response = adaptor.find(
|
27
|
-
resource: 'account',
|
28
|
-
id: resource.ledger_id
|
29
|
-
)
|
30
|
-
|
31
|
-
success(response: response)
|
32
|
-
end
|
33
21
|
end
|
34
22
|
end
|
35
23
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
# https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/account
|
4
|
+
# Requires full update
|
5
5
|
module LedgerSync
|
6
6
|
module Adaptors
|
7
7
|
module QuickBooksOnline
|
8
8
|
module Account
|
9
9
|
module Operations
|
10
|
-
class Update < Operation::
|
10
|
+
class Update < Operation::FullUpdate
|
11
11
|
class Contract < LedgerSync::Adaptors::Contract
|
12
12
|
params do
|
13
|
+
required(:external_id).maybe(:string)
|
13
14
|
required(:ledger_id).filled(:string)
|
14
15
|
required(:name).filled(:string)
|
15
16
|
required(:classification).filled(:string)
|
@@ -21,38 +22,6 @@ module LedgerSync
|
|
21
22
|
required(:active).maybe(:bool)
|
22
23
|
end
|
23
24
|
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def operate
|
28
|
-
ledger_resource_data = adaptor.find(
|
29
|
-
resource: 'account',
|
30
|
-
id: resource.ledger_id
|
31
|
-
)
|
32
|
-
|
33
|
-
response = adaptor.post(
|
34
|
-
resource: 'account',
|
35
|
-
payload: merge_into(from: local_resource_data, to: ledger_resource_data)
|
36
|
-
)
|
37
|
-
|
38
|
-
resource.ledger_id = response.dig('Id')
|
39
|
-
success(response: response)
|
40
|
-
end
|
41
|
-
|
42
|
-
def local_resource_data
|
43
|
-
{
|
44
|
-
'Name' => resource.name,
|
45
|
-
'Classification' => Mapping::ACCOUNT_CLASSIFICATIONS[resource.classification],
|
46
|
-
'AccountType' => Mapping::ACCOUNT_TYPES[resource.account_type],
|
47
|
-
'AccountSubType' => Mapping::ACCOUNT_SUB_TYPES[resource.account_sub_type],
|
48
|
-
'AcctNum' => resource.number,
|
49
|
-
'CurrencyRef' => {
|
50
|
-
'value' => resource.currency,
|
51
|
-
},
|
52
|
-
'Description' => resource.description,
|
53
|
-
'Active' => resource.active
|
54
|
-
}
|
55
|
-
end
|
56
25
|
end
|
57
26
|
end
|
58
27
|
end
|
@@ -1,20 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module LedgerSync
|
4
4
|
module Adaptors
|
5
5
|
module QuickBooksOnline
|
6
6
|
module Account
|
7
|
-
class Searcher <
|
8
|
-
def next_searcher
|
9
|
-
paginate(limit: limit, offset: offset + limit)
|
10
|
-
end
|
11
|
-
|
12
|
-
def previous_searcher
|
13
|
-
return nil if offset <= 1
|
14
|
-
|
15
|
-
paginate(limit: limit, offset: offset - limit)
|
16
|
-
end
|
17
|
-
|
7
|
+
class Searcher < QuickBooksOnline::Searcher
|
18
8
|
def resources
|
19
9
|
@resources ||= begin
|
20
10
|
adaptor
|
@@ -25,41 +15,10 @@ module LedgerSync
|
|
25
15
|
offset: offset
|
26
16
|
)
|
27
17
|
.map do |c|
|
28
|
-
LedgerSync::Account.new(
|
29
|
-
ledger_id: c.fetch('Id'),
|
30
|
-
name: c.fetch('Name'),
|
31
|
-
classification: Mapping::ACCOUNT_CLASSIFICATIONS.invert[c.fetch('Classification')] || c.fetch('Classification'),
|
32
|
-
account_type: Mapping::ACCOUNT_TYPES.invert[c.fetch('AccountType')] || c.fetch('AccountType'),
|
33
|
-
account_sub_type: Mapping::ACCOUNT_SUB_TYPES.invert[c.fetch('AccountSubType')] || c.fetch('AccountSubType')
|
34
|
-
)
|
18
|
+
LedgerSerializer.new(resource: LedgerSync::Account.new).deserialize(hash: c)
|
35
19
|
end
|
36
20
|
end
|
37
21
|
end
|
38
|
-
|
39
|
-
def search
|
40
|
-
super
|
41
|
-
rescue OAuth2::Error => e
|
42
|
-
@response = e # TODO: Better catch/raise errors as LedgerSync::Error
|
43
|
-
failure
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
# Pagination uses notation of limit and offset
|
48
|
-
# limit: number of results per page
|
49
|
-
#
|
50
|
-
# offset: position of first result in a list.
|
51
|
-
# starts from 1, not 0
|
52
|
-
#
|
53
|
-
# More here:
|
54
|
-
# https://developer.intuit.com/app/developer/qbo/docs/develop/explore-the-quickbooks-online-api/data-queries#pagination
|
55
|
-
|
56
|
-
def limit
|
57
|
-
pagination.fetch(:limit, 10).to_i
|
58
|
-
end
|
59
|
-
|
60
|
-
def offset
|
61
|
-
pagination.fetch(:offset, 1).to_i
|
62
|
-
end
|
63
22
|
end
|
64
23
|
end
|
65
24
|
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'oauth2'
|
2
4
|
|
3
5
|
module LedgerSync
|
4
6
|
module Adaptors
|
5
7
|
module QuickBooksOnline
|
6
8
|
class Adaptor < LedgerSync::Adaptors::Adaptor
|
7
|
-
AUTHORIZE_URL = 'https://appcenter.intuit.com/connect/oauth2'
|
9
|
+
AUTHORIZE_URL = 'https://appcenter.intuit.com/connect/oauth2'
|
8
10
|
OAUTH_HEADERS = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }.freeze
|
9
|
-
ROOT_URI = 'https://quickbooks.api.intuit.com'
|
10
|
-
ROOT_SANDBOX_URI = 'https://sandbox-quickbooks.api.intuit.com'
|
11
|
-
SITE_URL = 'https://appcenter.intuit.com/connect/oauth2'
|
12
|
-
TOKEN_URL = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
|
11
|
+
ROOT_URI = 'https://quickbooks.api.intuit.com'
|
12
|
+
ROOT_SANDBOX_URI = 'https://sandbox-quickbooks.api.intuit.com'
|
13
|
+
SITE_URL = 'https://appcenter.intuit.com/connect/oauth2'
|
14
|
+
TOKEN_URL = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
|
13
15
|
|
14
16
|
attr_reader :access_token,
|
15
17
|
:client_id,
|
@@ -43,7 +45,17 @@ module LedgerSync
|
|
43
45
|
@root_uri = (test ? ROOT_SANDBOX_URI : ROOT_URI)
|
44
46
|
end
|
45
47
|
|
48
|
+
def authorization_url(redirect_uri:)
|
49
|
+
oauth_client.auth_code.authorize_url(
|
50
|
+
redirect_uri: redirect_uri,
|
51
|
+
response_type: 'code',
|
52
|
+
state: SecureRandom.hex(12),
|
53
|
+
scope: 'com.intuit.quickbooks.accounting'
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
46
57
|
def find(resource:, id:)
|
58
|
+
resource = resource.to_s
|
47
59
|
url = "#{oauth_base_uri}/#{resourcify(resource)}/#{id}"
|
48
60
|
|
49
61
|
response = request(:get, url, headers: OAUTH_HEADERS.dup)
|
@@ -51,7 +63,7 @@ module LedgerSync
|
|
51
63
|
end
|
52
64
|
|
53
65
|
def parse_operation_error(error:, operation:)
|
54
|
-
return nil unless error.
|
66
|
+
return nil unless error.is_a?(OAuth2::Error)
|
55
67
|
|
56
68
|
Util::OperationErrorParser.new(
|
57
69
|
error: error,
|
@@ -60,6 +72,7 @@ module LedgerSync
|
|
60
72
|
end
|
61
73
|
|
62
74
|
def post(resource:, payload:)
|
75
|
+
resource = resource.to_s
|
63
76
|
url = "#{oauth_base_uri}/#{resourcify(resource)}"
|
64
77
|
|
65
78
|
response = request(:post, url, headers: OAUTH_HEADERS.dup, body: payload.to_json)
|
@@ -67,6 +80,7 @@ module LedgerSync
|
|
67
80
|
end
|
68
81
|
|
69
82
|
def query(resource:, query:, limit: 10, offset: 1)
|
83
|
+
resource = resource.to_s
|
70
84
|
full_query = "SELECT * FROM #{resource.classify} WHERE #{query} STARTPOSITION #{offset} MAXRESULTS #{limit}"
|
71
85
|
url = "#{oauth_base_uri}/query?query=#{CGI.escape(full_query)}"
|
72
86
|
|
@@ -75,35 +89,54 @@ module LedgerSync
|
|
75
89
|
end
|
76
90
|
|
77
91
|
def refresh!
|
78
|
-
|
92
|
+
set_credentials_from_oauth_token(token: oauth.refresh!)
|
79
93
|
|
80
|
-
|
81
|
-
|
94
|
+
self
|
95
|
+
rescue OAuth2::Error => e
|
96
|
+
raise parse_error(error: e)
|
97
|
+
end
|
82
98
|
|
83
|
-
|
84
|
-
|
99
|
+
def set_credentials_from_oauth_code(code:, redirect_uri:)
|
100
|
+
oauth_token = oauth_client.auth_code.get_token(
|
101
|
+
code,
|
102
|
+
redirect_uri: redirect_uri
|
103
|
+
)
|
85
104
|
|
86
|
-
|
87
|
-
@refresh_token = refreshed.refresh_token
|
105
|
+
set_credentials_from_oauth_token(token: oauth_token)
|
88
106
|
|
89
|
-
|
107
|
+
oauth_token
|
108
|
+
end
|
90
109
|
|
91
|
-
|
92
|
-
|
93
|
-
|
110
|
+
def url_for(resource:)
|
111
|
+
base_url = test ? 'https://app.sandbox.qbo.intuit.com/app' : 'https://app.qbo.intuit.com/app'
|
112
|
+
resource_path = case resource
|
113
|
+
when LedgerSync::Account
|
114
|
+
"/register?accountId=#{resource.ledger_id}"
|
115
|
+
when LedgerSync::Bill
|
116
|
+
"/bill?txnId=#{resource.ledger_id}"
|
117
|
+
when LedgerSync::Customer
|
118
|
+
"/customerdetail?nameId=#{resource.ledger_id}"
|
119
|
+
when LedgerSync::Deposit
|
120
|
+
"/deposit?txnId=#{resource.ledger_id}"
|
121
|
+
when LedgerSync::Expense
|
122
|
+
"/expense?txnId=#{resource.ledger_id}"
|
123
|
+
when LedgerSync::JournalEntry
|
124
|
+
"/journal?txnId=#{resource.ledger_id}"
|
125
|
+
when LedgerSync::Payment
|
126
|
+
"/recvpayment?txnId=#{resource.ledger_id}"
|
127
|
+
when LedgerSync::Transfer
|
128
|
+
"/transfer?txnId=#{resource.ledger_id}"
|
129
|
+
when LedgerSync::Vendor
|
130
|
+
"/vendordetail?nameId=#{resource.ledger_id}"
|
131
|
+
end
|
132
|
+
|
133
|
+
base_url + resource_path
|
94
134
|
end
|
95
135
|
|
96
136
|
def self.ledger_attributes_to_save
|
97
137
|
%i[access_token expires_at refresh_token refresh_token_expires_at]
|
98
138
|
end
|
99
139
|
|
100
|
-
# def self.url_for(resource:)
|
101
|
-
# case resource
|
102
|
-
# when Customer
|
103
|
-
# # TODO: How can we get the proper URL? In dev, a random sandbox is used.
|
104
|
-
# end
|
105
|
-
# end
|
106
|
-
|
107
140
|
private
|
108
141
|
|
109
142
|
def oauth_base_uri
|
@@ -135,14 +168,15 @@ module LedgerSync
|
|
135
168
|
def request(method, *args)
|
136
169
|
oauth.send(method, *args)
|
137
170
|
rescue OAuth2::Error => e
|
138
|
-
error = parse_error(error: e)
|
139
|
-
|
171
|
+
error = parse_error(error: e) || parse_operation_error(error: e, operation: nil) || e
|
172
|
+
|
173
|
+
raise error unless error.is_a?(Error::AdaptorError::AuthenticationError)
|
140
174
|
|
141
175
|
begin
|
142
176
|
refresh!
|
143
177
|
oauth.send(method, *args)
|
144
|
-
rescue OAuth2::Error =>
|
145
|
-
raise parse_error(error:
|
178
|
+
rescue OAuth2::Error => e
|
179
|
+
raise parse_error(error: e) || e
|
146
180
|
end
|
147
181
|
end
|
148
182
|
|
@@ -156,6 +190,19 @@ module LedgerSync
|
|
156
190
|
def resourcify(str)
|
157
191
|
str.tr('_', '')
|
158
192
|
end
|
193
|
+
|
194
|
+
def set_credentials_from_oauth_token(token:)
|
195
|
+
@previous_access_tokens << access_token if access_token.present?
|
196
|
+
@access_token = token.token
|
197
|
+
|
198
|
+
@expires_at = Time&.at(token.expires_at.to_i)&.to_datetime
|
199
|
+
@refresh_token_expires_at = Time&.at(Time.now.to_i + token.params['x_refresh_token_expires_in'])&.to_datetime unless token.params['x_refresh_token_expires_in'].nil?
|
200
|
+
|
201
|
+
@previous_refresh_tokens << refresh_token if refresh_token.present?
|
202
|
+
@refresh_token = token.refresh_token
|
203
|
+
|
204
|
+
oauth(force: true) # Ensure we update the memoized @oauth
|
205
|
+
end
|
159
206
|
end
|
160
207
|
end
|
161
208
|
end
|