{-# 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