1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE ScopedTypeVariables #-}
3 {-# LANGUAGE StandaloneDeriving #-}
5 import Test.HUnit hiding (test)
6 import Test.Framework.Providers.HUnit (hUnitTestToTests)
7 import Test.Framework.Runners.Console (defaultMain)
9 import Control.Applicative (Applicative(..))
10 import Control.Monad (Monad(..))
11 import Control.Monad.IO.Class (liftIO)
12 import Data.Bool (Bool(..))
13 import Data.Decimal (DecimalRaw(..))
14 import qualified Data.Either
15 import Data.Either (rights, either)
16 import Data.Eq (Eq(..))
17 import Data.Function (($), (.), id, const)
18 import Data.Functor (Functor(..), (<$>))
19 import Data.List ((++))
20 import Data.List.NonEmpty (NonEmpty(..))
21 import qualified Data.Map.Strict as Map
22 import Data.Maybe (Maybe(..), maybe)
23 import Data.Monoid (Monoid(..))
24 import Data.Text (Text)
25 import qualified Data.Text as Text
26 import qualified Data.Text.Lazy as TL
27 import qualified Data.Time.Calendar as Time
28 import qualified Data.Time.LocalTime as Time
30 import qualified Text.Parsec as R hiding (char, space, spaces, string)
31 import qualified Text.Parsec.Pos as R
33 import qualified Hcompta.Chart as Chart
34 import qualified Hcompta.Date as Date
35 import qualified Hcompta.Lib.Parsec as R
36 import qualified Hcompta.Posting as Posting
37 import qualified Hcompta.Tag as Tag
38 import qualified Hcompta.Transaction as Transaction
40 import qualified Hcompta.Format.Ledger as Ledger
41 import qualified Hcompta.Format.Ledger.Read as Ledger
42 import qualified Hcompta.Format.Ledger.Write as Ledger
44 deriving instance Eq Ledger.Amount
47 main = defaultMain $ hUnitTestToTests test
53 let (==>) (txt::Text) =
54 (~:) (Text.unpack txt) .
56 (rights [R.runParser_with_Error
57 (Ledger.read_date id Nothing <* R.eof) () "" txt])
60 [ Time.zonedTimeToUTC $
63 (Time.fromGregorian 2000 01 01)
64 (Time.TimeOfDay 0 0 0))
67 [ Time.zonedTimeToUTC $
70 (Time.fromGregorian 2000 01 01)
71 (Time.TimeOfDay 0 0 0))
73 , "2000-01-01_12:34" ==>
74 [ Time.zonedTimeToUTC $
77 (Time.fromGregorian 2000 01 01)
78 (Time.TimeOfDay 12 34 0))
80 , "2000-01-01_12:34:56" ==>
81 [ Time.zonedTimeToUTC $
84 (Time.fromGregorian 2000 01 01)
85 (Time.TimeOfDay 12 34 56))
87 , "2000-01-01_12:34_CET" ==>
88 [ Time.zonedTimeToUTC $
91 (Time.fromGregorian 2000 01 01)
92 (Time.TimeOfDay 12 34 0))
93 (Time.TimeZone 60 True "CET") ]
94 , "2000-01-01_12:34+01:30" ==>
95 [ Time.zonedTimeToUTC $
98 (Time.fromGregorian 2000 01 01)
99 (Time.TimeOfDay 12 34 0))
100 (Time.TimeZone 90 False "+01:30") ]
101 , "2000-01-01_12:34:56_CET" ==>
102 [ Time.zonedTimeToUTC $
105 (Time.fromGregorian 2000 01 01)
106 (Time.TimeOfDay 12 34 56))
107 (Time.TimeZone 60 True "CET") ]
108 , "2001-02-29" ==> []
110 let (==>) (txt::Text, def) =
111 (~:) (Text.unpack txt) .
112 (~?=) (rights [R.runParser_with_Error
113 (Ledger.read_date id (Just def) <* R.eof) () "" txt])
115 [ ("01-01", 2000) ==>
116 [ Time.zonedTimeToUTC $
119 (Time.fromGregorian 2000 01 01)
120 (Time.TimeOfDay 0 0 0))
123 , "read_account_section" ~:
124 let (==>) (txt::Text) b =
125 (~:) (Text.unpack txt) $
127 (rights [R.runParser (Ledger.read_account_section <* R.eof) () "" txt])
128 (if b then [txt] else [])
156 (Ledger.read_account_section)
162 let (==>) (txt::Text) =
163 (~:) (Text.unpack txt) .
164 (~?=) (rights [R.runParser
165 (Ledger.read_account <* R.eof) () "" txt])
173 , "A:B" ==> ["A":|["B"]]
174 , "A:B:C" ==> ["A":|["B","C"]]
175 , "Aa:Bbb:Cccc" ==> ["Aa":|["Bbb", "Cccc"]]
176 , "A a : B b b : C c c c" ==> ["A a ":|[" B b b ", " C c c c"]]
177 , "A: :C" ==> ["A":|[" ", "C"]]
179 , "A:B:(C)" ==> ["A":|["B", "(C)"]]
182 let (==>) (txt::Text) =
183 (~:) (Text.unpack txt) .
184 (~?=) (rights [R.runParser (Ledger.read_amount <* R.eof) () "" txt])
189 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
192 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
194 [( mempty { Ledger.amount_style_fractioning = Just '.' }
195 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
197 [( mempty { Ledger.amount_style_fractioning = Just '.' }
198 , Ledger.amount { Ledger.amount_quantity = Decimal 1 0 } )]
200 [( mempty { Ledger.amount_style_fractioning = Just ',' }
201 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
203 [( mempty { Ledger.amount_style_fractioning = Just ',' }
204 , Ledger.amount { Ledger.amount_quantity = Decimal 1 0 } )]
208 [( mempty { Ledger.amount_style_fractioning = Just '.' }
209 , Ledger.amount { Ledger.amount_quantity = Decimal 1 0 } )]
211 [( mempty { Ledger.amount_style_fractioning = Just '.' }
212 , Ledger.amount { Ledger.amount_quantity = Decimal 2 0 } )]
214 [( mempty { Ledger.amount_style_fractioning = Just ',' }
215 , Ledger.amount { Ledger.amount_quantity = Decimal 1 0 } )]
217 [( mempty { Ledger.amount_style_fractioning = Just ',' }
218 , Ledger.amount { Ledger.amount_quantity = Decimal 2 0 } )]
220 [( mempty { Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [1] }
221 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
223 [( mempty { Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [2] }
224 , Ledger.amount { Ledger.amount_quantity = Decimal 0 0 } )]
227 { Ledger.amount_style_fractioning = Just '.'
228 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [3] }
229 , Ledger.amount { Ledger.amount_quantity = Decimal 2 0 } )]
232 { Ledger.amount_style_fractioning = Just ','
233 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '.' [3] }
234 , Ledger.amount { Ledger.amount_quantity = Decimal 2 0 } )]
237 { Ledger.amount_style_fractioning = Just '.'
238 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [3] }
239 , Ledger.amount { Ledger.amount_quantity = Decimal 2 100000 } )]
242 { Ledger.amount_style_fractioning = Just ','
243 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '.' [3] }
244 , Ledger.amount { Ledger.amount_quantity = Decimal 2 100000 } )]
250 , Ledger.amount { Ledger.amount_quantity = Decimal 0 123 } )]
252 [( mempty { Ledger.amount_style_fractioning = Just '.' }
253 , Ledger.amount { Ledger.amount_quantity = Decimal 1 12 } )]
255 [( mempty { Ledger.amount_style_fractioning = Just ',' }
256 , Ledger.amount { Ledger.amount_quantity = Decimal 1 12 } )]
258 [( mempty { Ledger.amount_style_fractioning = Just '.' }
259 , Ledger.amount { Ledger.amount_quantity = Decimal 2 1234 } )]
261 [( mempty { Ledger.amount_style_fractioning = Just ',' }
262 , Ledger.amount { Ledger.amount_quantity = Decimal 2 1234 } )]
264 [( mempty { Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [1] }
265 , Ledger.amount { Ledger.amount_quantity = Decimal 0 12 } )]
267 [( mempty { Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [2] }
268 , Ledger.amount { Ledger.amount_quantity = Decimal 0 123 } )]
270 [( mempty { Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [3, 2] }
271 , Ledger.amount { Ledger.amount_quantity = Decimal 0 123456 } )]
272 , "1_23_456,7890_12345_678901" ==>
274 { Ledger.amount_style_fractioning = Just ','
275 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [3, 2]
276 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [4, 5, 6] }
277 , Ledger.amount { Ledger.amount_quantity = Decimal 15 123456789012345678901 } )]
278 , "1_23_456.7890_12345_678901" ==>
280 { Ledger.amount_style_fractioning = Just '.'
281 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [3, 2]
282 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [4, 5, 6] }
283 , Ledger.amount { Ledger.amount_quantity = Decimal 15 123456789012345678901 } )]
284 , "1,23,456.7890_12345_678901" ==>
286 { Ledger.amount_style_fractioning = Just '.'
287 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [3, 2]
288 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [4, 5, 6] }
289 , Ledger.amount { Ledger.amount_quantity = Decimal 15 123456789012345678901 } )]
290 , "1.23.456,7890_12345_678901" ==>
292 { Ledger.amount_style_fractioning = Just ','
293 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '.' [3, 2]
294 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [4, 5, 6] }
295 , Ledger.amount { Ledger.amount_quantity = Decimal 15 123456789012345678901 } )]
296 , "123456_78901_2345.678_90_1" ==>
298 { Ledger.amount_style_fractioning = Just '.'
299 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '_' [4, 5, 6]
300 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [3, 2] }
301 , Ledger.amount { Ledger.amount_quantity = Decimal 6 123456789012345678901 } )]
304 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
305 , Ledger.amount_style_unit_spaced = Just False }
307 { Ledger.amount_quantity = Decimal 0 1
308 , Ledger.amount_unit = "$" } )]
311 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Right
312 , Ledger.amount_style_unit_spaced = Just False }
314 { Ledger.amount_quantity = Decimal 0 1
315 , Ledger.amount_unit = "$" } )]
318 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
319 , Ledger.amount_style_unit_spaced = Just True }
321 { Ledger.amount_quantity = Decimal 0 1
322 , Ledger.amount_unit = "$" } )]
325 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Right
326 , Ledger.amount_style_unit_spaced = Just True }
328 { Ledger.amount_quantity = Decimal 0 1
329 , Ledger.amount_unit = "$" } )]
332 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
333 , Ledger.amount_style_unit_spaced = Just False }
335 { Ledger.amount_quantity = Decimal 0 (-1)
336 , Ledger.amount_unit = "$" } )]
339 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
340 , Ledger.amount_style_unit_spaced = Just False }
342 { Ledger.amount_quantity = Decimal 0 1
343 , Ledger.amount_unit = "4 2" } )]
346 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Right
347 , Ledger.amount_style_unit_spaced = Just False }
349 { Ledger.amount_quantity = Decimal 0 1
350 , Ledger.amount_unit = "4 2" } )]
353 { Ledger.amount_style_fractioning = Just ','
354 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '.' [3]
355 , Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
356 , Ledger.amount_style_unit_spaced = Just False }
358 { Ledger.amount_quantity = Decimal 2 100000
359 , Ledger.amount_unit = "$" } )]
362 { Ledger.amount_style_fractioning = Just ','
363 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping '.' [3]
364 , Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Right
365 , Ledger.amount_style_unit_spaced = Just False }
367 { Ledger.amount_quantity = Decimal 2 100000
368 , Ledger.amount_unit = "$" } )]
370 , "read_posting_type" ~:
371 let (==>) a (ty, ac) =
372 let read (t::Text) = rights [R.runParser
373 (Ledger.read_account <* R.eof) () "" t] in
374 (~:) (Text.unpack a) $
376 (Ledger.read_posting_type <$> read a)
377 (Ledger.Posting_Typed ty <$> read (maybe a id ac))
379 [ "A" ==> (Ledger.Posting_Type_Regular, Nothing)
380 , "(" ==> (Ledger.Posting_Type_Regular, Nothing)
381 , ")" ==> (Ledger.Posting_Type_Regular, Nothing)
382 , "()" ==> (Ledger.Posting_Type_Regular, Nothing)
383 , "( )" ==> (Ledger.Posting_Type_Regular, Nothing)
384 , "(A)" ==> (Ledger.Posting_Type_Virtual, Just "A")
385 , "(A:B:C)" ==> (Ledger.Posting_Type_Virtual, Just "A:B:C")
386 , "A:B:C" ==> (Ledger.Posting_Type_Regular, Nothing)
387 , "(A):B:C" ==> (Ledger.Posting_Type_Regular, Nothing)
388 , "A:(B):C" ==> (Ledger.Posting_Type_Regular, Nothing)
389 , "A:B:(C)" ==> (Ledger.Posting_Type_Regular, Nothing)
390 , "[" ==> (Ledger.Posting_Type_Regular, Nothing)
391 , "]" ==> (Ledger.Posting_Type_Regular, Nothing)
392 , "[]" ==> (Ledger.Posting_Type_Regular, Nothing)
393 , "[ ]" ==> (Ledger.Posting_Type_Regular, Nothing)
394 , "[A]" ==> (Ledger.Posting_Type_Virtual_Balanced, Just "A")
395 , "[A:B:C]" ==> (Ledger.Posting_Type_Virtual_Balanced, Just "A:B:C")
396 , "A:B:C" ==> (Ledger.Posting_Type_Regular, Nothing)
397 , "[A]:B:C" ==> (Ledger.Posting_Type_Regular, Nothing)
398 , "A:[B]:C" ==> (Ledger.Posting_Type_Regular, Nothing)
399 , "A:B:[C]" ==> (Ledger.Posting_Type_Regular, Nothing)
402 let (==>) (txt::Text, end) =
403 (~:) (Text.unpack txt) .
404 (~?=) (rights [R.runParser (Ledger.read_comment <* end) () "" txt])
406 [ ("; some comment", R.eof) ==> [" some comment"]
407 , ("; some comment \n", R.newline <* R.eof) ==> [ " some comment " ]
408 , ("; some comment \r\n", R.string "\r\n" <* R.eof) ==> [ " some comment " ]
411 let (==>) (txt::Text, end) =
412 (~:) (Text.unpack txt) .
413 (~?=) (rights [R.runParser (Ledger.read_comments <* end) () "" txt])
415 [ ("; some comment\n ; some other comment", R.eof) ==> [ [" some comment", " some other comment"] ]
416 , ("; some comment \n", R.string "\n" <* R.eof) ==> [ [" some comment "] ]
418 , "read_tag_value" ~:
419 let (==>) (txt::Text, end) =
420 (~:) (Text.unpack txt) .
421 (~?=) (rights [R.runParser (Ledger.read_tag_value <* end) () "" txt])
423 [ (",", R.eof) ==> [","]
424 , (",\n", R.char '\n' <* R.eof) ==> [","]
425 , (",x", R.eof) ==> [",x"]
426 , (",x:", R.string ",x:" <* R.eof) ==> [""]
427 , ("v, v, n:", R.string ", n:" <* R.eof) ==> ["v, v"]
430 let (==>) (txt::Text, end) =
431 (~:) (Text.unpack txt) .
432 (~?=) (rights [R.runParser (Ledger.read_tag <* end) () "" txt])
434 [ ("Name:" , R.eof) ==> [("Name":|[], "")]
435 , ("Name:Value" , R.eof) ==> [("Name":|[], "Value")]
436 , ("Name:Value\n" , R.string "\n" <* R.eof) ==> [("Name":|[], "Value")]
437 , ("Name:Val ue" , R.eof) ==> [("Name":|[], "Val ue")]
438 , ("Name:," , R.eof) ==> [("Name":|[], ",")]
439 , ("Name:Val,ue" , R.eof) ==> [("Name":|[], "Val,ue")]
440 , ("Name:Val,ue:" , R.string ",ue:" <* R.eof) ==> [("Name":|[], "Val")]
441 , ("Name:Val,ue :", R.eof) ==> [("Name":|[], "Val,ue :")]
444 let (==>) (txt::Text) =
445 (~:) (Text.unpack txt) .
446 (~?=) (rights [R.runParser (Ledger.read_tags <* R.eof) () "" txt]) .
449 [ "Name:" ==> [ ("Name":|[], [""]) ]
450 , "Name:," ==> [ ("Name":|[], [","]) ]
451 , "Name:,Name:" ==> [ ("Name":|[], ["", ""]) ]
454 , ("Name2":|[], [""])
456 , "Name: , Name2:" ==>
457 [ ("Name":|[], [" "])
458 , ("Name2":|[], [""])
460 , "Name:,Name2:,Name3:" ==>
462 , ("Name2":|[], [""])
463 , ("Name3":|[], [""])
465 , "Name:Val ue,Name2:V a l u e,Name3:V al ue" ==>
466 [ ("Name":|[], ["Val ue"])
467 , ("Name2":|[], ["V a l u e"])
468 , ("Name3":|[], ["V al ue"])
472 let (==>) (txt::Text) =
474 ( Ledger.read_context (const ()) Ledger.journal
475 ::Ledger.Read_Context () ()) in
476 (~:) (Text.unpack txt) .
477 (~?=) (rights [R.runParser_with_Error
478 (Ledger.read_posting <* R.eof) read_context "" txt]) .
479 fmap (\p -> Ledger.Posting_Typed Ledger.Posting_Type_Regular
480 p { Ledger.posting_sourcepos = R.newPos "" 1 1 })
482 [ " A:B:C" ==> [Ledger.posting ("A":|["B", "C"])]
484 , " !A:B:C" ==> [(Ledger.posting ("A":|["B", "C"]))
485 { Ledger.posting_status = True }]
486 , " *A:B:C" ==> [(Ledger.posting ("A":|["B", "C"]))
487 { Ledger.posting_status = True }]
488 , " A:B:C $1" ==> [Ledger.posting ("A":|["B", "C $1"])]
489 , " A:B:C $1" ==> [(Ledger.posting ("A":|["B", "C"]))
490 { Ledger.posting_amounts = Map.fromList [("$", 1)] }]
491 , " A:B:C $1 + 1€" ==> [(Ledger.posting ("A":|["B", "C"]))
492 { Ledger.posting_amounts = Map.fromList [("$", 1), ("€", 1)] }]
493 , " A:B:C $1 + 1$" ==> [(Ledger.posting ("A":|["B", "C"]))
494 { Ledger.posting_amounts = Map.fromList [("$", 2)] }]
495 , " A:B:C $1 + 1$ + 1$" ==> [(Ledger.posting ("A":|["B", "C"]))
496 { Ledger.posting_amounts = Map.fromList [("$", 3)] }]
497 , " A:B:C ; some comment" ==> [(Ledger.posting ("A":|["B", "C"]))
498 { Ledger.posting_amounts = Map.fromList []
499 , Ledger.posting_comments = [" some comment"] }]
500 , " A:B:C ; some comment\n ; some other comment" ==>
501 [(Ledger.posting ("A":|["B", "C"]))
502 { Ledger.posting_amounts = Map.fromList []
503 , Ledger.posting_comments = [" some comment", " some other comment"] }]
504 , " A:B:C $1 ; some comment" ==>
505 [(Ledger.posting ("A":|["B", "C"]))
506 { Ledger.posting_amounts = Map.fromList [("$", 1)]
507 , Ledger.posting_comments = [" some comment"] }]
509 [(Ledger.posting ("A":|["B", "C"]))
510 { Ledger.posting_comments = [" N:V"]
511 , Ledger.posting_tags = Posting.Posting_Tags $
512 Tag.from_List [ ("N":|[], "V") ] }]
513 , " A:B:C ; some comment N:V" ==>
514 [(Ledger.posting ("A":|["B", "C"]))
515 { Ledger.posting_comments = [" some comment N:V"]
516 , Ledger.posting_tags = Posting.Posting_Tags $
517 Tag.from_List [ ("N":|[], "V") ] }]
518 , " A:B:C ; some comment N:V v, N2:V2 v2" ==>
519 [(Ledger.posting ("A":|["B", "C"]))
520 { Ledger.posting_comments = [" some comment N:V v, N2:V2 v2"]
521 , Ledger.posting_tags = Posting.Posting_Tags $
524 , ("N2":|[], "V2 v2") ] }]
525 , " A:B:C ; N:V\n ; N:V2" ==>
526 [(Ledger.posting ("A":|["B", "C"]))
527 { Ledger.posting_comments = [" N:V", " N:V2"]
528 , Ledger.posting_tags = Posting.Posting_Tags $
533 , " A:B:C ; N:V\n ; N2:V" ==>
534 [(Ledger.posting ("A":|["B", "C"]))
535 { Ledger.posting_comments = [" N:V", " N2:V"]
536 , Ledger.posting_tags = Posting.Posting_Tags $
541 , " A:B:C ; date:2001-01-01" ==>
542 [(Ledger.posting ("A":|["B", "C"]))
543 { Ledger.posting_comments = [" date:2001-01-01"]
544 , Ledger.posting_dates =
545 [ Time.zonedTimeToUTC $
548 (Time.fromGregorian 2001 01 01)
549 (Time.TimeOfDay 0 0 0))
552 , Ledger.posting_tags = Posting.Posting_Tags $
554 [ ("date":|[], "2001-01-01") ] }]
555 , " (A:B:C) = Right (A:B:C)" ~:
556 (rights [R.runParser_with_Error
557 (Ledger.read_posting <* R.eof)
558 ( Ledger.read_context (const ()) Ledger.journal
559 ::Ledger.Read_Context () ())
560 "" (" (A:B:C)"::Text)]) ~?=
561 [Ledger.Posting_Typed
562 Ledger.Posting_Type_Virtual
563 (Ledger.posting ("A":|["B", "C"]))]
564 , " [A:B:C] = Right [A:B:C]" ~:
565 (rights [R.runParser_with_Error
566 (Ledger.read_posting <* R.eof)
567 ( Ledger.read_context (const ()) Ledger.journal
568 ::Ledger.Read_Context () ())
569 "" (" [A:B:C]"::Text)]) ~?=
570 [Ledger.Posting_Typed
571 Ledger.Posting_Type_Virtual_Balanced
572 (Ledger.posting ("A":|["B", "C"]))]
574 , "read_transaction" ~:
575 let (==>) (txt::Text) =
577 ( Ledger.read_context (const ()) Ledger.journal
578 ::Ledger.Read_Context () ()) in
579 (~:) (Text.unpack txt) .
580 (~?=) (rights [R.runParser_with_Error
581 (Ledger.read_transaction <* R.eof) read_context "" txt]) .
582 fmap (\t -> t { Ledger.transaction_sourcepos = R.newPos "" 1 1 })
584 [ "2000-01-01 some wording\n A:B:C $1\n a:b:c" ==>
586 { Ledger.transaction_dates=
587 ( Time.zonedTimeToUTC $
590 (Time.fromGregorian 2000 01 01)
591 (Time.TimeOfDay 0 0 0))
594 , Ledger.transaction_wording="some wording"
595 , Ledger.transaction_postings = Ledger.postings_by_account
596 [ (Ledger.posting ("A":|["B", "C"]))
597 { Ledger.posting_amounts = Map.fromList [ ("$", 1) ]
598 , Ledger.posting_sourcepos = R.newPos "" 2 1 }
599 , (Ledger.posting ("a":|["b", "c"]))
600 { Ledger.posting_amounts = Map.fromList [ ("$", -1) ]
601 , Ledger.posting_sourcepos = R.newPos "" 3 1 }
604 , "2000-01-01 some wording\n A:B:C $1\n a:b:c\n" ==> []
605 , "2000-01-01 some wording ; some comment\n ; some other;comment\n ; some Tag:\n ; some last comment\n A:B:C $1\n a:b:c" ==>
607 { Ledger.transaction_comments_after =
609 , " some other;comment"
611 , " some last comment"
613 , Ledger.transaction_dates=
614 ( Time.zonedTimeToUTC $
617 (Time.fromGregorian 2000 01 01)
618 (Time.TimeOfDay 0 0 0))
621 , Ledger.transaction_wording="some wording"
622 , Ledger.transaction_postings = Ledger.postings_by_account
623 [ (Ledger.posting ("A":|["B", "C"]))
624 { Ledger.posting_amounts = Map.fromList [ ("$", 1) ]
625 , Ledger.posting_sourcepos = R.newPos "" 5 1 }
626 , (Ledger.posting ("a":|["b", "c"]))
627 { Ledger.posting_amounts = Map.fromList [ ("$", -1) ]
628 , Ledger.posting_sourcepos = R.newPos "" 6 1 } ]
629 , Ledger.transaction_tags = Transaction.Transaction_Tags $
630 Tag.from_List [ ("Tag":|[], "") ] }]
632 , "read_journal" ~: TestList
633 [ "2000-01-01 1° wording\\n A:B:C $1\\n a:b:c\\n2000-01-02 2° wording\\n A:B:C $1\\n x:y:z" ~: TestCase $ do
635 R.runParserT_with_Error
636 (Ledger.read_journal "" {-<* R.eof-})
637 ( Ledger.read_context id Ledger.journal
638 ::Ledger.Read_Context (Ledger.Charted Ledger.Transaction)
639 ([Ledger.Charted Ledger.Transaction]))
640 "" ("2000-01-01 1° wording\n A:B:C $1\n a:b:c\n2000-01-02 2° wording\n A:B:C $1\n x:y:z"::Text)
641 ((\j -> j{Ledger.journal_last_read_time=Date.nil}) <$>
642 Data.Either.rights [jnl])
645 { Ledger.journal_content =
646 fmap (Chart.Charted mempty) $
648 { Ledger.transaction_dates=
649 ( Time.zonedTimeToUTC $
652 (Time.fromGregorian 2000 01 02)
653 (Time.TimeOfDay 0 0 0))
656 , Ledger.transaction_wording="2° wording"
657 , Ledger.transaction_postings = Ledger.postings_by_account
658 [ (Ledger.posting ("A":|["B", "C"]))
659 { Ledger.posting_amounts = Map.fromList
662 , Ledger.posting_sourcepos = R.newPos "" 5 1
664 , (Ledger.posting ("x":|["y", "z"]))
665 { Ledger.posting_amounts = Map.fromList
668 , Ledger.posting_sourcepos = R.newPos "" 6 1
671 , Ledger.transaction_sourcepos = R.newPos "" 4 1
674 { Ledger.transaction_dates=
675 ( Time.zonedTimeToUTC $
678 (Time.fromGregorian 2000 01 01)
679 (Time.TimeOfDay 0 0 0))
682 , Ledger.transaction_wording="1° wording"
683 , Ledger.transaction_postings = Ledger.postings_by_account
684 [ (Ledger.posting ("A":|["B", "C"]))
685 { Ledger.posting_amounts = Map.fromList
688 , Ledger.posting_sourcepos = R.newPos "" 2 1
690 , (Ledger.posting ("a":|["b", "c"]))
691 { Ledger.posting_amounts = Map.fromList
694 , Ledger.posting_sourcepos = R.newPos "" 3 1
697 , Ledger.transaction_sourcepos = R.newPos "" 1 1
700 , Ledger.journal_files = [""]
701 , Ledger.journal_amount_styles = Ledger.Amount_Styles $ Map.fromList
704 { Ledger.amount_style_unit_side = Just Ledger.Amount_Style_Side_Left
705 , Ledger.amount_style_unit_spaced = Just False }
712 , "Write" ~: TestList
714 let (==>) (txt::Text) e =
715 (~:) (Text.unpack txt) $
719 { Ledger.write_style_color = False
720 , Ledger.write_style_align = True } .
721 Ledger.write_date <$>
722 rights [R.runParser_with_Error
723 (Ledger.read_date id Nothing <* R.eof) () "" txt])
729 { Ledger.write_style_color = False
730 , Ledger.write_style_align = True } $
731 Ledger.write_date Date.nil)
733 , "2000-01-01" ==> "2000-01-01"
734 , "2000-01-01_12:34:51_CET" ==> "2000-01-01_11:34:51"
735 , "2000-01-01_12:34:51+01:10" ==> "2000-01-01_11:24:51"
736 , "2000-01-01_12:34:51-01:10" ==> "2000-01-01_13:44:51"
737 , "2000-01-01_01:02:03" ==> "2000-01-01_01:02:03"
738 , "2000-01-01_01:02" ==> "2000-01-01_01:02"
739 , "2000-01-01_01:00" ==> "2000-01-01_01:00"
742 let (<==) (txt::Text) e =
743 (~:) (Text.unpack txt) $
747 { Ledger.write_style_color = False
748 , Ledger.write_style_align = True } $
749 Ledger.write_amount e)
757 , Ledger.amount { Ledger.amount_quantity = Decimal 2 0 } )
760 , Ledger.amount { Ledger.amount_quantity = Decimal 0 123 } )
763 , Ledger.amount { Ledger.amount_quantity = Decimal 0 (- 123) } )
765 ( mempty { Ledger.amount_style_fractioning = Just '.' }
766 , Ledger.amount { Ledger.amount_quantity = Decimal 1 123 } )
769 { Ledger.amount_style_fractioning = Just '.'
770 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [3]
772 , Ledger.amount { Ledger.amount_quantity = Decimal 2 123456 })
773 , "123,456,789,01,2.3456789" <==
775 { Ledger.amount_style_fractioning = Just '.'
776 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [1, 2, 3]
778 , Ledger.amount { Ledger.amount_quantity = Decimal 7 1234567890123456789 } )
779 , "1234567.8_90_123_456_789" <==
781 { Ledger.amount_style_fractioning = Just '.'
782 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [1, 2, 3]
784 , Ledger.amount { Ledger.amount_quantity = Decimal 12 1234567890123456789 })
785 , "1,2,3,4,5,6,7,89,012.3456789" <==
787 { Ledger.amount_style_fractioning = Just '.'
788 , Ledger.amount_style_grouping_integral = Just $ Ledger.Amount_Style_Grouping ',' [3, 2, 1]
790 , Ledger.amount { Ledger.amount_quantity = Decimal 7 1234567890123456789 })
791 , "1234567.890_12_3_4_5_6_7_8_9" <==
793 { Ledger.amount_style_fractioning = Just '.'
794 , Ledger.amount_style_grouping_fractional = Just $ Ledger.Amount_Style_Grouping '_' [3, 2, 1]
796 , Ledger.amount { Ledger.amount_quantity = Decimal 12 1234567890123456789 })
798 , "write_amount_length" ~:
799 let (==>) (txt::Text) =
800 (~:) (Text.unpack txt) $
802 (Ledger.write_amount_length <$>
803 rights [R.runParser (Ledger.read_amount <* R.eof) () "" txt])
805 in TestList $ (==>) <$>
813 , "123,456,789,01,2.3456789"
814 , "1234567.8_90_123_456_789"
815 , "1,2,3,4,5,6,7,89,012.3456789"
816 , "1234567.890_12_3_4_5_6_7_8_9"
817 , "1000000.000_00_0_0_0_0_0_0_0"
830 (~:) (Text.unpack txt) $
832 (let read (t::Text) =
834 (Ledger.read_account <* R.eof)
838 { Ledger.write_style_color = False
839 , Ledger.write_style_align = True } <$>
841 let Ledger.Posting_Typed ty ac = Ledger.read_posting_type a in
842 return $ Ledger.write_account ty ac)
845 in TestList $ (==>) <$>
850 , "write_transaction" ~:
851 let (==>) (txt::Text) =
852 (~:) (Text.unpack txt) .
854 let write (txn, ctx) =
857 { Ledger.write_style_color = False
858 , Ledger.write_style_align = True } $
859 let jnl = Ledger.read_context_journal ctx in
860 let sty = Ledger.journal_amount_styles jnl in
861 Ledger.write_transaction sty txn in
862 either (const []) (pure . write) $
863 R.runParser_with_Error
864 (R.and_state (Ledger.read_transaction <* R.eof))
865 ( Ledger.read_context Chart.charted Ledger.journal
866 ::Ledger.Read_Context Ledger.Transaction [Ledger.Transaction] )
869 [ "2000-01-01 some wording\n A:B:C $1\n a:b:c" ==>
870 ["2000-01-01 some wording\n\tA:B:C $1\n\ta:b:c $-1\n"]
871 , "2000-01-01 some wording\n\tA:B:C $1\n\ta:b:c\n\t ; first comment\n\t ; second comment\n\t ; third comment" ==>
872 ["2000-01-01 some wording\n\tA:B:C $1\n\ta:b:c $-1\n\t ; first comment\n\t ; second comment\n\t ; third comment\n"]
873 , "2000-01-01 some wording\n\tA:B:C $1\n\tAA:BB:CC $123" ==> []
878 { Ledger.write_style_color = False
879 , Ledger.write_style_align = True } $
880 Ledger.write_transaction
883 ~?= "1970-01-01\n\n")