1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE TypeFamilies #-}
6 module Hcompta.Format.Ledger where
8 import Data.Data (Data(..))
9 import Data.Functor.Compose (Compose(..))
10 import qualified Data.List as Data.List
11 import Data.Map.Strict (Map)
12 import qualified Data.Map.Strict as Data.Map
13 import Data.Text (Text)
14 import qualified Data.Time.Clock as Time
15 import qualified Data.Time.Clock.POSIX as Time
16 import Data.Typeable (Typeable)
17 import Text.Parsec.Pos (SourcePos, initialPos)
19 import Hcompta.Account (Account)
20 import Hcompta.Amount (Amount)
21 import qualified Hcompta.Amount as Amount
22 import qualified Hcompta.Balance as Balance
23 import Hcompta.Date (Date)
24 import qualified Hcompta.Date as Date
25 import qualified Hcompta.Filter as Filter
26 import Hcompta.Lib.Parsec ()
27 import qualified Hcompta.GL as GL
30 type Description = Text
34 -- * The 'Journal' type
38 { journal_file :: FilePath
39 , journal_includes :: [Journal]
40 , journal_last_read_time :: Time.UTCTime
41 , journal_transactions :: Map Date [Transaction]
42 , journal_unit_styles :: Map Amount.Unit Amount.Style
43 } deriving (Data, Eq, Show, Typeable)
49 , journal_includes = []
50 , journal_last_read_time = Time.posixSecondsToUTCTime 0
51 , journal_transactions = Data.Map.empty
52 , journal_unit_styles = Data.Map.empty
55 -- * The 'Transaction' type
59 { transaction_code :: Code
60 , transaction_comments_before :: [Comment]
61 , transaction_comments_after :: [Comment]
62 , transaction_dates :: (Date, [Date])
63 , transaction_description :: Description
64 , transaction_postings :: Posting_by_Account
65 , transaction_virtual_postings :: Posting_by_Account
66 , transaction_balanced_virtual_postings :: Posting_by_Account
67 , transaction_sourcepos :: SourcePos
68 , transaction_status :: Status
69 , transaction_tags :: Tag_by_Name
70 } deriving (Data, Eq, Show, Typeable)
72 transaction :: Transaction
75 { transaction_code = ""
76 , transaction_comments_before = []
77 , transaction_comments_after = []
78 , transaction_dates = (Date.nil, [])
79 , transaction_description = ""
80 , transaction_postings = Data.Map.empty
81 , transaction_virtual_postings = Data.Map.empty
82 , transaction_balanced_virtual_postings = Data.Map.empty
83 , transaction_sourcepos = initialPos ""
84 , transaction_status = False
85 , transaction_tags = Data.Map.empty
88 instance Filter.Transaction Transaction where
89 type Transaction_Posting Transaction = Posting
90 transaction_date = fst . transaction_dates
91 transaction_description = transaction_description
92 transaction_postings = transaction_postings
93 transaction_tags = transaction_tags
96 instance Filter.GL (GL.GL_Line Transaction) where
97 type GL_Amount (GL.GL_Line Transaction) = Amount
98 register_account = GL.posting_account . GL.register_line_posting
99 register_date = GL.transaction_date . GL.register_line_transaction
100 register_amount_positive = Amount.sum_positive . GL.posting_amount . GL.register_line_posting
101 register_amount_negative = Amount.sum_negative . GL.posting_amount . GL.register_line_posting
102 register_amount_balance = Amount.sum_balance . GL.posting_amount . GL.register_line_posting
103 register_sum_positive = Amount.sum_positive . GL.register_line_sum
104 register_sum_negative = Amount.sum_negative . GL.register_line_sum
105 register_sum_balance = Amount.sum_balance . GL.register_line_sum
108 instance GL.Transaction Transaction where
109 type Transaction_Posting Transaction = Posting
110 type Transaction_Postings Transaction = Compose (Map Account) []
111 transaction_date = fst . transaction_dates
112 transaction_postings = Compose . transaction_postings
114 -- | Return a 'Data.Map.Map' associating
115 -- the given 'Transaction's with their respective 'Date'.
116 transaction_by_Date :: [Transaction] -> Map Date [Transaction]
117 transaction_by_Date =
118 Data.Map.fromListWith (flip (++)) .
119 Data.List.map (\t -> (fst $ transaction_dates t, [t]))
121 -- * The 'Posting' type
125 { posting_account :: Account
126 , posting_amounts :: Map Amount.Unit Amount
127 , posting_comments :: [Comment]
128 , posting_dates :: [Date]
129 , posting_sourcepos :: SourcePos
130 , posting_status :: Bool
131 , posting_tags :: Tag_by_Name
132 } deriving (Data, Eq, Show, Typeable)
135 = Posting_Type_Regular
136 | Posting_Type_Virtual
137 | Posting_Type_Virtual_Balanced
138 deriving (Data, Eq, Read, Show, Typeable)
140 posting :: Account -> Posting
143 { posting_account = acct
144 , posting_amounts = Data.Map.empty
145 , posting_comments = []
147 , posting_status = False
148 , posting_sourcepos = initialPos ""
149 , posting_tags = Data.Map.empty
153 Balance.Posting Posting where
154 type Posting_Amount Posting = Amount.Sum Amount
155 posting_account = posting_account
156 posting_amounts = Data.Map.map Amount.sum . posting_amounts
157 posting_set_amounts amounts p =
158 p { posting_amounts=Data.Map.map Amount.sum_balance amounts }
160 instance Filter.Posting Posting where
161 type Posting_Amount Posting = Amount
162 posting_account = posting_account
163 posting_amounts = posting_amounts
165 instance GL.Posting Posting where
166 type Posting_Amount Posting = Amount.Sum (Map Amount.Unit Amount)
167 posting_account = posting_account
168 posting_amount = Amount.sum . posting_amounts
170 -- ** The 'Posting' mappings
172 type Posting_by_Account
173 = Map Account [Posting]
175 type Posting_by_Amount_and_Account
176 = Map Amount.By_Unit Posting_by_Account
178 type Posting_by_Signs_and_Account
179 = Map Amount.Signs Posting_by_Account
181 -- | Return a Data.'Data.Map.Map' associating the given 'Posting's with their respective 'Account'.
182 posting_by_Account :: [Posting] -> Posting_by_Account
184 Data.Map.fromListWith (flip (++)) .
186 (\p -> (posting_account p, [p]))
188 posting_by_Amount_and_Account :: Posting_by_Account -> Posting_by_Amount_and_Account
189 posting_by_Amount_and_Account =
190 Data.Map.foldlWithKey
195 (Data.Map.unionWith (++))
197 (Data.Map.singleton acct [p])))))
200 posting_by_Signs_and_Account :: Posting_by_Account -> Posting_by_Signs_and_Account
201 posting_by_Signs_and_Account =
202 Data.Map.foldlWithKey
207 (Data.Map.unionWith (++))
208 (Amount.signs $ posting_amounts p)
209 (Data.Map.singleton acct [p])))))
214 type Tag = (Tag_Name, Tag_Value)
216 type Tag_Value = Text
218 type Tag_by_Name = Map Tag_Name [Tag_Value]
220 -- | Return a 'Data.Map.Map' associating the 'Value's of the given 'Tag's with their respective 'Name'.
221 tag_by_Name :: [Tag] -> Tag_by_Name
223 Data.Map.fromListWith (flip (++)) .
224 Data.List.map (\(n, v) -> (n, [v]))