1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE NamedFieldPuns #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE TypeFamilies #-}
5 module Hcompta.Format.Ledger where
7 import Data.Data (Data(..))
8 import Data.Typeable (Typeable)
9 import Text.Parsec.Pos (SourcePos, initialPos)
10 import qualified Data.Map.Strict as Data.Map
11 import qualified Data.List as Data.List
12 import qualified Data.Time.Clock as Time
13 import qualified Data.Time.Clock.POSIX as Time
15 import Hcompta.Lib.Parsec ()
16 import qualified Hcompta.Calc.Balance as Calc.Balance
17 import Hcompta.Model.Date (Date)
18 import qualified Hcompta.Model.Date as Date
19 import Hcompta.Model.Account (Account)
20 -- import qualified Hcompta.Model.Account as Account
21 import Hcompta.Model.Amount (Amount)
22 import qualified Hcompta.Model.Amount as Amount
23 import Data.Text (Text)
26 type Description = Text
30 -- * The 'Journal' type
34 { journal_file :: FilePath
35 , journal_includes :: [Journal]
36 , journal_last_read_time :: Time.UTCTime
37 , journal_transactions :: Transaction_by_Date
38 , journal_unit_styles :: Data.Map.Map Amount.Unit Amount.Style
39 } deriving (Data, Eq, Show, Typeable)
45 , journal_includes = []
46 , journal_last_read_time = Time.posixSecondsToUTCTime 0
47 , journal_transactions = Data.Map.empty
48 , journal_unit_styles = Data.Map.empty
52 -- * The 'Transaction' type
56 { transaction_code :: Code
57 , transaction_comments_before :: [Comment]
58 , transaction_comments_after :: [Comment]
59 , transaction_dates :: (Date, [Date])
60 , transaction_description :: Description
61 , transaction_postings :: Posting_by_Account
62 , transaction_virtual_postings :: Posting_by_Account
63 , transaction_balanced_virtual_postings :: Posting_by_Account
64 , transaction_sourcepos :: SourcePos
65 , transaction_status :: Status
66 , transaction_tags :: Tag_by_Name
67 } deriving (Data, Eq, Show, Typeable)
69 transaction :: Transaction
72 { transaction_code = ""
73 , transaction_comments_before = []
74 , transaction_comments_after = []
75 , transaction_dates = (Date.nil, [])
76 , transaction_description = ""
77 , transaction_postings = Data.Map.empty
78 , transaction_virtual_postings = Data.Map.empty
79 , transaction_balanced_virtual_postings = Data.Map.empty
80 , transaction_sourcepos = initialPos ""
81 , transaction_status = False
82 , transaction_tags = Data.Map.empty
85 -- ** The 'Transaction_by_Date' mapping
87 type Transaction_by_Date
88 = Data.Map.Map Date.UTC [Transaction]
90 -- | Return a Data.'Data.Map.Map' associating
91 -- the given 'Transaction's with their respective 'Date'.
92 transaction_by_Date :: [Transaction] -> Transaction_by_Date
94 Data.Map.fromListWith (flip (++)) .
95 Data.List.map (\t -> (Date.to_UTC $ fst $ transaction_dates t, [t]))
97 -- * The 'Posting' type
101 { posting_account :: Account
102 , posting_amounts :: Amount.By_Unit
103 , posting_comments :: [Comment]
104 , posting_dates :: [Date]
105 , posting_sourcepos :: SourcePos
106 , posting_status :: Bool
107 , posting_tags :: Tag_by_Name
108 } deriving (Data, Eq, Show, Typeable)
111 = Posting_Type_Regular
112 | Posting_Type_Virtual
113 | Posting_Type_Virtual_Balanced
114 deriving (Data, Eq, Read, Show, Typeable)
116 posting :: Account -> Posting
119 { posting_account = acct
120 , posting_amounts = Data.Map.empty
121 , posting_comments = []
123 , posting_status = False
124 , posting_sourcepos = initialPos ""
125 , posting_tags = Data.Map.empty
128 instance Calc.Balance.Posting Posting
130 type Posting_Amount Posting = Amount
131 type Posting_Unit Posting = Amount.Unit
132 posting_account = posting_account
133 posting_amounts = posting_amounts
134 posting_make acct amounts = (posting acct) { posting_amounts=amounts }
136 -- ** The 'Posting' mappings
138 type Posting_by_Account
139 = Data.Map.Map Account [Posting]
141 type Posting_by_Amount_and_Account
142 = Data.Map.Map Amount.By_Unit Posting_by_Account
144 type Posting_by_Signs_and_Account
145 = Data.Map.Map Amount.Signs Posting_by_Account
147 -- | Return a Data.'Data.Map.Map' associating the given 'Posting's with their respective 'Account'.
148 posting_by_Account :: [Posting] -> Posting_by_Account
150 Data.Map.fromListWith (flip (++)) .
152 (\p -> (posting_account p, [p]))
154 posting_by_Amount_and_Account :: Posting_by_Account -> Posting_by_Amount_and_Account
155 posting_by_Amount_and_Account =
156 Data.Map.foldlWithKey
161 (Data.Map.unionWith (++))
163 (Data.Map.singleton acct [p])))))
166 posting_by_Signs_and_Account :: Posting_by_Account -> Posting_by_Signs_and_Account
167 posting_by_Signs_and_Account =
168 Data.Map.foldlWithKey
173 (Data.Map.unionWith (++))
174 (Amount.signs $ posting_amounts p)
175 (Data.Map.singleton acct [p])))))
180 type Tag = (Tag_Name, Tag_Value)
182 type Tag_Value = Text
184 type Tag_by_Name = Data.Map.Map Tag_Name [Tag_Value]
186 -- | Return a 'Data.Map.Map' associating the 'Value's of the given 'Tag's with their respective 'Name'.
187 tag_by_Name :: [Tag] -> Tag_by_Name
189 Data.Map.fromListWith (flip (++)) .
190 Data.List.map (\(n, v) -> (n, [v]))