{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Hledger.Data.Transaction
(
nulltransaction
, transaction
, txnTieKnot
, txnUntieKnot
, hasRealPostings
, realPostings
, assignmentPostings
, virtualPostings
, balancedVirtualPostings
, transactionsPostings
, transactionTransformPostings
, transactionApplyValuation
, transactionToCost
, transactionAddInferredEquityPostings
, transactionInferCostsFromEquity
, transactionApplyAliases
, transactionMapPostings
, transactionMapPostingAmounts
, transactionAmounts
, partitionAndCheckConversionPostings
, transactionDate2
, transactionDateOrDate2
, transactionPayee
, transactionNote
, showTransaction
, showTransactionOneLineAmounts
, showTransactionLineFirstPart
, showTransactionBeancount
, transactionFile
, annotateErrorWithTransaction
, tests_Transaction
) where
import Control.Monad.Trans.State (StateT(..), evalStateT)
import Data.Bifunctor (first, second)
import Data.Foldable (foldlM)
import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Data.Semigroup (Endo(..))
import Data.Text (Text)
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import Data.Time.Calendar (Day, fromGregorian)
import Hledger.Utils
import Hledger.Data.Types
import Hledger.Data.Dates
import Hledger.Data.Posting
import Hledger.Data.Amount
import Hledger.Data.Valuation
import Data.Decimal (normalizeDecimal, decimalPlaces)
import Data.Functor ((<&>))
instance HasAmounts Transaction where
styleAmounts :: Map CommoditySymbol AmountStyle -> Transaction -> Transaction
styleAmounts Map CommoditySymbol AmountStyle
styles Transaction
t = Transaction
t{tpostings :: [Posting]
tpostings=Map CommoditySymbol AmountStyle -> [Posting] -> [Posting]
forall a. HasAmounts a => Map CommoditySymbol AmountStyle -> a -> a
styleAmounts Map CommoditySymbol AmountStyle
styles ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t}
nulltransaction :: Transaction
nulltransaction :: Transaction
nulltransaction = Transaction :: Integer
-> CommoditySymbol
-> (SourcePos, SourcePos)
-> Day
-> Maybe Day
-> Status
-> CommoditySymbol
-> CommoditySymbol
-> CommoditySymbol
-> [Tag]
-> [Posting]
-> Transaction
Transaction {
tindex :: Integer
tindex=Integer
0,
tsourcepos :: (SourcePos, SourcePos)
tsourcepos=(SourcePos, SourcePos)
nullsourcepos,
tdate :: Day
tdate=Day
nulldate,
tdate2 :: Maybe Day
tdate2=Maybe Day
forall a. Maybe a
Nothing,
tstatus :: Status
tstatus=Status
Unmarked,
tcode :: CommoditySymbol
tcode=CommoditySymbol
"",
tdescription :: CommoditySymbol
tdescription=CommoditySymbol
"",
tcomment :: CommoditySymbol
tcomment=CommoditySymbol
"",
ttags :: [Tag]
ttags=[],
tpostings :: [Posting]
tpostings=[],
tprecedingcomment :: CommoditySymbol
tprecedingcomment=CommoditySymbol
""
}
transaction :: Day -> [Posting] -> Transaction
transaction :: Day -> [Posting] -> Transaction
transaction Day
day [Posting]
ps = Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$ Transaction
nulltransaction{tdate :: Day
tdate=Day
day, tpostings :: [Posting]
tpostings=[Posting]
ps}
transactionPayee :: Transaction -> Text
transactionPayee :: Transaction -> CommoditySymbol
transactionPayee = Tag -> CommoditySymbol
forall a b. (a, b) -> a
fst (Tag -> CommoditySymbol)
-> (Transaction -> Tag) -> Transaction -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> Tag
payeeAndNoteFromDescription (CommoditySymbol -> Tag)
-> (Transaction -> CommoditySymbol) -> Transaction -> Tag
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> CommoditySymbol
tdescription
transactionNote :: Transaction -> Text
transactionNote :: Transaction -> CommoditySymbol
transactionNote = Tag -> CommoditySymbol
forall a b. (a, b) -> b
snd (Tag -> CommoditySymbol)
-> (Transaction -> Tag) -> Transaction -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> Tag
payeeAndNoteFromDescription (CommoditySymbol -> Tag)
-> (Transaction -> CommoditySymbol) -> Transaction -> Tag
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> CommoditySymbol
tdescription
payeeAndNoteFromDescription :: Text -> (Text,Text)
payeeAndNoteFromDescription :: CommoditySymbol -> Tag
payeeAndNoteFromDescription CommoditySymbol
t
| CommoditySymbol -> Bool
T.null CommoditySymbol
n = (CommoditySymbol
t, CommoditySymbol
t)
| Bool
otherwise = (CommoditySymbol -> CommoditySymbol
T.strip CommoditySymbol
p, CommoditySymbol -> CommoditySymbol
T.strip (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ Int -> CommoditySymbol -> CommoditySymbol
T.drop Int
1 CommoditySymbol
n)
where
(CommoditySymbol
p, CommoditySymbol
n) = (Char -> Bool) -> CommoditySymbol -> Tag
T.span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'|') CommoditySymbol
t
payeeAndNoteFromDescription' :: Text -> (Text,Text)
payeeAndNoteFromDescription' :: CommoditySymbol -> Tag
payeeAndNoteFromDescription' CommoditySymbol
t =
if Maybe Char -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Char -> Bool) -> Maybe Char -> Bool
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> CommoditySymbol -> Maybe Char
T.find (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'|') CommoditySymbol
t then CommoditySymbol -> Tag
payeeAndNoteFromDescription CommoditySymbol
t else (CommoditySymbol
"",CommoditySymbol
t)
showTransaction :: Transaction -> Text
showTransaction :: Transaction -> CommoditySymbol
showTransaction = Text -> CommoditySymbol
TL.toStrict (Text -> CommoditySymbol)
-> (Transaction -> Text) -> Transaction -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
TB.toLazyText (Builder -> Text)
-> (Transaction -> Builder) -> Transaction -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Transaction -> Builder
showTransactionHelper Bool
False
showTransactionOneLineAmounts :: Transaction -> Text
showTransactionOneLineAmounts :: Transaction -> CommoditySymbol
showTransactionOneLineAmounts = Text -> CommoditySymbol
TL.toStrict (Text -> CommoditySymbol)
-> (Transaction -> Text) -> Transaction -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
TB.toLazyText (Builder -> Text)
-> (Transaction -> Builder) -> Transaction -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Transaction -> Builder
showTransactionHelper Bool
True
showTransactionHelper :: Bool -> Transaction -> TB.Builder
showTransactionHelper :: Bool -> Transaction -> Builder
showTransactionHelper Bool
onelineamounts Transaction
t =
CommoditySymbol -> Builder
TB.fromText CommoditySymbol
descriptionline Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
newline
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> (CommoditySymbol -> Builder) -> [CommoditySymbol] -> Builder
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
newline) (Builder -> Builder)
-> (CommoditySymbol -> Builder) -> CommoditySymbol -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> Builder
TB.fromText) [CommoditySymbol]
newlinecomments
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> (CommoditySymbol -> Builder) -> [CommoditySymbol] -> Builder
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
newline) (Builder -> Builder)
-> (CommoditySymbol -> Builder) -> CommoditySymbol -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> Builder
TB.fromText) (Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
onelineamounts ([Posting] -> [CommoditySymbol]) -> [Posting] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t)
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
newline
where
descriptionline :: CommoditySymbol
descriptionline = CommoditySymbol -> CommoditySymbol
T.stripEnd (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
showTransactionLineFirstPart Transaction
t CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> [CommoditySymbol] -> CommoditySymbol
T.concat [CommoditySymbol
desc, CommoditySymbol
samelinecomment]
desc :: CommoditySymbol
desc = if CommoditySymbol -> Bool
T.null CommoditySymbol
d then CommoditySymbol
"" else CommoditySymbol
" " CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
d where d :: CommoditySymbol
d = Transaction -> CommoditySymbol
tdescription Transaction
t
(CommoditySymbol
samelinecomment, [CommoditySymbol]
newlinecomments) =
case CommoditySymbol -> [CommoditySymbol]
renderCommentLines (Transaction -> CommoditySymbol
tcomment Transaction
t) of [] -> (CommoditySymbol
"",[])
CommoditySymbol
c:[CommoditySymbol]
cs -> (CommoditySymbol
c,[CommoditySymbol]
cs)
newline :: Builder
newline = Char -> Builder
TB.singleton Char
'\n'
showTransactionLineFirstPart :: Transaction -> CommoditySymbol
showTransactionLineFirstPart Transaction
t = [CommoditySymbol] -> CommoditySymbol
T.concat [CommoditySymbol
date, CommoditySymbol
status, CommoditySymbol
code]
where
date :: CommoditySymbol
date = Day -> CommoditySymbol
showDate (Transaction -> Day
tdate Transaction
t) CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
-> (Day -> CommoditySymbol) -> Maybe Day -> CommoditySymbol
forall b a. b -> (a -> b) -> Maybe a -> b
maybe CommoditySymbol
"" ((CommoditySymbol
"="CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<>) (CommoditySymbol -> CommoditySymbol)
-> (Day -> CommoditySymbol) -> Day -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> CommoditySymbol
showDate) (Transaction -> Maybe Day
tdate2 Transaction
t)
status :: CommoditySymbol
status | Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Cleared = CommoditySymbol
" *"
| Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Pending = CommoditySymbol
" !"
| Bool
otherwise = CommoditySymbol
""
code :: CommoditySymbol
code = if CommoditySymbol -> Bool
T.null (Transaction -> CommoditySymbol
tcode Transaction
t) then CommoditySymbol
"" else CommoditySymbol
-> CommoditySymbol -> CommoditySymbol -> CommoditySymbol
wrap CommoditySymbol
" (" CommoditySymbol
")" (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
tcode Transaction
t
showTransactionBeancount :: Transaction -> Text
showTransactionBeancount :: Transaction -> CommoditySymbol
showTransactionBeancount Transaction
t =
CommoditySymbol
firstline CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
nl
CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> (CommoditySymbol -> CommoditySymbol)
-> [CommoditySymbol] -> CommoditySymbol
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
nl)) [CommoditySymbol]
newlinecomments
CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> (CommoditySymbol -> CommoditySymbol)
-> [CommoditySymbol] -> CommoditySymbol
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
nl)) ([Posting] -> [CommoditySymbol]
postingsAsLinesBeancount ([Posting] -> [CommoditySymbol]) -> [Posting] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t)
CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<> CommoditySymbol
nl
where
nl :: CommoditySymbol
nl = CommoditySymbol
"\n"
firstline :: CommoditySymbol
firstline = [CommoditySymbol] -> CommoditySymbol
T.concat [CommoditySymbol
date, CommoditySymbol
status, CommoditySymbol
payee, CommoditySymbol
note, CommoditySymbol
tags, CommoditySymbol
samelinecomment]
date :: CommoditySymbol
date = Day -> CommoditySymbol
showDate (Day -> CommoditySymbol) -> Day -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ Transaction -> Day
tdate Transaction
t
status :: CommoditySymbol
status = if Transaction -> Status
tstatus Transaction
t Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Pending then CommoditySymbol
" !" else CommoditySymbol
" *"
(CommoditySymbol
payee,CommoditySymbol
note) =
case CommoditySymbol -> Tag
payeeAndNoteFromDescription' (CommoditySymbol -> Tag) -> CommoditySymbol -> Tag
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
tdescription Transaction
t of
(CommoditySymbol
"",CommoditySymbol
"") -> (CommoditySymbol
"", CommoditySymbol
"" )
(CommoditySymbol
"",CommoditySymbol
n ) -> (CommoditySymbol
"" , CommoditySymbol -> CommoditySymbol
wrapq CommoditySymbol
n )
(CommoditySymbol
p ,CommoditySymbol
"") -> (CommoditySymbol -> CommoditySymbol
wrapq CommoditySymbol
p, CommoditySymbol -> CommoditySymbol
wrapq CommoditySymbol
"")
(CommoditySymbol
p ,CommoditySymbol
n ) -> (CommoditySymbol -> CommoditySymbol
wrapq CommoditySymbol
p, CommoditySymbol -> CommoditySymbol
wrapq CommoditySymbol
n )
where
wrapq :: CommoditySymbol -> CommoditySymbol
wrapq = CommoditySymbol
-> CommoditySymbol -> CommoditySymbol -> CommoditySymbol
wrap CommoditySymbol
" \"" CommoditySymbol
"\"" (CommoditySymbol -> CommoditySymbol)
-> (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol
-> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> CommoditySymbol
escapeDoubleQuotes (CommoditySymbol -> CommoditySymbol)
-> (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol
-> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> CommoditySymbol
escapeBackslash
tags :: CommoditySymbol
tags = [CommoditySymbol] -> CommoditySymbol
T.concat ([CommoditySymbol] -> CommoditySymbol)
-> [CommoditySymbol] -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ (Tag -> CommoditySymbol) -> [Tag] -> [CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map ((CommoditySymbol
" #"CommoditySymbol -> CommoditySymbol -> CommoditySymbol
forall a. Semigroup a => a -> a -> a
<>)(CommoditySymbol -> CommoditySymbol)
-> (Tag -> CommoditySymbol) -> Tag -> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Tag -> CommoditySymbol
forall a b. (a, b) -> a
fst) ([Tag] -> [CommoditySymbol]) -> [Tag] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Tag]
ttags Transaction
t
(CommoditySymbol
samelinecomment, [CommoditySymbol]
newlinecomments) =
case CommoditySymbol -> [CommoditySymbol]
renderCommentLines (Transaction -> CommoditySymbol
tcomment Transaction
t) of [] -> (CommoditySymbol
"",[])
CommoditySymbol
c:[CommoditySymbol]
cs -> (CommoditySymbol
c,[CommoditySymbol]
cs)
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
transactionDate2 :: Transaction -> Day
transactionDate2 :: Transaction -> Day
transactionDate2 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
transactionDateOrDate2 :: WhichDate -> Transaction -> Day
transactionDateOrDate2 :: WhichDate -> Transaction -> Day
transactionDateOrDate2 WhichDate
PrimaryDate = Transaction -> Day
tdate
transactionDateOrDate2 WhichDate
SecondaryDate = Transaction -> Day
transactionDate2
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 (\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 Transaction
t 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 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 -> Day -> ValuationType -> Transaction -> Transaction
transactionApplyValuation :: PriceOracle
-> Map CommoditySymbol AmountStyle
-> Day
-> Day
-> ValuationType
-> Transaction
-> Transaction
transactionApplyValuation PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Day
periodlast Day
today ValuationType
v =
(Posting -> Posting) -> Transaction -> Transaction
transactionTransformPostings (PriceOracle
-> Map CommoditySymbol AmountStyle
-> Day
-> Day
-> ValuationType
-> Posting
-> Posting
postingApplyValuation PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Day
periodlast Day
today ValuationType
v)
transactionToCost :: ConversionOp -> Transaction -> Transaction
transactionToCost :: ConversionOp -> Transaction -> Transaction
transactionToCost ConversionOp
cost Transaction
t = Transaction
t{tpostings :: [Posting]
tpostings = (Posting -> Maybe Posting) -> [Posting] -> [Posting]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ConversionOp -> Posting -> Maybe Posting
postingToCost ConversionOp
cost) ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t}
transactionAddInferredEquityPostings :: Bool -> AccountName -> Transaction -> Transaction
transactionAddInferredEquityPostings :: Bool -> CommoditySymbol -> Transaction -> Transaction
transactionAddInferredEquityPostings Bool
verbosetags CommoditySymbol
equityAcct Transaction
t =
Transaction
t{tpostings :: [Posting]
tpostings=(Posting -> [Posting]) -> [Posting] -> [Posting]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Bool -> CommoditySymbol -> Posting -> [Posting]
postingAddInferredEquityPostings Bool
verbosetags CommoditySymbol
equityAcct) ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t}
type IdxPosting = (Int, Posting)
label :: [Char] -> [Char] -> [Char]
label [Char]
s = (([Char]
s [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
": ")[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++)
transactionInferCostsFromEquity :: Bool -> [AccountName] -> Transaction -> Either String Transaction
transactionInferCostsFromEquity :: Bool
-> [CommoditySymbol] -> Transaction -> Either [Char] Transaction
transactionInferCostsFromEquity Bool
dryrun [CommoditySymbol]
conversionaccts Transaction
t = (CommoditySymbol -> [Char])
-> Either CommoditySymbol Transaction -> Either [Char] Transaction
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (Transaction -> [Char] -> [Char]
annotateErrorWithTransaction Transaction
t ([Char] -> [Char])
-> (CommoditySymbol -> [Char]) -> CommoditySymbol -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> [Char]
T.unpack) (Either CommoditySymbol Transaction -> Either [Char] Transaction)
-> Either CommoditySymbol Transaction -> Either [Char] Transaction
forall a b. (a -> b) -> a -> b
$ do
let npostings :: [(Int, Posting)]
npostings = [Int] -> [Posting] -> [(Int, Posting)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0..] ([Posting] -> [(Int, Posting)]) -> [Posting] -> [(Int, Posting)]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
([((Int, Posting), (Int, Posting))]
conversionPairs, ([(Int, Posting)], [(Int, Posting)])
otherps) <- Bool
-> [CommoditySymbol]
-> [(Int, Posting)]
-> Either
CommoditySymbol
([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
partitionAndCheckConversionPostings Bool
False [CommoditySymbol]
conversionaccts [(Int, Posting)]
npostings
(Int, Posting) -> (Int, Posting)
processposting <- (((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> [((Int, Posting), (Int, Posting))]
-> ([(Int, Posting)], [(Int, Posting)])
-> Either CommoditySymbol ((Int, Posting) -> (Int, Posting))
transformIndexedPostingsF (Bool
-> ((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
addCostsToPostings Bool
dryrun) [((Int, Posting), (Int, Posting))]
conversionPairs ([(Int, Posting)], [(Int, Posting)])
otherps
Transaction -> Either CommoditySymbol Transaction
forall (m :: * -> *) a. Monad m => a -> m a
return Transaction
t{tpostings :: [Posting]
tpostings = ((Int, Posting) -> Posting) -> [(Int, Posting)] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map ((Int, Posting) -> Posting
forall a b. (a, b) -> b
snd ((Int, Posting) -> Posting)
-> ((Int, Posting) -> (Int, Posting)) -> (Int, Posting) -> Posting
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, Posting) -> (Int, Posting)
processposting) [(Int, Posting)]
npostings}
where
transformIndexedPostingsF ::
((IdxPosting, IdxPosting) -> StateT ([IdxPosting],[IdxPosting]) (Either Text) (IdxPosting -> IdxPosting)) ->
[(IdxPosting, IdxPosting)] ->
([IdxPosting],[IdxPosting]) ->
(Either Text (IdxPosting -> IdxPosting))
transformIndexedPostingsF :: (((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> [((Int, Posting), (Int, Posting))]
-> ([(Int, Posting)], [(Int, Posting)])
-> Either CommoditySymbol ((Int, Posting) -> (Int, Posting))
transformIndexedPostingsF ((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
updatefn = StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
-> ([(Int, Posting)], [(Int, Posting)])
-> Either CommoditySymbol ((Int, Posting) -> (Int, Posting))
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT (StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
-> ([(Int, Posting)], [(Int, Posting)])
-> Either CommoditySymbol ((Int, Posting) -> (Int, Posting)))
-> ([((Int, Posting), (Int, Posting))]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> [((Int, Posting), (Int, Posting))]
-> ([(Int, Posting)], [(Int, Posting)])
-> Either CommoditySymbol ((Int, Posting) -> (Int, Posting))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([(Int, Posting) -> (Int, Posting)]
-> (Int, Posting) -> (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
[(Int, Posting) -> (Int, Posting)]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Endo (Int, Posting) -> (Int, Posting) -> (Int, Posting)
forall a. Endo a -> a -> a
appEndo (Endo (Int, Posting) -> (Int, Posting) -> (Int, Posting))
-> ([(Int, Posting) -> (Int, Posting)] -> Endo (Int, Posting))
-> [(Int, Posting) -> (Int, Posting)]
-> (Int, Posting)
-> (Int, Posting)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (((Int, Posting) -> (Int, Posting)) -> Endo (Int, Posting))
-> [(Int, Posting) -> (Int, Posting)] -> Endo (Int, Posting)
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((Int, Posting) -> (Int, Posting)) -> Endo (Int, Posting)
forall a. (a -> a) -> Endo a
Endo) (StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
[(Int, Posting) -> (Int, Posting)]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> ([((Int, Posting), (Int, Posting))]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
[(Int, Posting) -> (Int, Posting)])
-> [((Int, Posting), (Int, Posting))]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> [((Int, Posting), (Int, Posting))]
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
[(Int, Posting) -> (Int, Posting)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
updatefn)
addCostsToPostings :: Bool -> (IdxPosting, IdxPosting) -> StateT ([IdxPosting], [IdxPosting]) (Either Text) (IdxPosting -> IdxPosting)
addCostsToPostings :: Bool
-> ((Int, Posting), (Int, Posting))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
addCostsToPostings Bool
dryrun' ((Int
n1, Posting
cp1), (Int
n2, Posting
cp2)) = (([(Int, Posting)], [(Int, Posting)])
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)])))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
StateT ((([(Int, Posting)], [(Int, Posting)])
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)])))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting)))
-> (([(Int, Posting)], [(Int, Posting)])
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)])))
-> StateT
([(Int, Posting)], [(Int, Posting)])
(Either CommoditySymbol)
((Int, Posting) -> (Int, Posting))
forall a b. (a -> b) -> a -> b
$ \([(Int, Posting)]
costps, [(Int, Posting)]
otherps) -> do
Amount
ca1 <- Posting -> Either CommoditySymbol Amount
conversionPostingAmountNoCost Posting
cp1
Amount
ca2 <- Posting -> Either CommoditySymbol Amount
conversionPostingAmountNoCost Posting
cp2
let
matchingCostPs :: [(Int, Posting)]
matchingCostPs =
([(Int, Posting)] -> [Char])
-> [(Int, Posting)] -> [(Int, Posting)]
forall a. Show a => (a -> [Char]) -> a -> a
dbg7With ([Char] -> [Char] -> [Char]
label [Char]
"matched costful postings"([Char] -> [Char])
-> ([(Int, Posting)] -> [Char]) -> [(Int, Posting)] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Int -> [Char]
forall a. Show a => a -> [Char]
show(Int -> [Char])
-> ([(Int, Posting)] -> Int) -> [(Int, Posting)] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.[(Int, Posting)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length) ([(Int, Posting)] -> [(Int, Posting)])
-> [(Int, Posting)] -> [(Int, Posting)]
forall a b. (a -> b) -> a -> b
$
((Int, Posting) -> Maybe (Int, Posting))
-> [(Int, Posting)] -> [(Int, Posting)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((Posting -> Maybe Posting)
-> (Int, Posting) -> Maybe (Int, Posting)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ((Posting -> Maybe Posting)
-> (Int, Posting) -> Maybe (Int, Posting))
-> (Posting -> Maybe Posting)
-> (Int, Posting)
-> Maybe (Int, Posting)
forall a b. (a -> b) -> a -> b
$ Amount -> Amount -> Posting -> Maybe Posting
costfulPostingIfMatchesBothAmounts Amount
ca1 Amount
ca2) [(Int, Posting)]
costps
matchingOtherPs :: [(Int, (Posting, Amount))]
matchingOtherPs =
([(Int, (Posting, Amount))] -> [Char])
-> [(Int, (Posting, Amount))] -> [(Int, (Posting, Amount))]
forall a. Show a => (a -> [Char]) -> a -> a
dbg7With ([Char] -> [Char] -> [Char]
label [Char]
"matched costless postings"([Char] -> [Char])
-> ([(Int, (Posting, Amount))] -> [Char])
-> [(Int, (Posting, Amount))]
-> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Int -> [Char]
forall a. Show a => a -> [Char]
show(Int -> [Char])
-> ([(Int, (Posting, Amount))] -> Int)
-> [(Int, (Posting, Amount))]
-> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.[(Int, (Posting, Amount))] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length) ([(Int, (Posting, Amount))] -> [(Int, (Posting, Amount))])
-> [(Int, (Posting, Amount))] -> [(Int, (Posting, Amount))]
forall a b. (a -> b) -> a -> b
$
if Bool
dryrun'
then [(Int
n,(Posting
p, Amount
a)) | (Int
n,Posting
p) <- [(Int, Posting)]
otherps, let Just Amount
a = Posting -> Maybe Amount
postingSingleAmount Posting
p]
else ((Int, Posting) -> Maybe (Int, (Posting, Amount)))
-> [(Int, Posting)] -> [(Int, (Posting, Amount))]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((Posting -> Maybe (Posting, Amount))
-> (Int, Posting) -> Maybe (Int, (Posting, Amount))
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ((Posting -> Maybe (Posting, Amount))
-> (Int, Posting) -> Maybe (Int, (Posting, Amount)))
-> (Posting -> Maybe (Posting, Amount))
-> (Int, Posting)
-> Maybe (Int, (Posting, Amount))
forall a b. (a -> b) -> a -> b
$ Amount -> Amount -> Posting -> Maybe (Posting, Amount)
addCostIfMatchesOneAmount Amount
ca1 Amount
ca2) [(Int, Posting)]
otherps
postingAddCostAndOrTag :: Int -> Posting -> (Int, Posting) -> (Int, Posting)
postingAddCostAndOrTag Int
np Posting
costp (Int
n,Posting
p) =
(Int
n, if | Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
np -> Posting
costp Posting -> [Tag] -> Posting
`postingAddTags` [(CommoditySymbol
"_price-matched",CommoditySymbol
"")]
| Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
n1 Bool -> Bool -> Bool
|| Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
n2 -> Posting
p Posting -> [Tag] -> Posting
`postingAddTags` [(CommoditySymbol
"_conversion-matched",CommoditySymbol
"")]
| Bool
otherwise -> Posting
p)
(CommoditySymbol -> CommoditySymbol)
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ([Posting] -> CommoditySymbol -> CommoditySymbol
annotateWithPostings [Posting
cp1, Posting
cp2]) (Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)])))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
forall a b. (a -> b) -> a -> b
$
if
| [(Int
np, Posting
costp)] <- [(Int, Posting)]
matchingCostPs
, Just [(Int, Posting)]
newcostps <- Int -> [(Int, Posting)] -> Maybe [(Int, Posting)]
forall a b. Eq a => a -> [(a, b)] -> Maybe [(a, b)]
deleteIdx Int
np [(Int, Posting)]
costps
-> ((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
forall a b. b -> Either a b
Right (Int -> Posting -> (Int, Posting) -> (Int, Posting)
postingAddCostAndOrTag Int
np Posting
costp, (if Bool
dryrun' then [(Int, Posting)]
costps else [(Int, Posting)]
newcostps, [(Int, Posting)]
otherps))
| [] <- [(Int, Posting)]
matchingCostPs
, (Int
np, (Posting
costp, Amount
amt)):[(Int, (Posting, Amount))]
nps <- [(Int, (Posting, Amount))]
matchingOtherPs
, Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ ((Int, (Posting, Amount)) -> Bool)
-> [(Int, (Posting, Amount))] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Amount -> Amount -> Bool
amountsMatch Amount
amt (Amount -> Bool)
-> ((Int, (Posting, Amount)) -> Amount)
-> (Int, (Posting, Amount))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Posting, Amount) -> Amount
forall a b. (a, b) -> b
snd ((Posting, Amount) -> Amount)
-> ((Int, (Posting, Amount)) -> (Posting, Amount))
-> (Int, (Posting, Amount))
-> Amount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, (Posting, Amount)) -> (Posting, Amount)
forall a b. (a, b) -> b
snd) [(Int, (Posting, Amount))]
nps
, Just [(Int, Posting)]
newotherps <- Int -> [(Int, Posting)] -> Maybe [(Int, Posting)]
forall a b. Eq a => a -> [(a, b)] -> Maybe [(a, b)]
deleteIdx Int
np [(Int, Posting)]
otherps
-> ((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
forall a b. b -> Either a b
Right (Int -> Posting -> (Int, Posting) -> (Int, Posting)
postingAddCostAndOrTag Int
np Posting
costp, ([(Int, Posting)]
costps, if Bool
dryrun' then [(Int, Posting)]
otherps else [(Int, Posting)]
newotherps))
| Bool
otherwise -> ((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
-> Either
CommoditySymbol
((Int, Posting) -> (Int, Posting),
([(Int, Posting)], [(Int, Posting)]))
forall a b. b -> Either a b
Right ((Int, Posting) -> (Int, Posting)
forall a. a -> a
id, ([(Int, Posting)]
costps, [(Int, Posting)]
otherps))
costfulPostingIfMatchesBothAmounts :: Amount -> Amount -> Posting -> Maybe Posting
costfulPostingIfMatchesBothAmounts :: Amount -> Amount -> Posting -> Maybe Posting
costfulPostingIfMatchesBothAmounts Amount
a1 Amount
a2 Posting
costfulp = do
a :: Amount
a@Amount{aprice :: Amount -> Maybe AmountPrice
aprice=Just AmountPrice
_} <- Posting -> Maybe Amount
postingSingleAmount Posting
costfulp
if
| Integer -> Amount -> Amount -> Bool -> Bool
forall a a. (Show a, Show a) => a -> Amount -> Amount -> a -> a
dbgamtmatch Integer
1 Amount
a1 Amount
a (Amount -> Amount -> Bool
amountsMatch (-Amount
a1) Amount
a) Bool -> Bool -> Bool
&& Integer -> Amount -> Amount -> Bool -> Bool
forall a a. (Show a, Show a) => a -> Amount -> Amount -> a -> a
dbgcostmatch Integer
2 Amount
a2 Amount
a (Amount -> Amount -> Bool
amountsMatch Amount
a2 (Amount -> Amount
amountCost Amount
a)) -> Posting -> Maybe Posting
forall a. a -> Maybe a
Just Posting
costfulp
| Integer -> Amount -> Amount -> Bool -> Bool
forall a a. (Show a, Show a) => a -> Amount -> Amount -> a -> a
dbgamtmatch Integer
2 Amount
a2 Amount
a (Amount -> Amount -> Bool
amountsMatch (-Amount
a2) Amount
a) Bool -> Bool -> Bool
&& Integer -> Amount -> Amount -> Bool -> Bool
forall a a. (Show a, Show a) => a -> Amount -> Amount -> a -> a
dbgcostmatch Integer
1 Amount
a1 Amount
a (Amount -> Amount -> Bool
amountsMatch Amount
a1 (Amount -> Amount
amountCost Amount
a)) -> Posting -> Maybe Posting
forall a. a -> Maybe a
Just Posting
costfulp
| Bool
otherwise -> Maybe Posting
forall a. Maybe a
Nothing
where
dbgamtmatch :: a -> Amount -> Amount -> a -> a
dbgamtmatch a
n Amount
a Amount
b = [Char] -> a -> a
forall a. Show a => [Char] -> a -> a
dbg7 ([Char]
"conversion posting " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>a -> [Char]
forall a. Show a => a -> [Char]
show a
n[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmount Amount
a[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" balances amount "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmountWithoutPrice Amount
b [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" of costful posting "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmount Amount
b[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" at precision "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
dbgShowAmountPrecision Amount
a[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" ?")
dbgcostmatch :: a -> Amount -> Amount -> a -> a
dbgcostmatch a
n Amount
a Amount
b = [Char] -> a -> a
forall a. Show a => [Char] -> a -> a
dbg7 ([Char]
"and\nconversion posting "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>a -> [Char]
forall a. Show a => a -> [Char]
show a
n[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmount Amount
a[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" matches cost " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmount (Amount -> Amount
amountCost Amount
b)[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" of costful posting "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
showAmount Amount
b[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" at precision "[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>Amount -> [Char]
dbgShowAmountPrecision Amount
a[Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<>[Char]
" ?")
addCostIfMatchesOneAmount :: Amount -> Amount -> Posting -> Maybe (Posting, Amount)
addCostIfMatchesOneAmount :: Amount -> Amount -> Posting -> Maybe (Posting, Amount)
addCostIfMatchesOneAmount Amount
a1 Amount
a2 Posting
p = do
Amount
a <- Posting -> Maybe Amount
postingSingleAmount Posting
p
let newp :: Amount -> Posting
newp Amount
cost = Posting
p{pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount Amount
a{aprice :: Maybe AmountPrice
aprice = AmountPrice -> Maybe AmountPrice
forall a. a -> Maybe a
Just (AmountPrice -> Maybe AmountPrice)
-> AmountPrice -> Maybe AmountPrice
forall a b. (a -> b) -> a -> b
$ Amount -> AmountPrice
TotalPrice Amount
cost}}
if
| Amount -> Amount -> Bool
amountsMatch (-Amount
a1) Amount
a -> (Posting, Amount) -> Maybe (Posting, Amount)
forall a. a -> Maybe a
Just (Amount -> Posting
newp Amount
a2, Amount
a2)
| Amount -> Amount -> Bool
amountsMatch (-Amount
a2) Amount
a -> (Posting, Amount) -> Maybe (Posting, Amount)
forall a. a -> Maybe a
Just (Amount -> Posting
newp Amount
a1, Amount
a1)
| Bool
otherwise -> Maybe (Posting, Amount)
forall a. Maybe a
Nothing
conversionPostingAmountNoCost :: Posting -> Either CommoditySymbol Amount
conversionPostingAmountNoCost Posting
p = case Posting -> Maybe Amount
postingSingleAmount Posting
p of
Just a :: Amount
a@Amount{aprice :: Amount -> Maybe AmountPrice
aprice=Maybe AmountPrice
Nothing} -> Amount -> Either CommoditySymbol Amount
forall a b. b -> Either a b
Right Amount
a
Just Amount{aprice :: Amount -> Maybe AmountPrice
aprice=Just AmountPrice
_} -> CommoditySymbol -> Either CommoditySymbol Amount
forall a b. a -> Either a b
Left (CommoditySymbol -> Either CommoditySymbol Amount)
-> CommoditySymbol -> Either CommoditySymbol Amount
forall a b. (a -> b) -> a -> b
$ [Posting] -> CommoditySymbol -> CommoditySymbol
annotateWithPostings [Posting
p] CommoditySymbol
"Conversion postings must not have a cost:"
Maybe Amount
Nothing -> CommoditySymbol -> Either CommoditySymbol Amount
forall a b. a -> Either a b
Left (CommoditySymbol -> Either CommoditySymbol Amount)
-> CommoditySymbol -> Either CommoditySymbol Amount
forall a b. (a -> b) -> a -> b
$ [Posting] -> CommoditySymbol -> CommoditySymbol
annotateWithPostings [Posting
p] CommoditySymbol
"Conversion postings must have a single-commodity amount:"
amountsMatch :: Amount -> Amount -> Bool
amountsMatch Amount
a Amount
b = Amount -> Bool
amountLooksZero (Amount -> Bool) -> Amount -> Bool
forall a b. (a -> b) -> a -> b
$ AmountPrecision -> Amount -> Amount
amountSetPrecision (AmountStyle -> AmountPrecision
asprecision (AmountStyle -> AmountPrecision) -> AmountStyle -> AmountPrecision
forall a b. (a -> b) -> a -> b
$ Amount -> AmountStyle
astyle Amount
a) (Amount -> Amount) -> Amount -> Amount
forall a b. (a -> b) -> a -> b
$ Amount
a Amount -> Amount -> Amount
forall a. Num a => a -> a -> a
- Amount
b
deleteIdx :: a -> [(a, b)] -> Maybe [(a, b)]
deleteIdx a
n = ((a, b) -> Bool) -> [(a, b)] -> Maybe [(a, b)]
forall a. (a -> Bool) -> [a] -> Maybe [a]
deleteUniqueMatch ((a
na -> a -> Bool
forall a. Eq a => a -> a -> Bool
==) (a -> Bool) -> ((a, b) -> a) -> (a, b) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, b) -> a
forall a b. (a, b) -> a
fst)
deleteUniqueMatch :: (a -> Bool) -> [a] -> Maybe [a]
deleteUniqueMatch a -> Bool
p (a
x:[a]
xs) | a -> Bool
p a
x = if (a -> Bool) -> [a] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any a -> Bool
p [a]
xs then Maybe [a]
forall a. Maybe a
Nothing else [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
xs
| Bool
otherwise = (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:) ([a] -> [a]) -> Maybe [a] -> Maybe [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (a -> Bool) -> [a] -> Maybe [a]
deleteUniqueMatch a -> Bool
p [a]
xs
deleteUniqueMatch a -> Bool
_ [] = Maybe [a]
forall a. Maybe a
Nothing
annotateWithPostings :: [Posting] -> CommoditySymbol -> CommoditySymbol
annotateWithPostings [Posting]
xs CommoditySymbol
str = [CommoditySymbol] -> CommoditySymbol
T.unlines ([CommoditySymbol] -> CommoditySymbol)
-> [CommoditySymbol] -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ CommoditySymbol
str CommoditySymbol -> [CommoditySymbol] -> [CommoditySymbol]
forall a. a -> [a] -> [a]
: Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False [Posting]
xs
dbgShowAmountPrecision :: Amount -> [Char]
dbgShowAmountPrecision Amount
a =
case AmountStyle -> AmountPrecision
asprecision (AmountStyle -> AmountPrecision) -> AmountStyle -> AmountPrecision
forall a b. (a -> b) -> a -> b
$ Amount -> AmountStyle
astyle Amount
a of
Precision Word8
n -> Word8 -> [Char]
forall a. Show a => a -> [Char]
show Word8
n
AmountPrecision
NaturalPrecision -> Word8 -> [Char]
forall a. Show a => a -> [Char]
show (Word8 -> [Char]) -> Word8 -> [Char]
forall a b. (a -> b) -> a -> b
$ DecimalRaw Integer -> Word8
forall i. DecimalRaw i -> Word8
decimalPlaces (DecimalRaw Integer -> Word8) -> DecimalRaw Integer -> Word8
forall a b. (a -> b) -> a -> b
$ DecimalRaw Integer -> DecimalRaw Integer
forall i. Integral i => DecimalRaw i -> DecimalRaw i
normalizeDecimal (DecimalRaw Integer -> DecimalRaw Integer)
-> DecimalRaw Integer -> DecimalRaw Integer
forall a b. (a -> b) -> a -> b
$ Amount -> DecimalRaw Integer
aquantity Amount
a
partitionAndCheckConversionPostings :: Bool -> [AccountName] -> [IdxPosting] -> Either Text ( [(IdxPosting, IdxPosting)], ([IdxPosting], [IdxPosting]) )
partitionAndCheckConversionPostings :: Bool
-> [CommoditySymbol]
-> [(Int, Posting)]
-> Either
CommoditySymbol
([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
partitionAndCheckConversionPostings Bool
check [CommoditySymbol]
conversionaccts =
((([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> (Int, Posting)
-> Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting)))
-> (([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> [(Int, Posting)]
-> Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldlM (([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> (Int, Posting)
-> Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
forall a a a.
IsString a =>
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])), Maybe a)
-> (a, Posting)
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
select (([], ([], [])), Maybe (Int, Posting)
forall a. Maybe a
Nothing)
([(Int, Posting)]
-> Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting)))
-> (Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> Either
CommoditySymbol
([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])))
-> [(Int, Posting)]
-> Either
CommoditySymbol
([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> ((([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])))
-> Either
CommoditySymbol
(([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> Either
CommoditySymbol
([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((([(Int, Posting)], [(Int, Posting)])
-> ([(Int, Posting)], [(Int, Posting)]))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second (([(Int, Posting)] -> [(Int, Posting)])
-> ([(Int, Posting)], [(Int, Posting)])
-> ([(Int, Posting)], [(Int, Posting)])
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second [(Int, Posting)] -> [(Int, Posting)]
forall a. [a] -> [a]
reverse) (([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])))
-> ((([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])))
-> (([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)])),
Maybe (Int, Posting))
-> ([((Int, Posting), (Int, Posting))],
([(Int, Posting)], [(Int, Posting)]))
forall a b. (a, b) -> a
fst)
where
select :: (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])), Maybe a)
-> (a, Posting)
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
select (([(a, (a, Posting))]
cs, others :: ([(a, Posting)], [(a, Posting)])
others@([(a, Posting)]
ps, [(a, Posting)]
os)), Maybe a
Nothing) np :: (a, Posting)
np@(a
_, Posting
p)
| Posting -> Bool
isConversion Posting
p = (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. b -> Either a b
Right (([(a, (a, Posting))]
cs, ([(a, Posting)], [(a, Posting)])
others), (a, Posting) -> Maybe (a, Posting)
forall a. a -> Maybe a
Just (a, Posting)
np)
| Posting -> Bool
hasCost Posting
p = (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. b -> Either a b
Right (([(a, (a, Posting))]
cs, ((a, Posting)
np(a, Posting) -> [(a, Posting)] -> [(a, Posting)]
forall a. a -> [a] -> [a]
:[(a, Posting)]
ps, [(a, Posting)]
os)), Maybe (a, Posting)
forall a. Maybe a
Nothing)
| Bool
otherwise = (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. b -> Either a b
Right (([(a, (a, Posting))]
cs, ([(a, Posting)]
ps, (a, Posting)
np(a, Posting) -> [(a, Posting)] -> [(a, Posting)]
forall a. a -> [a] -> [a]
:[(a, Posting)]
os)), Maybe (a, Posting)
forall a. Maybe a
Nothing)
select (([(a, (a, Posting))]
cs, others :: ([(a, Posting)], [(a, Posting)])
others@([(a, Posting)]
ps,[(a, Posting)]
os)), Just a
lst) np :: (a, Posting)
np@(a
_, Posting
p)
| Posting -> Bool
isConversion Posting
p = (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. b -> Either a b
Right (((a
lst, (a, Posting)
np)(a, (a, Posting)) -> [(a, (a, Posting))] -> [(a, (a, Posting))]
forall a. a -> [a] -> [a]
:[(a, (a, Posting))]
cs, ([(a, Posting)], [(a, Posting)])
others), Maybe (a, Posting)
forall a. Maybe a
Nothing)
| Bool
check = a
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. a -> Either a b
Left a
"Conversion postings must occur in adjacent pairs"
| Bool
otherwise = (([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
-> Either
a
(([(a, (a, Posting))], ([(a, Posting)], [(a, Posting)])),
Maybe (a, Posting))
forall a b. b -> Either a b
Right (([(a, (a, Posting))]
cs, ([(a, Posting)]
ps, (a, Posting)
np(a, Posting) -> [(a, Posting)] -> [(a, Posting)]
forall a. a -> [a] -> [a]
:[(a, Posting)]
os)), Maybe (a, Posting)
forall a. Maybe a
Nothing)
isConversion :: Posting -> Bool
isConversion Posting
p = Posting -> CommoditySymbol
paccount Posting
p CommoditySymbol -> [CommoditySymbol] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [CommoditySymbol]
conversionaccts
hasCost :: Posting -> Bool
hasCost Posting
p = Maybe AmountPrice -> Bool
forall a. Maybe a -> Bool
isJust (Maybe AmountPrice -> Bool) -> Maybe AmountPrice -> Bool
forall a b. (a -> b) -> a -> b
$ Amount -> Maybe AmountPrice
aprice (Amount -> Maybe AmountPrice) -> Maybe Amount -> Maybe AmountPrice
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Posting -> Maybe Amount
postingSingleAmount Posting
p
postingSingleAmount :: Posting -> Maybe Amount
postingSingleAmount :: Posting -> Maybe Amount
postingSingleAmount Posting
p = case MixedAmount -> [Amount]
amountsRaw (Posting -> MixedAmount
pamount Posting
p) of
[Amount
a] -> Amount -> Maybe Amount
forall a. a -> Maybe a
Just Amount
a
[Amount]
_ -> Maybe Amount
forall a. Maybe a
Nothing
transactionApplyAliases :: [AccountAlias] -> Transaction -> Either RegexError Transaction
transactionApplyAliases :: [AccountAlias] -> Transaction -> Either [Char] Transaction
transactionApplyAliases [AccountAlias]
aliases Transaction
t =
case (Posting -> Either [Char] Posting)
-> [Posting] -> Either [Char] [Posting]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([AccountAlias] -> Posting -> Either [Char] Posting
postingApplyAliases [AccountAlias]
aliases) ([Posting] -> Either [Char] [Posting])
-> [Posting] -> Either [Char] [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t of
Right [Posting]
ps -> Transaction -> Either [Char] Transaction
forall a b. b -> Either a b
Right (Transaction -> Either [Char] Transaction)
-> Transaction -> Either [Char] Transaction
forall a b. (a -> b) -> a -> b
$ Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$ Transaction
t{tpostings :: [Posting]
tpostings=[Posting]
ps}
Left [Char]
err -> [Char] -> Either [Char] Transaction
forall a b. a -> Either a b
Left [Char]
err
transactionMapPostings :: (Posting -> Posting) -> Transaction -> Transaction
transactionMapPostings :: (Posting -> Posting) -> Transaction -> Transaction
transactionMapPostings 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}
transactionMapPostingAmounts :: (MixedAmount -> MixedAmount) -> Transaction -> Transaction
transactionMapPostingAmounts :: (MixedAmount -> MixedAmount) -> Transaction -> Transaction
transactionMapPostingAmounts MixedAmount -> MixedAmount
f = (Posting -> Posting) -> Transaction -> Transaction
transactionMapPostings ((MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount MixedAmount -> MixedAmount
f)
transactionAmounts :: Transaction -> [MixedAmount]
transactionAmounts :: Transaction -> [MixedAmount]
transactionAmounts = (Posting -> MixedAmount) -> [Posting] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> MixedAmount
pamount ([Posting] -> [MixedAmount])
-> (Transaction -> [Posting]) -> Transaction -> [MixedAmount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Transaction -> [Posting]
tpostings
transactionFile :: Transaction -> FilePath
transactionFile :: Transaction -> [Char]
transactionFile Transaction{(SourcePos, SourcePos)
tsourcepos :: (SourcePos, SourcePos)
tsourcepos :: Transaction -> (SourcePos, SourcePos)
tsourcepos} = SourcePos -> [Char]
sourceName (SourcePos -> [Char]) -> SourcePos -> [Char]
forall a b. (a -> b) -> a -> b
$ (SourcePos, SourcePos) -> SourcePos
forall a b. (a, b) -> a
fst (SourcePos, SourcePos)
tsourcepos
annotateErrorWithTransaction :: Transaction -> String -> String
annotateErrorWithTransaction :: Transaction -> [Char] -> [Char]
annotateErrorWithTransaction Transaction
t [Char]
s =
[[Char]] -> [Char]
unlines [ (SourcePos, SourcePos) -> [Char]
sourcePosPairPretty ((SourcePos, SourcePos) -> [Char])
-> (SourcePos, SourcePos) -> [Char]
forall a b. (a -> b) -> a -> b
$ Transaction -> (SourcePos, SourcePos)
tsourcepos Transaction
t, [Char]
s
, CommoditySymbol -> [Char]
T.unpack (CommoditySymbol -> [Char])
-> (CommoditySymbol -> CommoditySymbol)
-> CommoditySymbol
-> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> CommoditySymbol
T.stripEnd (CommoditySymbol -> [Char]) -> CommoditySymbol -> [Char]
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
showTransaction Transaction
t
]
tests_Transaction :: TestTree
tests_Transaction :: TestTree
tests_Transaction =
[Char] -> [TestTree] -> TestTree
testGroup [Char]
"Transaction" [
[Char] -> [TestTree] -> TestTree
testGroup [Char]
"showPostingLines" [
[Char] -> Assertion -> TestTree
testCase [Char]
"null posting" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Posting -> [CommoditySymbol]
showPostingLines Posting
nullposting [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= [CommoditySymbol
" 0"]
, [Char] -> Assertion -> TestTree
testCase [Char]
"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 :: CommoditySymbol
paccount = CommoditySymbol
"a"
, pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
mixed [DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, DecimalRaw Integer -> Amount
hrs DecimalRaw Integer
2]
, pcomment :: CommoditySymbol
pcomment = CommoditySymbol
"pcomment1\npcomment2\n tag3: val3 \n"
, ptype :: PostingType
ptype = PostingType
RegularPosting
, ptags :: [Tag]
ptags = [(CommoditySymbol
"ptag1", CommoditySymbol
"val1"), (CommoditySymbol
"ptag2", CommoditySymbol
"val2")]
}
in Posting -> [CommoditySymbol]
showPostingLines Posting
p [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" * a $1.00 ; pcomment1"
, CommoditySymbol
" ; pcomment2"
, CommoditySymbol
" ; tag3: val3 "
, CommoditySymbol
" * a 2.00h ; pcomment1"
, CommoditySymbol
" ; pcomment2"
, CommoditySymbol
" ; tag3: val3 "
]
]
, let
timp :: Transaction
timp = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"a" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, CommoditySymbol
"b" CommoditySymbol -> Amount -> Posting
`post` Amount
missingamt]}
texp :: Transaction
texp = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"a" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, CommoditySymbol
"b" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd (-DecimalRaw Integer
1)]}
texp1 :: Transaction
texp1 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"(a)" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1]}
texp2 :: Transaction
texp2 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"a" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, CommoditySymbol
"b" CommoditySymbol -> Amount -> Posting
`post` (DecimalRaw Integer -> Amount
hrs (-DecimalRaw Integer
1) Amount -> Amount -> Amount
`at` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1)]}
texp2b :: Transaction
texp2b = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"a" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, CommoditySymbol
"b" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
hrs (-DecimalRaw Integer
1)]}
t3 :: Transaction
t3 = Transaction
nulltransaction {tpostings :: [Posting]
tpostings = [CommoditySymbol
"a" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, CommoditySymbol
"b" CommoditySymbol -> Amount -> Posting
`post` Amount
missingamt, CommoditySymbol
"c" CommoditySymbol -> Amount -> Posting
`post` DecimalRaw Integer -> Amount
usd (-DecimalRaw Integer
1)]}
in [Char] -> [TestTree] -> TestTree
testGroup [Char]
"postingsAsLines" [
[Char] -> Assertion -> TestTree
testCase [Char]
"null-transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
nulltransaction) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= []
, [Char] -> Assertion -> TestTree
testCase [Char]
"implicit-amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
timp) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" a $1.00"
, CommoditySymbol
" b"
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"explicit-amounts" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" a $1.00"
, CommoditySymbol
" b $-1.00"
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"one-explicit-amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp1) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" (a) $1.00"
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"explicit-amounts-two-commodities" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp2) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" a $1.00"
, CommoditySymbol
" b -1.00h @ $1.00"
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"explicit-amounts-not-explicitly-balanced" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
texp2b) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[ CommoditySymbol
" a $1.00"
, CommoditySymbol
" b -1.00h"
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"implicit-amount-not-last" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [CommoditySymbol]
postingsAsLines Bool
False (Transaction -> [Posting]
tpostings Transaction
t3) [CommoditySymbol] -> [CommoditySymbol] -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[CommoditySymbol
" a $1.00", CommoditySymbol
" b", CommoditySymbol
" c $-1.00"]
]
, [Char] -> [TestTree] -> TestTree
testGroup [Char]
"showTransaction" [
[Char] -> Assertion -> TestTree
testCase [Char]
"null transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
showTransaction Transaction
nulltransaction CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= CommoditySymbol
"0000-01-01\n\n"
, [Char] -> Assertion -> TestTree
testCase [Char]
"non-null transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ Transaction -> CommoditySymbol
showTransaction
Transaction
nulltransaction
{ tdate :: Day
tdate = Integer -> Int -> Int -> Day
fromGregorian Integer
2012 Int
05 Int
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
$ Integer -> Int -> Int -> Day
fromGregorian Integer
2012 Int
05 Int
15
, tstatus :: Status
tstatus = Status
Unmarked
, tcode :: CommoditySymbol
tcode = CommoditySymbol
"code"
, tdescription :: CommoditySymbol
tdescription = CommoditySymbol
"desc"
, tcomment :: CommoditySymbol
tcomment = CommoditySymbol
"tcomment1\ntcomment2\n"
, ttags :: [Tag]
ttags = [(CommoditySymbol
"ttag1", CommoditySymbol
"val1")]
, tpostings :: [Posting]
tpostings =
[ Posting
nullposting
{ pstatus :: Status
pstatus = Status
Cleared
, paccount :: CommoditySymbol
paccount = CommoditySymbol
"a"
, pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
mixed [DecimalRaw Integer -> Amount
usd DecimalRaw Integer
1, DecimalRaw Integer -> Amount
hrs DecimalRaw Integer
2]
, pcomment :: CommoditySymbol
pcomment = CommoditySymbol
"\npcomment2\n"
, ptype :: PostingType
ptype = PostingType
RegularPosting
, ptags :: [Tag]
ptags = [(CommoditySymbol
"ptag1", CommoditySymbol
"val1"), (CommoditySymbol
"ptag2", CommoditySymbol
"val2")]
}
]
} CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
[CommoditySymbol] -> CommoditySymbol
T.unlines
[ CommoditySymbol
"2012-05-14=2012-05-15 (code) desc ; tcomment1"
, CommoditySymbol
" ; tcomment2"
, CommoditySymbol
" * a $1.00"
, CommoditySymbol
" ; pcomment2"
, CommoditySymbol
" * a 2.00h"
, CommoditySymbol
" ; pcomment2"
, CommoditySymbol
""
]
, [Char] -> Assertion -> TestTree
testCase [Char]
"show a balanced transaction" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(let t :: Transaction
t =
Integer
-> CommoditySymbol
-> (SourcePos, SourcePos)
-> Day
-> Maybe Day
-> Status
-> CommoditySymbol
-> CommoditySymbol
-> CommoditySymbol
-> [Tag]
-> [Posting]
-> Transaction
Transaction
Integer
0
CommoditySymbol
""
(SourcePos, SourcePos)
nullsourcepos
(Integer -> Int -> Int -> Day
fromGregorian Integer
2007 Int
01 Int
28)
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
CommoditySymbol
""
CommoditySymbol
"coopportunity"
CommoditySymbol
""
[]
[ Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"expenses:food:groceries", pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount (DecimalRaw Integer -> Amount
usd DecimalRaw Integer
47.18), ptransaction :: Maybe Transaction
ptransaction = Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
t}
, Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"assets:checking", pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount (DecimalRaw Integer -> Amount
usd (-DecimalRaw Integer
47.18)), ptransaction :: Maybe Transaction
ptransaction = Transaction -> Maybe Transaction
forall a. a -> Maybe a
Just Transaction
t}
]
in Transaction -> CommoditySymbol
showTransaction Transaction
t) CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([CommoditySymbol] -> CommoditySymbol
T.unlines
[ CommoditySymbol
"2007-01-28 coopportunity"
, CommoditySymbol
" expenses:food:groceries $47.18"
, CommoditySymbol
" assets:checking $-47.18"
, CommoditySymbol
""
])
, [Char] -> Assertion -> TestTree
testCase [Char]
"show an unbalanced transaction, should not elide" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> CommoditySymbol
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> CommoditySymbol
-> (SourcePos, SourcePos)
-> Day
-> Maybe Day
-> Status
-> CommoditySymbol
-> CommoditySymbol
-> CommoditySymbol
-> [Tag]
-> [Posting]
-> Transaction
Transaction
Integer
0
CommoditySymbol
""
(SourcePos, SourcePos)
nullsourcepos
(Integer -> Int -> Int -> Day
fromGregorian Integer
2007 Int
01 Int
28)
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
CommoditySymbol
""
CommoditySymbol
"coopportunity"
CommoditySymbol
""
[]
[ Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"expenses:food:groceries", pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount (DecimalRaw Integer -> Amount
usd DecimalRaw Integer
47.18)}
, Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"assets:checking", pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount (DecimalRaw Integer -> Amount
usd (-DecimalRaw Integer
47.19))}
])) CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([CommoditySymbol] -> CommoditySymbol
T.unlines
[ CommoditySymbol
"2007-01-28 coopportunity"
, CommoditySymbol
" expenses:food:groceries $47.18"
, CommoditySymbol
" assets:checking $-47.19"
, CommoditySymbol
""
])
, [Char] -> Assertion -> TestTree
testCase [Char]
"show a transaction with one posting and a missing amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> CommoditySymbol
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> CommoditySymbol
-> (SourcePos, SourcePos)
-> Day
-> Maybe Day
-> Status
-> CommoditySymbol
-> CommoditySymbol
-> CommoditySymbol
-> [Tag]
-> [Posting]
-> Transaction
Transaction
Integer
0
CommoditySymbol
""
(SourcePos, SourcePos)
nullsourcepos
(Integer -> Int -> Int -> Day
fromGregorian Integer
2007 Int
01 Int
28)
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
CommoditySymbol
""
CommoditySymbol
"coopportunity"
CommoditySymbol
""
[]
[Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"expenses:food:groceries", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}])) CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([CommoditySymbol] -> CommoditySymbol
T.unlines [CommoditySymbol
"2007-01-28 coopportunity", CommoditySymbol
" expenses:food:groceries", CommoditySymbol
""])
, [Char] -> Assertion -> TestTree
testCase [Char]
"show a transaction with a priced commodityless amount" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$
(Transaction -> CommoditySymbol
showTransaction
(Transaction -> Transaction
txnTieKnot (Transaction -> Transaction) -> Transaction -> Transaction
forall a b. (a -> b) -> a -> b
$
Integer
-> CommoditySymbol
-> (SourcePos, SourcePos)
-> Day
-> Maybe Day
-> Status
-> CommoditySymbol
-> CommoditySymbol
-> CommoditySymbol
-> [Tag]
-> [Posting]
-> Transaction
Transaction
Integer
0
CommoditySymbol
""
(SourcePos, SourcePos)
nullsourcepos
(Integer -> Int -> Int -> Day
fromGregorian Integer
2010 Int
01 Int
01)
Maybe Day
forall a. Maybe a
Nothing
Status
Unmarked
CommoditySymbol
""
CommoditySymbol
"x"
CommoditySymbol
""
[]
[ Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"a", pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ DecimalRaw Integer -> Amount
num DecimalRaw Integer
1 Amount -> Amount -> Amount
`at` (DecimalRaw Integer -> Amount
usd DecimalRaw Integer
2 Amount -> AmountPrecision -> Amount
`withPrecision` Word8 -> AmountPrecision
Precision Word8
0)}
, Posting
posting {paccount :: CommoditySymbol
paccount = CommoditySymbol
"b", pamount :: MixedAmount
pamount = MixedAmount
missingmixedamt}
])) CommoditySymbol -> CommoditySymbol -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?=
([CommoditySymbol] -> CommoditySymbol
T.unlines [CommoditySymbol
"2010-01-01 x", CommoditySymbol
" a 1 @ $2", CommoditySymbol
" b", CommoditySymbol
""])
]
]