1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE ScopedTypeVariables #-}
6 {-# LANGUAGE TypeFamilies #-}
7 module Hcompta.Format.JCC.Write
8 ( module Hcompta.Format.JCC.Write
9 , module Hcompta.Format.JCC.Date.Write
13 import Data.Char (isSpace)
14 import qualified Data.Foldable
15 import Data.Foldable (Foldable(..))
16 import Data.Functor (Functor(..), (<$>))
17 import qualified Data.Functor.Compose
19 import qualified Data.List.NonEmpty as NonEmpty
20 import Data.Map.Strict (Map)
21 import qualified Data.Map.Strict as Map
22 import Data.Maybe (Maybe(..), maybe)
23 import Data.Monoid (Monoid(..))
24 import Data.Ord (Ord(..))
25 import qualified Data.Text as Text
26 import qualified Data.Text.Lazy as TL
27 import Prelude (($), (.), Bounded(..), Int, IO, Num(..), flip)
28 import System.IO (Handle)
30 import qualified Hcompta.Account as Account
31 import Hcompta.Account ( Account_Anchor(..)
35 import qualified Hcompta.Anchor as Anchor
36 import Hcompta.Chart (Chart)
37 import qualified Hcompta.Chart as Chart
38 import qualified Hcompta.Format.JCC as JCC
39 import Hcompta.Format.JCC
48 import qualified Hcompta.Format.JCC.Amount as Amount
49 import qualified Hcompta.Format.JCC.Amount.Write as Amount.Write
50 import qualified Hcompta.Format.JCC.Read as Read
51 -- import Hcompta.Lib.Consable (Consable(..))
52 import Hcompta.Lib.Leijen (Doc, (<>))
53 import qualified Hcompta.Lib.Leijen as W
54 import qualified Hcompta.Lib.TreeMap as TreeMap
55 import qualified Hcompta.Tag as Tag
56 import Hcompta.Tag (Tags(..))
57 import Hcompta.Anchor (Anchors(..))
58 import Hcompta.Transaction ( Transaction_Anchor(..)
59 , Transaction_Anchors(..)
61 , Transaction_Tags(..) )
63 import Hcompta.Format.JCC.Date.Write
67 comment :: Comment -> Doc
70 W.char Read.comment_begin
71 <> (case Text.uncons com of
72 Just (c, _) | not $ Data.Char.isSpace c -> W.space
76 comments :: Doc -> [Comment] -> Doc
79 Data.List.intersperse W.line .
80 Data.List.map (\c -> prefix <> comment c)
84 account :: Account -> Doc
89 (NonEmpty.map account_section acct)
90 where sep = W.bold $ W.dullblack $ W.char Read.account_section_sep
92 account_section :: Account.Account_Section Account -> Doc
93 account_section = W.strict_text
95 -- ** Measure 'Account'
97 account_length :: Account -> Int
100 (\acc -> (1 +) . (acc +) . Text.length)
103 -- ** Write 'Account_Anchor'
105 account_anchor :: Account_Anchor -> Doc
106 account_anchor (Account_Anchor anchor) =
108 (:) (op $ W.char Read.account_anchor_prefix) $
111 (op $ W.char Read.account_anchor_sep)
112 (W.strict_text <$> anchor)
113 where op = W.bold . W.dullyellow
115 account_anchor_length :: Account_Anchor -> Int
116 account_anchor_length (Account_Anchor anch) =
118 (\acc -> (1 +) . (acc +) . Text.length)
121 -- ** Write 'Account_Tag'
123 account_tag :: Account_Tag -> Doc
124 account_tag (Account_Tag (path, value)) =
126 (:) (op $ W.char Read.account_tag_prefix) $
129 (op $ W.char Read.account_tag_sep)
130 (W.strict_text <$> path)) <>
134 (op $ W.char Read.account_tag_value_prefix) <>
136 where op = W.bold . W.dullyellow
140 amounts :: Amount.Styles -> Map Unit Quantity -> Doc
146 else doc <> W.space <>
147 (W.bold $ W.yellow $ W.char Read.amount_sep) <>
149 (Amount.Write.amount $
150 Amount.style styles $
151 JCC.Amount unit qty))
154 -- ** Measure 'Amount's
156 amounts_length :: Amount.Styles -> Map Unit Quantity -> Int
157 amounts_length styles amts =
162 (\unit qty -> (3 +) . (+)
163 (Amount.Write.amount_length $
164 Amount.style styles $
165 JCC.Amount unit qty))
170 posting :: Amount.Styles -> Posting_Lengths -> Posting -> Doc
171 posting styles max_posting_length
174 , posting_account_anchor
176 , posting_comments=cmts
181 let (doc_acct, len_acct) =
182 case posting_account_anchor of
184 ( account posting_account
185 , account_length posting_account )
187 ( account_anchor a <> maybe W.empty account sa
188 , account_anchor_length a + maybe 0 account_length sa )
189 case Map.null posting_amounts of
192 let len_amts = amounts_length styles posting_amounts in
194 W.fill (2 + max_posting_length - (len_acct + len_amts)) (W.space <> W.space) <>
195 amounts styles posting_amounts
198 [c] -> W.space <> comment c
199 _ -> W.line <> do comments (W.text " ") cmts)
201 -- ** Measure 'Posting'
203 type Posting_Lengths = (Int)
207 -> Map Account [Posting]
210 postings_lengths styles ps pl =
214 case posting_account_anchor p of
215 Nothing -> account_length $ posting_account p
217 account_anchor_length a +
218 maybe 0 account_length sa in
221 + amounts_length styles (posting_amounts p) )
223 (Data.Functor.Compose.Compose ps)
225 -- * Write 'Transaction'
227 transaction :: Amount.Styles -> Transaction -> Doc
228 transaction styles t = transaction_with_lengths styles (transaction_lengths styles t 0) t
230 transactions :: Foldable ts => Amount.Styles -> ts Transaction -> Doc
231 transactions styles ts = do
232 let transaction_lengths_ =
233 Data.Foldable.foldr (transaction_lengths styles) 0 ts
236 transaction_with_lengths styles transaction_lengths_ t <>
237 (if W.is_empty doc then W.empty else W.line <> doc)
242 transaction_with_lengths
244 -> Transaction_Lengths
245 -> Transaction -> Doc
246 transaction_with_lengths
250 { transaction_comments
251 , transaction_dates=(first_date, dates)
252 , transaction_wording
253 , transaction_postings
254 , transaction_anchors=Transaction_Anchors (Anchors anchors)
255 , transaction_tags=Transaction_Tags (Tags tags)
258 Data.List.intersperse
259 (W.char Read.date_sep)
260 (date <$> (first_date:dates))) <> do
261 (case transaction_wording of
263 _ -> W.space <> (W.dullmagenta $ W.strict_text transaction_wording)) <> do
265 (case transaction_comments of
267 _ -> comments W.space transaction_comments <> W.line) <> do
269 (\path () -> ((W.string " " <>
270 transaction_anchor (Transaction_Anchor path) <> W.line) <>))
271 W.empty anchors <> do
275 (\value -> (<>) (W.string " " <>
276 transaction_tag (Transaction_Tag (path, value)) <> W.line)))
279 (W.vsep . fmap (posting styles posting_lengths_))
280 transaction_postings <> W.line
282 -- ** Measure 'Transaction'
284 type Transaction_Lengths = Posting_Lengths
294 { transaction_postings
295 } posting_lengths_ = do
297 (flip $ postings_lengths styles)
299 [ transaction_postings
302 -- ** Write 'Transaction_Tag'
304 transaction_tag :: Transaction_Tag -> Doc
305 transaction_tag (Transaction_Tag (path, value)) =
307 (:) (W.bold $ W.dullyellow $ W.char Read.transaction_tag_prefix) $
310 (op $ W.char Read.transaction_tag_sep)
311 (transaction_tag_section <$> path)) <>
315 (op $ W.char Read.transaction_tag_value_prefix) <>
318 op = W.bold . W.yellow
320 transaction_tag_section :: Tag.Section -> Doc
321 transaction_tag_section = W.bold . W.strict_text
323 -- ** Write 'Transaction_Anchor'
325 transaction_anchor :: Transaction_Anchor -> Doc
326 transaction_anchor (Transaction_Anchor path) =
328 (:) (op $ W.char Read.transaction_anchor_prefix) $
331 (op $ W.char Read.transaction_anchor_sep)
332 (transaction_anchor_section <$> path)
334 op = W.bold . W.yellow
336 transaction_anchor_section :: Anchor.Section -> Doc
337 transaction_anchor_section = W.bold . W.strict_text
343 , Monoid (ts Transaction)
344 ) => Journal (ts Transaction) -> Doc
345 journal Journal{ journal_content, journal_amount_styles } =
346 transactions journal_amount_styles journal_content
350 chart :: Chart Account -> Doc
352 TreeMap.foldl_with_Path
353 (\doc acct (Account_Tags (Tags ca)) ->
355 account acct <> W.line <>
361 ddd <> W.string " " <> account_tag (Account_Tag (tn, tv)) <> W.line)
374 { style_align :: Bool
375 , style_color :: Bool
384 show :: Style -> Doc -> TL.Text
385 show Style{style_color, style_align} =
388 then W.renderPretty style_color 1.0 maxBound
389 else W.renderCompact style_color
391 put :: Style -> Doc -> Handle -> IO ()
392 put Style{style_color, style_align} doc handle =
395 then W.renderPretty style_color 1.0 maxBound doc
396 else W.renderCompact style_color doc