{-# LANGUAGE TupleSections #-} import Prelude import Test.HUnit import Test.Framework.Providers.HUnit (hUnitTestToTests) import Test.Framework.Runners.Console (defaultMain) import qualified Data.List import qualified Data.Map import qualified Hcompta.Model as Model import qualified Hcompta.Model.Account as Account import qualified Hcompta.Model.Amount as Amount import qualified Hcompta.Model.Amount.Quantity as Quantity import qualified Hcompta.Model.Transaction as Transaction import qualified Hcompta.Model.Transaction.Posting as Posting import Hcompta.Model.Transaction.Posting (Posting) import qualified Hcompta.Calc as Calc import qualified Hcompta.Calc.Balance as Calc.Balance main :: IO () main = defaultMain $ hUnitTestToTests test_Hcompta test_Hcompta :: Test test_Hcompta = TestList [ "Model" ~: TestList [ "Account" ~: TestList [ "fold" ~: TestList [ "[] = []" ~: (reverse $ Account.fold [] (:) []) ~?= [] , "[A] = [[A]]" ~: (reverse $ Account.fold ["A"] (:) []) ~?= [["A"]] , "[A, B] = [[A], [A, B]]" ~: (reverse $ Account.fold ["A", "B"] (:) []) ~?= [["A"], ["A", "B"]] , "[A, B, C] = [[A], [A, B], [A, B, C]]" ~: (reverse $ Account.fold ["A", "B", "C"] (:) []) ~?= [["A"], ["A", "B"], ["A", "B", "C"]] ] , "chomp" ~: TestList [ "[] = []" ~: Account.chomp [] ~?= [] , "[A] = []" ~: Account.chomp ["A"] ~?= [] , "[A, B] = [A]" ~: Account.chomp ["A", "B"] ~?= ["A"] , "[A, B, C] = [A, B]" ~: Account.chomp ["A", "B", "C"] ~?= ["A", "B"] ] ] ] , "Calc" ~: TestList [ "Balance" ~: TestList [ "posting" ~: TestList [ "[A+$1] = A+$1 & $+1" ~: (Calc.Balance.posting Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1 ] } Calc.Balance.null) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "[A+$1, A-$1] = {A+$0, $+0}" ~: (Data.List.foldl (flip Calc.Balance.posting) Calc.Balance.null [ Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1 ] } , Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 0 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "[A+$1, A-€1] = {A+$1-€1, $+1 €-1}" ~: (Data.List.foldl (flip Calc.Balance.posting) Calc.Balance.null [ Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1 ] } , Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.eur $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1, Amount.eur $ -1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ -1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "[A+$1, B-$1] = {A+$1 B-$1, $+0}" ~: (Data.List.foldl (flip Calc.Balance.posting) Calc.Balance.null [ Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1 ] } , Posting.null { Posting.account=["B"] , Posting.amounts=Amount.from_List [ Amount.usd $ -1 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ -1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.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.posting) Calc.Balance.null [ Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2 ] } , Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 0, Amount.eur $ 0 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 0 , Calc.Balance.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.posting) Calc.Balance.null [ Posting.null { Posting.account=["A"] , Posting.amounts=Amount.from_List [ Amount.usd $ 1, Amount.eur $ 2, Amount.gbp $ 3 ] } , Posting.null { Posting.account=["B"] , Posting.amounts=Amount.from_List [ Amount.usd $ -1, Amount.eur $ -2, Amount.gbp $ -3 ] } ]) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["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.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.gbp $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } ] } ] , "union" ~: TestList [ "null null = null" ~: Calc.Balance.union Calc.Balance.null Calc.Balance.null ~?= Calc.Balance.null , "{A+$1, $+1} {A+$1, $+1} = {A+$2, $+2}" ~: Calc.Balance.union (Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] }) (Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 2 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 2 , Calc.Balance.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.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] }) (Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["B"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["B"]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 2 , Calc.Balance.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.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] }) (Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["B"], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["B"]] } ] }) ~?= Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["B"]] } ] } ] , "expand" ~: TestList [ "null_By_Account = null_By_Account" ~: Calc.Balance.expand Calc.Balance.null_By_Account ~?= (Calc.Balance.Expanded $ Calc.Balance.null_By_Account) , "A+$1 = A+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ]) , "A/A+$1 = A+$1 A/A+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["A", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) , "A/B+$1 = A+$1 A/B+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) ]) , "A/B/C+$1 = A+$1 A/B+$1 A/B/C+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A", "B", "C"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["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+$1 A/B+$1 = A+$2 A/B+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 2 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) ]) , "A+$1 A/B+$1 B/A+$1 = A+$2 A/B+$1 B/A+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 2 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) , "A+$1 A/B+$1 B/A+$1 = A+$2 A/B+$1 B/A+$1" ~: Calc.Balance.expand (Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ~?= (Calc.Balance.Expanded $ Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 2 ]) , (["A", "B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ 1 ]) , (["B", "A"], Amount.from_List [ Amount.usd $ 1 ]) ]) ] , "is_equilibrated" ~: TestList [ "null = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.null , "{A+$0, $+0} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 0 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "{A+$1, $+1} = False" ~: TestCase $ (@=?) False $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "{A+$0+€0, $0 €+0} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 0, Amount.eur $ 0 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "{A+$1, B-$1, $+0} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ -1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } ] } , "{A+$1 B, $+1} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List []) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } ] } , "{A+$1 B+€1, $+1 €+1} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.eur $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["B"]] } ] } , "{A+$1 B-$1+€1, $+0 €+1} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["A"], Amount.from_List [ Amount.usd $ 1 ]) , (["B"], Amount.from_List [ Amount.usd $ -1, Amount.eur $ 1 ]) ] , Calc.Balance.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 1 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["B"]] } ] } , "{A+$1+€2+£3 B-$1-€2-£3, $+0 €+0 £+0} = True" ~: TestCase $ (@=?) True $ Calc.Balance.is_equilibrated $ Calc.Balance.Balance { Calc.Balance.by_account = Data.Map.fromList [ (["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.by_unit = Data.Map.fromList $ Data.List.map Calc.Balance.assoc_by_amount_unit $ [ Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.usd $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.eur $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } , Calc.Balance.Sum_by_Unit { Calc.Balance.amount = Amount.gbp $ 0 , Calc.Balance.accounts = Data.Map.fromList $ Data.List.map (,()) [["A"], ["B"]] } ] } ] ] ] ]