{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RecordWildCards #-}
module Hledger.Data.Transaction (
nulltransaction,
transaction,
txnTieKnot,
txnUntieKnot,
showAccountName,
hasRealPostings,
realPostings,
assignmentPostings,
virtualPostings,
balancedVirtualPostings,
transactionsPostings,
isTransactionBalanced,
balanceTransaction,
balanceTransactionHelper,
transactionTransformPostings,
transactionApplyValuation,
transactionToCost,
transactionDate2,
transactionPostingBalances,
transactionPayee,
transactionNote,
showTransaction,
showTransactionOneLineAmounts,
showTransactionUnelided,
showTransactionUnelidedOneLineAmounts,
showPostingLines,
sourceFilePath,
sourceFirstLine,
showGenericSourcePos,
annotateErrorWithTransaction,
tests_Transaction
)
where
import Data.List
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Calendar
import Text.Printf
import qualified Data.Map as M
import Hledger.Utils
import Hledger.Data.Types
import Hledger.Data.Dates
import Hledger.Data.Posting
import Hledger.Data.Amount
import Hledger.Data.Valuation
sourceFilePath :: GenericSourcePos -> FilePath
sourceFilePath :: GenericSourcePos -> FilePath
sourceFilePath = \case
GenericSourcePos fp :: FilePath
fp _ _ -> FilePath
fp
JournalSourcePos fp :: FilePath
fp _ -> FilePath
fp
sourceFirstLine :: GenericSourcePos -> Int
sourceFirstLine :: GenericSourcePos -> Int
sourceFirstLine = \case
GenericSourcePos _ line :: Int
line _ -> Int
line
JournalSourcePos _ (line :: Int
line, _) -> Int
line
showGenericSourcePos :: GenericSourcePos -> String
showGenericSourcePos :: GenericSourcePos -> FilePath
showGenericSourcePos = \case
GenericSourcePos fp :: FilePath
fp line :: Int
line column :: Int
column -> FilePath -> FilePath
forall a. Show a => a -> FilePath
show FilePath
fp FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " (line " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Int -> FilePath
forall a. Show a => a -> FilePath
show Int
line FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ", column " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Int -> FilePath
forall a. Show a => a -> FilePath
show Int
column FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ")"
JournalSourcePos fp :: FilePath
fp (line :: Int
line, line' :: Int
line') -> FilePath -> FilePath
forall a. Show a => a -> FilePath
show FilePath
fp FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " (lines " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Int -> FilePath
forall a. Show a => a -> FilePath
show Int
line FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ "-" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Int -> FilePath
forall a. Show a => a -> FilePath
show Int
line' FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ")"
nulltransaction :: Transaction
nulltransaction :: Transaction
nulltransaction = Transaction :: Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction {
tindex :: Integer
tindex=0,
tsourcepos :: GenericSourcePos
tsourcepos=GenericSourcePos
nullsourcepos,
tdate :: Day
tdate=Day
nulldate,
tdate2 :: Maybe Day
tdate2=Maybe Day
forall a. Maybe a
Nothing,
tstatus :: Status
tstatus=Status
Unmarked,
tcode :: Text
tcode="",
tdescription :: Text
tdescription="",
tcomment :: Text
tcomment="",
ttags :: [Tag]
ttags=[],
tpostings :: [Posting]
tpostings=[],
tprecedingcomment :: Text
tprecedingcomment=""
}
transaction :: String -> [Posting] -> Transaction
transaction :: FilePath -> [Posting] -> Transaction
transaction datestr :: FilePath
datestr ps :: [Posting]
ps = Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$ Transaction
nulltransaction{tdate :: Day
tdate=FilePath -> Day
parsedate FilePath
datestr, tpostings :: [Posting]
tpostings=[Posting]
ps}
transactionPayee :: Transaction -> Text
transactionPayee :: Transaction -> Text
transactionPayee = Tag -> Text
forall a b. (a, b) -> a
fst (Tag -> Text) -> (Transaction -> Tag) -> Transaction -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Tag
payeeAndNoteFromDescription (Text -> Tag) -> (Transaction -> Text) -> Transaction -> Tag
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
tdescription
transactionNote :: Transaction -> Text
transactionNote :: Transaction -> Text
transactionNote = Tag -> Text
forall a b. (a, b) -> b
snd (Tag -> Text) -> (Transaction -> Tag) -> Transaction -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Tag
payeeAndNoteFromDescription (Text -> Tag) -> (Transaction -> Text) -> Transaction -> Tag
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> Text
tdescription
payeeAndNoteFromDescription :: Text -> (Text,Text)
payeeAndNoteFromDescription :: Text -> Tag
payeeAndNoteFromDescription t :: Text
t
| Text -> Bool
T.null Text
n = (Text
t, Text
t)
| Bool
otherwise = (Text -> Text
textstrip Text
p, Text -> Text
textstrip (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
T.drop 1 Text
n)
where
(p :: Text
p, n :: Text
n) = (Char -> Bool) -> Text -> Tag
T.span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '|') Text
t
showTransaction :: Transaction -> String
showTransaction :: Transaction -> FilePath
showTransaction = Bool -> Transaction -> FilePath
showTransactionHelper Bool
False
showTransactionUnelided :: Transaction -> String
showTransactionUnelided :: Transaction -> FilePath
showTransactionUnelided = Transaction -> FilePath
showTransaction
showTransactionOneLineAmounts :: Transaction -> String
showTransactionOneLineAmounts :: Transaction -> FilePath
showTransactionOneLineAmounts = Bool -> Transaction -> FilePath
showTransactionHelper Bool
True
showTransactionUnelidedOneLineAmounts :: Transaction -> FilePath
showTransactionUnelidedOneLineAmounts = Transaction -> FilePath
showTransactionOneLineAmounts
showTransactionHelper :: Bool -> Transaction -> String
showTransactionHelper :: Bool -> Transaction -> FilePath
showTransactionHelper onelineamounts :: Bool
onelineamounts t :: Transaction
t =
[FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$ [FilePath
descriptionline]
[FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
newlinecomments
[FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ (Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
onelineamounts (Transaction -> [Posting]
tpostings Transaction
t))
[FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [""]
where
descriptionline :: FilePath
descriptionline = FilePath -> FilePath
rstrip (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [FilePath
date, FilePath
status, FilePath
code, FilePath
desc, FilePath
samelinecomment]
date :: FilePath
date = Day -> FilePath
showDate (Transaction -> Day
tdate Transaction
t) FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> (Day -> FilePath) -> Maybe Day -> FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" (("="FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++) (FilePath -> FilePath) -> (Day -> FilePath) -> Day -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> FilePath
showDate) (Transaction -> Maybe Day
tdate2 Transaction
t)
status :: FilePath
status | Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Cleared = " *"
| Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Pending = " !"
| Bool
otherwise = ""
code :: FilePath
code = if Text -> Int
T.length (Transaction -> Text
tcode Transaction
t) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 0 then FilePath -> FilePath -> FilePath
forall r. PrintfType r => FilePath -> r
printf " (%s)" (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Text -> FilePath
T.unpack (Text -> FilePath) -> Text -> FilePath
forall a b. (a -> b) -> a -> b
$ Transaction -> Text
tcode Transaction
t else ""
desc :: FilePath
desc = if FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
d then "" else " " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
d where d :: FilePath
d = Text -> FilePath
T.unpack (Text -> FilePath) -> Text -> FilePath
forall a b. (a -> b) -> a -> b
$ Transaction -> Text
tdescription Transaction
t
(samelinecomment :: FilePath
samelinecomment, newlinecomments :: [FilePath]
newlinecomments) =
case Text -> [FilePath]
renderCommentLines (Transaction -> Text
tcomment Transaction
t) of [] -> ("",[])
c :: FilePath
c:cs :: [FilePath]
cs -> (FilePath
c,[FilePath]
cs)
renderCommentLines :: Text -> [String]
t :: Text
t =
case FilePath -> [FilePath]
lines (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$ Text -> FilePath
T.unpack Text
t of
[] -> []
[l :: FilePath
l] -> [(FilePath -> FilePath
commentSpace (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
comment) FilePath
l]
("":ls :: [FilePath]
ls) -> "" FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath -> FilePath
lineIndent (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
comment) [FilePath]
ls
(l :: FilePath
l:ls :: [FilePath]
ls) -> (FilePath -> FilePath
commentSpace (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
comment) FilePath
l FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath -> FilePath
lineIndent (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
comment) [FilePath]
ls
where
comment :: FilePath -> FilePath
comment = ("; "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++)
postingsAsLines :: Bool -> [Posting] -> [String]
postingsAsLines :: Bool -> [Posting] -> [FilePath]
postingsAsLines onelineamounts :: Bool
onelineamounts ps :: [Posting]
ps = (Posting -> [FilePath]) -> [Posting] -> [FilePath]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Bool -> Bool -> [Posting] -> Posting -> [FilePath]
postingAsLines Bool
False Bool
onelineamounts [Posting]
ps) [Posting]
ps
postingAsLines :: Bool -> Bool -> [Posting] -> Posting -> [String]
postingAsLines :: Bool -> Bool -> [Posting] -> Posting -> [FilePath]
postingAsLines elideamount :: Bool
elideamount onelineamounts :: Bool
onelineamounts pstoalignwith :: [Posting]
pstoalignwith p :: Posting
p = [[FilePath]] -> [FilePath]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [
[FilePath]
postingblock
[FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
newlinecomments
| [FilePath]
postingblock <- [[FilePath]]
postingblocks]
where
postingblocks :: [[FilePath]]
postingblocks = [(FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map FilePath -> FilePath
rstrip ([FilePath] -> [FilePath]) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> a -> b
$ FilePath -> [FilePath]
lines (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
concatTopPadded [FilePath
statusandaccount, " ", FilePath
amount, FilePath
assertion, FilePath
samelinecomment] | FilePath
amount <- [FilePath]
shownAmounts]
assertion :: FilePath
assertion = FilePath
-> (BalanceAssertion -> FilePath)
-> Maybe BalanceAssertion
-> FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" ((' 'Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:)(FilePath -> FilePath)
-> (BalanceAssertion -> FilePath) -> BalanceAssertion -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
.BalanceAssertion -> FilePath
showBalanceAssertion) (Maybe BalanceAssertion -> FilePath)
-> Maybe BalanceAssertion -> FilePath
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe BalanceAssertion
pbalanceassertion Posting
p
statusandaccount :: FilePath
statusandaccount = FilePath -> FilePath
lineIndent (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Maybe Int -> Maybe Int -> Bool -> Bool -> FilePath -> FilePath
fitString (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Int
minwidth) Maybe Int
forall a. Maybe a
Nothing Bool
False Bool
True (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Posting -> FilePath
pstatusandacct Posting
p
where
minwidth :: Int
minwidth = [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (Posting -> Int) -> [Posting] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map ((2Int -> Int -> Int
forall a. Num a => a -> a -> a
+) (Int -> Int) -> (Posting -> Int) -> Posting -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Int
textWidth (Text -> Int) -> (Posting -> Text) -> Posting -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Text
T.pack (FilePath -> Text) -> (Posting -> FilePath) -> Posting -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> FilePath
pacctstr) [Posting]
pstoalignwith
pstatusandacct :: Posting -> FilePath
pstatusandacct p' :: Posting
p' = Posting -> FilePath
pstatusprefix Posting
p' FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Posting -> FilePath
pacctstr Posting
p'
pstatusprefix :: Posting -> FilePath
pstatusprefix p' :: Posting
p' | FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
s = ""
| Bool
otherwise = FilePath
s FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " "
where s :: FilePath
s = Status -> FilePath
forall a. Show a => a -> FilePath
show (Status -> FilePath) -> Status -> FilePath
forall a b. (a -> b) -> a -> b
$ Posting -> Status
pstatus Posting
p'
pacctstr :: Posting -> FilePath
pacctstr p' :: Posting
p' = Maybe Int -> PostingType -> Text -> FilePath
showAccountName Maybe Int
forall a. Maybe a
Nothing (Posting -> PostingType
ptype Posting
p') (Posting -> Text
paccount Posting
p')
shownAmounts :: [FilePath]
shownAmounts
| Bool
elideamount = [""]
| Bool
onelineamounts = [Maybe Int -> Maybe Int -> Bool -> Bool -> FilePath -> FilePath
fitString (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
amtwidth) Maybe Int
forall a. Maybe a
Nothing Bool
False Bool
False (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ MixedAmount -> FilePath
showMixedAmountOneLine (MixedAmount -> FilePath) -> MixedAmount -> FilePath
forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p]
| [Amount] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (MixedAmount -> [Amount]
amounts (MixedAmount -> [Amount]) -> MixedAmount -> [Amount]
forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p) = [""]
| Bool
otherwise = (Amount -> FilePath) -> [Amount] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (Maybe Int -> Maybe Int -> Bool -> Bool -> FilePath -> FilePath
fitStringMulti (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
amtwidth) Maybe Int
forall a. Maybe a
Nothing Bool
False Bool
False (FilePath -> FilePath)
-> (Amount -> FilePath) -> Amount -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> FilePath
showAmount ) ([Amount] -> [FilePath])
-> (MixedAmount -> [Amount]) -> MixedAmount -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> [Amount]
amounts (MixedAmount -> [FilePath]) -> MixedAmount -> [FilePath]
forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p
where
amtwidth :: Int
amtwidth = [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ 12 Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: (Posting -> Int) -> [Posting] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath -> Int
strWidth (FilePath -> Int) -> (Posting -> FilePath) -> Posting -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> FilePath
showMixedAmount (MixedAmount -> FilePath)
-> (Posting -> MixedAmount) -> Posting -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> MixedAmount
pamount) [Posting]
pstoalignwith
(samelinecomment :: FilePath
samelinecomment, newlinecomments :: [FilePath]
newlinecomments) =
case Text -> [FilePath]
renderCommentLines (Posting -> Text
pcomment Posting
p) of [] -> ("",[])
c :: FilePath
c:cs :: [FilePath]
cs -> (FilePath
c,[FilePath]
cs)
showBalanceAssertion :: BalanceAssertion -> FilePath
showBalanceAssertion BalanceAssertion{..} =
"=" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ['=' | Bool
batotal] FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ['*' | Bool
bainclusive] FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Amount -> FilePath
showAmountWithZeroCommodity Amount
baamount
showPostingLines :: Posting -> [String]
showPostingLines :: Posting -> [FilePath]
showPostingLines p :: Posting
p = Bool -> Bool -> [Posting] -> Posting -> [FilePath]
postingAsLines Bool
False Bool
False [Posting]
ps Posting
p where
ps :: [Posting]
ps | Just t :: Transaction
t <- Posting -> Maybe Transaction
ptransaction Posting
p = Transaction -> [Posting]
tpostings Transaction
t
| Bool
otherwise = [Posting
p]
lineIndent :: String -> String
lineIndent :: FilePath -> FilePath
lineIndent = (" "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++)
commentSpace :: String -> String
= (" "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++)
showAccountName :: Maybe Int -> PostingType -> AccountName -> String
showAccountName :: Maybe Int -> PostingType -> Text -> FilePath
showAccountName w :: Maybe Int
w = PostingType -> Text -> FilePath
fmt
where
fmt :: PostingType -> Text -> FilePath
fmt RegularPosting = Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
take Int
w' (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
T.unpack
fmt VirtualPosting = FilePath -> FilePath
parenthesise (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
take (Int
w'Int -> Int -> Int
forall a. Num a => a -> a -> a
-2) (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
T.unpack
fmt BalancedVirtualPosting = FilePath -> FilePath
bracket (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
take (Int
w'Int -> Int -> Int
forall a. Num a => a -> a -> a
-2) (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> (Text -> FilePath) -> Text -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
T.unpack
w' :: Int
w' = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe 999999 Maybe Int
w
parenthesise :: String -> String
parenthesise :: FilePath -> FilePath
parenthesise s :: FilePath
s = "("FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
sFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++")"
bracket :: String -> String
bracket :: FilePath -> FilePath
bracket s :: FilePath
s = "["FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
sFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++"]"
hasRealPostings :: Transaction -> Bool
hasRealPostings :: Transaction -> Bool
hasRealPostings = Bool -> Bool
not (Bool -> Bool) -> (Transaction -> Bool) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Posting] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Posting] -> Bool)
-> (Transaction -> [Posting]) -> Transaction -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
realPostings
realPostings :: Transaction -> [Posting]
realPostings :: Transaction -> [Posting]
realPostings = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter Posting -> Bool
isReal ([Posting] -> [Posting])
-> (Transaction -> [Posting]) -> Transaction -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings
assignmentPostings :: Transaction -> [Posting]
assignmentPostings :: Transaction -> [Posting]
assignmentPostings = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter Posting -> Bool
hasBalanceAssignment ([Posting] -> [Posting])
-> (Transaction -> [Posting]) -> Transaction -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings
virtualPostings :: Transaction -> [Posting]
virtualPostings :: Transaction -> [Posting]
virtualPostings = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter Posting -> Bool
isVirtual ([Posting] -> [Posting])
-> (Transaction -> [Posting]) -> Transaction -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings
balancedVirtualPostings :: Transaction -> [Posting]
balancedVirtualPostings :: Transaction -> [Posting]
balancedVirtualPostings = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter Posting -> Bool
isBalancedVirtual ([Posting] -> [Posting])
-> (Transaction -> [Posting]) -> Transaction -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings
transactionsPostings :: [Transaction] -> [Posting]
transactionsPostings :: [Transaction] -> [Posting]
transactionsPostings = (Transaction -> [Posting]) -> [Transaction] -> [Posting]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Transaction -> [Posting]
tpostings
transactionPostingBalances :: Transaction -> (MixedAmount,MixedAmount,MixedAmount)
transactionPostingBalances :: Transaction -> (MixedAmount, MixedAmount, MixedAmount)
transactionPostingBalances t :: Transaction
t = ([Posting] -> MixedAmount
sumPostings ([Posting] -> MixedAmount) -> [Posting] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
realPostings Transaction
t
,[Posting] -> MixedAmount
sumPostings ([Posting] -> MixedAmount) -> [Posting] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
virtualPostings Transaction
t
,[Posting] -> MixedAmount
sumPostings ([Posting] -> MixedAmount) -> [Posting] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
balancedVirtualPostings Transaction
t)
isTransactionBalanced :: Maybe (M.Map CommoditySymbol AmountStyle) -> Transaction -> Bool
isTransactionBalanced :: Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced styles :: Maybe (Map Text AmountStyle)
styles t :: Transaction
t =
MixedAmount -> Bool
isZeroMixedAmount MixedAmount
rsum' Bool -> Bool -> Bool
&& MixedAmount -> Bool
isZeroMixedAmount MixedAmount
bvsum'
where
(rsum :: MixedAmount
rsum, _, bvsum :: MixedAmount
bvsum) = Transaction -> (MixedAmount, MixedAmount, MixedAmount)
transactionPostingBalances Transaction
t
rsum' :: MixedAmount
rsum' = MixedAmount -> MixedAmount
canonicalise (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> MixedAmount
costOfMixedAmount MixedAmount
rsum
bvsum' :: MixedAmount
bvsum' = MixedAmount -> MixedAmount
canonicalise (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> MixedAmount
costOfMixedAmount MixedAmount
bvsum
canonicalise :: MixedAmount -> MixedAmount
canonicalise = (MixedAmount -> MixedAmount)
-> (Map Text AmountStyle -> MixedAmount -> MixedAmount)
-> Maybe (Map Text AmountStyle)
-> MixedAmount
-> MixedAmount
forall b a. b -> (a -> b) -> Maybe a -> b
maybe MixedAmount -> MixedAmount
forall a. a -> a
id Map Text AmountStyle -> MixedAmount -> MixedAmount
canonicaliseMixedAmount Maybe (Map Text AmountStyle)
styles
balanceTransaction ::
Maybe (M.Map CommoditySymbol AmountStyle)
-> Transaction
-> Either String Transaction
balanceTransaction :: Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction mstyles :: Maybe (Map Text AmountStyle)
mstyles = ((Transaction, [(Text, MixedAmount)]) -> Transaction)
-> Either FilePath (Transaction, [(Text, MixedAmount)])
-> Either FilePath Transaction
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Transaction, [(Text, MixedAmount)]) -> Transaction
forall a b. (a, b) -> a
fst (Either FilePath (Transaction, [(Text, MixedAmount)])
-> Either FilePath Transaction)
-> (Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)]))
-> Transaction
-> Either FilePath Transaction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe (Map Text AmountStyle)
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
balanceTransactionHelper Maybe (Map Text AmountStyle)
mstyles
balanceTransactionHelper ::
Maybe (M.Map CommoditySymbol AmountStyle)
-> Transaction
-> Either String (Transaction, [(AccountName, MixedAmount)])
balanceTransactionHelper :: Maybe (Map Text AmountStyle)
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
balanceTransactionHelper mstyles :: Maybe (Map Text AmountStyle)
mstyles t :: Transaction
t = do
(t' :: Transaction
t', inferredamtsandaccts :: [(Text, MixedAmount)]
inferredamtsandaccts) <-
Map Text AmountStyle
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
inferBalancingAmount (Map Text AmountStyle
-> Maybe (Map Text AmountStyle) -> Map Text AmountStyle
forall a. a -> Maybe a -> a
fromMaybe Map Text AmountStyle
forall k a. Map k a
M.empty Maybe (Map Text AmountStyle)
mstyles) (Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)]))
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. (a -> b) -> a -> b
$ Transaction -> Transaction
inferBalancingPrices Transaction
t
if Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
mstyles Transaction
t'
then (Transaction, [(Text, MixedAmount)])
-> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. b -> Either a b
Right (Transaction -> Transaction
txnTieKnot Transaction
t', [(Text, MixedAmount)]
inferredamtsandaccts)
else FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. a -> Either a b
Left (FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)]))
-> FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath -> FilePath
annotateErrorWithTransaction Transaction
t' (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath
nonzerobalanceerror Transaction
t'
where
nonzerobalanceerror :: Transaction -> String
nonzerobalanceerror :: Transaction -> FilePath
nonzerobalanceerror t :: Transaction
t = FilePath -> FilePath -> FilePath -> FilePath -> FilePath
forall r. PrintfType r => FilePath -> r
printf "could not balance this transaction (%s%s%s)" FilePath
rmsg FilePath
sep FilePath
bvmsg
where
(rsum :: MixedAmount
rsum, _, bvsum :: MixedAmount
bvsum) = Transaction -> (MixedAmount, MixedAmount, MixedAmount)
transactionPostingBalances Transaction
t
rmsg :: FilePath
rmsg | MixedAmount -> Bool
isReallyZeroMixedAmountCost MixedAmount
rsum = ""
| Bool
otherwise = "real postings are off by "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ MixedAmount -> FilePath
showMixedAmount (MixedAmount -> MixedAmount
costOfMixedAmount MixedAmount
rsum)
bvmsg :: FilePath
bvmsg | MixedAmount -> Bool
isReallyZeroMixedAmountCost MixedAmount
bvsum = ""
| Bool
otherwise = "balanced virtual postings are off by "
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ MixedAmount -> FilePath
showMixedAmount (MixedAmount -> MixedAmount
costOfMixedAmount MixedAmount
bvsum)
sep :: FilePath
sep = if Bool -> Bool
not (FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
rmsg) Bool -> Bool -> Bool
&& Bool -> Bool
not (FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
bvmsg) then "; " else "" :: String
annotateErrorWithTransaction :: Transaction -> String -> String
annotateErrorWithTransaction :: Transaction -> FilePath -> FilePath
annotateErrorWithTransaction t :: Transaction
t s :: FilePath
s = FilePath -> [FilePath] -> FilePath
forall a. [a] -> [[a]] -> [a]
intercalate "\n" [GenericSourcePos -> FilePath
showGenericSourcePos (GenericSourcePos -> FilePath) -> GenericSourcePos -> FilePath
forall a b. (a -> b) -> a -> b
$ Transaction -> GenericSourcePos
tsourcepos Transaction
t, FilePath
s, Transaction -> FilePath
showTransaction Transaction
t]
inferBalancingAmount ::
M.Map CommoditySymbol AmountStyle
-> Transaction
-> Either String (Transaction, [(AccountName, MixedAmount)])
inferBalancingAmount :: Map Text AmountStyle
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
inferBalancingAmount styles :: Map Text AmountStyle
styles t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps}
| [Posting] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Posting]
amountlessrealps Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 1
= FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. a -> Either a b
Left (FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)]))
-> FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath -> FilePath
annotateErrorWithTransaction Transaction
t "could not balance this transaction - can't have more than one real posting with no amount (remember to put 2 or more spaces before amounts)"
| [Posting] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Posting]
amountlessbvps Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 1
= FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. a -> Either a b
Left (FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)]))
-> FilePath -> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath -> FilePath
annotateErrorWithTransaction Transaction
t "could not balance this transaction - can't have more than one balanced virtual posting with no amount (remember to put 2 or more spaces before amounts)"
| Bool
otherwise
= let psandinferredamts :: [(Posting, Maybe MixedAmount)]
psandinferredamts = (Posting -> (Posting, Maybe MixedAmount))
-> [Posting] -> [(Posting, Maybe MixedAmount)]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> (Posting, Maybe MixedAmount)
inferamount [Posting]
ps
inferredacctsandamts :: [(Text, MixedAmount)]
inferredacctsandamts = [(Posting -> Text
paccount Posting
p, MixedAmount
amt) | (p :: Posting
p, Just amt :: MixedAmount
amt) <- [(Posting, Maybe MixedAmount)]
psandinferredamts]
in (Transaction, [(Text, MixedAmount)])
-> Either FilePath (Transaction, [(Text, MixedAmount)])
forall a b. b -> Either a b
Right (Transaction
t{tpostings :: [Posting]
tpostings=((Posting, Maybe MixedAmount) -> Posting)
-> [(Posting, Maybe MixedAmount)] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (Posting, Maybe MixedAmount) -> Posting
forall a b. (a, b) -> a
fst [(Posting, Maybe MixedAmount)]
psandinferredamts}, [(Text, MixedAmount)]
inferredacctsandamts)
where
(amountfulrealps :: [Posting]
amountfulrealps, amountlessrealps :: [Posting]
amountlessrealps) = (Posting -> Bool) -> [Posting] -> ([Posting], [Posting])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Posting -> Bool
hasAmount (Transaction -> [Posting]
realPostings Transaction
t)
realsum :: MixedAmount
realsum = [MixedAmount] -> MixedAmount
forall a. Num a => [a] -> a
sumStrict ([MixedAmount] -> MixedAmount) -> [MixedAmount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ (Posting -> MixedAmount) -> [Posting] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> MixedAmount
pamount [Posting]
amountfulrealps
(amountfulbvps :: [Posting]
amountfulbvps, amountlessbvps :: [Posting]
amountlessbvps) = (Posting -> Bool) -> [Posting] -> ([Posting], [Posting])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition Posting -> Bool
hasAmount (Transaction -> [Posting]
balancedVirtualPostings Transaction
t)
bvsum :: MixedAmount
bvsum = [MixedAmount] -> MixedAmount
forall a. Num a => [a] -> a
sumStrict ([MixedAmount] -> MixedAmount) -> [MixedAmount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ (Posting -> MixedAmount) -> [Posting] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> MixedAmount
pamount [Posting]
amountfulbvps
inferamount :: Posting -> (Posting, Maybe MixedAmount)
inferamount :: Posting -> (Posting, Maybe MixedAmount)
inferamount p :: Posting
p =
let
minferredamt :: Maybe MixedAmount
minferredamt = case Posting -> PostingType
ptype Posting
p of
RegularPosting | Bool -> Bool
not (Posting -> Bool
hasAmount Posting
p) -> MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
realsum
BalancedVirtualPosting | Bool -> Bool
not (Posting -> Bool
hasAmount Posting
p) -> MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
bvsum
_ -> Maybe MixedAmount
forall a. Maybe a
Nothing
in
case Maybe MixedAmount
minferredamt of
Nothing -> (Posting
p, Maybe MixedAmount
forall a. Maybe a
Nothing)
Just a :: MixedAmount
a -> (Posting
p{pamount :: MixedAmount
pamount=MixedAmount
a', poriginal :: Maybe Posting
poriginal=Posting -> Maybe Posting
forall a. a -> Maybe a
Just (Posting -> Maybe Posting) -> Posting -> Maybe Posting
forall a b. (a -> b) -> a -> b
$ Posting -> Posting
originalPosting Posting
p}, MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
Just MixedAmount
a')
where
a' :: MixedAmount
a' = Map Text AmountStyle -> MixedAmount -> MixedAmount
styleMixedAmount Map Text AmountStyle
styles (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> MixedAmount
normaliseMixedAmount (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> MixedAmount
costOfMixedAmount (-MixedAmount
a)
inferBalancingPrices :: Transaction -> Transaction
inferBalancingPrices :: Transaction -> Transaction
inferBalancingPrices t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps} = Transaction
t{tpostings :: [Posting]
tpostings=[Posting]
ps'}
where
ps' :: [Posting]
ps' = (Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (Transaction -> PostingType -> Posting -> Posting
priceInferrerFor Transaction
t PostingType
BalancedVirtualPosting (Posting -> Posting) -> (Posting -> Posting) -> Posting -> Posting
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> PostingType -> Posting -> Posting
priceInferrerFor Transaction
t PostingType
RegularPosting) [Posting]
ps
priceInferrerFor :: Transaction -> PostingType -> (Posting -> Posting)
priceInferrerFor :: Transaction -> PostingType -> Posting -> Posting
priceInferrerFor t :: Transaction
t pt :: PostingType
pt = Posting -> Posting
inferprice
where
postings :: [Posting]
postings = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter ((PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
==PostingType
pt)(PostingType -> Bool)
-> (Posting -> PostingType) -> Posting -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Posting -> PostingType
ptype) ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
pmixedamounts :: [MixedAmount]
pmixedamounts = (Posting -> MixedAmount) -> [Posting] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> MixedAmount
pamount [Posting]
postings
pamounts :: [Amount]
pamounts = (MixedAmount -> [Amount]) -> [MixedAmount] -> [Amount]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap MixedAmount -> [Amount]
amounts [MixedAmount]
pmixedamounts
pcommodities :: [Text]
pcommodities = (Amount -> Text) -> [Amount] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Amount -> Text
acommodity [Amount]
pamounts
sumamounts :: [Amount]
sumamounts = MixedAmount -> [Amount]
amounts (MixedAmount -> [Amount]) -> MixedAmount -> [Amount]
forall a b. (a -> b) -> a -> b
$ [MixedAmount] -> MixedAmount
forall a. Num a => [a] -> a
sumStrict [MixedAmount]
pmixedamounts
sumcommodities :: [Text]
sumcommodities = (Amount -> Text) -> [Amount] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Amount -> Text
acommodity [Amount]
sumamounts
sumprices :: [Maybe AmountPrice]
sumprices = (Maybe AmountPrice -> Bool)
-> [Maybe AmountPrice] -> [Maybe AmountPrice]
forall a. (a -> Bool) -> [a] -> [a]
filter (Maybe AmountPrice -> Maybe AmountPrice -> Bool
forall a. Eq a => a -> a -> Bool
/=Maybe AmountPrice
forall a. Maybe a
Nothing) ([Maybe AmountPrice] -> [Maybe AmountPrice])
-> [Maybe AmountPrice] -> [Maybe AmountPrice]
forall a b. (a -> b) -> a -> b
$ (Amount -> Maybe AmountPrice) -> [Amount] -> [Maybe AmountPrice]
forall a b. (a -> b) -> [a] -> [b]
map Amount -> Maybe AmountPrice
aprice [Amount]
sumamounts
caninferprices :: Bool
caninferprices = [Text] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
sumcommodities Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 2 Bool -> Bool -> Bool
&& [Maybe AmountPrice] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Maybe AmountPrice]
sumprices
inferprice :: Posting -> Posting
inferprice p :: Posting
p@Posting{pamount :: Posting -> MixedAmount
pamount=Mixed [a :: Amount
a]}
| Bool
caninferprices Bool -> Bool -> Bool
&& Posting -> PostingType
ptype Posting
p PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
== PostingType
pt Bool -> Bool -> Bool
&& Amount -> Text
acommodity Amount
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
fromcommodity
= Posting
p{pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Amount
a{aprice :: Maybe AmountPrice
aprice=AmountPrice -> Maybe AmountPrice
forall a. a -> Maybe a
Just AmountPrice
conversionprice}], poriginal :: Maybe Posting
poriginal=Posting -> Maybe Posting
forall a. a -> Maybe a
Just (Posting -> Maybe Posting) -> Posting -> Maybe Posting
forall a b. (a -> b) -> a -> b
$ Posting -> Posting
originalPosting Posting
p}
where
fromcommodity :: Text
fromcommodity = [Text] -> Text
forall a. [a] -> a
head ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
sumcommodities) [Text]
pcommodities
conversionprice :: AmountPrice
conversionprice
| Int
fromcountInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==1 = Amount -> AmountPrice
TotalPrice (Amount -> AmountPrice) -> Amount -> AmountPrice
forall a b. (a -> b) -> a -> b
$ Amount -> Amount
forall a. Num a => a -> a
abs Amount
toamount Amount -> Int -> Amount
`withPrecision` Int
maxprecision
| Bool
otherwise = Amount -> AmountPrice
UnitPrice (Amount -> AmountPrice) -> Amount -> AmountPrice
forall a b. (a -> b) -> a -> b
$ Amount -> Amount
forall a. Num a => a -> a
abs Amount
unitprice Amount -> Int -> Amount
`withPrecision` Int
unitprecision
where
fromcount :: Int
fromcount = [Amount] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Amount] -> Int) -> [Amount] -> Int
forall a b. (a -> b) -> a -> b
$ (Amount -> Bool) -> [Amount] -> [Amount]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==Text
fromcommodity)(Text -> Bool) -> (Amount -> Text) -> Amount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Amount -> Text
acommodity) [Amount]
pamounts
fromamount :: Amount
fromamount = [Amount] -> Amount
forall a. [a] -> a
head ([Amount] -> Amount) -> [Amount] -> Amount
forall a b. (a -> b) -> a -> b
$ (Amount -> Bool) -> [Amount] -> [Amount]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==Text
fromcommodity)(Text -> Bool) -> (Amount -> Text) -> Amount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Amount -> Text
acommodity) [Amount]
sumamounts
tocommodity :: Text
tocommodity = [Text] -> Text
forall a. [a] -> a
head ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/=Text
fromcommodity) [Text]
sumcommodities
toamount :: Amount
toamount = [Amount] -> Amount
forall a. [a] -> a
head ([Amount] -> Amount) -> [Amount] -> Amount
forall a b. (a -> b) -> a -> b
$ (Amount -> Bool) -> [Amount] -> [Amount]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
==Text
tocommodity)(Text -> Bool) -> (Amount -> Text) -> Amount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Amount -> Text
acommodity) [Amount]
sumamounts
unitprice :: Amount
unitprice = (Amount -> Quantity
aquantity Amount
fromamount) Quantity -> Amount -> Amount
`divideAmount` Amount
toamount
unitprecision :: Int
unitprecision = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max 2 (AmountStyle -> Int
asprecision (Amount -> AmountStyle
astyle Amount
toamount) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ AmountStyle -> Int
asprecision (Amount -> AmountStyle
astyle Amount
fromamount))
inferprice p :: Posting
p = Posting
p
transactionDate2 :: Transaction -> Day
transactionDate2 :: Transaction -> Day
transactionDate2 t :: Transaction
t = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe (Transaction -> Day
tdate Transaction
t) (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$ Transaction -> Maybe Day
tdate2 Transaction
t
txnTieKnot :: Transaction -> Transaction
txnTieKnot :: Transaction -> Transaction
txnTieKnot t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps} = Transaction
t' where
t' :: Transaction
t' = Transaction
t{tpostings :: [Posting]
tpostings=(Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (Transaction -> Posting -> Posting
postingSetTransaction Transaction
t') [Posting]
ps}
txnUntieKnot :: Transaction -> Transaction
txnUntieKnot :: Transaction -> Transaction
txnUntieKnot t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps} = Transaction
t{tpostings :: [Posting]
tpostings=(Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (\p :: Posting
p -> Posting
p{ptransaction :: Maybe Transaction
ptransaction=Maybe Transaction
forall a. Maybe a
Nothing}) [Posting]
ps}
postingSetTransaction :: Transaction -> Posting -> Posting
postingSetTransaction :: Transaction -> Posting -> Posting
postingSetTransaction t :: Transaction
t p :: Posting
p = Posting
p{ptransaction :: Maybe Transaction
ptransaction=Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
t}
transactionTransformPostings :: (Posting -> Posting) -> Transaction -> Transaction
transactionTransformPostings :: (Posting -> Posting) -> Transaction -> Transaction
transactionTransformPostings f :: Posting -> Posting
f t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps} = Transaction
t{tpostings :: [Posting]
tpostings=(Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> Posting
f [Posting]
ps}
transactionApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Maybe Day -> Day -> Bool -> Transaction -> ValuationType -> Transaction
transactionApplyValuation :: PriceOracle
-> Map Text AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> Transaction
-> ValuationType
-> Transaction
transactionApplyValuation priceoracle :: PriceOracle
priceoracle styles :: Map Text AmountStyle
styles periodlast :: Day
periodlast mreportlast :: Maybe Day
mreportlast today :: Day
today ismultiperiod :: Bool
ismultiperiod t :: Transaction
t v :: ValuationType
v =
(Posting -> Posting) -> Transaction -> Transaction
transactionTransformPostings (\p :: Posting
p -> PriceOracle
-> Map Text AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> Posting
-> ValuationType
-> Posting
postingApplyValuation PriceOracle
priceoracle Map Text AmountStyle
styles Day
periodlast Maybe Day
mreportlast Day
today Bool
ismultiperiod Posting
p ValuationType
v) Transaction
t
transactionToCost :: M.Map CommoditySymbol AmountStyle -> Transaction -> Transaction
transactionToCost :: Map Text AmountStyle -> Transaction -> Transaction
transactionToCost styles :: Map Text AmountStyle
styles t :: Transaction
t@Transaction{tpostings :: Transaction -> [Posting]
tpostings=[Posting]
ps} = Transaction
t{tpostings :: [Posting]
tpostings=(Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (Map Text AmountStyle -> Posting -> Posting
postingToCost Map Text AmountStyle
styles) [Posting]
ps}
tests_Transaction :: TestTree
tests_Transaction =
FilePath -> [TestTree] -> TestTree
tests "Transaction" [
FilePath -> [TestTree] -> TestTree
tests "postingAsLines" [
FilePath -> Assertion -> TestTree
test "null posting" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> Bool -> [Posting] -> Posting -> [FilePath]
postingAsLines Bool
False Bool
False [Posting
posting] Posting
posting [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= [""]
, FilePath -> Assertion -> TestTree
test "non-null posting" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
let p :: Posting
p =
Posting
posting
{ pstatus :: Status
pstatus = Status
Cleared
, paccount :: Text
paccount = "a"
, pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1, Quantity -> Amount
hrs 2]
, pcomment :: Text
pcomment = "pcomment1\npcomment2\n tag3: val3 \n"
, ptype :: PostingType
ptype = PostingType
RegularPosting
, ptags :: [Tag]
ptags = [("ptag1", "val1"), ("ptag2", "val2")]
}
in Bool -> Bool -> [Posting] -> Posting -> [FilePath]
postingAsLines Bool
False Bool
False [Posting
p] Posting
p [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " * a $1.00 ; pcomment1"
, " ; pcomment2"
, " ; tag3: val3 "
, " * a 2.00h ; pcomment1"
, " ; pcomment2"
, " ; tag3: val3 "
]
]
, let
timp :: Transaction
timp = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1, "b" Text -> Amount -> Posting
`post` Amount
missingamt]}
texp :: Transaction
texp = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1, "b" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-1)]}
texp1 :: Transaction
texp1 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["(a)" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1]}
texp2 :: Transaction
texp2 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1, "b" Text -> Amount -> Posting
`post` (Quantity -> Amount
hrs (-1) Amount -> Amount -> Amount
`at` Quantity -> Amount
usd 1)]}
texp2b :: Transaction
texp2b = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1, "b" Text -> Amount -> Posting
`post` Quantity -> Amount
hrs (-1)]}
t3 :: Transaction
t3 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1, "b" Text -> Amount -> Posting
`post` Amount
missingamt, "c" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-1)]}
in FilePath -> [TestTree] -> TestTree
tests "postingsAsLines" [
FilePath -> Assertion -> TestTree
test "null-transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
nulltransaction) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= []
, FilePath -> Assertion -> TestTree
test "implicit-amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
timp) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " a $1.00"
, " b"
]
, FilePath -> Assertion -> TestTree
test "explicit-amounts" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " a $1.00"
, " b $-1.00"
]
, FilePath -> Assertion -> TestTree
test "one-explicit-amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp1) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " (a) $1.00"
]
, FilePath -> Assertion -> TestTree
test "explicit-amounts-two-commodities" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp2) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " a $1.00"
, " b -1.00h @ $1.00"
]
, FilePath -> Assertion -> TestTree
test "explicit-amounts-not-explicitly-balanced" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp2b) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ " a $1.00"
, " b -1.00h"
]
, FilePath -> Assertion -> TestTree
test "implicit-amount-not-last" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [FilePath]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
t3) [FilePath] -> [FilePath] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[" a $1.00", " b", " c $-1.00"]
]
, FilePath -> Assertion -> TestTree
test "inferBalancingAmount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
((Transaction, [(Text, MixedAmount)]) -> Transaction
forall a b. (a, b) -> a
fst ((Transaction, [(Text, MixedAmount)]) -> Transaction)
-> Either FilePath (Transaction, [(Text, MixedAmount)])
-> Either FilePath Transaction
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map Text AmountStyle
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
inferBalancingAmount Map Text AmountStyle
forall k a. Map k a
M.empty Transaction
nulltransaction) Either FilePath Transaction
-> Either FilePath Transaction -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Transaction -> Either FilePath Transaction
forall a b. b -> Either a b
Right Transaction
nulltransaction
((Transaction, [(Text, MixedAmount)]) -> Transaction
forall a b. (a, b) -> a
fst ((Transaction, [(Text, MixedAmount)]) -> Transaction)
-> Either FilePath (Transaction, [(Text, MixedAmount)])
-> Either FilePath Transaction
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map Text AmountStyle
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
inferBalancingAmount Map Text AmountStyle
forall k a. Map k a
M.empty Transaction
nulltransaction{tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-5), "b" Text -> Amount -> Posting
`post` Amount
missingamt]}) Either FilePath Transaction
-> Either FilePath Transaction -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
Transaction -> Either FilePath Transaction
forall a b. b -> Either a b
Right Transaction
nulltransaction{tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-5), "b" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 5]}
((Transaction, [(Text, MixedAmount)]) -> Transaction
forall a b. (a, b) -> a
fst ((Transaction, [(Text, MixedAmount)]) -> Transaction)
-> Either FilePath (Transaction, [(Text, MixedAmount)])
-> Either FilePath Transaction
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map Text AmountStyle
-> Transaction
-> Either FilePath (Transaction, [(Text, MixedAmount)])
inferBalancingAmount Map Text AmountStyle
forall k a. Map k a
M.empty Transaction
nulltransaction{tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-5), "b" Text -> Amount -> Posting
`post` (Quantity -> Amount
eur 3 Amount -> Amount -> Amount
@@ Quantity -> Amount
usd 4), "c" Text -> Amount -> Posting
`post` Amount
missingamt]}) Either FilePath Transaction
-> Either FilePath Transaction -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
Transaction -> Either FilePath Transaction
forall a b. b -> Either a b
Right Transaction
nulltransaction{tpostings :: [Posting]
tpostings = ["a" Text -> Amount -> Posting
`post` Quantity -> Amount
usd (-5), "b" Text -> Amount -> Posting
`post` (Quantity -> Amount
eur 3 Amount -> Amount -> Amount
@@ Quantity -> Amount
usd 4), "c" Text -> Amount -> Posting
`post` Quantity -> Amount
usd 1]}
, FilePath -> [TestTree] -> TestTree
tests "showTransaction" [
FilePath -> Assertion -> TestTree
test "null transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath
showTransaction Transaction
nulltransaction FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "0000/01/01\n\n"
, FilePath -> Assertion -> TestTree
test "non-null transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Transaction -> FilePath
showTransaction
Transaction
nulltransaction
{ tdate :: Day
tdate = FilePath -> Day
parsedate "2012/05/14"
, tdate2 :: Maybe Day
tdate2 = Day -> Maybe Day
forall a. a -> Maybe a
Just (Day -> Maybe Day) -> Day -> Maybe Day
forall a b. (a -> b) -> a -> b
$ FilePath -> Day
parsedate "2012/05/15"
, tstatus :: Status
tstatus = Status
Unmarked
, tcode :: Text
tcode = "code"
, tdescription :: Text
tdescription = "desc"
, tcomment :: Text
tcomment = "tcomment1\ntcomment2\n"
, ttags :: [Tag]
ttags = [("ttag1", "val1")]
, tpostings :: [Posting]
tpostings =
[ Posting
nullposting
{ pstatus :: Status
pstatus = Status
Cleared
, paccount :: Text
paccount = "a"
, pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1, Quantity -> Amount
hrs 2]
, pcomment :: Text
pcomment = "\npcomment2\n"
, ptype :: PostingType
ptype = PostingType
RegularPosting
, ptags :: [Tag]
ptags = [("ptag1", "val1"), ("ptag2", "val2")]
}
]
} FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[FilePath] -> FilePath
unlines
[ "2012/05/14=2012/05/15 (code) desc ; tcomment1"
, " ; tcomment2"
, " * a $1.00"
, " ; pcomment2"
, " * a 2.00h"
, " ; pcomment2"
, ""
]
, FilePath -> Assertion -> TestTree
test "show a balanced transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(let t :: Transaction
t =
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"coopportunity"
""
[]
[ Posting
posting {paccount :: Text
paccount = "expenses:food:groceries", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 47.18], ptransaction :: Maybe Transaction
ptransaction = Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
t}
, Posting
posting {paccount :: Text
paccount = "assets:checking", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-47.18)], ptransaction :: Maybe Transaction
ptransaction = Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
t}
]
in Transaction -> FilePath
showTransaction Transaction
t) FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([FilePath] -> FilePath
unlines
[ "2007/01/28 coopportunity"
, " expenses:food:groceries $47.18"
, " assets:checking $-47.18"
, ""
])
, FilePath -> Assertion -> TestTree
test "show an unbalanced transaction, should not elide" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> FilePath
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"coopportunity"
""
[]
[ Posting
posting {paccount :: Text
paccount = "expenses:food:groceries", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 47.18]}
, Posting
posting {paccount :: Text
paccount = "assets:checking", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-47.19)]}
])) FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([FilePath] -> FilePath
unlines
[ "2007/01/28 coopportunity"
, " expenses:food:groceries $47.18"
, " assets:checking $-47.19"
, ""
])
, FilePath -> Assertion -> TestTree
test "show a transaction with one posting and a missing amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> FilePath
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"coopportunity"
""
[]
[Posting
posting {paccount :: Text
paccount = "expenses:food:groceries", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}])) FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([FilePath] -> FilePath
unlines ["2007/01/28 coopportunity", " expenses:food:groceries", ""])
, FilePath -> Assertion -> TestTree
test "show a transaction with a priced commodityless amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> FilePath
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2010/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"x"
""
[]
[ Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
num 1 Amount -> Amount -> Amount
`at` (Quantity -> Amount
usd 2 Amount -> Int -> Amount
`withPrecision` 0)]}
, Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}
])) FilePath -> FilePath -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([FilePath] -> FilePath
unlines ["2010/01/01 x", " a 1 @ $2", " b", ""])
]
, FilePath -> [TestTree] -> TestTree
tests "balanceTransaction" [
FilePath -> Assertion -> TestTree
test "detect unbalanced entry, sign error" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
Either FilePath Transaction -> Assertion
forall b a. (HasCallStack, Eq b, Show b) => Either a b -> Assertion
assertLeft
(Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"test"
""
[]
[Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1]}, Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1]}]))
,FilePath -> Assertion -> TestTree
test "detect unbalanced entry, multiple missing amounts" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
Either FilePath Transaction -> Assertion
forall b a. (HasCallStack, Eq b, Show b) => Either a b -> Assertion
assertLeft (Either FilePath Transaction -> Assertion)
-> Either FilePath Transaction -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"test"
""
[]
[ Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}
, Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}
])
,FilePath -> Assertion -> TestTree
test "one missing amount is inferred" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Posting -> MixedAmount
pamount (Posting -> MixedAmount)
-> (Transaction -> Posting) -> Transaction -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Posting] -> Posting
forall a. [a] -> a
last ([Posting] -> Posting)
-> (Transaction -> [Posting]) -> Transaction -> Posting
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings (Transaction -> MixedAmount)
-> Either FilePath Transaction -> Either FilePath MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
""
""
[]
[Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1]}, Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}])) Either FilePath MixedAmount
-> Either FilePath MixedAmount -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
MixedAmount -> Either FilePath MixedAmount
forall a b. b -> Either a b
Right ([Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1)])
,FilePath -> Assertion -> TestTree
test "conversion price is inferred" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Posting -> MixedAmount
pamount (Posting -> MixedAmount)
-> (Transaction -> Posting) -> Transaction -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Posting] -> Posting
forall a. [a] -> a
head ([Posting] -> Posting)
-> (Transaction -> [Posting]) -> Transaction -> Posting
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings (Transaction -> MixedAmount)
-> Either FilePath Transaction -> Either FilePath MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2007/01/28")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
""
""
[]
[ Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.35]}
, Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
eur (-1)]}
])) Either FilePath MixedAmount
-> Either FilePath MixedAmount -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
MixedAmount -> Either FilePath MixedAmount
forall a b. b -> Either a b
Right ([Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.35 Amount -> Amount -> Amount
@@ (Quantity -> Amount
eur 1 Amount -> Int -> Amount
`withPrecision` Int
maxprecision)])
,FilePath -> Assertion -> TestTree
test "balanceTransaction balances based on cost if there are unit prices" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
Either FilePath Transaction -> Assertion
forall a b. (HasCallStack, Eq a, Show a) => Either a b -> Assertion
assertRight (Either FilePath Transaction -> Assertion)
-> Either FilePath Transaction -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2011/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
""
""
[]
[ Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1 Amount -> Amount -> Amount
`at` Quantity -> Amount
eur 2]}
, Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-2) Amount -> Amount -> Amount
`at` Quantity -> Amount
eur 1]}
])
,FilePath -> Assertion -> TestTree
test "balanceTransaction balances based on cost if there are total prices" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
Either FilePath Transaction -> Assertion
forall a b. (HasCallStack, Eq a, Show a) => Either a b -> Assertion
assertRight (Either FilePath Transaction -> Assertion)
-> Either FilePath Transaction -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle)
-> Transaction -> Either FilePath Transaction
balanceTransaction
Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing
(Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2011/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
""
""
[]
[ Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1 Amount -> Amount -> Amount
@@ Quantity -> Amount
eur 1]}
, Posting
posting {paccount :: Text
paccount = "a", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-2) Amount -> Amount -> Amount
@@ Quantity -> Amount
eur 1]}
])
]
, FilePath -> [TestTree] -> TestTree
tests "isTransactionBalanced" [
FilePath -> Assertion -> TestTree
test "detect balanced" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[ Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}
, Posting
posting {paccount :: Text
paccount = "c", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1.00)]}
]
,FilePath -> Assertion -> TestTree
test "detect unbalanced" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[ Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}
, Posting
posting {paccount :: Text
paccount = "c", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1.01)]}
]
,FilePath -> Assertion -> TestTree
test "detect unbalanced, one posting" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}]
,FilePath -> Assertion -> TestTree
test "one zero posting is considered balanced for now" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 0]}]
,FilePath -> Assertion -> TestTree
test "virtual postings don't need to balance" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[ Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}
, Posting
posting {paccount :: Text
paccount = "c", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1.00)]}
, Posting
posting {paccount :: Text
paccount = "d", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 100], ptype :: PostingType
ptype = PostingType
VirtualPosting}
]
,FilePath -> Assertion -> TestTree
test "balanced virtual postings need to balance among themselves" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[ Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}
, Posting
posting {paccount :: Text
paccount = "c", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1.00)]}
, Posting
posting {paccount :: Text
paccount = "d", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 100], ptype :: PostingType
ptype = PostingType
BalancedVirtualPosting}
]
,FilePath -> Assertion -> TestTree
test "balanced virtual postings need to balance among themselves (2)" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
HasCallStack => FilePath -> Bool -> Assertion
FilePath -> Bool -> Assertion
assertBool "" (Bool -> Assertion) -> Bool -> Assertion
forall a b. (a -> b) -> a -> b
$
Maybe (Map Text AmountStyle) -> Transaction -> Bool
isTransactionBalanced Maybe (Map Text AmountStyle)
forall a. Maybe a
Nothing (Transaction -> Bool) -> Transaction -> Bool
forall a b. (a -> b) -> a -> b
$
Integer
-> Text
-> GenericSourcePos
-> Day
-> Maybe Day
-> Status
-> Text
-> Text
-> Text
-> [Tag]
-> [Posting]
-> Transaction
Transaction
0
""
GenericSourcePos
nullsourcepos
(FilePath -> Day
parsedate "2009/01/01")
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
""
"a"
""
[]
[ Posting
posting {paccount :: Text
paccount = "b", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 1.00]}
, Posting
posting {paccount :: Text
paccount = "c", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-1.00)]}
, Posting
posting {paccount :: Text
paccount = "d", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd 100], ptype :: PostingType
ptype = PostingType
BalancedVirtualPosting}
, Posting
posting {paccount :: Text
paccount = "3", pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed [Quantity -> Amount
usd (-100)], ptype :: PostingType
ptype = PostingType
BalancedVirtualPosting}
]
]
]