{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} import Prelude import Test.HUnit hiding ((~?)) import Test.Framework.Providers.HUnit (hUnitTestToTests) import Test.Framework.Runners.Console (defaultMain) import Control.Applicative ((<*)) import Control.Arrow ((***)) import Control.Monad.IO.Class (liftIO) import Data.Decimal (DecimalRaw(..)) import qualified Data.Either import qualified Data.List import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.Map.Strict as Data.Map import Data.Text (Text) import qualified Data.Time.Calendar as Time import qualified Data.Time.LocalTime as Time import qualified Text.Parsec as P hiding (char, space, spaces, string) import qualified Text.Parsec.Pos as P -- import qualified Text.PrettyPrint.Leijen.Text as PP import qualified Hcompta.Model.Account as Account import Hcompta.Model.Account (Account) import qualified Hcompta.Model.Amount as Amount import Hcompta.Model.Amount (Amount) import qualified Hcompta.Model.Amount.Style as Amount.Style import qualified Hcompta.Model.Date as Date import qualified Hcompta.Model.Date.Read as Date.Read import qualified Hcompta.Model.Filter as Filter import qualified Hcompta.Model.Filter.Read as Filter.Read import qualified Hcompta.Calc.Balance as Calc.Balance import qualified Hcompta.Format.Ledger as Format.Ledger import qualified Hcompta.Format.Ledger.Read as Format.Ledger.Read import qualified Hcompta.Format.Ledger.Write as Format.Ledger.Write import qualified Hcompta.Lib.TreeMap as Lib.TreeMap import qualified Hcompta.Lib.Parsec as P import qualified Hcompta.Lib.Foldable as Lib.Foldable main :: IO () main = defaultMain $ hUnitTestToTests test_Hcompta (~?) :: String -> Bool -> Test (~?) s b = s ~: (b ~?= True) test_Hcompta :: Test test_Hcompta = TestList [ "Lib" ~: TestList [ "TreeMap" ~: TestList [ "insert" ~: TestList [ "[] 0" ~: (Lib.TreeMap.insert const ((0::Int):|[]) () Lib.TreeMap.empty) ~?= (Lib.TreeMap.TreeMap $ Data.Map.fromList [ ((0::Int), Lib.TreeMap.leaf ()) ]) , "[] 0/1" ~: (Lib.TreeMap.insert const ((0::Int):|1:[]) () Lib.TreeMap.empty) ~?= (Lib.TreeMap.TreeMap $ Data.Map.fromList [ ((0::Int), Lib.TreeMap.Node { Lib.TreeMap.node_value = Nothing , Lib.TreeMap.node_size = 1 , Lib.TreeMap.node_descendants = Lib.TreeMap.singleton ((1::Int):|[]) () }) ]) ] , "union" ~: TestList [ ] , "map_by_depth_first" ~: TestList [ ] , "flatten" ~: TestList [ "[0, 0/1, 0/1/2]" ~: (Lib.TreeMap.flatten id $ Lib.TreeMap.from_List const [ (((0::Integer):|[]), ()) , ((0:|1:[]), ()) , ((0:|1:2:[]), ()) ] ) ~?= (Data.Map.fromList [ ((0:|[]), ()) , ((0:|1:[]), ()) , ((0:|1:2:[]), ()) ]) , "[1, 1/2, 1/22, 1/2/3, 1/2/33, 11, 11/2, 11/2/3, 11/2/33]" ~: (Lib.TreeMap.flatten id $ Lib.TreeMap.from_List const [ ((1:|[]), ()) , ((1:|2:[]), ()) , ((1:|22:[]), ()) , ((1:|2:3:[]), ()) , ((1:|2:33:[]), ()) , ((11:|[]), ()) , ((11:|2:[]), ()) , ((11:|2:3:[]), ()) , ((11:|2:33:[]), ()) ] ) ~?= (Data.Map.fromList [ (((1::Integer):|[]), ()) , ((1:|2:[]), ()) , ((1:|22:[]), ()) , ((1:|2:3:[]), ()) , ((1:|2:33:[]), ()) , ((11:|[]), ()) , ((11:|2:[]), ()) , ((11:|2:3:[]), ()) , ((11:|2:33:[]), ()) ]) ] ] , "Foldable" ~: TestList [ "accumLeftsAndFoldrRights" ~: TestList [ "Left" ~: (Lib.Foldable.accumLeftsAndFoldrRights (++) [""] $ [Left [0]]) ~?= (([(0::Integer)], [(""::String)])) , "repeat Left" ~: ((take 1 *** take 0) $ Lib.Foldable.accumLeftsAndFoldrRights (++) [""] $ ( repeat (Left [0]) )) ~?= ([(0::Integer)], ([]::[String])) , "Right:Left:Right:Left" ~: (Lib.Foldable.accumLeftsAndFoldrRights (++) ["0"] $ ( Right ["2"]:Left [1]:Right ["1"]:Left [0]:[] )) ~?= (([1, 0]::[Integer]), (["2", "1", "0"]::[String])) , "Right:Left:Right:repeat Left" ~: ((take 1 *** take 2) $ Lib.Foldable.accumLeftsAndFoldrRights (++) ["0"] $ ( Right ["2"]:Left [1]:Right ["1"]:repeat (Left [0]) )) ~?= (([1]::[Integer]), (["2", "1"]::[String])) ] ] ] , "Model" ~: TestList [ "Account" ~: TestList [ "foldr" ~: TestList [ "[A]" ~: (reverse $ Account.foldr ("A":|[]) (:) []) ~?= ["A":|[]] , "[A, B]" ~: (reverse $ Account.foldr ("A":|["B"]) (:) []) ~?= ["A":|[], "A":|["B"]] , "[A, B, C]" ~: (reverse $ Account.foldr ("A":|["B", "C"]) (:) []) ~?= ["A":|[], "A":|["B"], "A":|["B", "C"]] ] , "ascending" ~: TestList [ "[A]" ~: Account.ascending ("A":|[]) ~?= Nothing , "[A, B]" ~: Account.ascending ("A":|["B"]) ~?= Just ("A":|[]) , "[A, B, C]" ~: Account.ascending ("A":|["B", "C"]) ~?= Just ("A":|["B"]) ] ] , "Amount" ~: TestList [ "+" ~: TestList [ "$1 + 1$ = $2" ~: (+) (Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Left } , Amount.unit = "$" }) (Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Right } , Amount.unit = "$" }) ~?= (Amount.nil { Amount.quantity = Decimal 0 2 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Left } , Amount.unit = "$" }) ] , "from_List" ~: TestList [ "from_List [$1, 1$] = $2" ~: Amount.from_List [ Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Left } , Amount.unit = "$" } , Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Right } , Amount.unit = "$" } ] ~?= Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = Decimal 0 2 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just $ Amount.Style.Side_Left } , Amount.unit = "$" }) ] ] ] , "Filter" ~: TestList [ "test" ~: TestList [ "Test_Account" ~: TestList [ "A A" ~? Filter.test [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") ] (("A":|[]::Account)) , "* A" ~? Filter.test [ Filter.Test_Account_Section_Any ] (("A":|[]::Account)) , ": A" ~? Filter.test [ Filter.Test_Account_Section_Many ] (("A":|[]::Account)) , ":A A" ~? Filter.test [ Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") ] (("A":|[]::Account)) , "A: A" ~? Filter.test [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many ] (("A":|[]::Account)) , "A: A:B" ~? Filter.test [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many ] (("A":|"B":[]::Account)) , "A:B A:B" ~? Filter.test [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") ] (("A":|"B":[]::Account)) , "A::B A:B" ~? Filter.test [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") ] (("A":|"B":[]::Account)) , ":B: A:B:C" ~? Filter.test [ Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") , Filter.Test_Account_Section_Many ] (("A":|"B":"C":[]::Account)) , ":C A:B:C" ~? Filter.test [ Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "C") ] (("A":|"B":"C":[]::Account)) ] , "Test_Bool" ~: TestList [ "Any A" ~? Filter.test (Filter.Any::Filter.Test_Bool Filter.Test_Account) (("A":|[]::Account)) ] ] , "Read" ~: TestList [ "test_account_section" ~: TestList [ "*" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("*"::Text)]) ~?= [ [Filter.Test_Account_Section_Any] ] , "A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A"::Text)]) ~?= [ [Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A")] ] , "AA" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("AA"::Text)]) ~?= [ [Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "AA")] ] , "::A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("::A"::Text)]) ~?= [ [ Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") ] ] , ":A" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" (":A"::Text)]) ~?= [ [ Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") ] ] , "A:" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A:"::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many ] ] , "A::" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A::"::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many ] ] , "A:B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A:B"::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") ] ] , "A::B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A::B"::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") ] ] , "A:::B" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.eof) () "" ("A:::B"::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many , Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "B") ] ] , "A: " ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_account <* P.char ' ' <* P.eof) () "" ("A: "::Text)]) ~?= [ [ Filter.Test_Account_Section_Text (Filter.Test_Text_Exact "A") , Filter.Test_Account_Section_Many ] ] ] , "test_bool" ~: TestList [ "( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_bool [ P.char 'E' >> return (return True) ] <* P.eof) () "" ("( E )"::Text)]) ~?= [ Filter.And (Filter.Bool True) Filter.Any ] , "( ( E ) )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_bool [ P.char 'E' >> return (return True) ] <* P.eof) () "" ("( ( E ) )"::Text)]) ~?= [ Filter.And (Filter.And (Filter.Bool True) Filter.Any) Filter.Any ] , "( E ) & ( E )" ~: (Data.Either.rights $ [P.runParser (Filter.Read.test_bool [ P.char 'E' >> return (return True) ] <* P.eof) () "" ("( E ) & ( E )"::Text)]) ~?= [ Filter.And (Filter.And (Filter.Bool True) Filter.Any) (Filter.And (Filter.Bool True) Filter.Any) ] ] ] ] ] , "Calc" ~: TestList [ "Balance" ~: TestList [ "balance" ~: TestList [ "[A+$1] = A+$1 & $+1" ~: (Calc.Balance.balance (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } Calc.Balance.nil) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "[A+$1, A-$1] = {A+$0, $+0}" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ [ ( "A":|[] , Data.Map.fromListWith const $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance s, s)) [ Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } ] ) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "[A+$1, A-€1] = {A+$1-€1, $+1 €-1}" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.eur $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1, Amount.eur $ -1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Nothing , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 1 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.eur $ -1 , Calc.Balance.amount_sum_positive = Nothing , Calc.Balance.amount_sum_balance = Amount.eur $ -1 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "[A+$1, B-$1] = {A+$1 B-$1, $+0}" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ -1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } , "[A+$1, B+$1]" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } , "[A+$1+€2, A-$1-€2] = {A+$0+€0, $+0 €+0}" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2 ] } , (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ [ ("A":|[] , Data.Map.fromListWith const $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance s, s)) [ Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } , Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.eur $ -2 , Calc.Balance.amount_sum_positive = Just $ Amount.eur $ 2 , Calc.Balance.amount_sum_balance = Amount.eur $ 0 } ] ) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.eur $ -2 , Calc.Balance.amount_sum_positive = Just $ Amount.eur $ 2 , Calc.Balance.amount_sum_balance = Amount.eur $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "[A+$1+€2+£3, B-$1-2€-£3] = {A+$1+€2+£3 B-$1-€2-£3, $+0 €+0 £+0}" ~: (Data.List.foldl (flip Calc.Balance.balance) Calc.Balance.nil [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2, Amount.gbp $ 3 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2, Amount.gbp $ -3 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2, Amount.gbp $ 3 ]) , ("B":|[], Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2, Amount.gbp $ -3 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.usd $ -1 , Calc.Balance.amount_sum_positive = Just $ Amount.usd $ 1 , Calc.Balance.amount_sum_balance = Amount.usd $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.eur $ -2 , Calc.Balance.amount_sum_positive = Just $ Amount.eur $ 2 , Calc.Balance.amount_sum_balance = Amount.eur $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.Amount_Sum { Calc.Balance.amount_sum_negative = Just $ Amount.gbp $ -3 , Calc.Balance.amount_sum_positive = Just $ Amount.gbp $ 3 , Calc.Balance.amount_sum_balance = Amount.gbp $ 0 } , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } ] , "union" ~: TestList [ "nil nil = nil" ~: Calc.Balance.union Calc.Balance.nil Calc.Balance.nil ~?= (Calc.Balance.nil::Calc.Balance.Balance Amount) , "{A+$1, $+1} {A+$1, $+1} = {A+$2, $+2}" ~: Calc.Balance.union (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] }) (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 2 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "{A+$1, $+1} {B+$1, $+1} = {A+$1 B+$1, $+2}" ~: Calc.Balance.union (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] }) (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("B":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } , "{A+$1, $+1} {B+€1, €+1} = {A+$1 B+€1, $+1 €+1}" ~: Calc.Balance.union (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] }) (Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("B":|[], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ] } ] , "expanded" ~: TestList [ "nil_By_Account" ~: Calc.Balance.expanded Lib.TreeMap.empty ~?= (Lib.TreeMap.empty::Calc.Balance.Expanded Amount) , "A+$1 = A+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const $ [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A/A+$1 = A+$1 A/A+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|["A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [] }) , ("A":|["A"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A/B+$1 = A+$1 A/B+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|["B"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A/B/C+$1 = A+$1 A/B+$1 A/B/C+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|["B", "C"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const $ [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [] }) , ("A":|["B", "C"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A+$1 A/B+$1 = A+$2 A/B+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 2 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/B/C+$1 = A+$3 A/B+$2 A/B/C+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B"], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B", "C"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 3 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 2 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B", "C"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/B/C+$1 A/B/C/D+$1 = A+$4 A/B+$3 A/B/C+$2 A/B/C/D+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B"], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B", "C"], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B", "C", "D"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 4 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 3 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B", "C"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 2 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B", "C", "D"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) , "A+$1 A/B+$1 A/BB+$1 AA/B+$1 = A+$3 A/B+$1 A/BB+$1 AA+$1 AA/B+$1" ~: Calc.Balance.expanded (Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["B"], Amount.from_List [ Amount.usd $ 1 ]) , ("A":|["BB"], Amount.from_List [ Amount.usd $ 1 ]) , ("AA":|["B"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Lib.TreeMap.from_List const [ ("A":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 3 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("A":|["BB"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) , ("AA":|[], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [] }) , ("AA":|["B"], Calc.Balance.Account_Sum_Expanded { Calc.Balance.inclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] , Calc.Balance.exclusive = Data.Map.map Calc.Balance.amount_sum $ Amount.from_List [ Amount.usd $ 1 ] }) ]) ] , "deviation" ~: TestList [ "{A+$1, $1}" ~: (Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List []) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] }) ~?= (Calc.Balance.Deviation $ Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ]) , "{A+$1 B+$1, $2}" ~: (Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) [ "A":|[] , "B":|[] ] } ] }) ~?= (Calc.Balance.Deviation $ Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) [ ] } ]) ] , "is_equilibrium_inferrable" ~: TestList [ "nil" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ (Calc.Balance.nil::Calc.Balance.Balance Amount.Amount) , "{A+$0, $+0}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 0 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "{A+$1, $+1}" ~: TestCase $ (@=?) False $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "{A+$0+€0, $0 €+0}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 0, Amount.eur $ 0 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "{A+$1, B-$1, $+0}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ -1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } , "{A+$1 B, $+1}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List []) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } ] } , "{A+$1 B+€1, $+1 €+1}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ] } , "{A+$1 B-$1+€1, $+0 €+1}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1 ]) , ("B":|[], Amount.from_List [ Amount.usd $ -1, Amount.eur $ 1 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 1 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["B":|[]] } ] } , "{A+$1+€2+£3 B-$1-€2-£3, $+0 €+0 £+0}" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrium_inferrable $ Calc.Balance.deviation $ Calc.Balance.Balance { Calc.Balance.balance_by_account = Lib.TreeMap.from_List const $ Data.List.map (id *** Data.Map.map Calc.Balance.amount_sum) $ [ ("A":|[], Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2, Amount.gbp $ 3 ]) , ("B":|[], Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2, Amount.gbp $ -3 ]) ] , Calc.Balance.balance_by_unit = Data.Map.fromList $ Data.List.map (\s -> (Amount.unit $ Calc.Balance.amount_sum_balance $ Calc.Balance.unit_sum_amount s, s)) [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.eur $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } , Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.gbp $ 0 , Calc.Balance.unit_sum_accounts = Data.Map.fromList $ Data.List.map (,()) ["A":|[], "B":|[]] } ] } ] , "infer_equilibrium" ~: TestList [ "{A+$1 B}" ~: (snd $ Calc.Balance.infer_equilibrium $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [] } ]) ~?= (Right $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1 ] } ]) , "{A+$1 B-1€}" ~: (snd $ Calc.Balance.infer_equilibrium $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.eur $ -1 ] } ]) ~?= (Right $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 1] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.eur $ -1, Amount.usd $ -1 ] } ]) , "{A+$1 B+$1}" ~: (snd $ Calc.Balance.infer_equilibrium $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } ]) ~?= (Left [ Calc.Balance.Unit_Sum { Calc.Balance.unit_sum_amount = Calc.Balance.amount_sum $ Amount.usd $ 2 , Calc.Balance.unit_sum_accounts = Data.Map.fromList []} ]) , "{A+$1 B-$1 B-1€}" ~: (snd $ Calc.Balance.infer_equilibrium $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -1 ] } ]) ~?= (Right $ Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 1 ] } , (Format.Ledger.posting ("B":|[])) { Format.Ledger.posting_amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -1 ] } ]) ] ] ] , "Format" ~: TestList [ "Ledger" ~: TestList [ "Read" ~: TestList [ "account_name" ~: TestList [ "\"\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" (""::Text)]) ~?= [] , "\"A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A"::Text)]) ~?= ["A"] , "\"AA\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("AA"::Text)]) ~?= ["AA"] , "\" \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" (" "::Text)]) ~?= [] , "\":\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" (":"::Text)]) ~?= [] , "\"A:\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A:"::Text)]) ~?= [] , "\":A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" (":A"::Text)]) ~?= [] , "\"A \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A "::Text)]) ~?= [] , "\"A \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name) () "" ("A "::Text)]) ~?= ["A"] , "\"A A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A A"::Text)]) ~?= ["A A"] , "\"A \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A "::Text)]) ~?= [] , "\"A \\n\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A \n"::Text)]) ~?= [] , "\"(A)A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("(A)A"::Text)]) ~?= ["(A)A"] , "\"( )A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("( )A"::Text)]) ~?= ["( )A"] , "\"(A) A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("(A) A"::Text)]) ~?= ["(A) A"] , "\"[ ]A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("[ ]A"::Text)]) ~?= ["[ ]A"] , "\"(A) \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("(A) "::Text)]) ~?= [] , "\"(A)\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("(A)"::Text)]) ~?= ["(A)"] , "\"A(A)\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("A(A)"::Text)]) ~?= [("A(A)"::Text)] , "\"[A]A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("[A]A"::Text)]) ~?= ["[A]A"] , "\"[A] A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("[A] A"::Text)]) ~?= ["[A] A"] , "\"[A] \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("[A] "::Text)]) ~?= [] , "\"[A]\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account_name <* P.eof) () "" ("[A]"::Text)]) ~?= ["[A]"] ] , "account" ~: TestList [ "\"\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" (""::Text)]) ~?= [] , "\"A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A"::Text)]) ~?= ["A":|[]] , "\"A:\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A:"::Text)]) ~?= [] , "\":A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" (":A"::Text)]) ~?= [] , "\"A \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A "::Text)]) ~?= [] , "\" A\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" (" A"::Text)]) ~?= [] , "\"A:B\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A:B"::Text)]) ~?= ["A":|["B"]] , "\"A:B:C\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A:B:C"::Text)]) ~?= ["A":|["B", "C"]] , "\"Aa:Bbb:Cccc\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("Aa:Bbb:Cccc"::Text)]) ~?= ["Aa":|["Bbb", "Cccc"]] , "\"A a : B b b : C c c c\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A a : B b b : C c c c"::Text)]) ~?= ["A a ":|[" B b b ", " C c c c"]] , "\"A: :C\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A: :C"::Text)]) ~?= ["A":|[" ", "C"]] , "\"A::C\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A::C"::Text)]) ~?= [] , "\"A:B:(C)\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.account <* P.eof) () "" ("A:B:(C)"::Text)]) ~?= ["A":|["B", "(C)"]] ] , "posting_type" ~: TestList [ "A" ~: Format.Ledger.Read.posting_type ("A":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|[]) , "(" ~: Format.Ledger.Read.posting_type ("(":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "(":|[]) , ")" ~: Format.Ledger.Read.posting_type (")":|[]) ~?= (Format.Ledger.Posting_Type_Regular, ")":|[]) , "()" ~: Format.Ledger.Read.posting_type ("()":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "()":|[]) , "( )" ~: Format.Ledger.Read.posting_type ("( )":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "( )":|[]) , "(A)" ~: Format.Ledger.Read.posting_type ("(A)":|[]) ~?= (Format.Ledger.Posting_Type_Virtual, "A":|[]) , "(A:B:C)" ~: Format.Ledger.Read.posting_type ("(A":|["B", "C)"]) ~?= (Format.Ledger.Posting_Type_Virtual, "A":|["B", "C"]) , "A:B:C" ~: Format.Ledger.Read.posting_type ("A":|["B", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["B", "C"]) , "(A):B:C" ~: Format.Ledger.Read.posting_type ("(A)":|["B", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "(A)":|["B", "C"]) , "A:(B):C" ~: Format.Ledger.Read.posting_type ("A":|["(B)", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["(B)", "C"]) , "A:B:(C)" ~: Format.Ledger.Read.posting_type ("A":|["B", "(C)"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["B", "(C)"]) , "[" ~: Format.Ledger.Read.posting_type ("[":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "[":|[]) , "]" ~: Format.Ledger.Read.posting_type ("]":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "]":|[]) , "[]" ~: Format.Ledger.Read.posting_type ("[]":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "[]":|[]) , "[ ]" ~: Format.Ledger.Read.posting_type ("[ ]":|[]) ~?= (Format.Ledger.Posting_Type_Regular, "[ ]":|[]) , "[A]" ~: Format.Ledger.Read.posting_type ("[A]":|[]) ~?= (Format.Ledger.Posting_Type_Virtual_Balanced, "A":|[]) , "[A:B:C]" ~: Format.Ledger.Read.posting_type ("[A":|["B", "C]"]) ~?= (Format.Ledger.Posting_Type_Virtual_Balanced, "A":|["B", "C"]) , "A:B:C" ~: Format.Ledger.Read.posting_type ("A":|["B", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["B", "C"]) , "[A]:B:C" ~: Format.Ledger.Read.posting_type ("[A]":|["B", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "[A]":|["B", "C"]) , "A:[B]:C" ~: Format.Ledger.Read.posting_type ("A":|["[B]", "C"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["[B]", "C"]) , "A:B:[C]" ~: Format.Ledger.Read.posting_type ("A":|["B", "[C]"]) ~?= (Format.Ledger.Posting_Type_Regular, "A":|["B", "[C]"]) ] , "amount" ~: TestList [ "\"\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" (""::Text)]) ~?= [] , "\"0\" = Right 0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 }] , "\"00\" = Right 0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 }] , "\"0.\" = Right 0." ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0."::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' } }] , "\".0\" = Right 0.0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" (".0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 1 } }] , "\"0,\" = Right 0," ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0,"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' } }] , "\",0\" = Right 0,0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" (",0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.precision = 1 } }] , "\"0_\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0_"::Text)]) ~?= [] , "\"_0\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("_0"::Text)]) ~?= [] , "\"0.0\" = Right 0.0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0.0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 1 } }] , "\"00.00\" = Right 0.00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("00.00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 2 } }] , "\"0,0\" = Right 0,0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0,0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.precision = 1 } }] , "\"00,00\" = Right 0,00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("00,00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.precision = 2 } }] , "\"0_0\" = Right 0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0_0"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [1] , Amount.Style.precision = 0 } }] , "\"00_00\" = Right 0" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("00_00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [2] , Amount.Style.precision = 0 } }] , "\"0,000.00\" = Right 0,000.00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("0,000.00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3] , Amount.Style.precision = 2 } }] , "\"0.000,00\" = Right 0.000,00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount) () "" ("0.000,00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 0 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '.' [3] , Amount.Style.precision = 2 } }] , "\"1,000.00\" = Right 1,000.00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1,000.00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1000 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3] , Amount.Style.precision = 2 } }] , "\"1.000,00\" = Right 1.000,00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount) () "" ("1.000,00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1000 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '.' [3] , Amount.Style.precision = 2 } }] , "\"1,000.00.\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount) () "" ("1,000.00."::Text)]) ~?= [] , "\"1.000,00,\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount) () "" ("1.000,00,"::Text)]) ~?= [] , "\"1,000.00_\" = Left" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount) () "" ("1,000.00_"::Text)]) ~?= [] , "\"12\" = Right 12" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("123"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 123 }] , "\"1.2\" = Right 1.2" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1.2"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 1 12 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 1 } }] , "\"1,2\" = Right 1,2" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1,2"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 1 12 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.precision = 1 } }] , "\"12.23\" = Right 12.23" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("12.34"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 2 1234 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 2 } }] , "\"12,23\" = Right 12,23" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("12,34"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 2 1234 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.precision = 2 } }] , "\"1_2\" = Right 1_2" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1_2"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 12 , Amount.style = Amount.Style.nil { Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [1] , Amount.Style.precision = 0 } }] , "\"1_23\" = Right 1_23" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1_23"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 123 , Amount.style = Amount.Style.nil { Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [2] , Amount.Style.precision = 0 } }] , "\"1_23_456\" = Right 1_23_456" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1_23_456"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 123456 , Amount.style = Amount.Style.nil { Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [3, 2] , Amount.Style.precision = 0 } }] , "\"1_23_456.7890_12345_678901\" = Right 1_23_456.7890_12345_678901" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1_23_456.7890_12345_678901"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 15 123456789012345678901 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [3, 2] , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping '_' [4, 5, 6] , Amount.Style.precision = 15 } }] , "\"123456_78901_2345.678_90_1\" = Right 123456_78901_2345.678_90_1" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("123456_78901_2345.678_90_1"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 6 123456789012345678901 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '_' [4, 5, 6] , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping '_' [3, 2] , Amount.Style.precision = 6 } }] , "\"$1\" = Right $1" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("$1"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }] , "\"1$\" = Right 1$" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1$"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Right , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }] , "\"$ 1\" = Right $ 1" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("$ 1"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just True } , Amount.unit = "$" }] , "\"1 $\" = Right 1 $" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1 $"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Right , Amount.Style.unit_spaced = Just True } , Amount.unit = "$" }] , "\"-$1\" = Right $-1" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("-$1"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 (-1) , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }] , "\"\\\"4 2\\\"1\" = Right \\\"4 2\\\"1" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("\"4 2\"1"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "4 2" }] , "\"1\\\"4 2\\\"\" = Right 1\\\"4 2\\\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1\"4 2\""::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Nothing , Amount.Style.grouping_integral = Nothing , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 0 , Amount.Style.unit_side = Just Amount.Style.Side_Right , Amount.Style.unit_spaced = Just False } , Amount.unit = "4 2" }] , "\"$1.000,00\" = Right $1.000,00" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("$1.000,00"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1000 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '.' [3] , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 2 , Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }] , "\"1.000,00$\" = Right 1.000,00$" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.amount <* P.eof) () "" ("1.000,00$"::Text)]) ~?= [Amount.nil { Amount.quantity = Decimal 0 1000 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just ',' , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping '.' [3] , Amount.Style.grouping_fractional = Nothing , Amount.Style.precision = 2 , Amount.Style.unit_side = Just Amount.Style.Side_Right , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }] ] , "comment" ~: TestList [ "; some comment = Right \" some comment\"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.comment <* P.eof) () "" ("; some comment"::Text)]) ~?= [ " some comment" ] , "; some comment \\n = Right \" some comment \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.comment <* P.newline <* P.eof) () "" ("; some comment \n"::Text)]) ~?= [ " some comment " ] , "; some comment \\r\\n = Right \" some comment \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.comment <* P.string "\r\n" <* P.eof) () "" ("; some comment \r\n"::Text)]) ~?= [ " some comment " ] ] , "comments" ~: TestList [ "; some comment\\n ; some other comment = Right [\" some comment\", \" some other comment\"]" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.comments <* P.eof) () "" ("; some comment\n ; some other comment"::Text)]) ~?= [ [" some comment", " some other comment"] ] , "; some comment \\n = Right \" some comment \"" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.comments <* P.string "\n" <* P.eof) () "" ("; some comment \n"::Text)]) ~?= [ [" some comment "] ] ] , "date" ~: TestList [ "2000/01/01" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc)] , "2000/01/01 some text" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing) () "" ("2000/01/01 some text"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc)] , "2000/01/01 12:34" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01 12:34"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.utc)] , "2000/01/01 12:34:56" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01 12:34:56"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 56)) (Time.utc)] , "2000/01/01 12:34 CET" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01 12:34 CET"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.TimeZone 60 True "CET")] , "2000/01/01 12:34 +0130" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01 12:34 +0130"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 0)) (Time.TimeZone 90 False "+0130")] , "2000/01/01 12:34:56 CET" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2000/01/01 12:34:56 CET"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 56)) (Time.TimeZone 60 True "CET")] , "2001/02/29" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id Nothing <* P.eof) () "" ("2001/02/29"::Text)]) ~?= [] , "01/01" ~: (Data.Either.rights $ [P.runParser_with_Error (Date.Read.date id (Just 2000) <* P.eof) () "" ("01/01"::Text)]) ~?= [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc)] ] , "tag_value" ~: TestList [ "," ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag_value <* P.eof) () "" (","::Text)]) ~?= [","] , ",\\n" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag_value <* P.char '\n' <* P.eof) () "" (",\n"::Text)]) ~?= [","] , ",x" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag_value <* P.eof) () "" (",x"::Text)]) ~?= [",x"] , ",x:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag_value <* P.string ",x:" <* P.eof) () "" (",x:"::Text)]) ~?= [""] , "v, v, n:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag_value <* P.string ", n:" <* P.eof) () "" ("v, v, n:"::Text)]) ~?= ["v, v"] ] , "tag" ~: TestList [ "Name:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.eof) () "" ("Name:"::Text)]) ~?= [("Name", "")] , "Name:Value" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.eof) () "" ("Name:Value"::Text)]) ~?= [("Name", "Value")] , "Name:Value\\n" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.string "\n" <* P.eof) () "" ("Name:Value\n"::Text)]) ~?= [("Name", "Value")] , "Name:Val ue" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.eof) () "" ("Name:Val ue"::Text)]) ~?= [("Name", "Val ue")] , "Name:," ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.eof) () "" ("Name:,"::Text)]) ~?= [("Name", ",")] , "Name:Val,ue" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.eof) () "" ("Name:Val,ue"::Text)]) ~?= [("Name", "Val,ue")] , "Name:Val,ue:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tag <* P.string ",ue:" <* P.eof) () "" ("Name:Val,ue:"::Text)]) ~?= [("Name", "Val")] ] , "tags" ~: TestList [ "Name:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:"::Text)]) ~?= [Data.Map.fromList [ ("Name", [""]) ] ] , "Name:," ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:,"::Text)]) ~?= [Data.Map.fromList [ ("Name", [","]) ] ] , "Name:,Name:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:,Name:"::Text)]) ~?= [Data.Map.fromList [ ("Name", ["", ""]) ] ] , "Name:,Name2:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:,Name2:"::Text)]) ~?= [Data.Map.fromList [ ("Name", [""]) , ("Name2", [""]) ] ] , "Name: , Name2:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name: , Name2:"::Text)]) ~?= [Data.Map.fromList [ ("Name", [" "]) , ("Name2", [""]) ] ] , "Name:,Name2:,Name3:" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:,Name2:,Name3:"::Text)]) ~?= [Data.Map.fromList [ ("Name", [""]) , ("Name2", [""]) , ("Name3", [""]) ] ] , "Name:Val ue,Name2:V a l u e,Name3:V al ue" ~: (Data.Either.rights $ [P.runParser (Format.Ledger.Read.tags <* P.eof) () "" ("Name:Val ue,Name2:V a l u e,Name3:V al ue"::Text)]) ~?= [Data.Map.fromList [ ("Name", ["Val ue"]) , ("Name2", ["V a l u e"]) , ("Name3", ["V al ue"]) ] ] ] , "posting" ~: TestList [ " A:B:C = Right A:B:C" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C"::Text)]) ~?= [ ( (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } , Format.Ledger.Posting_Type_Regular ) ] , " !A:B:C = Right !A:B:C" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" !A:B:C"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_status = True } ] , " *A:B:C = Right *A:B:C" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" *A:B:C"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [] , Format.Ledger.posting_comments = [] , Format.Ledger.posting_dates = [] , Format.Ledger.posting_status = True , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [] } ] , " A:B:C $1 = Right A:B:C $1" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C $1"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B","C $1"])) { Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C $1 = Right A:B:C $1" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C $1"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C $1 + 1€ = Right A:B:C $1 + 1€" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C $1 + 1€"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) , ("€", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Right , Amount.Style.unit_spaced = Just False } , Amount.unit = "€" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C $1 + 1$ = Right A:B:C $2" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C $1 + 1$"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 2 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C $1 + 1$ + 1$ = Right A:B:C $3" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C $1 + 1$ + 1$"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 3 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C ; some comment = Right A:B:C ; some comment" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; some comment"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [] , Format.Ledger.posting_comments = [" some comment"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C ; some comment\\n ; some other comment = Right A:B:C ; some comment\\n ; some other comment" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; some comment\n ; some other comment"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [] , Format.Ledger.posting_comments = [" some comment", " some other comment"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C $1 ; some comment = Right A:B:C $1 ; some comment" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting) Format.Ledger.Read.nil_Context "" (" A:B:C $1 ; some comment"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_comments = [" some comment"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } ] , " A:B:C ; N:V = Right A:B:C ; N:V" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; N:V"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" N:V"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("N", ["V"]) ] } ] , " A:B:C ; some comment N:V = Right A:B:C ; some comment N:V" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; some comment N:V"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" some comment N:V"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("N", ["V"]) ] } ] , " A:B:C ; some comment N:V v, N2:V2 v2 = Right A:B:C ; some comment N:V v, N2:V2 v2" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting ) Format.Ledger.Read.nil_Context "" (" A:B:C ; some comment N:V v, N2:V2 v2"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" some comment N:V v, N2:V2 v2"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("N", ["V v"]) , ("N2", ["V2 v2"]) ] } ] , " A:B:C ; N:V\\n ; N:V2 = Right A:B:C ; N:V\\n ; N:V2" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; N:V\n ; N:V2"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" N:V", " N:V2"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("N", ["V", "V2"]) ] } ] , " A:B:C ; N:V\\n ; N2:V = Right A:B:C ; N:V\\n ; N2:V" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; N:V\n ; N2:V"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" N:V", " N2:V"] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("N", ["V"]) , ("N2", ["V"]) ] } ] , " A:B:C ; date:2001/01/01 = Right A:B:C ; date:2001/01/01" ~: (Data.List.map fst $ Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" A:B:C ; date:2001/01/01"::Text)]) ~?= [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_comments = [" date:2001/01/01"] , Format.Ledger.posting_dates = [ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2001 01 01) (Time.TimeOfDay 0 0 0)) Time.utc ] , Format.Ledger.posting_sourcepos = P.newPos "" 1 1 , Format.Ledger.posting_tags = Data.Map.fromList [ ("date", ["2001/01/01"]) ] } ] , " (A:B:C) = Right (A:B:C)" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" (A:B:C)"::Text)]) ~?= [ ( (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } , Format.Ledger.Posting_Type_Virtual ) ] , " [A:B:C] = Right [A:B:C]" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.posting <* P.eof) Format.Ledger.Read.nil_Context "" (" [A:B:C]"::Text)]) ~?= [ ( (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_sourcepos = P.newPos "" 1 1 } , Format.Ledger.Posting_Type_Virtual_Balanced ) ] ] , "transaction" ~: TestList [ "2000/01/01 some description\\n A:B:C $1\\n a:b:c" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.transaction <* P.eof) Format.Ledger.Read.nil_Context "" ("2000/01/01 some description\n A:B:C $1\n a:b:c"::Text)]) ~?= [ Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="some description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 2 1 } , (Format.Ledger.posting ("a":|["b", "c"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = -1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 3 1 } ] , Format.Ledger.transaction_sourcepos = P.newPos "" 1 1 } ] , "2000/01/01 some description\\n A:B:C $1\\n a:b:c\\n" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.transaction <* P.newline <* P.eof) Format.Ledger.Read.nil_Context "" ("2000/01/01 some description\n A:B:C $1\n a:b:c\n"::Text)]) ~?= [ Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="some description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 2 1 } , (Format.Ledger.posting ("a":|["b", "c"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = -1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 3 1 } ] , Format.Ledger.transaction_sourcepos = P.newPos "" 1 1 } ] , "2000/01/01 some description ; some comment\\n ; some other;comment\\n ; some Tag:\\n ; some last comment\\n A:B:C $1\\n a:b:c" ~: (Data.Either.rights $ [P.runParser_with_Error (Format.Ledger.Read.transaction <* P.eof) Format.Ledger.Read.nil_Context "" ("2000/01/01 some description ; some comment\n ; some other;comment\n ; some Tag:\n ; some last comment\n A:B:C $1\n a:b:c"::Text)]) ~?= [ Format.Ledger.transaction { Format.Ledger.transaction_comments_after = [ " some comment" , " some other;comment" , " some Tag:" , " some last comment" ] , Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="some description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 5 1 } , (Format.Ledger.posting ("a":|["b", "c"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = -1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 6 1 } ] , Format.Ledger.transaction_tags = Data.Map.fromList [ ("Tag", [""]) ] , Format.Ledger.transaction_sourcepos = P.newPos "" 1 1 } ] ] , "journal" ~: TestList [ "2000/01/01 1° description\\n A:B:C $1\\n a:b:c\\n2000/01/02 2° description\\n A:B:C $1\\n x:y:z" ~: TestCase $ do jnl <- liftIO $ P.runParserT_with_Error (Format.Ledger.Read.journal "" {-<* P.eof-}) Format.Ledger.Read.nil_Context "" ("2000/01/01 1° description\n A:B:C $1\n a:b:c\n2000/01/02 2° description\n A:B:C $1\n x:y:z"::Text) (Data.List.map (\j -> j{Format.Ledger.journal_last_read_time= Format.Ledger.journal_last_read_time Format.Ledger.journal}) $ Data.Either.rights [jnl]) @?= [ Format.Ledger.journal { Format.Ledger.journal_transactions = Format.Ledger.transaction_by_Date [ Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="1° description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 2 1 } , (Format.Ledger.posting ("a":|["b", "c"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = -1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 3 1 } ] , Format.Ledger.transaction_sourcepos = P.newPos "" 1 1 } , Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 02) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="2° description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 5 1 } , (Format.Ledger.posting ("x":|["y", "z"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = -1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] , Format.Ledger.posting_sourcepos = P.newPos "" 6 1 } ] , Format.Ledger.transaction_sourcepos = P.newPos "" 4 1 } ] } ] ] ] , "Write" ~: TestList [ "account" ~: TestList [ "A" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.account Format.Ledger.Posting_Type_Regular $ "A":|[]) ~?= "A") , "A:B:C" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.account Format.Ledger.Posting_Type_Regular $ "A":|["B", "C"]) ~?= "A:B:C") , "(A:B:C)" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.account Format.Ledger.Posting_Type_Virtual $ "A":|["B", "C"]) ~?= "(A:B:C)") , "[A:B:C]" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.account Format.Ledger.Posting_Type_Virtual_Balanced $ "A":|["B", "C"]) ~?= "[A:B:C]") ] , "amount" ~: TestList [ "nil" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil) ~?= "0") , "nil @ prec=2" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.style = Amount.Style.nil { Amount.Style.precision = 2 } }) ~?= "0.00") , "123" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 0 123 }) ~?= "123") , "-123" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 0 (- 123) }) ~?= "-123") , "12.3 @ prec=0" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 1 123 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' } }) ~?= "12") , "12.5 @ prec=0" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 1 125 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' } }) ~?= "13") , "12.3 @ prec=1" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 1 123 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 1 } }) ~?= "12.3") , "1,234.56 @ prec=2" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 2 123456 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 2 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3] } }) ~?= "1,234.56") , "123,456,789,01,2.3456789 @ prec=7" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 7 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 7 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [1, 2, 3] } }) ~?= "123,456,789,01,2.3456789") , "1234567.8,90,123,456,789 @ prec=12" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 12 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 12 , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping ',' [1, 2, 3] } }) ~?= "1234567.8,90,123,456,789") , "1,2,3,4,5,6,7,89,012.3456789 @ prec=7" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 7 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 7 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3, 2, 1] } }) ~?= "1,2,3,4,5,6,7,89,012.3456789") , "1234567.890,12,3,4,5,6,7,8,9 @ prec=12" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.amount Amount.nil { Amount.quantity = Decimal 12 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 12 , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping ',' [3, 2, 1] } }) ~?= "1234567.890,12,3,4,5,6,7,8,9") ] , "amount_length" ~: TestList [ "nil" ~: ((Format.Ledger.Write.amount_length Amount.nil) ~?= 1) , "nil @ prec=2" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.style = Amount.Style.nil { Amount.Style.precision = 2 } }) ~?= 4) , "123" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 0 123 }) ~?= 3) , "-123" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 0 (- 123) }) ~?= 4) , "12.3 @ prec=0" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 1 123 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' } }) ~?= 2) , "12.5 @ prec=0" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 1 125 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' } }) ~?= 2) , "12.3 @ prec=1" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 1 123 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 1 } }) ~?= 4) , "1,234.56 @ prec=2" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 2 123456 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 2 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3] } }) ~?= 8) , "123,456,789,01,2.3456789 @ prec=7" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 7 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 7 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [1, 2, 3] } }) ~?= 24) , "1234567.8,90,123,456,789 @ prec=12" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 12 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 12 , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping ',' [1, 2, 3] } }) ~?= 24) , "1,2,3,4,5,6,7,89,012.3456789 @ prec=7" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 7 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 7 , Amount.Style.grouping_integral = Just $ Amount.Style.Grouping ',' [3, 2, 1] } }) ~?= 28) , "1234567.890,12,3,4,5,6,7,8,9 @ prec=12" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 12 1234567890123456789 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 12 , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping ',' [3, 2, 1] } }) ~?= 28) , "1000000.000,00,0,0,0,0,0,0,0 @ prec=12" ~: ((Format.Ledger.Write.amount_length Amount.nil { Amount.quantity = Decimal 12 1000000000000000000 , Amount.style = Amount.Style.nil { Amount.Style.fractioning = Just '.' , Amount.Style.precision = 12 , Amount.Style.grouping_fractional = Just $ Amount.Style.Grouping ',' [3, 2, 1] } }) ~?= 28) , "999 @ prec=0" ~: ((Format.Ledger.Write.amount_length $ Amount.nil { Amount.quantity = Decimal 0 999 , Amount.style = Amount.Style.nil { Amount.Style.precision = 0 } }) ~?= 3) , "1000 @ prec=0" ~: ((Format.Ledger.Write.amount_length $ Amount.nil { Amount.quantity = Decimal 0 1000 , Amount.style = Amount.Style.nil { Amount.Style.precision = 0 } }) ~?= 4) , "10,00€ @ prec=2" ~: ((Format.Ledger.Write.amount_length $ Amount.eur 10) ~?= 6) ] , "date" ~: TestList [ "nil" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date Date.nil) ~?= "1970/01/01") , "2000/01/01 12:34:51 CET" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 51)) (Time.TimeZone 60 False "CET")) ~?= "2000/01/01 11:34:51" , "2000/01/01 12:34:51 +0100" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 12 34 51)) (Time.TimeZone 60 False "")) ~?= "2000/01/01 11:34:51" , "2000/01/01 01:02:03" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 1 2 3)) (Time.utc)) ~?= "2000/01/01 01:02:03" , "01/01 01:02" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 0 01 01) (Time.TimeOfDay 1 2 0)) (Time.utc)) ~?= "01/01 01:02" , "01/01 01:00" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 0 01 01) (Time.TimeOfDay 1 0 0)) (Time.utc)) ~?= "01/01 01:00" , "01/01 00:01" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 0 01 01) (Time.TimeOfDay 0 1 0)) (Time.utc)) ~?= "01/01 00:01" , "01/01" ~: (Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.date $ Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 0 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc)) ~?= "01/01" ] , "transaction" ~: TestList [ "nil" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.transaction Format.Ledger.transaction) ~?= "1970/01/01\n") , "2000/01/01 some description\\n\\ta:b:c\\n\\t ; first comment\\n\\t ; second comment\\n\\t ; third comment\\n\\tA:B:C $1" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.transaction $ Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="some description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] } , (Format.Ledger.posting ("a":|["b", "c"])) { Format.Ledger.posting_comments = ["first comment","second comment","third comment"] } ] }) ~?= "2000/01/01 some description\n\ta:b:c\n\t ; first comment\n\t ; second comment\n\t ; third comment\n\tA:B:C $1") , "2000/01/01 some description\\n\\tA:B:C $1\\n\\tAA:BB:CC $123" ~: ((Format.Ledger.Write.show Format.Ledger.Write.Style { Format.Ledger.Write.style_color=False , Format.Ledger.Write.style_align=True } $ Format.Ledger.Write.transaction $ Format.Ledger.transaction { Format.Ledger.transaction_dates= ( Time.zonedTimeToUTC $ Time.ZonedTime (Time.LocalTime (Time.fromGregorian 2000 01 01) (Time.TimeOfDay 0 0 0)) (Time.utc) , [] ) , Format.Ledger.transaction_description="some description" , Format.Ledger.transaction_postings = Format.Ledger.posting_by_Account [ (Format.Ledger.posting ("A":|["B", "C"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 1 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] } , (Format.Ledger.posting ("AA":|["BB", "CC"])) { Format.Ledger.posting_amounts = Data.Map.fromList [ ("$", Amount.nil { Amount.quantity = 123 , Amount.style = Amount.Style.nil { Amount.Style.unit_side = Just Amount.Style.Side_Left , Amount.Style.unit_spaced = Just False } , Amount.unit = "$" }) ] } ] }) ~?= "2000/01/01 some description\n\tA:B:C $1\n\tAA:BB:CC $123") ] ] ] ] ]