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,293 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class JournalEntryError < Error; end
|
4
|
+
|
5
|
+
class JournalEntry
|
6
|
+
|
7
|
+
include Domain
|
8
|
+
|
9
|
+
include Roles::Attributable
|
10
|
+
include Roles::Describable
|
11
|
+
include Roles::Identifiable
|
12
|
+
include Roles::Storable
|
13
|
+
include Roles::Timeable
|
14
|
+
include Roles::Versionable
|
15
|
+
|
16
|
+
include Roles::Creatable
|
17
|
+
include Roles::Readable
|
18
|
+
include Roles::Updatable
|
19
|
+
|
20
|
+
include Roles::Progressable
|
21
|
+
include Roles::Collectable
|
22
|
+
|
23
|
+
include Roles::Restable
|
24
|
+
|
25
|
+
attr_reader :org, :book, :post_delay
|
26
|
+
|
27
|
+
attr_accessor :effective_at
|
28
|
+
|
29
|
+
def self.post_keys
|
30
|
+
[ :effective_at, :description, :reference ]
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.patch_keys
|
34
|
+
[ :id, :effective_at, :description, :reference, :version ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.root_klass
|
38
|
+
JournalEntry
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.sub_klasses
|
42
|
+
[ active_klass, archived_klass, posting_klass, posted_klass ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.active_klass
|
46
|
+
ActiveJournalEntry
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.archived_klass
|
50
|
+
ArchivedJournalEntry
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.posting_klass
|
54
|
+
PostingJournalEntry
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.posted_klass
|
58
|
+
PostedJournalEntry
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.create_and_post args
|
62
|
+
begin
|
63
|
+
client = args[:client]
|
64
|
+
|
65
|
+
active_journal_entry = client.active_journal_entry args
|
66
|
+
|
67
|
+
JournalEntry.send :validate_creatability, active_journal_entry.attributes
|
68
|
+
|
69
|
+
order = 1
|
70
|
+
|
71
|
+
arg_lines = args[:lines]
|
72
|
+
|
73
|
+
arg_lines.each do |line_args|
|
74
|
+
if line_args[:description].nil?
|
75
|
+
line_args.merge! :description => active_journal_entry.description
|
76
|
+
end
|
77
|
+
|
78
|
+
if line_args[:reference].nil?
|
79
|
+
line_args.merge! :reference => active_journal_entry.reference
|
80
|
+
end
|
81
|
+
|
82
|
+
line_args.merge! :order => '%07.2f' % order
|
83
|
+
|
84
|
+
order += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
store = args[:store]
|
88
|
+
|
89
|
+
active_lines = []
|
90
|
+
|
91
|
+
arg_lines.each do |line_args|
|
92
|
+
line_args.merge! :journal_entry => active_journal_entry
|
93
|
+
|
94
|
+
line = client.active_line line_args.merge( :id => UUID.as_string )
|
95
|
+
|
96
|
+
Line.send :raise_unless_create_and_postable, line.attributes
|
97
|
+
Line.send :validate_creatability_modules, line.attributes
|
98
|
+
|
99
|
+
active_lines << line
|
100
|
+
end
|
101
|
+
|
102
|
+
store.create_and_post(
|
103
|
+
:active_journal_entry => active_journal_entry,
|
104
|
+
:active_lines => active_lines,
|
105
|
+
:posting_journal_entry => client.posting_journal_entry( { } ) )
|
106
|
+
|
107
|
+
rescue Exception => e
|
108
|
+
raise JournalEntryError, "Cannot create and post: #{e}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize args
|
113
|
+
describable args
|
114
|
+
identifiable args
|
115
|
+
storable args
|
116
|
+
versionable args
|
117
|
+
|
118
|
+
@org = args[:org]
|
119
|
+
@book = args[:book]
|
120
|
+
@effective_at = utc_or_nil args[:effective_at]
|
121
|
+
@post_delay = args[:post_delay] || 0
|
122
|
+
|
123
|
+
@reason = args[:reason] if args.has_key? :reason
|
124
|
+
end
|
125
|
+
|
126
|
+
def line args
|
127
|
+
client.lines args.merge( :journal_entry => self )
|
128
|
+
end
|
129
|
+
|
130
|
+
def lines args={ }, &block
|
131
|
+
args.merge! :action => args[:action] || :starting,
|
132
|
+
:state => args[:state] || line_state,
|
133
|
+
:order => args[:order] || '0',
|
134
|
+
:journal_entry => self
|
135
|
+
|
136
|
+
client.lines.collect args, &block
|
137
|
+
end
|
138
|
+
|
139
|
+
def balance
|
140
|
+
store.journal_entry_balance :store => store,
|
141
|
+
:client => client,
|
142
|
+
:journal_entry => self,
|
143
|
+
:state => line_state
|
144
|
+
end
|
145
|
+
|
146
|
+
def balanced?
|
147
|
+
balance.balanced?
|
148
|
+
end
|
149
|
+
|
150
|
+
class Entity < Grape::Entity
|
151
|
+
root 'journal_entries', 'journal_entry'
|
152
|
+
|
153
|
+
expose :id, :documentation => { :type => 'string', :desc => 'journal entry ID' }
|
154
|
+
|
155
|
+
expose :book, :format_with => :id,
|
156
|
+
:documentation => { :type => 'string', :desc => 'book ID' }
|
157
|
+
|
158
|
+
expose :effective_at, :format_with => :time,
|
159
|
+
:documentation => { :type => 'string', :desc => 'effective at date' }
|
160
|
+
|
161
|
+
expose :description, :documentation => { :type => 'string', :desc => 'description' }
|
162
|
+
|
163
|
+
expose :reference, :documentation => { :type => 'string', :desc => 'reference URI' }
|
164
|
+
|
165
|
+
expose :version, :documentation => { :type => 'integer', :desc => 'version' }
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def self.raise_unless_creatable args
|
171
|
+
book = args[:book]
|
172
|
+
|
173
|
+
if book.nil? or not book.kind_of? Book
|
174
|
+
raise JournalEntryError, ':book is required and must be a Book'
|
175
|
+
elsif UUID.invalid? book.id
|
176
|
+
raise JournalEntryError, ':book must have a valid :id'
|
177
|
+
end
|
178
|
+
|
179
|
+
effective_at = args[:effective_at]
|
180
|
+
|
181
|
+
if effective_at.nil? or not effective_at.kind_of? Time
|
182
|
+
raise JournalEntryError, ':effective_at is required and must be a Time'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def line_state
|
187
|
+
:active
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class ArchivedJournalEntry < JournalEntry
|
192
|
+
include Roles::Activatable
|
193
|
+
include Roles::Archivable
|
194
|
+
|
195
|
+
class Entity < JournalEntry::Entity
|
196
|
+
root 'archived_journal_entries', 'archived_journal_entry'
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.sub_klasses
|
200
|
+
[ archived_klass ]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class ActiveJournalEntry < JournalEntry
|
205
|
+
|
206
|
+
include Roles::Postable
|
207
|
+
include Roles::Activatable
|
208
|
+
include Roles::Archivable
|
209
|
+
|
210
|
+
attr_reader :reason
|
211
|
+
|
212
|
+
class Entity < JournalEntry::Entity
|
213
|
+
root 'active_journal_entries', 'active_journal_entry'
|
214
|
+
|
215
|
+
expose :reason
|
216
|
+
end
|
217
|
+
|
218
|
+
def self.sub_klasses
|
219
|
+
[ active_klass ]
|
220
|
+
end
|
221
|
+
|
222
|
+
def create_line args
|
223
|
+
args.merge! :client => client,
|
224
|
+
:journal_entry => self,
|
225
|
+
:effective_at => effective_at
|
226
|
+
|
227
|
+
Line.raise_on_duplicate_orders args
|
228
|
+
|
229
|
+
client.active_lines.validate_creatability args
|
230
|
+
|
231
|
+
active_line = client.active_lines args
|
232
|
+
|
233
|
+
store.create_line :line => active_line
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
def set_reason reason
|
239
|
+
@reason = reason
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class PostingJournalEntry < JournalEntry
|
244
|
+
include Roles::Postable
|
245
|
+
|
246
|
+
class Entity < JournalEntry::Entity
|
247
|
+
root 'posting_journal_entries', 'posting_journal_entry'
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.sub_klasses
|
251
|
+
[ posting_klass ]
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.update args
|
255
|
+
raise JournalEntryError, 'posting journal entries are not updatable'
|
256
|
+
end
|
257
|
+
|
258
|
+
def update
|
259
|
+
self.class.update( { } )
|
260
|
+
end
|
261
|
+
|
262
|
+
def balance
|
263
|
+
raise JournalEntryError, 'cannot call balance on a PostingJournalEntry'
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class PostedJournalEntry < JournalEntry
|
268
|
+
include Roles::Postable
|
269
|
+
|
270
|
+
class Entity < JournalEntry::Entity
|
271
|
+
root 'posted_journal_entries', 'posted_journal_entry'
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.sub_klasses
|
275
|
+
[ posted_klass ]
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.update args
|
279
|
+
raise JournalEntryError, 'posted journal entries are not updatable'
|
280
|
+
end
|
281
|
+
|
282
|
+
def update
|
283
|
+
self.class.update( { } )
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def line_state
|
289
|
+
:posted
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class KeyError < Error; end
|
4
|
+
|
5
|
+
class Key
|
6
|
+
|
7
|
+
include Domain
|
8
|
+
|
9
|
+
include Roles::Attributable
|
10
|
+
include Roles::Identifiable
|
11
|
+
include Roles::Storable
|
12
|
+
|
13
|
+
include Roles::Creatable
|
14
|
+
include Roles::Readable
|
15
|
+
include Roles::Collectable
|
16
|
+
|
17
|
+
include Roles::Activatable
|
18
|
+
include Roles::Archivable
|
19
|
+
|
20
|
+
include Roles::Restable
|
21
|
+
|
22
|
+
attr_reader :identity, :secret
|
23
|
+
|
24
|
+
def self.post_keys
|
25
|
+
[ :id, :identity ]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.root_klass
|
29
|
+
Key
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.sub_klasses
|
33
|
+
[ archived_klass, active_klass ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.active_klass
|
37
|
+
ActiveKey
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.archived_klass
|
41
|
+
ArchivedKey
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize args
|
45
|
+
identifiable args
|
46
|
+
storable args
|
47
|
+
|
48
|
+
@identity = args[:identity]
|
49
|
+
|
50
|
+
# TODO separate new and create
|
51
|
+
|
52
|
+
if args[:bcrypt].nil?
|
53
|
+
@secret = args[:secret] || UUID.as_string
|
54
|
+
|
55
|
+
@bcrypt = BCrypt::Password.create( @secret, :cost => 5 )
|
56
|
+
elsif args[:bcrypt]
|
57
|
+
@secret = args[:secret]
|
58
|
+
|
59
|
+
@bcrypt = BCrypt::Password.new args[:bcrypt]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def authenticates? this_secret
|
64
|
+
bcrypt == this_secret
|
65
|
+
end
|
66
|
+
|
67
|
+
class Entity < Grape::Entity
|
68
|
+
root 'keys', 'key'
|
69
|
+
|
70
|
+
expose :id, :documentation => { :type => 'string', :desc => 'key ID' }
|
71
|
+
|
72
|
+
expose :identity, :format_with => :id,
|
73
|
+
:documentation => { :type => 'string', :desc => 'identity ID' }
|
74
|
+
|
75
|
+
expose :secret, :documentation => { :type => 'string', :desc => 'key secret' }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_reader :bcrypt
|
81
|
+
|
82
|
+
def self.raise_unless_creatable args
|
83
|
+
identity = args[:identity]
|
84
|
+
|
85
|
+
if identity.nil? or not identity.kind_of? Identity
|
86
|
+
raise KeyError, ':identity is required and must be an Identity'
|
87
|
+
elsif UUID.invalid? identity.id
|
88
|
+
raise KeyError, ':identity must have a valid :id'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ArchivedKey < Key
|
94
|
+
class Entity < Key::Entity
|
95
|
+
root 'archived_keys', 'archived_key'
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.sub_klasses
|
99
|
+
[ archived_klass ]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class ActiveKey < Key
|
104
|
+
class Entity < Key::Entity
|
105
|
+
root 'active_keys', 'active_key'
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.sub_klasses
|
109
|
+
[ active_klass ]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class LineError < Error; end
|
4
|
+
|
5
|
+
class Line
|
6
|
+
|
7
|
+
include Domain::Value
|
8
|
+
include Domain
|
9
|
+
|
10
|
+
include Roles::Attributable
|
11
|
+
include Roles::Describable
|
12
|
+
include Roles::Identifiable
|
13
|
+
include Roles::Storable
|
14
|
+
include Roles::Timeable
|
15
|
+
include Roles::Versionable
|
16
|
+
|
17
|
+
include Roles::Creatable
|
18
|
+
include Roles::Readable
|
19
|
+
|
20
|
+
include Roles::Collectable
|
21
|
+
|
22
|
+
include Roles::Restable
|
23
|
+
|
24
|
+
attr_reader :journal_entry
|
25
|
+
attr_accessor :account, :value, :order
|
26
|
+
|
27
|
+
def self.root_klass
|
28
|
+
Line
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.sub_klasses
|
32
|
+
[ archived_klass, active_klass, posted_klass ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.archived_klass
|
36
|
+
ArchivedLine
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.active_klass
|
40
|
+
ActiveLine
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.posted_klass
|
44
|
+
PostedLine
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.update args
|
48
|
+
raise_on_duplicate_orders args
|
49
|
+
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.specialized_raise_unless_creatable args
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize args
|
57
|
+
describable args
|
58
|
+
identifiable args
|
59
|
+
storable args
|
60
|
+
versionable args
|
61
|
+
|
62
|
+
@journal_entry = args[:journal_entry]
|
63
|
+
@account = args[:account]
|
64
|
+
@value = args[:value]
|
65
|
+
@order = args[:order]
|
66
|
+
|
67
|
+
specialized_initialization args
|
68
|
+
end
|
69
|
+
|
70
|
+
def amount
|
71
|
+
value.amount
|
72
|
+
end
|
73
|
+
|
74
|
+
class Entity < Grape::Entity
|
75
|
+
root 'lines', 'line'
|
76
|
+
|
77
|
+
expose :id, :documentation => { :type => 'string', :desc => 'line ID' }
|
78
|
+
|
79
|
+
expose :journal_entry, :format_with => :id,
|
80
|
+
:documentation => { :type => 'string', :desc => 'journal entry ID' }
|
81
|
+
|
82
|
+
expose :account, :format_with => :id,
|
83
|
+
:documentation => { :type => 'string', :desc => 'account ID' }
|
84
|
+
|
85
|
+
expose :description, :documentation => { :type => 'string', :desc => 'description' }
|
86
|
+
|
87
|
+
expose :reference, :documentation => { :type => 'string', :desc => 'reference URI' }
|
88
|
+
|
89
|
+
expose :value, :format_with => :value,
|
90
|
+
:documentation => { :type => 'string', :desc => 'value' }
|
91
|
+
|
92
|
+
expose :order, :documentation => { :type => 'string', :desc => 'order' }
|
93
|
+
|
94
|
+
expose :version, :documentation => { :type => 'integer', :desc => 'version' }
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def self.specialized_keys
|
100
|
+
[]
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.raise_on_duplicate_orders args
|
104
|
+
client = args[:client]
|
105
|
+
|
106
|
+
journal_entry = client.journal_entry :id => args[:journal_entry].id
|
107
|
+
|
108
|
+
order = BigDecimal args[:order]
|
109
|
+
|
110
|
+
journal_entry.lines.each do |line|
|
111
|
+
if order == BigDecimal( line.order )
|
112
|
+
raise LineError, "lines must have unique orders: #{args[:orders]}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.raise_unless_creatable args
|
118
|
+
raise_on_duplicate_orders args
|
119
|
+
|
120
|
+
raise_unless_create_and_postable args
|
121
|
+
|
122
|
+
journal_entry = args[:journal_entry]
|
123
|
+
|
124
|
+
if UUID.invalid? journal_entry.id
|
125
|
+
raise LineError, ':journal_entry must have a valid :id'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.raise_unless_create_and_postable args
|
130
|
+
journal_entry = args[:journal_entry]
|
131
|
+
|
132
|
+
if journal_entry.nil? or not journal_entry.kind_of? JournalEntry
|
133
|
+
raise LineError, ':journal_entry is required and must be a JournalEntry'
|
134
|
+
end
|
135
|
+
|
136
|
+
account = args[:account]
|
137
|
+
|
138
|
+
if account.nil? or not account.kind_of? Account
|
139
|
+
raise LineError, ':account is required and must be an Account'
|
140
|
+
elsif UUID.invalid? account.id
|
141
|
+
raise LineError, ':account must have a valid :id'
|
142
|
+
end
|
143
|
+
|
144
|
+
value = args[:value]
|
145
|
+
|
146
|
+
if value.nil? or not value.kind_of? Value
|
147
|
+
raise LineError, ':value is required and must be a Value'
|
148
|
+
end
|
149
|
+
|
150
|
+
order = args[:order]
|
151
|
+
|
152
|
+
if order.nil? or
|
153
|
+
order !~ /^\d{1,4}[.]?\d{0,2}$/
|
154
|
+
raise LineError, ':order is required and in the form [###]#[.##]'
|
155
|
+
end
|
156
|
+
|
157
|
+
bd_order = BigDecimal order
|
158
|
+
|
159
|
+
if bd_order <= 0 or bd_order >= 10_000
|
160
|
+
raise LineError, ':order must be > 0 and < 10,000'
|
161
|
+
end
|
162
|
+
|
163
|
+
specialized_raise_unless_creatable args
|
164
|
+
end
|
165
|
+
|
166
|
+
def specialized_initialization args
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class ArchivedLine < Line
|
171
|
+
|
172
|
+
include Roles::Updatable
|
173
|
+
include Roles::Archivable
|
174
|
+
include Roles::Activatable
|
175
|
+
|
176
|
+
class Entity < Line::Entity
|
177
|
+
root 'archived_lines', 'archived_line'
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.sub_klasses
|
181
|
+
[ archived_klass ]
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.post_keys
|
185
|
+
[ :account, :journal_entry, :description, :reference, :value, :order ]
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.patch_keys
|
189
|
+
[ :id, :account, :description, :reference, :value, :order, :version ]
|
190
|
+
end
|
191
|
+
|
192
|
+
def posted?
|
193
|
+
false
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class ActiveLine < Line
|
198
|
+
|
199
|
+
include Roles::Updatable
|
200
|
+
include Roles::Archivable
|
201
|
+
include Roles::Activatable
|
202
|
+
|
203
|
+
class Entity < Line::Entity
|
204
|
+
root 'active_lines', 'active_line'
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.sub_klasses
|
208
|
+
[ active_klass ]
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.post_keys
|
212
|
+
[ :account, :journal_entry, :description, :reference, :value, :order ]
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.patch_keys
|
216
|
+
[ :id, :account, :description, :reference, :value, :order, :version ]
|
217
|
+
end
|
218
|
+
|
219
|
+
def posted?
|
220
|
+
false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class PostedLineError < Error; end
|
225
|
+
|
226
|
+
class PostedLine < Line
|
227
|
+
attr_reader :effective_at, :posted_at, :balance
|
228
|
+
|
229
|
+
class Entity < Line::Entity
|
230
|
+
root 'posted_lines', 'posted_line'
|
231
|
+
|
232
|
+
expose :effective_at, :format_with => :time,
|
233
|
+
:documentation => { :type => 'string', :desc => 'effective time' }
|
234
|
+
|
235
|
+
expose :posted_at, :format_with => :time,
|
236
|
+
:documentation => { :type => 'string', :desc => 'posted time' }
|
237
|
+
|
238
|
+
expose :balance, :format_with => :balance
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.sub_klasses
|
242
|
+
[ posted_klass ]
|
243
|
+
end
|
244
|
+
|
245
|
+
def posted?
|
246
|
+
true
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
attr_writer :balance
|
252
|
+
|
253
|
+
def self.specialized_raise_unless_creatable args
|
254
|
+
effective_at = args[:effective_at]
|
255
|
+
|
256
|
+
if effective_at.nil? or not effective_at.kind_of? Time
|
257
|
+
raise PostedLineError, ':effective_at is required and must be a Time'
|
258
|
+
end
|
259
|
+
|
260
|
+
if args[:posted_at].nil?
|
261
|
+
raise PostedLineError, ':posted_at is required'
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def specialized_initialization args
|
266
|
+
@effective_at = utc_or_nil args[:effective_at]
|
267
|
+
@posted_at = utc_or_nil args[:posted_at]
|
268
|
+
@balance = args[:balance]
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|