subledger 0.7.7
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.
- data/.gitignore +9 -0
- data/LICENSE.txt +12 -0
- data/README.md +136 -0
- data/Rakefile +5 -0
- data/bin/subledger +6 -0
- data/lib/subledger/actor.rb +32 -0
- data/lib/subledger/collection_name.rb +25 -0
- data/lib/subledger/domain/account.rb +168 -0
- data/lib/subledger/domain/balance.rb +102 -0
- data/lib/subledger/domain/book.rb +111 -0
- data/lib/subledger/domain/category.rb +157 -0
- data/lib/subledger/domain/control.rb +180 -0
- data/lib/subledger/domain/formatters.rb +31 -0
- data/lib/subledger/domain/identity.rb +159 -0
- data/lib/subledger/domain/journal_entry.rb +293 -0
- data/lib/subledger/domain/key.rb +113 -0
- data/lib/subledger/domain/line.rb +272 -0
- data/lib/subledger/domain/org.rb +110 -0
- data/lib/subledger/domain/report.rb +247 -0
- data/lib/subledger/domain/report_rendering.rb +233 -0
- data/lib/subledger/domain/roles/activatable.rb +11 -0
- data/lib/subledger/domain/roles/archivable.rb +11 -0
- data/lib/subledger/domain/roles/attributable.rb +14 -0
- data/lib/subledger/domain/roles/collectable.rb +175 -0
- data/lib/subledger/domain/roles/creatable.rb +58 -0
- data/lib/subledger/domain/roles/describable.rb +33 -0
- data/lib/subledger/domain/roles/describable_report_rendering.rb +50 -0
- data/lib/subledger/domain/roles/identifiable.rb +15 -0
- data/lib/subledger/domain/roles/postable.rb +54 -0
- data/lib/subledger/domain/roles/progressable.rb +11 -0
- data/lib/subledger/domain/roles/readable.rb +34 -0
- data/lib/subledger/domain/roles/restable.rb +69 -0
- data/lib/subledger/domain/roles/storable.rb +30 -0
- data/lib/subledger/domain/roles/timeable.rb +18 -0
- data/lib/subledger/domain/roles/updatable.rb +35 -0
- data/lib/subledger/domain/roles/versionable.rb +35 -0
- data/lib/subledger/domain/roles.rb +16 -0
- data/lib/subledger/domain/value/credit.rb +16 -0
- data/lib/subledger/domain/value/debit.rb +16 -0
- data/lib/subledger/domain/value/zero.rb +24 -0
- data/lib/subledger/domain/value.rb +111 -0
- data/lib/subledger/domain.rb +95 -0
- data/lib/subledger/exception_handler.rb +65 -0
- data/lib/subledger/interface/client.rb +295 -0
- data/lib/subledger/interface/dispatcher.rb +20 -0
- data/lib/subledger/interface.rb +2 -0
- data/lib/subledger/path.rb +106 -0
- data/lib/subledger/rest.rb +128 -0
- data/lib/subledger/server.rb +3 -0
- data/lib/subledger/store/api/errors.rb +95 -0
- data/lib/subledger/store/api/roles/activate.rb +21 -0
- data/lib/subledger/store/api/roles/archive.rb +21 -0
- data/lib/subledger/store/api/roles/balance.rb +39 -0
- data/lib/subledger/store/api/roles/categories.rb +51 -0
- data/lib/subledger/store/api/roles/collect.rb +58 -0
- data/lib/subledger/store/api/roles/create.rb +26 -0
- data/lib/subledger/store/api/roles/create_and_post.rb +35 -0
- data/lib/subledger/store/api/roles/create_identity.rb +39 -0
- data/lib/subledger/store/api/roles/create_line.rb +24 -0
- data/lib/subledger/store/api/roles/first_and_last_line.rb +31 -0
- data/lib/subledger/store/api/roles/post.rb +25 -0
- data/lib/subledger/store/api/roles/progress.rb +21 -0
- data/lib/subledger/store/api/roles/read.rb +19 -0
- data/lib/subledger/store/api/roles/reports.rb +77 -0
- data/lib/subledger/store/api/roles/update.rb +24 -0
- data/lib/subledger/store/api/store.rb +103 -0
- data/lib/subledger/store/api.rb +20 -0
- data/lib/subledger/store.rb +236 -0
- data/lib/subledger/supervisor.rb +21 -0
- data/lib/subledger/uuid.rb +52 -0
- data/lib/subledger/version.rb +4 -0
- data/lib/subledger.rb +234 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/subledger_account_spec.rb +354 -0
- data/spec/subledger_book_spec.rb +130 -0
- data/spec/subledger_category_spec.rb +203 -0
- data/spec/subledger_control_spec.rb +43 -0
- data/spec/subledger_identity_spec.rb +47 -0
- data/spec/subledger_journal_entry_spec.rb +417 -0
- data/spec/subledger_key_spec.rb +43 -0
- data/spec/subledger_org_spec.rb +68 -0
- data/spec/subledger_report_spec.rb +295 -0
- data/spec/subledger_spec.rb +101 -0
- data/subledger.gemspec +52 -0
- metadata +205 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Path
|
3
|
+
def self.for_collection args
|
4
|
+
"/#{ API_VERSION }#{ path args }"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.for_entity args
|
8
|
+
"#{ for_collection args }/#{ args[ :anchor ].id }"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.for_entity_without_api_version args
|
12
|
+
"#{ path args }/#{ args[ :anchor ].id }"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.for_collect args
|
16
|
+
action = args[:action]
|
17
|
+
state = args[:state]
|
18
|
+
limit = args[:limit]
|
19
|
+
collection_name = args[:collection_name]
|
20
|
+
|
21
|
+
key = key_for args
|
22
|
+
value = URI.escape value_for( args ).to_s
|
23
|
+
|
24
|
+
path = for_collection( args ) + '?'
|
25
|
+
|
26
|
+
query_params = []
|
27
|
+
|
28
|
+
query_params << "state=#{ state }" unless collection_name == :account_lines
|
29
|
+
query_params << "action=#{ action }"
|
30
|
+
query_params << "#{ key }=#{ value }" unless value.respond_to?( :length ) and value.length.zero?
|
31
|
+
query_params << "limit=#{ limit }" unless limit.nil?
|
32
|
+
|
33
|
+
path + query_params.join('&')
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
COLLECTION_PATHS = {
|
39
|
+
:identities => lambda { |anchor| 'identities' },
|
40
|
+
:keys => lambda { |anchor| "identities/#{anchor.identity.id}/keys" },
|
41
|
+
:controls => lambda { |anchor| "identities/#{anchor.identity.id}/controls" },
|
42
|
+
:orgs => lambda { |anchor| 'orgs' },
|
43
|
+
:books => lambda { |anchor| "orgs/#{anchor.org.id}/books" },
|
44
|
+
:accounts => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/accounts" },
|
45
|
+
:journal_entries => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/journal_entries" },
|
46
|
+
:lines => lambda { |anchor| "orgs/#{anchor.journal_entry.book.org.id}/books/#{anchor.journal_entry.book.id}/journal_entries/#{anchor.journal_entry.id}/lines" },
|
47
|
+
:account_lines => lambda { |anchor| "orgs/#{anchor.account.book.org.id}/books/#{anchor.account.book.id}/accounts/#{anchor.account.id}/lines" },
|
48
|
+
:reports => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/reports" },
|
49
|
+
:categories => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/categories" },
|
50
|
+
:report_renderings => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/report_renderings" }
|
51
|
+
}
|
52
|
+
|
53
|
+
def self.path args
|
54
|
+
anchor = args[:anchor]
|
55
|
+
|
56
|
+
collection_name = CollectionName.without_state args
|
57
|
+
|
58
|
+
path = COLLECTION_PATHS[collection_name].call anchor
|
59
|
+
|
60
|
+
"/#{path}"
|
61
|
+
end
|
62
|
+
|
63
|
+
KEY_FOR = { :books => 'description',
|
64
|
+
:accounts => 'description',
|
65
|
+
:journal_entries => 'effective_at',
|
66
|
+
:lines => 'order',
|
67
|
+
:account_lines => 'effective_at',
|
68
|
+
:reports => 'description',
|
69
|
+
:categories => 'description',
|
70
|
+
:report_renderings => 'rendered_at'
|
71
|
+
}
|
72
|
+
|
73
|
+
def self.key_for args
|
74
|
+
action = args[:action]
|
75
|
+
|
76
|
+
if [ :preceding, :following ].include? action
|
77
|
+
'id'
|
78
|
+
else
|
79
|
+
KEY_FOR[ CollectionName.without_state args ]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
VALUE_FOR = { :books => lambda { |anchor| anchor.description },
|
84
|
+
:accounts => lambda { |anchor| anchor.description },
|
85
|
+
:journal_entries => lambda { |anchor| anchor.effective_at.iso8601(3) },
|
86
|
+
:lines => lambda { |anchor| anchor.order },
|
87
|
+
:account_lines => lambda { |anchor| anchor.effective_at.iso8601(3) },
|
88
|
+
:reports => lambda { |anchor| anchor.description },
|
89
|
+
:categories => lambda { |anchor| anchor.description },
|
90
|
+
:report_renderings => lambda { |anchor| anchor.rendered_at.iso8601(3) }
|
91
|
+
}
|
92
|
+
|
93
|
+
def self.value_for args
|
94
|
+
action = args[:action]
|
95
|
+
anchor = args[:anchor]
|
96
|
+
|
97
|
+
if [ :preceding, :following ].include? action
|
98
|
+
anchor.id
|
99
|
+
else
|
100
|
+
collection_name = CollectionName.without_state args
|
101
|
+
|
102
|
+
VALUE_FOR[collection_name].call anchor
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Rest
|
3
|
+
def self.to_args rest_args, client
|
4
|
+
args = { }
|
5
|
+
|
6
|
+
rest_args.each do |key, value|
|
7
|
+
key_symbol = key.to_sym
|
8
|
+
|
9
|
+
unless value.nil?
|
10
|
+
code = LAMBDAS[key]
|
11
|
+
|
12
|
+
args[key_symbol] = if code.nil? or value.kind_of? Domain
|
13
|
+
value
|
14
|
+
else
|
15
|
+
code.call client, args, key, value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
args
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.to_balance rest_args, client
|
24
|
+
to_args( rest_args, client )[:balance]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.present_entity entity
|
28
|
+
{ entity.entity_name => entity.serializable_hash }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.present_collection entity_name, collection
|
32
|
+
entities = collection.collect { |entity| entity.serializable_hash }
|
33
|
+
|
34
|
+
{ entity_name => entities }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
ACCOUNT = lambda do |client, args, key, value|
|
40
|
+
client.account :id => value
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO eliminate if condition in BALANCE LAMBDA
|
44
|
+
|
45
|
+
BALANCE = lambda do |client, args, key, value|
|
46
|
+
if value.has_key? :debit_value
|
47
|
+
client.balance :debit_value => debit_value( client, value[:debit_value ]['amount'] ),
|
48
|
+
:credit_value => credit_value( client, value[:credit_value]['amount'] )
|
49
|
+
else
|
50
|
+
client.balance :debit_value => debit_value( client, value['debit_value' ]['amount'] ),
|
51
|
+
:credit_value => credit_value( client, value['credit_value']['amount'] )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
BOOK = lambda do |client, args, key, value|
|
56
|
+
client.book :id => value
|
57
|
+
end
|
58
|
+
|
59
|
+
CATEGORIES = lambda do |client, args, key, value|
|
60
|
+
args[:categories] = MultiJson.dump value
|
61
|
+
end
|
62
|
+
|
63
|
+
IDENTITY = lambda do |client, args, key, value|
|
64
|
+
client.identity :id => value
|
65
|
+
end
|
66
|
+
|
67
|
+
JOURNAL_ENTRY = lambda do |client, args, key, value|
|
68
|
+
client.journal_entry :id => value
|
69
|
+
end
|
70
|
+
|
71
|
+
MODE = lambda do |client, args, key, value|
|
72
|
+
value.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
NB = lambda do | client, args, key, value |
|
76
|
+
client.send value
|
77
|
+
end
|
78
|
+
|
79
|
+
ORG = lambda do |client, args, key, value|
|
80
|
+
client.org :id => value
|
81
|
+
end
|
82
|
+
|
83
|
+
TIME = lambda do |client, args, key, value|
|
84
|
+
Subledger.parse_time key, value
|
85
|
+
end
|
86
|
+
|
87
|
+
VALUE = lambda do |client, args, key, value|
|
88
|
+
client.send value['type'], value['amount']
|
89
|
+
end
|
90
|
+
|
91
|
+
VERSION = lambda do |client, args, key, value|
|
92
|
+
value.to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
REPORT = lambda do |client, args, key, value|
|
96
|
+
client.report :id => value
|
97
|
+
end
|
98
|
+
|
99
|
+
LAMBDAS = {
|
100
|
+
'account' => ACCOUNT,
|
101
|
+
'balance' => BALANCE,
|
102
|
+
'book' => BOOK,
|
103
|
+
'categories' => CATEGORIES,
|
104
|
+
'completed_at' => TIME,
|
105
|
+
'credit_value' => VALUE,
|
106
|
+
'debit_value' => VALUE,
|
107
|
+
'effective_at' => TIME,
|
108
|
+
'identity' => IDENTITY,
|
109
|
+
'journal_entry' => JOURNAL_ENTRY,
|
110
|
+
'mode' => MODE,
|
111
|
+
'normal_balance' => NB,
|
112
|
+
'org' => ORG,
|
113
|
+
'posted_at' => TIME,
|
114
|
+
'rendered_at' => TIME,
|
115
|
+
'report' => REPORT,
|
116
|
+
'value' => VALUE,
|
117
|
+
'version' => VERSION
|
118
|
+
}
|
119
|
+
|
120
|
+
def self.debit_value client, amount
|
121
|
+
BigDecimal( amount ) == 0 ? client.zero : client.debit( amount )
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.credit_value client, amount
|
125
|
+
BigDecimal( amount ) == 0 ? client.zero : client.credit( amount )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Errors
|
5
|
+
# Raised when the Subledger API returns a 400 status code
|
6
|
+
class BadRequest < Error; end
|
7
|
+
|
8
|
+
# Raised when the Subledger API returns a 401 status code
|
9
|
+
class Unauthorized < Error; end
|
10
|
+
|
11
|
+
# Raised when the Subledger API returns a 403 status code
|
12
|
+
class Forbidden < Error; end
|
13
|
+
|
14
|
+
# Raised when the Subledger API returns a 404 status code
|
15
|
+
class NotFound < Error; end
|
16
|
+
|
17
|
+
# Raised when the Subledger API returns a 406 status code
|
18
|
+
class NotAcceptable < Error; end
|
19
|
+
|
20
|
+
# Raised when the Subledger API returns a 409 status code
|
21
|
+
class Conflict < Error; end
|
22
|
+
|
23
|
+
# Raised when the Subledger API returns a 422 status code
|
24
|
+
class UnprocessableEntity < Error; end
|
25
|
+
|
26
|
+
# Raised when the Subledger API returns a 500 status code
|
27
|
+
class InternalServerError < Error; end
|
28
|
+
|
29
|
+
# Raised when the Subledger API returns a 501 status code
|
30
|
+
class NotImplemented < Error; end
|
31
|
+
|
32
|
+
# Raised when the Subledger API returns a 502 status code
|
33
|
+
class BadGateway < Error; end
|
34
|
+
|
35
|
+
# Raised when the Subledger API returns a 503 status code
|
36
|
+
class ServiceUnavailable < Error; end
|
37
|
+
|
38
|
+
# Raise when the Subledger API returns any other status code
|
39
|
+
class HttpError < Error; end
|
40
|
+
|
41
|
+
class ServerNotAvailable < Error
|
42
|
+
def initialize wrapped
|
43
|
+
@wrapped = wrapped
|
44
|
+
end
|
45
|
+
|
46
|
+
def message
|
47
|
+
"#{super} was not avaiable. Please check your configuration. (#{@wrapped.message})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ResponseError < Faraday::Response::Middleware
|
52
|
+
|
53
|
+
EXCEPTIONS = {
|
54
|
+
400 => BadRequest,
|
55
|
+
401 => Unauthorized,
|
56
|
+
403 => Forbidden,
|
57
|
+
404 => NotFound,
|
58
|
+
406 => NotAcceptable,
|
59
|
+
409 => Store::UpdateConflictError,
|
60
|
+
422 => UnprocessableEntity,
|
61
|
+
500 => InternalServerError,
|
62
|
+
501 => NotImplemented,
|
63
|
+
502 => BadGateway,
|
64
|
+
503 => ServiceUnavailable
|
65
|
+
}
|
66
|
+
|
67
|
+
def call env
|
68
|
+
@app.call(env).on_complete do
|
69
|
+
status = env[:status].to_i
|
70
|
+
|
71
|
+
if ( 400..600 ).include? status
|
72
|
+
raise exception_for( status ), response_for( env )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def exception_for status
|
78
|
+
EXCEPTIONS[status] || HttpError
|
79
|
+
end
|
80
|
+
|
81
|
+
def response_for env
|
82
|
+
{ :status => env[:status],
|
83
|
+
:url => url_for( env ),
|
84
|
+
:headers => env[:response_headers],
|
85
|
+
:body => env[:body] }
|
86
|
+
end
|
87
|
+
|
88
|
+
def url_for env
|
89
|
+
env[:url].to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Activate
|
5
|
+
def activate activatable
|
6
|
+
path = Path.for_entity( :anchor => activatable ) + '/activate'
|
7
|
+
|
8
|
+
begin
|
9
|
+
json_body = http.post do |req|
|
10
|
+
req.url path
|
11
|
+
end.body
|
12
|
+
rescue Exception => e
|
13
|
+
raise ActivateError, "Cannot activate #{activatable}: #{e}"
|
14
|
+
end
|
15
|
+
|
16
|
+
new_or_initialize json_body, activatable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Archive
|
5
|
+
def archive archivable
|
6
|
+
path = Path.for_entity( :anchor => archivable ) + '/archive'
|
7
|
+
|
8
|
+
begin
|
9
|
+
json_body = http.post do |req|
|
10
|
+
req.url path
|
11
|
+
end.body
|
12
|
+
rescue Exception => e
|
13
|
+
raise ArchiveError, "Cannot archive #{archivable}: #{e}"
|
14
|
+
end
|
15
|
+
|
16
|
+
new_or_initialize json_body, archivable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Balance
|
5
|
+
def account_balance args
|
6
|
+
client = args[:client]
|
7
|
+
account = args[:account]
|
8
|
+
at = args[:at].iso8601(3)
|
9
|
+
|
10
|
+
path = Path.for_entity( :anchor => account ) + '/balance?at=' + at
|
11
|
+
|
12
|
+
begin
|
13
|
+
response_hash = parse_json(
|
14
|
+
http.get( path ).body )
|
15
|
+
rescue Exception => e
|
16
|
+
raise BalanceError, "Cannot balance #{account}: #{e}"
|
17
|
+
end
|
18
|
+
|
19
|
+
Rest.to_balance response_hash, client
|
20
|
+
end
|
21
|
+
|
22
|
+
def journal_entry_balance args
|
23
|
+
client = args[:client]
|
24
|
+
journal_entry = args[:journal_entry]
|
25
|
+
|
26
|
+
path = Path.for_entity( :anchor => journal_entry ) + '/balance'
|
27
|
+
|
28
|
+
begin
|
29
|
+
response_hash = parse_json( http.get( path ).body )
|
30
|
+
rescue Exception => e
|
31
|
+
raise BalanceError, "Cannot balance #{journal_entry}: #{e}"
|
32
|
+
end
|
33
|
+
|
34
|
+
Rest.to_balance response_hash, client
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Categories
|
5
|
+
def attach_account_to_category args
|
6
|
+
category = args[:category]
|
7
|
+
account = args[:account]
|
8
|
+
|
9
|
+
path = Path.for_entity( :anchor => category ) + '/attach'
|
10
|
+
|
11
|
+
attach_post_hash = { 'account' => account.id }
|
12
|
+
|
13
|
+
begin
|
14
|
+
json_body = http.post do |req|
|
15
|
+
req.url path
|
16
|
+
req.body = attach_post_hash
|
17
|
+
end.body
|
18
|
+
rescue Exception => e
|
19
|
+
raise AttachError, "Cannot attach #{account}: #{e}"
|
20
|
+
end
|
21
|
+
|
22
|
+
account
|
23
|
+
end
|
24
|
+
|
25
|
+
def detach_account_from_category args
|
26
|
+
category = args[:category]
|
27
|
+
account = args[:account]
|
28
|
+
|
29
|
+
path = Path.for_entity( :anchor => category ) + '/detach'
|
30
|
+
|
31
|
+
detach_post_hash = { 'account' => account.id }
|
32
|
+
|
33
|
+
begin
|
34
|
+
json_body = http.post do |req|
|
35
|
+
req.url path
|
36
|
+
req.body = detach_post_hash
|
37
|
+
end.body
|
38
|
+
rescue Exception => e
|
39
|
+
raise DetachError, "Cannot detach #{account}: #{e}"
|
40
|
+
end
|
41
|
+
|
42
|
+
account
|
43
|
+
end
|
44
|
+
|
45
|
+
def collect_accounts_for_category category
|
46
|
+
raise CategoryError, 'category#accounts is not yet implemented'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Collect
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def before args
|
9
|
+
collect_with args
|
10
|
+
end
|
11
|
+
|
12
|
+
def ending args
|
13
|
+
collect_with args
|
14
|
+
end
|
15
|
+
|
16
|
+
def starting args
|
17
|
+
collect_with args
|
18
|
+
end
|
19
|
+
|
20
|
+
def after args
|
21
|
+
collect_with args
|
22
|
+
end
|
23
|
+
|
24
|
+
def preceding args
|
25
|
+
collect_with args
|
26
|
+
end
|
27
|
+
|
28
|
+
def following args
|
29
|
+
collect_with args
|
30
|
+
end
|
31
|
+
|
32
|
+
def collect_with args
|
33
|
+
path = Path.for_collect args
|
34
|
+
|
35
|
+
anchor = args[:anchor]
|
36
|
+
|
37
|
+
klass = anchor.class
|
38
|
+
|
39
|
+
begin
|
40
|
+
collection = parse_json http.get( path ).body
|
41
|
+
rescue Exception => e
|
42
|
+
raise CollectError, "Cannot collect: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
client = anchor.client
|
46
|
+
|
47
|
+
key = collection.keys.first
|
48
|
+
|
49
|
+
collection[key].collect do |response_hash|
|
50
|
+
args = Rest.to_args response_hash, client
|
51
|
+
|
52
|
+
client.send key, args
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Create
|
5
|
+
def create creatable
|
6
|
+
unless creatable.id.nil?
|
7
|
+
raise CreateError, 'cannot create an object with an :id'
|
8
|
+
end
|
9
|
+
|
10
|
+
path = Path.for_collection :anchor => creatable
|
11
|
+
|
12
|
+
begin
|
13
|
+
json_body = http.post do |req|
|
14
|
+
req.url path
|
15
|
+
req.body = creatable.post_hash
|
16
|
+
end.body
|
17
|
+
rescue Exception => e
|
18
|
+
raise CreateError, "Cannot create #{creatable}: #{e}"
|
19
|
+
end
|
20
|
+
|
21
|
+
new_or_initialize json_body, creatable
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateAndPost
|
5
|
+
def create_and_post args
|
6
|
+
dje = args[:active_journal_entry]
|
7
|
+
dje_lines = args[:active_lines]
|
8
|
+
pje = args[:posting_journal_entry]
|
9
|
+
|
10
|
+
path = Path.for_collection( :anchor => dje ) + '/create_and_post'
|
11
|
+
|
12
|
+
json_body = http.post do |req|
|
13
|
+
req.url path
|
14
|
+
|
15
|
+
unless dje.post_delay.zero?
|
16
|
+
req.headers['X-Subledger-Post-Slowly'] = 'true'
|
17
|
+
end
|
18
|
+
|
19
|
+
req.body = create_and_post_hash( dje, dje_lines )
|
20
|
+
end.body
|
21
|
+
|
22
|
+
new_or_initialize json_body, pje
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def create_and_post_hash dje, dje_lines
|
28
|
+
lines = dje_lines.collect { |line| line.post_hash }
|
29
|
+
|
30
|
+
dje.post_hash.merge :lines => lines
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateIdentity
|
5
|
+
ACTIVE_IDENTITY = 'active_identity'
|
6
|
+
ACTIVE_KEY = 'active_key'
|
7
|
+
|
8
|
+
def create_identity identity
|
9
|
+
client = identity.client
|
10
|
+
|
11
|
+
path = Path.for_collection :anchor => identity
|
12
|
+
|
13
|
+
begin
|
14
|
+
json_body = http.post do |req|
|
15
|
+
req.url path
|
16
|
+
req.body = identity.post_hash
|
17
|
+
end.body
|
18
|
+
rescue Exception => e
|
19
|
+
raise CreateError, "Cannot create #{identity.class}: #{e}"
|
20
|
+
end
|
21
|
+
|
22
|
+
response_hash = parse_json json_body
|
23
|
+
|
24
|
+
identity_hash = { 'active_identity' => response_hash[ACTIVE_IDENTITY] }
|
25
|
+
identity_json = MultiJson.dump identity_hash
|
26
|
+
|
27
|
+
new_identity = new_or_initialize identity_json, identity
|
28
|
+
|
29
|
+
identity.send :initialize, new_identity.attributes
|
30
|
+
|
31
|
+
key_args = Rest.to_args response_hash[ACTIVE_KEY], client
|
32
|
+
key = client.active_keys key_args
|
33
|
+
|
34
|
+
return identity, key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateLine
|
5
|
+
def create_line args
|
6
|
+
begin
|
7
|
+
line = args[:line]
|
8
|
+
|
9
|
+
path = Path.for_entity( :anchor => line.journal_entry ) + '/create_line'
|
10
|
+
|
11
|
+
json_body = http.post do |req|
|
12
|
+
req.url path
|
13
|
+
req.body = line.post_hash
|
14
|
+
end.body
|
15
|
+
rescue Exception => e
|
16
|
+
raise CreateLineError, "Cannot create #{line}: #{e}"
|
17
|
+
end
|
18
|
+
|
19
|
+
new_or_initialize json_body, line
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module FirstAndLastLine
|
5
|
+
|
6
|
+
def first_and_last_line args
|
7
|
+
anchor = args[:account]
|
8
|
+
client = anchor.client
|
9
|
+
|
10
|
+
path = Path.for_entity( :anchor => anchor ) + '/first_and_last_line'
|
11
|
+
|
12
|
+
klass = anchor.class
|
13
|
+
|
14
|
+
begin
|
15
|
+
collection = parse_json http.get( path ).body
|
16
|
+
rescue Exception => e
|
17
|
+
raise FirstAndLastLineError, "Cannot get first and last line: #{e}"
|
18
|
+
end
|
19
|
+
|
20
|
+
key = collection.keys.first
|
21
|
+
|
22
|
+
collection[key].collect do |response_hash|
|
23
|
+
args = Rest.to_args response_hash, client
|
24
|
+
|
25
|
+
client.send key, args
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|