{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}
module Hledger.Data.Posting (
nullposting,
posting,
post,
vpost,
post',
vpost',
nullsourcepos,
nullassertion,
balassert,
balassertTot,
balassertParInc,
balassertTotInc,
originalPosting,
postingStatus,
isReal,
isVirtual,
isBalancedVirtual,
isEmptyPosting,
hasBalanceAssignment,
hasAmount,
postingAllTags,
transactionAllTags,
relatedPostings,
removePrices,
postingDate,
postingDate2,
isPostingInDateSpan,
isPostingInDateSpan',
accountNamesFromPostings,
accountNamePostingType,
accountNameWithoutPostingType,
accountNameWithPostingType,
joinAccountNames,
concatAccountNames,
accountNameApplyAliases,
accountNameApplyAliasesMemo,
commentJoin,
commentAddTag,
commentAddTagNextLine,
sumPostings,
showPosting,
showComment,
postingTransformAmount,
postingApplyValuation,
postingToCost,
tests_Posting
)
where
import Data.Foldable (asum)
import Data.List.Extra (nubSort)
import qualified Data.Map as M
import Data.Maybe
import Data.MemoUgly (memo)
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid
#endif
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Calendar
import Safe
import Hledger.Utils
import Hledger.Data.Types
import Hledger.Data.Amount
import Hledger.Data.AccountName
import Hledger.Data.Dates (nulldate, spanContainsDate)
import Hledger.Data.Valuation
nullposting, posting :: Posting
nullposting :: Posting
nullposting = Posting :: Maybe Day
-> Maybe Day
-> Status
-> AccountName
-> MixedAmount
-> AccountName
-> PostingType
-> [Tag]
-> Maybe BalanceAssertion
-> Maybe Transaction
-> Maybe Posting
-> Posting
Posting
{pdate :: Maybe Day
pdate=Maybe Day
forall a. Maybe a
Nothing
,pdate2 :: Maybe Day
pdate2=Maybe Day
forall a. Maybe a
Nothing
,pstatus :: Status
pstatus=Status
Unmarked
,paccount :: AccountName
paccount=""
,pamount :: MixedAmount
pamount=MixedAmount
nullmixedamt
,pcomment :: AccountName
pcomment=""
,ptype :: PostingType
ptype=PostingType
RegularPosting
,ptags :: [Tag]
ptags=[]
,pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=Maybe BalanceAssertion
forall a. Maybe a
Nothing
,ptransaction :: Maybe Transaction
ptransaction=Maybe Transaction
forall a. Maybe a
Nothing
,poriginal :: Maybe Posting
poriginal=Maybe Posting
forall a. Maybe a
Nothing
}
posting :: Posting
posting = Posting
nullposting
post :: AccountName -> Amount -> Posting
post :: AccountName -> Amount -> Posting
post acc :: AccountName
acc amt :: Amount
amt = Posting
posting {paccount :: AccountName
paccount=AccountName
acc, pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Amount
amt]}
vpost :: AccountName -> Amount -> Posting
vpost :: AccountName -> Amount -> Posting
vpost acc :: AccountName
acc amt :: Amount
amt = (AccountName -> Amount -> Posting
post AccountName
acc Amount
amt){ptype :: PostingType
ptype=PostingType
VirtualPosting}
post' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
post' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
post' acc :: AccountName
acc amt :: Amount
amt ass :: Maybe BalanceAssertion
ass = Posting
posting {paccount :: AccountName
paccount=AccountName
acc, pamount :: MixedAmount
pamount=[Amount] -> MixedAmount
Mixed [Amount
amt], pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=Maybe BalanceAssertion
ass}
vpost' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
vpost' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
vpost' acc :: AccountName
acc amt :: Amount
amt ass :: Maybe BalanceAssertion
ass = (AccountName -> Amount -> Maybe BalanceAssertion -> Posting
post' AccountName
acc Amount
amt Maybe BalanceAssertion
ass){ptype :: PostingType
ptype=PostingType
VirtualPosting, pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=Maybe BalanceAssertion
ass}
nullsourcepos :: GenericSourcePos
nullsourcepos :: GenericSourcePos
nullsourcepos = FilePath -> (Int, Int) -> GenericSourcePos
JournalSourcePos "" (1,1)
nullassertion :: BalanceAssertion
nullassertion :: BalanceAssertion
nullassertion = BalanceAssertion :: Amount -> Bool -> Bool -> GenericSourcePos -> BalanceAssertion
BalanceAssertion
{baamount :: Amount
baamount=Amount
nullamt
,batotal :: Bool
batotal=Bool
False
,bainclusive :: Bool
bainclusive=Bool
False
,baposition :: GenericSourcePos
baposition=GenericSourcePos
nullsourcepos
}
balassert :: Amount -> Maybe BalanceAssertion
balassert :: Amount -> Maybe BalanceAssertion
balassert amt :: Amount
amt = BalanceAssertion -> Maybe BalanceAssertion
forall a. a -> Maybe a
Just (BalanceAssertion -> Maybe BalanceAssertion)
-> BalanceAssertion -> Maybe BalanceAssertion
forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt}
balassertTot :: Amount -> Maybe BalanceAssertion
balassertTot :: Amount -> Maybe BalanceAssertion
balassertTot amt :: Amount
amt = BalanceAssertion -> Maybe BalanceAssertion
forall a. a -> Maybe a
Just (BalanceAssertion -> Maybe BalanceAssertion)
-> BalanceAssertion -> Maybe BalanceAssertion
forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt, batotal :: Bool
batotal=Bool
True}
balassertParInc :: Amount -> Maybe BalanceAssertion
balassertParInc :: Amount -> Maybe BalanceAssertion
balassertParInc amt :: Amount
amt = BalanceAssertion -> Maybe BalanceAssertion
forall a. a -> Maybe a
Just (BalanceAssertion -> Maybe BalanceAssertion)
-> BalanceAssertion -> Maybe BalanceAssertion
forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt, bainclusive :: Bool
bainclusive=Bool
True}
balassertTotInc :: Amount -> Maybe BalanceAssertion
balassertTotInc :: Amount -> Maybe BalanceAssertion
balassertTotInc amt :: Amount
amt = BalanceAssertion -> Maybe BalanceAssertion
forall a. a -> Maybe a
Just (BalanceAssertion -> Maybe BalanceAssertion)
-> BalanceAssertion -> Maybe BalanceAssertion
forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt, batotal :: Bool
batotal=Bool
True, bainclusive :: Bool
bainclusive=Bool
True}
originalPosting :: Posting -> Posting
originalPosting :: Posting -> Posting
originalPosting p :: Posting
p = Posting -> Maybe Posting -> Posting
forall a. a -> Maybe a -> a
fromMaybe Posting
p (Maybe Posting -> Posting) -> Maybe Posting -> Posting
forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Posting
poriginal Posting
p
showPosting :: Posting -> String
showPosting :: Posting -> FilePath
showPosting p :: Posting
p@Posting{paccount :: Posting -> AccountName
paccount=AccountName
a,pamount :: Posting -> MixedAmount
pamount=MixedAmount
amt,ptype :: Posting -> PostingType
ptype=PostingType
t} =
[FilePath] -> FilePath
unlines ([FilePath] -> FilePath) -> [FilePath] -> FilePath
forall a b. (a -> b) -> a -> b
$ [[FilePath] -> FilePath
concatTopPadded [Day -> FilePath
forall a. Show a => a -> FilePath
show (Posting -> Day
postingDate Posting
p) FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " ", AccountName -> FilePath
showaccountname AccountName
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ " ", MixedAmount -> FilePath
showamount MixedAmount
amt, AccountName -> FilePath
showComment (Posting -> AccountName
pcomment Posting
p)]]
where
ledger3ishlayout :: Bool
ledger3ishlayout = Bool
False
acctnamewidth :: Int
acctnamewidth = if Bool
ledger3ishlayout then 25 else 22
showaccountname :: AccountName -> FilePath
showaccountname = Maybe Int -> Maybe Int -> Bool -> Bool -> FilePath -> FilePath
fitString (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
acctnamewidth) Maybe Int
forall a. Maybe a
Nothing Bool
False Bool
False (FilePath -> FilePath)
-> (AccountName -> FilePath) -> AccountName -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
bracket (FilePath -> FilePath)
-> (AccountName -> FilePath) -> AccountName -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AccountName -> FilePath
T.unpack (AccountName -> FilePath)
-> (AccountName -> AccountName) -> AccountName -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> AccountName -> AccountName
elideAccountName Int
width
(bracket :: FilePath -> FilePath
bracket,width :: Int
width) = case PostingType
t of
BalancedVirtualPosting -> (\s :: FilePath
s -> "["FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
sFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++"]", Int
acctnamewidthInt -> Int -> Int
forall a. Num a => a -> a -> a
-2)
VirtualPosting -> (\s :: FilePath
s -> "("FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
sFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++")", Int
acctnamewidthInt -> Int -> Int
forall a. Num a => a -> a -> a
-2)
_ -> (FilePath -> FilePath
forall a. a -> a
id,Int
acctnamewidth)
showamount :: MixedAmount -> FilePath
showamount = Int -> FilePath -> FilePath
padLeftWide 12 (FilePath -> FilePath)
-> (MixedAmount -> FilePath) -> MixedAmount -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> FilePath
showMixedAmount
showComment :: Text -> String
t :: AccountName
t = if AccountName -> Bool
T.null AccountName
t then "" else " ;" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ AccountName -> FilePath
T.unpack AccountName
t
isReal :: Posting -> Bool
isReal :: Posting -> Bool
isReal p :: Posting
p = Posting -> PostingType
ptype Posting
p PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
== PostingType
RegularPosting
isVirtual :: Posting -> Bool
isVirtual :: Posting -> Bool
isVirtual p :: Posting
p = Posting -> PostingType
ptype Posting
p PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
== PostingType
VirtualPosting
isBalancedVirtual :: Posting -> Bool
isBalancedVirtual :: Posting -> Bool
isBalancedVirtual p :: Posting
p = Posting -> PostingType
ptype Posting
p PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
== PostingType
BalancedVirtualPosting
hasAmount :: Posting -> Bool
hasAmount :: Posting -> Bool
hasAmount = (MixedAmount -> MixedAmount -> Bool
forall a. Eq a => a -> a -> Bool
/= MixedAmount
missingmixedamt) (MixedAmount -> Bool)
-> (Posting -> MixedAmount) -> Posting -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> MixedAmount
pamount
hasBalanceAssignment :: Posting -> Bool
hasBalanceAssignment :: Posting -> Bool
hasBalanceAssignment p :: Posting
p = Bool -> Bool
not (Posting -> Bool
hasAmount Posting
p) Bool -> Bool -> Bool
&& Maybe BalanceAssertion -> Bool
forall a. Maybe a -> Bool
isJust (Posting -> Maybe BalanceAssertion
pbalanceassertion Posting
p)
accountNamesFromPostings :: [Posting] -> [AccountName]
accountNamesFromPostings :: [Posting] -> [AccountName]
accountNamesFromPostings = [AccountName] -> [AccountName]
forall a. Ord a => [a] -> [a]
nubSort ([AccountName] -> [AccountName])
-> ([Posting] -> [AccountName]) -> [Posting] -> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Posting -> AccountName) -> [Posting] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> AccountName
paccount
sumPostings :: [Posting] -> MixedAmount
sumPostings :: [Posting] -> MixedAmount
sumPostings = [MixedAmount] -> MixedAmount
forall a. Num a => [a] -> a
sumStrict ([MixedAmount] -> MixedAmount)
-> ([Posting] -> [MixedAmount]) -> [Posting] -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Posting -> MixedAmount) -> [Posting] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> MixedAmount
pamount
removePrices :: Posting -> Posting
removePrices :: Posting -> Posting
removePrices p :: Posting
p = Posting
p{ pamount :: MixedAmount
pamount = [Amount] -> MixedAmount
Mixed ([Amount] -> MixedAmount) -> [Amount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Amount -> Amount
remove (Amount -> Amount) -> [Amount] -> [Amount]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MixedAmount -> [Amount]
amounts (Posting -> MixedAmount
pamount Posting
p) }
where remove :: Amount -> Amount
remove a :: Amount
a = Amount
a { aprice :: Maybe AmountPrice
aprice = Maybe AmountPrice
forall a. Maybe a
Nothing }
postingDate :: Posting -> Day
postingDate :: Posting -> Day
postingDate p :: Posting
p = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe Day
nulldate (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$ [Maybe Day] -> Maybe Day
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum [Maybe Day]
dates
where dates :: [Maybe Day]
dates = [ Posting -> Maybe Day
pdate Posting
p, Transaction -> Day
tdate (Transaction -> Day) -> Maybe Transaction -> Maybe Day
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Posting -> Maybe Transaction
ptransaction Posting
p ]
postingDate2 :: Posting -> Day
postingDate2 :: Posting -> Day
postingDate2 p :: Posting
p = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe Day
nulldate (Maybe Day -> Day) -> Maybe Day -> Day
forall a b. (a -> b) -> a -> b
$ [Maybe Day] -> Maybe Day
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum [Maybe Day]
dates
where dates :: [Maybe Day]
dates = [ Posting -> Maybe Day
pdate2 Posting
p
, Transaction -> Maybe Day
tdate2 (Transaction -> Maybe Day) -> Maybe Transaction -> Maybe Day
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Posting -> Maybe Transaction
ptransaction Posting
p
, Posting -> Maybe Day
pdate Posting
p
, Transaction -> Day
tdate (Transaction -> Day) -> Maybe Transaction -> Maybe Day
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Posting -> Maybe Transaction
ptransaction Posting
p
]
postingStatus :: Posting -> Status
postingStatus :: Posting -> Status
postingStatus Posting{pstatus :: Posting -> Status
pstatus=Status
s, ptransaction :: Posting -> Maybe Transaction
ptransaction=Maybe Transaction
mt}
| Status
s Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
Unmarked = case Maybe Transaction
mt of Just t :: Transaction
t -> Transaction -> Status
tstatus Transaction
t
Nothing -> Status
Unmarked
| Bool
otherwise = Status
s
postingAllTags :: Posting -> [Tag]
postingAllTags :: Posting -> [Tag]
postingAllTags p :: Posting
p = Posting -> [Tag]
ptags Posting
p [Tag] -> [Tag] -> [Tag]
forall a. [a] -> [a] -> [a]
++ [Tag] -> (Transaction -> [Tag]) -> Maybe Transaction -> [Tag]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] Transaction -> [Tag]
ttags (Posting -> Maybe Transaction
ptransaction Posting
p)
transactionAllTags :: Transaction -> [Tag]
transactionAllTags :: Transaction -> [Tag]
transactionAllTags t :: Transaction
t = Transaction -> [Tag]
ttags Transaction
t [Tag] -> [Tag] -> [Tag]
forall a. [a] -> [a] -> [a]
++ (Posting -> [Tag]) -> [Posting] -> [Tag]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Posting -> [Tag]
ptags (Transaction -> [Posting]
tpostings Transaction
t)
relatedPostings :: Posting -> [Posting]
relatedPostings :: Posting -> [Posting]
relatedPostings p :: Posting
p@Posting{ptransaction :: Posting -> Maybe Transaction
ptransaction=Just t :: Transaction
t} = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter (Posting -> Posting -> Bool
forall a. Eq a => a -> a -> Bool
/= Posting
p) ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
relatedPostings _ = []
isPostingInDateSpan :: DateSpan -> Posting -> Bool
isPostingInDateSpan :: DateSpan -> Posting -> Bool
isPostingInDateSpan = WhichDate -> DateSpan -> Posting -> Bool
isPostingInDateSpan' WhichDate
PrimaryDate
isPostingInDateSpan' :: WhichDate -> DateSpan -> Posting -> Bool
isPostingInDateSpan' :: WhichDate -> DateSpan -> Posting -> Bool
isPostingInDateSpan' PrimaryDate s :: DateSpan
s = DateSpan -> Day -> Bool
spanContainsDate DateSpan
s (Day -> Bool) -> (Posting -> Day) -> Posting -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> Day
postingDate
isPostingInDateSpan' SecondaryDate s :: DateSpan
s = DateSpan -> Day -> Bool
spanContainsDate DateSpan
s (Day -> Bool) -> (Posting -> Day) -> Posting -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> Day
postingDate2
isEmptyPosting :: Posting -> Bool
isEmptyPosting :: Posting -> Bool
isEmptyPosting = MixedAmount -> Bool
mixedAmountLooksZero (MixedAmount -> Bool)
-> (Posting -> MixedAmount) -> Posting -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> MixedAmount
pamount
accountNamePostingType :: AccountName -> PostingType
accountNamePostingType :: AccountName -> PostingType
accountNamePostingType a :: AccountName
a
| AccountName -> Bool
T.null AccountName
a = PostingType
RegularPosting
| AccountName -> Char
T.head AccountName
a Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '[' Bool -> Bool -> Bool
&& AccountName -> Char
T.last AccountName
a Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== ']' = PostingType
BalancedVirtualPosting
| AccountName -> Char
T.head AccountName
a Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '(' Bool -> Bool -> Bool
&& AccountName -> Char
T.last AccountName
a Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== ')' = PostingType
VirtualPosting
| Bool
otherwise = PostingType
RegularPosting
accountNameWithoutPostingType :: AccountName -> AccountName
accountNameWithoutPostingType :: AccountName -> AccountName
accountNameWithoutPostingType a :: AccountName
a = case AccountName -> PostingType
accountNamePostingType AccountName
a of
BalancedVirtualPosting -> AccountName -> AccountName
T.init (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> AccountName
T.tail AccountName
a
VirtualPosting -> AccountName -> AccountName
T.init (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> AccountName
T.tail AccountName
a
RegularPosting -> AccountName
a
accountNameWithPostingType :: PostingType -> AccountName -> AccountName
accountNameWithPostingType :: PostingType -> AccountName -> AccountName
accountNameWithPostingType BalancedVirtualPosting a :: AccountName
a = "["AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<>AccountName -> AccountName
accountNameWithoutPostingType AccountName
aAccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<>"]"
accountNameWithPostingType VirtualPosting a :: AccountName
a = "("AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<>AccountName -> AccountName
accountNameWithoutPostingType AccountName
aAccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<>")"
accountNameWithPostingType RegularPosting a :: AccountName
a = AccountName -> AccountName
accountNameWithoutPostingType AccountName
a
joinAccountNames :: AccountName -> AccountName -> AccountName
joinAccountNames :: AccountName -> AccountName -> AccountName
joinAccountNames a :: AccountName
a b :: AccountName
b = [AccountName] -> AccountName
concatAccountNames ([AccountName] -> AccountName) -> [AccountName] -> AccountName
forall a b. (a -> b) -> a -> b
$ (AccountName -> Bool) -> [AccountName] -> [AccountName]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (AccountName -> Bool) -> AccountName -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AccountName -> Bool
T.null) [AccountName
a,AccountName
b]
concatAccountNames :: [AccountName] -> AccountName
concatAccountNames :: [AccountName] -> AccountName
concatAccountNames as :: [AccountName]
as = PostingType -> AccountName -> AccountName
accountNameWithPostingType PostingType
t (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ AccountName -> [AccountName] -> AccountName
T.intercalate ":" ([AccountName] -> AccountName) -> [AccountName] -> AccountName
forall a b. (a -> b) -> a -> b
$ (AccountName -> AccountName) -> [AccountName] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map AccountName -> AccountName
accountNameWithoutPostingType [AccountName]
as
where t :: PostingType
t = PostingType -> [PostingType] -> PostingType
forall a. a -> [a] -> a
headDef PostingType
RegularPosting ([PostingType] -> PostingType) -> [PostingType] -> PostingType
forall a b. (a -> b) -> a -> b
$ (PostingType -> Bool) -> [PostingType] -> [PostingType]
forall a. (a -> Bool) -> [a] -> [a]
filter (PostingType -> PostingType -> Bool
forall a. Eq a => a -> a -> Bool
/= PostingType
RegularPosting) ([PostingType] -> [PostingType]) -> [PostingType] -> [PostingType]
forall a b. (a -> b) -> a -> b
$ (AccountName -> PostingType) -> [AccountName] -> [PostingType]
forall a b. (a -> b) -> [a] -> [b]
map AccountName -> PostingType
accountNamePostingType [AccountName]
as
accountNameApplyAliases :: [AccountAlias] -> AccountName -> AccountName
accountNameApplyAliases :: [AccountAlias] -> AccountName -> AccountName
accountNameApplyAliases aliases :: [AccountAlias]
aliases a :: AccountName
a = PostingType -> AccountName -> AccountName
accountNameWithPostingType PostingType
atype AccountName
aname'
where
(aname :: AccountName
aname,atype :: PostingType
atype) = (AccountName -> AccountName
accountNameWithoutPostingType AccountName
a, AccountName -> PostingType
accountNamePostingType AccountName
a)
aname' :: AccountName
aname' = (AccountName -> AccountAlias -> AccountName)
-> AccountName -> [AccountAlias] -> AccountName
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl
(\acct :: AccountName
acct alias :: AccountAlias
alias -> FilePath -> AccountName -> AccountName
forall a. Show a => FilePath -> a -> a
dbg6 "result" (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. (a -> b) -> a -> b
$ AccountAlias -> AccountName -> AccountName
aliasReplace (FilePath -> AccountAlias -> AccountAlias
forall a. Show a => FilePath -> a -> a
dbg6 "alias" AccountAlias
alias) (FilePath -> AccountName -> AccountName
forall a. Show a => FilePath -> a -> a
dbg6 "account" AccountName
acct))
AccountName
aname
[AccountAlias]
aliases
accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> AccountName
accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> AccountName
accountNameApplyAliasesMemo aliases :: [AccountAlias]
aliases = (AccountName -> AccountName) -> AccountName -> AccountName
forall a b. Ord a => (a -> b) -> a -> b
memo ([AccountAlias] -> AccountName -> AccountName
accountNameApplyAliases [AccountAlias]
aliases)
aliasReplace :: AccountAlias -> AccountName -> AccountName
aliasReplace :: AccountAlias -> AccountName -> AccountName
aliasReplace (BasicAlias old :: AccountName
old new :: AccountName
new) a :: AccountName
a
| AccountName
old AccountName -> AccountName -> Bool
`isAccountNamePrefixOf` AccountName
a Bool -> Bool -> Bool
|| AccountName
old AccountName -> AccountName -> Bool
forall a. Eq a => a -> a -> Bool
== AccountName
a = AccountName
new AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> Int -> AccountName -> AccountName
T.drop (AccountName -> Int
T.length AccountName
old) AccountName
a
| Bool
otherwise = AccountName
a
aliasReplace (RegexAlias re :: FilePath
re repl :: FilePath
repl) a :: AccountName
a = FilePath -> AccountName
T.pack (FilePath -> AccountName) -> FilePath -> AccountName
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath -> FilePath -> FilePath
regexReplaceCIMemo FilePath
re FilePath
repl (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ AccountName -> FilePath
T.unpack AccountName
a
postingApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Maybe Day -> Day -> Bool -> Posting -> ValuationType -> Posting
postingApplyValuation :: PriceOracle
-> Map AccountName AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> Posting
-> ValuationType
-> Posting
postingApplyValuation priceoracle :: PriceOracle
priceoracle styles :: Map AccountName AmountStyle
styles periodlast :: Day
periodlast mreportlast :: Maybe Day
mreportlast today :: Day
today ismultiperiod :: Bool
ismultiperiod p :: Posting
p v :: ValuationType
v =
case ValuationType
v of
AtCost Nothing -> Map AccountName AmountStyle -> Posting -> Posting
postingToCost Map AccountName AmountStyle
styles Posting
p
AtCost mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
periodlast (Posting -> Posting) -> Posting -> Posting
forall a b. (a -> b) -> a -> b
$ Map AccountName AmountStyle -> Posting -> Posting
postingToCost Map AccountName AmountStyle
styles Posting
p
AtThen mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc (Posting -> Day
postingDate Posting
p) Posting
p
AtEnd mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
periodlast Posting
p
AtNow mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
today Posting
p
AtDefault mc :: Maybe AccountName
mc | Bool
ismultiperiod -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
periodlast Posting
p
AtDefault mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc (Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe Day
today Maybe Day
mreportlast) Posting
p
AtDate d :: Day
d mc :: Maybe AccountName
mc -> PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
d Posting
p
postingToCost :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
postingToCost :: Map AccountName AmountStyle -> Posting -> Posting
postingToCost styles :: Map AccountName AmountStyle
styles p :: Posting
p@Posting{pamount :: Posting -> MixedAmount
pamount=MixedAmount
a} = Posting
p{pamount :: MixedAmount
pamount=Map AccountName AmountStyle -> MixedAmount -> MixedAmount
styleMixedAmount Map AccountName AmountStyle
styles (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ MixedAmount -> MixedAmount
mixedAmountCost MixedAmount
a}
postingValueAtDate :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Maybe CommoditySymbol -> Day -> Posting -> Posting
postingValueAtDate :: PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> Posting
-> Posting
postingValueAtDate priceoracle :: PriceOracle
priceoracle styles :: Map AccountName AmountStyle
styles mc :: Maybe AccountName
mc d :: Day
d p :: Posting
p = (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount (PriceOracle
-> Map AccountName AmountStyle
-> Maybe AccountName
-> Day
-> MixedAmount
-> MixedAmount
mixedAmountValueAtDate PriceOracle
priceoracle Map AccountName AmountStyle
styles Maybe AccountName
mc Day
d) Posting
p
postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount f :: MixedAmount -> MixedAmount
f p :: Posting
p@Posting{pamount :: Posting -> MixedAmount
pamount=MixedAmount
a} = Posting
p{pamount :: MixedAmount
pamount=MixedAmount -> MixedAmount
f MixedAmount
a}
commentJoin :: Text -> Text -> Text
c1 :: AccountName
c1 c2 :: AccountName
c2
| AccountName -> Bool
T.null AccountName
c1 = AccountName
c2
| AccountName -> Bool
T.null AccountName
c2 = AccountName
c1
| Bool
otherwise = AccountName
c1 AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> ", " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
c2
commentAddTag :: Text -> Tag -> Text
c :: AccountName
c (t :: AccountName
t,v :: AccountName
v)
| AccountName -> Bool
T.null AccountName
c' = AccountName
tag
| Bool
otherwise = AccountName
c' AccountName -> AccountName -> AccountName
`commentJoin` AccountName
tag
where
c' :: AccountName
c' = AccountName -> AccountName
textchomp AccountName
c
tag :: AccountName
tag = AccountName
t AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> ": " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
v
commentAddTagNextLine :: Text -> Tag -> Text
cmt :: AccountName
cmt (t :: AccountName
t,v :: AccountName
v) =
AccountName
cmt AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> if "\n" AccountName -> AccountName -> Bool
`T.isSuffixOf` AccountName
cmt then "" else "\n" AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
t AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> ": " AccountName -> AccountName -> AccountName
forall a. Semigroup a => a -> a -> a
<> AccountName
v
tests_Posting :: TestTree
tests_Posting = FilePath -> [TestTree] -> TestTree
tests "Posting" [
FilePath -> Assertion -> TestTree
test "accountNamePostingType" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
AccountName -> PostingType
accountNamePostingType "a" PostingType -> PostingType -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
RegularPosting
AccountName -> PostingType
accountNamePostingType "(a)" PostingType -> PostingType -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
VirtualPosting
AccountName -> PostingType
accountNamePostingType "[a]" PostingType -> PostingType -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
BalancedVirtualPosting
,FilePath -> Assertion -> TestTree
test "accountNameWithoutPostingType" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
AccountName -> AccountName
accountNameWithoutPostingType "(a)" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "a"
,FilePath -> Assertion -> TestTree
test "accountNameWithPostingType" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
PostingType -> AccountName -> AccountName
accountNameWithPostingType PostingType
VirtualPosting "[a]" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "(a)"
,FilePath -> Assertion -> TestTree
test "joinAccountNames" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
"a" AccountName -> AccountName -> AccountName
`joinAccountNames` "b:c" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "a:b:c"
"a" AccountName -> AccountName -> AccountName
`joinAccountNames` "(b:c)" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "(a:b:c)"
"[a]" AccountName -> AccountName -> AccountName
`joinAccountNames` "(b:c)" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "[a:b:c]"
"" AccountName -> AccountName -> AccountName
`joinAccountNames` "a" AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "a"
,FilePath -> Assertion -> TestTree
test "concatAccountNames" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
[AccountName] -> AccountName
concatAccountNames [] AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= ""
[AccountName] -> AccountName
concatAccountNames ["a","(b)","[c:d]"] AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "(a:b:c:d)"
,FilePath -> Assertion -> TestTree
test "commentAddTag" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
AccountName -> Tag -> AccountName
commentAddTag "" ("a","") AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "a: "
AccountName -> Tag -> AccountName
commentAddTag "[1/2]" ("a","") AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "[1/2], a: "
,FilePath -> Assertion -> TestTree
test "commentAddTagNextLine" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
AccountName -> Tag -> AccountName
commentAddTagNextLine "" ("a","") AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "\na: "
AccountName -> Tag -> AccountName
commentAddTagNextLine "[1/2]" ("a","") AccountName -> AccountName -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= "[1/2]\na: "
]