From 5fd694aaf2d9d1986366a40d15ec3c63ae2f5b19 Mon Sep 17 00:00:00 2001 From: Eric Forgy Date: Fri, 22 Feb 2019 02:46:27 +0800 Subject: [PATCH] Initial --- Manifest.toml | 81 ++++++++++++++++++++++------------------- Project.toml | 13 +++++-- README.md | 1 + src/GeneralLedgers.jl | 83 ++++--------------------------------------- src/account.jl | 79 ++++++++++++++++++++++++++++++++++++++++ src/display.jl | 29 +++++++++++++++ src/entries.jl | 31 ++++++++++++++++ src/example.jl | 10 ++++++ src/show.jl | 24 ------------- 9 files changed, 210 insertions(+), 141 deletions(-) create mode 100644 README.md create mode 100644 src/account.jl create mode 100644 src/display.jl create mode 100644 src/entries.jl create mode 100644 src/example.jl delete mode 100644 src/show.jl diff --git a/Manifest.toml b/Manifest.toml index fcd7076..83ca304 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1,20 +1,18 @@ # This file is machine-generated - editing it directly is not advised +[[AbstractTrees]] +deps = ["Markdown", "Test"] +git-tree-sha1 = "6621d9645702c1c4e6970cc6a3eae440c768000b" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.2.1" + [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -[[BenchmarkTools]] -deps = ["JSON", "Printf", "Statistics", "Test"] -git-tree-sha1 = "5d1dd8577643ba9014574cd40d9c028cd5e4b85a" -uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "0.4.2" - -[[Countries]] -deps = ["DelimitedFiles", "ISOCurrencies"] -git-tree-sha1 = "5a8fd5dd76be1430bb074a29ad2b99d4aa3d3309" -repo-rev = "master" -repo-url = "https://github.com/JuliaFinance/Countries.jl.git" -uuid = "d5396ca0-2cf9-11e9-2c96-2532c40e11ac" +[[Currencies]] +deps = ["DelimitedFiles"] +git-tree-sha1 = "98344a4fcd55f9cd99cc966da276be2fd44e8b06" +uuid = "0fd90b74-7c1f-579e-9252-02cd883047b9" version = "0.1.0" [[Dates]] @@ -29,29 +27,27 @@ uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -[[ISOCurrencies]] -git-tree-sha1 = "204b1d228b42b033dca526d607a91fef7f3bafa6" -repo-rev = "master" -repo-url = "https://github.com/JuliaFinance/ISOCurrencies.jl.git" -uuid = "5683cba2-2ced-11e9-0d3f-33b1fe4e9d91" +[[Entities]] +deps = ["Jurisdictions", "Reexport"] +git-tree-sha1 = "45c65c262564c695bc87085016626b6cc0649b07" +uuid = "d68c5960-3734-11e9-0c07-a30dcbe4dda4" +version = "0.1.0" + +[[FinancialInstruments]] +deps = ["Dates", "Entities", "Reexport"] +git-tree-sha1 = "2a09c7d97463415e9d12e72a2601d9b4a278c488" +uuid = "fc5b99d0-3725-11e9-2623-bf561589b708" version = "0.1.0" [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[JSON]] -deps = ["Dates", "Distributed", "Mmap", "Sockets", "Test", "Unicode"] -git-tree-sha1 = "1f7a25b53ec67f5e9422f1f551ee216503f4a0fa" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.20.0" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[LinearAlgebra]] -deps = ["Libdl"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[Jurisdictions]] +deps = ["Markets", "Reexport"] +git-tree-sha1 = "6ce1c2adbfe0421ae11af14049a26deda860346d" +uuid = "044ac1b0-3745-11e9-192d-5d2cecb87306" +version = "0.1.0" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" @@ -60,9 +56,21 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[Markets]] +deps = ["Currencies", "Reexport"] +git-tree-sha1 = "7738553ae8be6bb2e9b20078b8d0ac60c2a01c79" +uuid = "b44f5480-3750-11e9-03b7-d5e34f572a03" +version = "0.1.0" + [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[Positions]] +deps = ["FinancialInstruments", "Reexport"] +git-tree-sha1 = "60903a5c32659d913f2eae3c3b7fe83aa116da90" +uuid = "81d999f0-3725-11e9-2196-fd457307c398" +version = "0.1.0" + [[Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -71,20 +79,19 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" deps = ["Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +[[Reexport]] +git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" +repo-rev = "master" +repo-url = "https://github.com/EricForgy/Reexport.jl" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "0.2.0+" + [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -[[SparseArrays]] -deps = ["LinearAlgebra", "Random"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - [[Test]] deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/Project.toml b/Project.toml index b5d0079..cd90df5 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,13 @@ authors = ["Eric Forgy "] version = "0.1.0" [deps] -BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -Countries = "d5396ca0-2cf9-11e9-2c96-2532c40e11ac" -ISOCurrencies = "5683cba2-2ced-11e9-0d3f-33b1fe4e9d91" +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Positions = "81d999f0-3725-11e9-2196-fd457307c398" +Reexport = "189a3867-3050-52da-a836-e630ba90ab69" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a2cabc --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# GeneralLedgers.jl diff --git a/src/GeneralLedgers.jl b/src/GeneralLedgers.jl index 23dd0a1..1862417 100644 --- a/src/GeneralLedgers.jl +++ b/src/GeneralLedgers.jl @@ -1,82 +1,11 @@ module GeneralLedgers -import ISOCurrencies.Currencies: Currency, USD +using Reexport, AbstractTrees +@reexport using Positions -# There should be three types of accounts: -# 1. GeneralLedger - Has no parent. Has children. Cannot post journal entries directly. -# 2. AccountGroup - Has a parent. Has children. Cannot post journal entries directly. -# 3. LedgerAccount - Has a parent. Has no children. Can post journal entries directly. - -# What about "Modules"? - -# - Financial Reports -# - Audit Controls -# - Inventory Management -# - Invoice / Customer / Purchase Order / Vendor / Receipts Management - -# Account types -# - Balance Sheet -# - Income Statement -# - Retained Earnings - -abstract type Ledger end -abstract type Account end - -struct GeneralLedger <: Ledger - name::String - currency::Currency - children::Dict{String,Union{Ledger,Account}} - - GeneralLedger(name::String,currency::Currency,children=Dict{String,Union{Ledger,Account}}()) = new(name,currency,children) -end - -struct AccountGroup <: Ledger - parent::Ledger - name::String - code::String - children::Dict{String,Union{Ledger,Account}} - - function AccountGroup(parent,name,code) - haskey(parent.children,code) && error("Account code $(code) already exists.") - account = new(parent,name,code,Dict{String,Union{Ledger,Account}}()) - parent.children[code] = account - return account - end -end - -struct DebitAccount <: Account - parent::Ledger - name::String - code::String - - function DebitAccount(parent,name,code) - haskey(parent.children,code) && error("Account code $(code) already exists.") - account = new(parent,name,code) - parent.children[code] = account - return account - end -end - -struct CreditAccount <: Account - parent::Ledger - name::String - code::String - - function CreditAccount(parent,name,code) - haskey(parent.children,code) && error("Account code $(code) already exists.") - account = new(parent,name,code) - parent.children[code] = account - return account - end -end - -include("show.jl") - -function example() - ledger = GeneralLedger("NewCo",USD) - assets = DebitAccount(ledger,"Assets","1") - liabilities = CreditAccount(ledger,"Liabilities","2") - return ledger -end +include("account.jl") +include("entries.jl") +include("display.jl") +include("example.jl") end # module diff --git a/src/account.jl b/src/account.jl new file mode 100644 index 0000000..d5cee8c --- /dev/null +++ b/src/account.jl @@ -0,0 +1,79 @@ +struct GeneralLedger{C} end +struct DebitGroup{C} end +struct CreditGroup{C} end +struct DebitAccount{C} end +struct CreditAccount{C} end + +mutable struct Account{C<:Currency} + parent::Union{Account,Nothing} + name::String + code::Union{String,Nothing} + isdebit::Union{Bool,Nothing} + balance::Union{Position{C},Nothing} + subaccounts::Union{Vector{Account},Nothing} + chart::Union{Dict{String,Account},Nothing} + + function Account{C}(parent,name,code,isdebit,balance,subaccounts,chart) where C<:Currency + a = new(parent,name,code,isdebit,balance,subaccounts,chart) + if !isnothing(parent) + push!(parent.subaccounts,a) + if isnothing(subaccounts) + add(a) + end + end + return a + end +end + +function get_ledger(a::Account) + p = a + while !isnothing(p.parent) + p = p.parent + end + return p +end + +function add(a::Account) + ledger = get_ledger(a) + ledger.chart[a.code] = a +end + +function accounttype(a::Account{C}) where C + if isnothing(a.parent) + return GeneralLedger{C}() + elseif a.isdebit + if isnothing(a.subaccounts) + return DebitAccount{C}() + else + return DebitGroup{C}() + end + else + if isnothing(a.subaccounts) + return CreditAccount{C}() + else + return CreditGroup{C}() + end + end +end + +DebitAccount(p,n,c,b::Position{C}=Position(Currencies.USD,0.)) where C = Account{C}(p,n,c,true,b,nothing,nothing) +CreditAccount(p,n,c,b::Position{C}=Position(Currencies.USD,0.)) where C = Account{C}(p,n,c,false,b,nothing,nothing) +DebitGroup(p,n,c,::C=Currencies.USD) where C = Account{C}(p,n,c,true,nothing,Vector{Account}(),nothing) +CreditGroup(p,n,c,::C=Currencies.USD) where C = Account{C}(p,n,c,false,nothing,Vector{Account}(),nothing) +GeneralLedger(n,::C=Currencies.USD) where C = Account{C}(nothing,n,nothing,true,nothing,Vector{Account}(),Dict{String,Account}()) + +currency(a::Account{C}) where C = C() + +balance(a::Account) = balance(a,accounttype(a)) +balance(a::Account{C}, ::Union{DebitAccount{C},CreditAccount{C}}) where C = a.balance +function balance(a::Account{C}, ::Any) where C + b = Position{C}(0.) + for account in a.subaccounts + if isequal(a.isdebit,account.isdebit) + b += balance(account) + else + b -= balance(account) + end + end + return b +end diff --git a/src/display.jl b/src/display.jl new file mode 100644 index 0000000..64228c3 --- /dev/null +++ b/src/display.jl @@ -0,0 +1,29 @@ +AbstractTrees.printnode(x) = AbstractTrees.printnode(stdout,x) + +AbstractTrees.children(a::Account) = _children(a,accounttype(a)) +_children(a::Account,::GeneralLedger) = isnothing(a.subaccounts) ? Vector{Account}() : [a.subaccounts;[a.chart]] +_children(a::Account,::Any) = isnothing(a.subaccounts) ? Vector{Account}() : a.subaccounts + +AbstractTrees.printnode(io::IO,a::Account) = _printnode(io,a,accounttype(a)) +_printnode(io::IO,a::Account,::GeneralLedger{C}) where C = print(io,a.name) +_printnode(io::IO,a::Account,::Union{DebitGroup,CreditGroup}) = print(io,a.name) +_printnode(io::IO,a::Account,::Union{DebitAccount,CreditAccount}) = print(io,"$(a.name): ",balance(a)) + +AbstractTrees.printnode(io::IO,c::Dict{String,Account}) = print(io,"Chart of Accounts:") + +Base.show(io::IO,a::Account) = _show(io,a,accounttype(a)) +_show(io::IO,a::Account,::Union{GeneralLedger,DebitGroup,CreditGroup}) = print_tree(io,a) +_show(io::IO,a::Account,t::Union{DebitAccount,CreditAccount}) = _printnode(io,a,t) + +Base.show(io::IO,a::DebitGroup{C}) where C = print(io,"DebitGroup{",C(),"}") +Base.show(io::IO,a::CreditGroup{C}) where C = print(io,"CreditGroup{",C(),"}") +Base.show(io::IO,a::DebitAccount{C}) where C = print(io,"DebitAccount{",C(),"}") +Base.show(io::IO,a::CreditAccount{C}) where C = print(io,"CreditAccount{",C(),"}") + +AbstractTrees.children(e::Entry) = [e.debit,e.credit,e.amount] +AbstractTrees.printnode(io::IO,c::Entry) = print(io,"Entry") +AbstractTrees.printnode(io::IO,c::Position{C}) where C = print(io,c) + +Base.show(io::IO,c::Position{C}) where C<:Currency = print(io,c.amount," ",C()) +Base.show(io::IO,e::Entry) = print_tree(io,e) + diff --git a/src/entries.jl b/src/entries.jl new file mode 100644 index 0000000..853c5c9 --- /dev/null +++ b/src/entries.jl @@ -0,0 +1,31 @@ +struct Entry{C<:Currency} + debit::Account{C} + credit::Account{C} + amount::Position{C} +end + +debit!(a::Account{C},c::Position{C}) where C = debit!(a,c,accounttype(a)) +credit!(a::Account{C},c::Position{C}) where C = credit!(a,c,accounttype(a)) + +function debit!(a::Account{C},c::Position{C},::DebitAccount{C}) where C + a.balance += c + return a +end +function debit!(a::Account{C},c::Position{C},::CreditAccount{C}) where C + a.balance -= c + return a +end +function credit!(a::Account{C},c::Position{C},::CreditAccount{C}) where C + a.balance += c + return a +end +function credit!(a::Account{C},c::Position{C},::DebitAccount{C}) where C + a.balance -= c + return a +end + +function post!(e::Entry) + debit!(e.debit,e.amount) + credit!(e.credit,e.amount) +end + diff --git a/src/example.jl b/src/example.jl new file mode 100644 index 0000000..8d6b455 --- /dev/null +++ b/src/example.jl @@ -0,0 +1,10 @@ +function example() + ledger = GeneralLedger("NewCo") + assets = DebitGroup(ledger,"Assets","1000000") + liabilities = CreditGroup(ledger,"Liabilities","2000000") + cash = DebitAccount(assets,"Cash","1010000") + payable = CreditAccount(liabilities,"Accounts Payable","2010000") + + entry = Entry(cash,payable,Position(Currencies.USD,10.)) + return ledger, assets, liabilities, cash, payable, entry +end \ No newline at end of file diff --git a/src/show.jl b/src/show.jl deleted file mode 100644 index e0c96f8..0000000 --- a/src/show.jl +++ /dev/null @@ -1,24 +0,0 @@ -function Base.show(io::IO,gl::GeneralLedger) - iobuff = IOBuffer() - print(iobuff,"GeneralLedger: $(gl.name)\n") - print(iobuff,"Currency: $(gl.currency)\n") - print(iobuff,"Accounts:\n") - for (key,child) in gl.children - print(iobuff," $(child.name) ($(child.code))\n") - end - print(io,String(take!(iobuff))) -end - -function Base.show(io::IO,acct::AccountGroup) - iobuff = IOBuffer() - print(iobuff,"AccountGroup: $(acct.name) ($(acct.code))\n") - print(iobuff,"SubAccounts:\n") - for (key,child) in acct.children - print(iobuff," $(child.name) ($(child.code))\n") - end - print(io,String(take!(iobuff))) -end - -Base.show(io::IO,acct::DebitAccount) = print(io,"DebitAccount: $(acct.name) ($(acct.code))\n") - -Base.show(io::IO,acct::CreditAccount) = print(io,"CreditAccount: $(acct.name) ($(acct.code))\n") \ No newline at end of file