1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE MultiParamTypeClasses #-}
4 {-# LANGUAGE NamedFieldPuns #-}
5 {-# LANGUAGE OverloadedStrings #-}
6 {-# LANGUAGE TupleSections #-}
7 {-# LANGUAGE TypeFamilies #-}
8 module Hcompta.Format.Ledger where
10 -- import Control.Applicative (Const(..))
12 import Data.Data (Data(..))
13 import Data.Eq (Eq(..))
14 import Data.Functor.Compose (Compose(..))
16 import Data.Map.Strict (Map)
17 import qualified Data.Map.Strict as Data.Map
18 import Data.Maybe (Maybe(..))
19 import Data.Monoid (Monoid(..))
20 import Data.Text (Text)
21 import Data.Tuple (fst)
22 import Data.Typeable (Typeable)
23 import Prelude (($), (.), FilePath, Num(..), flip, undefined)
24 import Text.Parsec.Pos (SourcePos, initialPos)
25 import Text.Show (Show)
27 import Hcompta.Account (Account)
28 import Hcompta.Amount (Amount)
29 import qualified Hcompta.Amount as Amount
30 -- import Hcompta.Balance (Balance(..))
31 import qualified Hcompta.Balance as Balance
32 import Hcompta.Date (Date)
33 import qualified Hcompta.Date as Date
34 import qualified Hcompta.Filter as Filter
35 -- import Hcompta.GL (GL(..))
36 import qualified Hcompta.GL as GL
37 import qualified Hcompta.Journal as Journal
38 -- import Hcompta.Lib.Consable
39 import Hcompta.Lib.Parsec ()
40 import qualified Hcompta.Stats as Stats
41 import qualified Hcompta.Tag as Tag
44 type Description = Text
48 -- * The 'Journal' type
50 data Monoid ts => Journal ts
52 { journal_file :: FilePath
53 , journal_includes :: [Journal ts]
54 , journal_last_read_time :: Date
55 , journal_transactions :: !ts
56 , journal_unit_styles :: Map Amount.Unit Amount.Style
57 } deriving (Data, Eq, Show, Typeable)
59 journal :: Monoid ts => Journal ts
62 { journal_file = mempty
63 , journal_includes = mempty
64 , journal_last_read_time = Date.nil
65 , journal_transactions = mempty
66 , journal_unit_styles = mempty
69 -- * The 'Transaction' type
73 { transaction_code :: Code
74 , transaction_comments_before :: [Comment]
75 , transaction_comments_after :: [Comment]
76 , transaction_dates :: (Date, [Date])
77 , transaction_description :: Description
78 , transaction_postings :: Map Account [Posting]
79 , transaction_virtual_postings :: Map Account [Posting]
80 , transaction_balanced_virtual_postings :: Map Account [Posting]
81 , transaction_sourcepos :: SourcePos
82 , transaction_status :: Status
83 , transaction_tags :: Map Tag.Path [Tag.Value]
84 } deriving (Data, Eq, Show, Typeable)
86 transaction :: Transaction
89 { transaction_code = ""
90 , transaction_comments_before = []
91 , transaction_comments_after = []
92 , transaction_dates = (Date.nil, [])
93 , transaction_description = ""
94 , transaction_postings = mempty
95 , transaction_virtual_postings = mempty
96 , transaction_balanced_virtual_postings = mempty
97 , transaction_sourcepos = initialPos ""
98 , transaction_status = False
99 , transaction_tags = mempty
102 instance Filter.Transaction Transaction where
103 type Transaction_Posting Transaction = Posting
104 type Transaction_Postings Transaction = Compose [] (Compose (Map Account) [])
105 transaction_date = fst . transaction_dates
106 transaction_description = transaction_description
107 transaction_postings t =
109 [ Compose $ transaction_postings t
111 transaction_postings_virtual t =
113 [ Compose $ transaction_virtual_postings t
114 , Compose $ transaction_balanced_virtual_postings t
116 transaction_tags = transaction_tags
118 instance Journal.Transaction Transaction where
119 transaction_date = fst . transaction_dates
121 instance Stats.Transaction Transaction where
122 type Transaction_Posting Transaction = Posting
123 type Transaction_Postings Transaction = Compose [] (Compose (Map Account) [])
124 transaction_date = fst . transaction_dates
125 transaction_postings t =
127 [ Compose $ transaction_postings t
128 , Compose $ transaction_virtual_postings t
129 , Compose $ transaction_balanced_virtual_postings t
131 transaction_postings_size t =
132 Data.Map.size (transaction_postings t) +
133 Data.Map.size (transaction_virtual_postings t) +
134 Data.Map.size (transaction_balanced_virtual_postings t)
138 instance GL.Transaction Transaction where
139 type Transaction_Posting Transaction = Posting
140 type Transaction_Postings Transaction = Compose [] (Compose (Map Account) [])
141 transaction_date = fst . transaction_dates
142 transaction_postings t =
144 [ Compose $ transaction_postings t
145 , Compose $ transaction_virtual_postings t
146 , Compose $ transaction_balanced_virtual_postings t
148 transaction_postings_filter f t =
149 t{ transaction_postings =
151 (\p -> case filter f p of
154 (transaction_postings t)
155 , transaction_virtual_postings =
157 (\p -> case filter f p of
160 (transaction_virtual_postings t)
161 , transaction_balanced_virtual_postings =
163 (\p -> case filter f p of
166 (transaction_balanced_virtual_postings t)
169 -- | Return a 'Data.Map.Map' associating
170 -- the given 'Transaction's with their respective 'Date'.
171 transaction_by_Date :: [Transaction] -> (Compose (Map Date) []) Transaction
172 transaction_by_Date =
174 Data.Map.fromListWith (flip (++)) .
175 Data.List.map (\t -> (fst $ transaction_dates t, [t]))
177 -- * The 'Posting' type
181 { posting_account :: Account
182 , posting_amounts :: Map Amount.Unit Amount
183 , posting_comments :: [Comment]
184 , posting_dates :: [Date]
185 , posting_sourcepos :: SourcePos
186 , posting_status :: Bool
187 , posting_tags :: Map Tag.Path [Tag.Value]
188 } deriving (Data, Eq, Show, Typeable)
190 posting :: Account -> Posting
193 { posting_account = acct
194 , posting_amounts = mempty
195 , posting_comments = mempty
196 , posting_dates = mempty
197 , posting_status = False
198 , posting_sourcepos = initialPos ""
199 , posting_tags = mempty
202 instance Balance.Posting Posting where
203 type Posting_Amount Posting = Amount.Sum Amount
204 posting_account = posting_account
205 posting_amounts = Data.Map.map Amount.sum . posting_amounts
206 posting_set_amounts amounts p =
207 p { posting_amounts=Data.Map.map Amount.sum_balance amounts }
209 instance Filter.Posting Posting where
210 type Posting_Amount Posting = Amount
211 posting_account = posting_account
212 posting_amounts = posting_amounts
213 posting_type = undefined
214 -- NOTE: the posting_type will be given to Filter.test
215 -- through instance Posting p => Posting (Posting_Type, p)
216 -- by Filter.transaction_postings
217 -- and Filter.transaction_postings_virtual
219 instance GL.Posting Posting where
220 type Posting_Amount Posting = Amount.Sum (Map Amount.Unit Amount)
221 posting_account = posting_account
222 posting_amount = Amount.sum . posting_amounts
224 instance Stats.Posting Posting where
225 type Posting_Amount Posting = Amount
226 posting_account = posting_account
227 posting_amounts = posting_amounts
229 -- ** The 'Posting' mappings
231 type Posting_by_Account
232 = Map Account [Posting]
234 type Posting_by_Amount_and_Account
235 = Map Amount.By_Unit Posting_by_Account
237 type Posting_by_Signs_and_Account
238 = Map Amount.Signs Posting_by_Account
240 -- | Return a Data.'Data.Map.Map' associating the given 'Posting's with their respective 'Account'.
241 posting_by_Account :: [Posting] -> Posting_by_Account
243 Data.Map.fromListWith (flip (++)) .
245 (\p -> (posting_account p, [p]))
247 posting_by_Amount_and_Account :: Posting_by_Account -> Posting_by_Amount_and_Account
248 posting_by_Amount_and_Account =
249 Data.Map.foldlWithKey
254 (Data.Map.unionWith (++))
256 (Data.Map.singleton acct [p])))))
259 posting_by_Signs_and_Account :: Posting_by_Account -> Posting_by_Signs_and_Account
260 posting_by_Signs_and_Account =
261 Data.Map.foldlWithKey
266 (Data.Map.unionWith (++))
267 (Amount.signs $ posting_amounts p)
268 (Data.Map.singleton acct [p])))))