@@ -12,94 +12,119 @@ Licensed under MIT License, see LICENSE.md
1212module Ledgers
1313
1414using UUIDs, StructArrays, AbstractTrees
15- using Instruments, Assets
15+ using Instruments
1616import Instruments: instrument, symbol, amount, name, currency
17+ using Assets: USD
1718
18- export Credit, Debit, Account, Ledger, Entry, AccountId, AccountInfo
19- export id, balance, credit!, debit!, post!, instrument, symbol, amount, name, currency
19+ export Account, Ledger, Entry, AccountId, AccountCode, AccountInfo, AccountGroup
20+ export id, balance, credit!, debit!, post!, instrument, symbol, amount, code, name, currency
21+ export parent, subaccounts, subgroups
2022
2123abstract type Identifier end
24+
2225struct AccountId <: Identifier
2326 value:: UUID
2427end
28+
2529AccountId () = AccountId (uuid4 ())
2630
2731struct AccountCode
2832 value:: String
2933end
3034
31- abstract type AccountType end
32- struct Credit <: AccountType end
33- struct Debit <: AccountType end
35+ abstract type AccountType{B <: Position } end
3436
35- mutable struct Account{P <: Position }
37+ mutable struct Account{B <: Position } <: AccountType{B }
3638 id:: AccountId
37- balance:: P
39+ balance:: B
3840end
39- Account (balance:: Position ) = Account (AccountId (), balance)
4041
41- abstract type AccountNode end
42- struct AccountInfo{AT<: AccountType ,A<: Account } <: AccountNode
43- account:: A
42+ Account (balance:: Position ) = Account {typeof(balance)} (AccountId (), balance)
43+
44+ abstract type AccountNode{B <: Position } <: AccountType{B} end
45+
46+ struct AccountInfo{B <: Position } <: AccountNode{B}
47+ account:: Account{B}
4448 code:: AccountCode
4549 name:: String
50+ isdebit:: Bool
4651
47- function AccountInfo (:: Type{T} , account, code, name) where {T<: AccountType }
48- return new {T} (account, code, name)
52+ function AccountInfo {B} (account:: Account{B} , code, name, isdebit= true , parent= nothing ) where {B <: Position }
53+ acc = new {B} (account, code, name, isdebit)
54+ parent === nothing || push! (parent. subaccounts, acc)
55+ acc
4956 end
5057end
5158
52- struct AccountGroup{AT<: AccountType } <: AccountNode
59+ AccountInfo (account:: Account{B} , code, name, isdebit= true , parent= nothing ) where {B <: Position } =
60+ AccountInfo {B} (account, code, name, isdebit, parent)
61+
62+ struct AccountGroup{B <: Position } <: AccountNode{B}
63+ id:: AccountId
5364 code:: AccountCode
5465 name:: String
55- parent:: Union{Nothing,AccountGroup{<:AccountType}}
56- subaccounts:: Vector{AccountInfo}
57- subgroups:: Vector{AccountGroup}
58-
59- function AccountGroup (:: Type{T} , account, name, parent= nothing ) where {T<: AccountType }
60- new {T} (account, code, name, parent, Vector {AccountInfo} (), Vector {AccountGroup} ())
66+ isdebit:: Bool
67+ parent:: Union{Nothing,AccountGroup{B}}
68+ subaccounts:: StructArray{AccountInfo{B}}
69+ subgroups:: StructArray{AccountGroup{B}}
70+
71+ function AccountGroup {B} (
72+ id,
73+ code,
74+ name,
75+ isdebit= true ,
76+ parent= nothing ,
77+ subaccounts= StructArray (Vector {AccountInfo{B}} ()),
78+ subgroups= StructArray (Vector {AccountGroup{B}} ())) where {B <: Position }
79+ acc = new {B} (id, code, name, isdebit, parent, subaccounts, subgroups)
80+ parent === nothing || push! (parent. subgroups, acc)
81+ acc
6182 end
6283end
6384
85+ AccountGroup (
86+ :: B ,
87+ code,
88+ name,
89+ isdebit= true ,
90+ parent= nothing ,
91+ subaccounts= StructArray (Vector {AccountInfo{B}} ()),
92+ subgroups= StructArray (Vector {AccountGroup{B}} ())) where {B <: Position } =
93+ AccountGroup {B} (AccountId (), code, name, isdebit, parent, subaccounts, subgroups)
94+
6495# Identity function (to make code more generic)
65- account (acc:: Account ) = acc
96+ account (acc:: AccountType ) = acc
6697account (info:: AccountInfo ) = info. account
6798
68- account_type ( :: Union{AccountInfo{AT},AccountGroup{AT}} ) where {AT} = AT
69- code (acc:: Union{<:AccountInfo,<:AccountGroup} ) = acc. code
70- name (acc:: Union{<:AccountInfo,<:AccountGroup} ) = acc. name
99+ code (acc :: AccountNode ) = acc . code
100+ name (acc:: AccountNode ) = acc. name
101+ isdebit (acc:: AccountNode ) = acc. isdebit
71102
72103parent (group:: AccountGroup ) = group. parent
73104subaccounts (group:: AccountGroup ) = group. subaccounts
74105subgroups (group:: AccountGroup ) = group. subgroups
75106
76- id (acc:: Account ) = acc. id
77- id (info:: AccountInfo ) = id (account (info))
107+ id (acc:: AccountType ) = account (acc). id
78108
79- balance (acc:: Account ) = acc. balance
80- balance (info:: AccountInfo ) = balance (account (info))
81- balance (group:: AccountGroup ) =
82- sum (map (info-> balance (info), subaccounts (group))) +
83- sum (map (grp-> balance (grp), subgroups (group)))
109+ balance (acc) = account (acc). balance
110+ balance (group:: AccountGroup ) = isempty (subgroups (group)) ?
111+ sum (balance .(subaccounts (group))) :
112+ sum (balance .(subaccounts (group))) + sum (balance .(subgroups (group)))
84113
85- instrument (:: Account{P} ) where {P<: Position } = instrument (P)
86- instrument (info:: AccountInfo ) = instrument (account (info))
114+ instrument (:: AccountType{B} ) where {B <: Position } = instrument (B)
87115
88- symbol (:: Account{P} ) where {P<: Position } = symbol (P)
89- symbol (info:: AccountInfo ) = symbol (account (info))
116+ symbol (:: AccountType{B} ) where {B <: Position } = symbol (B)
90117
91- currency (:: Account{P} ) where {P<: Position } = currency (P)
92- currency (info:: AccountInfo ) = currency (account (info))
118+ currency (:: AccountType{B} ) where {B <: Position } = currency (B)
93119
94- amount (acc:: Account ) = amount (balance (acc))
95- amount (info:: AccountInfo ) = amount (balance (account (info)))
120+ amount (acc:: AccountType ) = amount (balance (acc))
96121
97122debit! (acc:: Account , amt:: Position ) = (acc. balance += amt)
98123credit! (acc:: Account , amt:: Position ) = (acc. balance -= amt)
99124
100- struct Entry{D <: Account ,C <: Account }
101- debit:: D
102- credit:: C
125+ struct Entry{B <: Position }
126+ debit:: AccountInfo{B}
127+ credit:: AccountInfo{B}
103128end
104129
105130function post! (entry:: Entry , amt:: Position )
@@ -108,16 +133,16 @@ function post!(entry::Entry, amt::Position)
108133 entry
109134end
110135
111- struct Ledger{P<: Position ,I <: AccountId }
112- indexes:: Dict{I ,Int}
113- accounts:: StructArray{Account{P,I }}
136+ struct Ledger{P <: Position }
137+ indexes:: Dict{AccountId ,Int}
138+ accounts:: StructArray{Account{P}}
114139
115- function Ledger (accounts:: Vector{Account{P,I }} ) where {P<: Position ,I <: AccountId }
116- indexes = Dict {I ,Int} ()
140+ function Ledger (accounts:: Vector{Account{P}} ) where {P <: Position }
141+ indexes = Dict {AccountId ,Int} ()
117142 for (index, account) in enumerate (accounts)
118143 indexes[id (account)] = index
119144 end
120- new {P,I } (indexes, StructArray (accounts))
145+ new {P} (indexes, StructArray (accounts))
121146 end
122147end
123148
@@ -132,6 +157,18 @@ function add_account!(ledger::Ledger, acc::Account)
132157 ledger. indexes[id (acc)] = length (ledger. accounts)
133158end
134159
160+ function example ()
161+ group = AccountGroup (USD (0 ), AccountCode (" 0000000" ), " Account Group" , false )
162+ assets = AccountGroup (USD (0 ), AccountCode (" 1000000" ), " Assets" , true , group)
163+ liabilities = AccountGroup (USD (0 ), AccountCode (" 2000000" ), " Liabilities" , false , group)
164+ cash = AccountInfo (Account (USD (0 )), AccountCode (" 1010000" ), " Cash" , true , assets)
165+ payable = AccountInfo (Account (USD (0 )), AccountCode (" 2010000" ), " Accounts Payable" , false , liabilities)
166+
167+ entry = Entry (cash, payable)
168+ group, assets, liabilities, cash, payable, entry
169+ end
170+
171+
135172# const chartofaccounts = Dict{String,AccountGroup{<:Cash}}()
136173
137174# function getledger(a::AccountGroup)
@@ -174,15 +211,23 @@ end
174211# end
175212
176213Base. show (io:: IO , id:: Identifier ) = print (io, id. value)
214+
177215Base. show (io:: IO , code:: AccountCode ) = print (io, code. value)
178216
179- Base. show (io:: IO , :: Type{Debit} ) = print (io, " Debit" )
180- Base. show (io:: IO , :: Type{Credit} ) = print (io, " Credit" )
217+ Base. show (io:: IO , acc:: Account ) = print (io, " $(string (id (acc))) : $(balance (acc)) " )
218+
219+ Base. show (io:: IO , acc:: AccountNode ) = print_tree (io, acc)
220+
221+ Base. show (io:: IO , entry:: Entry ) = print_tree (io, entry)
222+
223+ AbstractTrees. children (acc:: AccountGroup ) = vcat (Vector (subgroups (acc)),Vector (subaccounts (acc)))
224+
225+ AbstractTrees. printnode (io:: IO , acc:: AccountNode ) =
226+ print (io, " [$(code (acc)) ] $(name (acc)) : $(isdebit (acc) ? balance (acc) : - balance (acc)) " )
181227
182- Base. show (io:: IO , acc:: Account ) = print (io, " $(string (id (acc))) : $(balance (acc)) ." )
183- Base. show (io:: IO , info:: AccountInfo{Debit} ) = print (io, " $(code (info)) - $(name (info)) : $(balance (info)) ." )
184- Base. show (io:: IO , info:: AccountInfo{Credit} ) = print (io, " $(code (info)) - $(name (info)) : $(- balance (info)) ." )
228+ AbstractTrees. children (entry:: Entry ) = [entry. debit, entry. credit]
185229
230+ AbstractTrees. printnode (io:: IO , :: Entry ) = print (io, " Entry:" )
186231
187232# AbstractTrees.children(info::AccountInfo) =
188233# isempty(subaccounts(info)) ? Vector{AccountInfo}() : subaccounts(info)
0 commit comments