1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Ledger where
import Data.Constraint.Extras.TH (deriveArgDict)
import Data.Dependent.Map (DMap, fromList, singleton, union, unionWithKey)
import Data.Dependent.Sum ((==>))
import Data.Functor.Identity
import Data.Functor.Identity (Identity (..))
import Data.GADT.Compare.TH (deriveGCompare, deriveGEq)
import Data.GADT.Show.TH (deriveGShow)
import Data.Iban
import Data.Map qualified as M
import Data.Text qualified as T
import Data.Time.Calendar
import Data.Time.Clock
import Data.UUID
import GHC.Generics
data AccountType = Asset | Equity | Liability | Expense | Income
data TxAction = Inc | Dec
txaOpp :: TxAction -> TxAction
txaOpp Inc = Dec
txaOpp Dec = Inc
onDebit :: AccountType -> TxAction
onDebit Asset = Inc
onDebit Equity = Dec
onDebit Liability = Dec
onDebit Expense = Inc
onDebit Income = Dec
onCredit :: AccountType -> TxAction
onCredit = txaOpp . onDebit
data TxType
= InterestCtx
| OnlineBankingTx
| RecurrentDirectTx
| PaymentTerminalTx
| CashPaymentTx
| AtmTx
| AutoSaveRoundingTx
| BatchTx
| DirectDebitTx
| PeriodicTx
data IbanTag = AccountTag | CounterpartyIbanTag deriving (Eq, Enum, Ord, Show)
data UnitTag = FiledTag | GooglePayTag deriving (Eq, Enum, Ord, Show)
data TextTag = DescTag | UserTag | CounterpartyNameTag | ReferenceTag | MandateIdTag | CreditorIdTag | OtherPartyTag | TransactionTag | TerminalTag | CardSeqNoTag deriving (Eq, Enum, Ord, Show)
data Label a where
TextLabel :: TextTag -> Label T.Text
IbanLabel :: IbanTag -> Label Iban
UnitLabel :: UnitTag -> Label ()
TimestampLabel :: Label UTCTime
deriveGEq ''Label
deriveGCompare ''Label
deriveGShow ''Label
deriveArgDict ''Label
type Labels = DMap Label Identity
data Money = Money Integer deriving (Show)
infixl 6 +€, -€
(+€) :: Money -> Money -> Money
Money x +€ Money y = Money (x + y)
(-€) :: Money -> Money -> Money
Money x -€ Money y = Money (x - y)
data Scalar = Amount Money | Rate Integer deriving (Show)
data AccountId = AccountId [T.Text] deriving (Show)
data CommodityId = CommodityId UUID deriving (Show)
data Account = Account
{ id :: AccountId,
description :: [T.Text],
commodityId :: CommodityId,
balance :: Money
}
-- A balance assertion is only valid when all transactions before it have been
-- cleared and the balance of the account agrees with the amount in the
-- assertion.
data BalAssert = BalAssert
{ account :: Account,
amount :: Integer,
labels :: Labels
}
data Tx = Tx
{ cleared :: Maybe Day,
commodityId :: CommodityId, -- the commodity w.r.t. which rates are calculated
debit :: M.Map AccountId Scalar,
credit :: M.Map AccountId Scalar,
labels :: Labels
}
deriving (Show, Generic)
data SeqTx = SeqTx [Integer] Tx
data LedgerEntry = TxEntry SeqTx | BalAssertEntry BalAssert
data Ledger = Ledger [LedgerEntry]
|