{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
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,
postingStripPrices,
postingApplyAliases,
postingApplyCommodityStyles,
postingStyleAmounts,
postingAddTags,
postingDate,
postingDate2,
postingDateOrDate2,
isPostingInDateSpan,
isPostingInDateSpan',
accountNamesFromPostings,
commentJoin,
commentAddTag,
commentAddTagUnspaced,
commentAddTagNextLine,
sumPostings,
showPosting,
showPostingLines,
postingAsLines,
postingsAsLines,
postingsAsLinesBeancount,
postingAsLinesBeancount,
showAccountName,
showAccountNameBeancount,
renderCommentLines,
showBalanceAssertion,
postingTransformAmount,
postingApplyValuation,
postingToCost,
postingAddInferredEquityPostings,
postingPriceDirectivesFromCost,
tests_Posting
)
where
import Data.Default (def)
import Data.Foldable (asum)
import Data.Function ((&))
import qualified Data.Map as M
import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Data.List (foldl', sort, union)
import qualified Data.Set as S
import Data.Text (Text)
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)
import Safe (maximumBound)
import Text.DocLayout (realLength)
import Text.Tabular.AsciiWide hiding (render)
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
instance HasAmounts BalanceAssertion where
styleAmounts :: Map Text AmountStyle -> BalanceAssertion -> BalanceAssertion
styleAmounts Map Text AmountStyle
styles ba :: BalanceAssertion
ba@BalanceAssertion{Amount
baamount :: BalanceAssertion -> Amount
baamount :: Amount
baamount} = BalanceAssertion
ba{baamount :: Amount
baamount=forall a. HasAmounts a => Map Text AmountStyle -> a -> a
styleAmounts Map Text AmountStyle
styles Amount
baamount}
instance HasAmounts Posting where
styleAmounts :: Map Text AmountStyle -> Posting -> Posting
styleAmounts Map Text AmountStyle
styles p :: Posting
p@Posting{MixedAmount
pamount :: Posting -> MixedAmount
pamount :: MixedAmount
pamount, Maybe BalanceAssertion
pbalanceassertion :: Posting -> Maybe BalanceAssertion
pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion} =
Posting
p{ pamount :: MixedAmount
pamount=forall a. HasAmounts a => Map Text AmountStyle -> a -> a
styleAmounts Map Text AmountStyle
styles MixedAmount
pamount
,pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=forall a. HasAmounts a => Map Text AmountStyle -> a -> a
styleAmounts Map Text AmountStyle
styles Maybe BalanceAssertion
pbalanceassertion
}
{-# DEPRECATED postingApplyCommodityStyles "please use styleAmounts instead" #-}
postingApplyCommodityStyles :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
postingApplyCommodityStyles :: Map Text AmountStyle -> Posting -> Posting
postingApplyCommodityStyles = forall a. HasAmounts a => Map Text AmountStyle -> a -> a
styleAmounts
{-# DEPRECATED postingStyleAmounts "please use styleAmounts instead" #-}
postingStyleAmounts :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
postingStyleAmounts :: Map Text AmountStyle -> Posting -> Posting
postingStyleAmounts = forall a. HasAmounts a => Map Text AmountStyle -> a -> a
styleAmounts
nullposting, posting :: Posting
nullposting :: Posting
nullposting = Posting
{pdate :: Maybe Day
pdate=forall a. Maybe a
Nothing
,pdate2 :: Maybe Day
pdate2=forall a. Maybe a
Nothing
,pstatus :: Status
pstatus=Status
Unmarked
,paccount :: Text
paccount=Text
""
,pamount :: MixedAmount
pamount=MixedAmount
nullmixedamt
,pcomment :: Text
pcomment=Text
""
,ptype :: PostingType
ptype=PostingType
RegularPosting
,ptags :: [Tag]
ptags=[]
,pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=forall a. Maybe a
Nothing
,ptransaction :: Maybe Transaction
ptransaction=forall a. Maybe a
Nothing
,poriginal :: Maybe Posting
poriginal=forall a. Maybe a
Nothing
}
posting :: Posting
posting = Posting
nullposting
post :: AccountName -> Amount -> Posting
post :: Text -> Amount -> Posting
post Text
acc Amount
amt = Posting
posting {paccount :: Text
paccount=Text
acc, pamount :: MixedAmount
pamount=Amount -> MixedAmount
mixedAmount Amount
amt}
vpost :: AccountName -> Amount -> Posting
vpost :: Text -> Amount -> Posting
vpost Text
acc Amount
amt = (Text -> Amount -> Posting
post Text
acc Amount
amt){ptype :: PostingType
ptype=PostingType
VirtualPosting}
post' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
post' :: Text -> Amount -> Maybe BalanceAssertion -> Posting
post' Text
acc Amount
amt Maybe BalanceAssertion
ass = Posting
posting {paccount :: Text
paccount=Text
acc, pamount :: MixedAmount
pamount=Amount -> MixedAmount
mixedAmount Amount
amt, pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=Maybe BalanceAssertion
ass}
vpost' :: AccountName -> Amount -> Maybe BalanceAssertion -> Posting
vpost' :: Text -> Amount -> Maybe BalanceAssertion -> Posting
vpost' Text
acc Amount
amt Maybe BalanceAssertion
ass = (Text -> Amount -> Maybe BalanceAssertion -> Posting
post' Text
acc Amount
amt Maybe BalanceAssertion
ass){ptype :: PostingType
ptype=PostingType
VirtualPosting, pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion=Maybe BalanceAssertion
ass}
nullsourcepos :: (SourcePos, SourcePos)
nullsourcepos :: (SourcePos, SourcePos)
nullsourcepos = (FilePath -> Pos -> Pos -> SourcePos
SourcePos FilePath
"" (Int -> Pos
mkPos Int
1) (Int -> Pos
mkPos Int
1), FilePath -> Pos -> Pos -> SourcePos
SourcePos FilePath
"" (Int -> Pos
mkPos Int
2) (Int -> Pos
mkPos Int
1))
nullassertion :: BalanceAssertion
nullassertion :: BalanceAssertion
nullassertion = BalanceAssertion
{baamount :: Amount
baamount=Amount
nullamt
,batotal :: Bool
batotal=Bool
False
,bainclusive :: Bool
bainclusive=Bool
False
,baposition :: SourcePos
baposition=FilePath -> SourcePos
initialPos FilePath
""
}
balassert :: Amount -> Maybe BalanceAssertion
balassert :: Amount -> Maybe BalanceAssertion
balassert Amount
amt = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt}
balassertTot :: Amount -> Maybe BalanceAssertion
balassertTot :: Amount -> Maybe BalanceAssertion
balassertTot Amount
amt = forall a. a -> Maybe a
Just 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 Amount
amt = forall a. a -> Maybe a
Just 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 Amount
amt = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ BalanceAssertion
nullassertion{baamount :: Amount
baamount=Amount
amt, batotal :: Bool
batotal=Bool
True, bainclusive :: Bool
bainclusive=Bool
True}
showBalanceAssertion :: BalanceAssertion -> WideBuilder
showBalanceAssertion :: BalanceAssertion -> WideBuilder
showBalanceAssertion BalanceAssertion
ba =
Char -> WideBuilder
singleton Char
'=' forall a. Semigroup a => a -> a -> a
<> WideBuilder
eq forall a. Semigroup a => a -> a -> a
<> WideBuilder
ast forall a. Semigroup a => a -> a -> a
<> Char -> WideBuilder
singleton Char
' ' forall a. Semigroup a => a -> a -> a
<> AmountDisplayOpts -> Amount -> WideBuilder
showAmountB forall a. Default a => a
def{displayZeroCommodity :: Bool
displayZeroCommodity=Bool
True} (BalanceAssertion -> Amount
baamount BalanceAssertion
ba)
where
eq :: WideBuilder
eq = if BalanceAssertion -> Bool
batotal BalanceAssertion
ba then Char -> WideBuilder
singleton Char
'=' else forall a. Monoid a => a
mempty
ast :: WideBuilder
ast = if BalanceAssertion -> Bool
bainclusive BalanceAssertion
ba then Char -> WideBuilder
singleton Char
'*' else forall a. Monoid a => a
mempty
singleton :: Char -> WideBuilder
singleton Char
c = Builder -> Int -> WideBuilder
WideBuilder (Char -> Builder
TB.singleton Char
c) Int
1
originalPosting :: Posting -> Posting
originalPosting :: Posting -> Posting
originalPosting Posting
p = forall a. a -> Maybe a -> a
fromMaybe Posting
p forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Posting
poriginal Posting
p
showPosting :: Posting -> String
showPosting :: Posting -> FilePath
showPosting Posting
p = Text -> FilePath
T.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Text] -> Text
T.unlines forall a b. (a -> b) -> a -> b
$ Bool -> [Posting] -> [Text]
postingsAsLines Bool
False [Posting
p]
showPostingLines :: Posting -> [Text]
showPostingLines :: Posting -> [Text]
showPostingLines Posting
p = forall {a} {b} {c}. (a, b, c) -> a
first3 forall a b. (a -> b) -> a -> b
$ Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLines Bool
False Bool
False Int
maxacctwidth Int
maxamtwidth Posting
p
where
linesWithWidths :: [([Text], Int, Int)]
linesWithWidths = forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLines Bool
False Bool
False Int
maxacctwidth Int
maxamtwidth) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Posting
p] Transaction -> [Posting]
tpostings forall a b. (a -> b) -> a -> b
$ Posting -> Maybe Transaction
ptransaction Posting
p
maxacctwidth :: Int
maxacctwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> b
second3 [([Text], Int, Int)]
linesWithWidths
maxamtwidth :: Int
maxamtwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> c
third3 [([Text], Int, Int)]
linesWithWidths
postingsAsLines :: Bool -> [Posting] -> [Text]
postingsAsLines :: Bool -> [Posting] -> [Text]
postingsAsLines Bool
onelineamounts [Posting]
ps = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap forall {a} {b} {c}. (a, b, c) -> a
first3 [([Text], Int, Int)]
linesWithWidths
where
linesWithWidths :: [([Text], Int, Int)]
linesWithWidths = forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLines Bool
False Bool
onelineamounts Int
maxacctwidth Int
maxamtwidth) [Posting]
ps
maxacctwidth :: Int
maxacctwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> b
second3 [([Text], Int, Int)]
linesWithWidths
maxamtwidth :: Int
maxamtwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> c
third3 [([Text], Int, Int)]
linesWithWidths
postingAsLines :: Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLines :: Bool -> Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLines Bool
elideamount Bool
onelineamounts Int
acctwidth Int
amtwidth Posting
p =
(forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (forall a. [a] -> [a] -> [a]
++ [Text]
newlinecomments) [[Text]]
postingblocks, Int
thisacctwidth, Int
thisamtwidth)
where
postingblocks :: [[Text]]
postingblocks = [forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
T.stripEnd forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.lines forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
TL.toStrict forall a b. (a -> b) -> a -> b
$
[Cell] -> Text
render [ Align -> Text -> Cell
textCell Align
BottomLeft Text
statusandaccount
, Align -> Text -> Cell
textCell Align
BottomLeft Text
" "
, Align -> [WideBuilder] -> Cell
Cell Align
BottomLeft [WideBuilder -> WideBuilder
pad WideBuilder
amt]
, Align -> [WideBuilder] -> Cell
Cell Align
BottomLeft [WideBuilder
assertion]
, Align -> Text -> Cell
textCell Align
BottomLeft Text
samelinecomment
]
| (WideBuilder
amt,WideBuilder
assertion) <- [(WideBuilder, WideBuilder)]
shownAmountsAssertions]
render :: [Cell] -> Text
render = TableOpts -> Header Cell -> Text
renderRow forall a. Default a => a
def{tableBorders :: Bool
tableBorders=Bool
False, borderSpaces :: Bool
borderSpaces=Bool
False} forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall h. Properties -> [Header h] -> Header h
Group Properties
NoLine forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall h. h -> Header h
Header
pad :: WideBuilder -> WideBuilder
pad WideBuilder
amt = Builder -> Int -> WideBuilder
WideBuilder (Text -> Builder
TB.fromText forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
T.replicate Int
w Text
" ") Int
w forall a. Semigroup a => a -> a -> a
<> WideBuilder
amt
where w :: Int
w = forall a. Ord a => a -> a -> a
max Int
12 Int
amtwidth forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
amt
pacctstr :: Posting -> Text
pacctstr Posting
p' = Maybe Int -> PostingType -> Text -> Text
showAccountName forall a. Maybe a
Nothing (Posting -> PostingType
ptype Posting
p') (Posting -> Text
paccount Posting
p')
pstatusandacct :: Posting -> Text
pstatusandacct Posting
p' = Posting -> Text
pstatusprefix Posting
p' forall a. Semigroup a => a -> a -> a
<> Posting -> Text
pacctstr Posting
p'
pstatusprefix :: Posting -> Text
pstatusprefix Posting
p' = case Posting -> Status
pstatus Posting
p' of
Status
Unmarked -> Text
""
Status
s -> FilePath -> Text
T.pack (forall a. Show a => a -> FilePath
show Status
s) forall a. Semigroup a => a -> a -> a
<> Text
" "
shownAmounts :: [WideBuilder]
shownAmounts
| Bool
elideamount = [forall a. Monoid a => a
mempty]
| Bool
otherwise = AmountDisplayOpts -> MixedAmount -> [WideBuilder]
showMixedAmountLinesB AmountDisplayOpts
displayopts forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p
where displayopts :: AmountDisplayOpts
displayopts = AmountDisplayOpts
noColour{
displayZeroCommodity :: Bool
displayZeroCommodity=Bool
True, displayAddDecimalMark :: Bool
displayAddDecimalMark=Bool
True, displayOneLine :: Bool
displayOneLine=Bool
onelineamounts
}
thisamtwidth :: Int
thisamtwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map WideBuilder -> Int
wbWidth [WideBuilder]
shownAmounts
shownAmountsAssertions :: [(WideBuilder, WideBuilder)]
shownAmountsAssertions = forall a b. [a] -> [b] -> [(a, b)]
zip [WideBuilder]
shownAmounts [WideBuilder]
shownAssertions
where
shownAssertions :: [WideBuilder]
shownAssertions = forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length [WideBuilder]
shownAmounts forall a. Num a => a -> a -> a
- Int
1) forall a. Monoid a => a
mempty forall a. [a] -> [a] -> [a]
++ [WideBuilder
assertion]
where
assertion :: WideBuilder
assertion = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. Monoid a => a
mempty ((Builder -> Int -> WideBuilder
WideBuilder (Char -> Builder
TB.singleton Char
' ') Int
1 forall a. Semigroup a => a -> a -> a
<>)forall b c a. (b -> c) -> (a -> b) -> a -> c
.BalanceAssertion -> WideBuilder
showBalanceAssertion) forall a b. (a -> b) -> a -> b
$ Posting -> Maybe BalanceAssertion
pbalanceassertion Posting
p
statusandaccount :: Text
statusandaccount = Text -> Text
lineIndent forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Int -> Maybe Int -> Bool -> Bool -> Text -> Text
fitText (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Int
2 forall a. Num a => a -> a -> a
+ Int
acctwidth) forall a. Maybe a
Nothing Bool
False Bool
True forall a b. (a -> b) -> a -> b
$ Posting -> Text
pstatusandacct Posting
p
thisacctwidth :: Int
thisacctwidth = forall a. HasChars a => a -> Int
realLength forall a b. (a -> b) -> a -> b
$ Posting -> Text
pacctstr Posting
p
(Text
samelinecomment, [Text]
newlinecomments) =
case Text -> [Text]
renderCommentLines (Posting -> Text
pcomment Posting
p) of [] -> (Text
"",[])
Text
c:[Text]
cs -> (Text
c,[Text]
cs)
showAccountName :: Maybe Int -> PostingType -> AccountName -> Text
showAccountName :: Maybe Int -> PostingType -> Text -> Text
showAccountName Maybe Int
w = PostingType -> Text -> Text
fmt
where
fmt :: PostingType -> Text -> Text
fmt PostingType
RegularPosting = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. a -> a
id Int -> Text -> Text
T.take Maybe Int
w
fmt PostingType
VirtualPosting = Text -> Text -> Text -> Text
wrap Text
"(" Text
")" forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. a -> a
id (Int -> Text -> Text
T.takeEnd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => a -> a -> a
subtract Int
2) Maybe Int
w
fmt PostingType
BalancedVirtualPosting = Text -> Text -> Text -> Text
wrap Text
"[" Text
"]" forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. a -> a
id (Int -> Text -> Text
T.takeEnd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => a -> a -> a
subtract Int
2) Maybe Int
w
postingsAsLinesBeancount :: [Posting] -> [Text]
postingsAsLinesBeancount :: [Posting] -> [Text]
postingsAsLinesBeancount [Posting]
ps = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap forall {a} {b} {c}. (a, b, c) -> a
first3 [([Text], Int, Int)]
linesWithWidths
where
linesWithWidths :: [([Text], Int, Int)]
linesWithWidths = forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLinesBeancount Bool
False Int
maxacctwidth Int
maxamtwidth) [Posting]
ps
maxacctwidth :: Int
maxacctwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> b
second3 [([Text], Int, Int)]
linesWithWidths
maxamtwidth :: Int
maxamtwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall {a} {b} {c}. (a, b, c) -> c
third3 [([Text], Int, Int)]
linesWithWidths
postingAsLinesBeancount :: Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLinesBeancount :: Bool -> Int -> Int -> Posting -> ([Text], Int, Int)
postingAsLinesBeancount Bool
elideamount Int
acctwidth Int
amtwidth Posting
p =
(forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (forall a. [a] -> [a] -> [a]
++ [Text]
newlinecomments) [[Text]]
postingblocks, Int
thisacctwidth, Int
thisamtwidth)
where
postingblocks :: [[Text]]
postingblocks = [forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
T.stripEnd forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.lines forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
TL.toStrict forall a b. (a -> b) -> a -> b
$
[Cell] -> Text
render [ Align -> Text -> Cell
textCell Align
BottomLeft Text
statusandaccount
, Align -> Text -> Cell
textCell Align
BottomLeft Text
" "
, Align -> [WideBuilder] -> Cell
Cell Align
BottomLeft [WideBuilder -> WideBuilder
pad WideBuilder
amt]
, Align -> Text -> Cell
textCell Align
BottomLeft Text
samelinecomment
]
| (WideBuilder
amt,WideBuilder
_assertion) <- [(WideBuilder, WideBuilder)]
shownAmountsAssertions]
render :: [Cell] -> Text
render = TableOpts -> Header Cell -> Text
renderRow forall a. Default a => a
def{tableBorders :: Bool
tableBorders=Bool
False, borderSpaces :: Bool
borderSpaces=Bool
False} forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall h. Properties -> [Header h] -> Header h
Group Properties
NoLine forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall h. h -> Header h
Header
pad :: WideBuilder -> WideBuilder
pad WideBuilder
amt = Builder -> Int -> WideBuilder
WideBuilder (Text -> Builder
TB.fromText forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
T.replicate Int
w Text
" ") Int
w forall a. Semigroup a => a -> a -> a
<> WideBuilder
amt
where w :: Int
w = forall a. Ord a => a -> a -> a
max Int
12 Int
amtwidth forall a. Num a => a -> a -> a
- WideBuilder -> Int
wbWidth WideBuilder
amt
pacct :: Text
pacct = Maybe Int -> Text -> Text
showAccountNameBeancount forall a. Maybe a
Nothing forall a b. (a -> b) -> a -> b
$ Posting -> Text
paccount Posting
p
pstatusandacct :: Posting -> Text
pstatusandacct Posting
p' = if Posting -> Status
pstatus Posting
p' forall a. Eq a => a -> a -> Bool
== Status
Pending then Text
"! " else Text
"" forall a. Semigroup a => a -> a -> a
<> Text
pacct
shownAmounts :: [WideBuilder]
shownAmounts
| Bool
elideamount = [forall a. Monoid a => a
mempty]
| Bool
otherwise = AmountDisplayOpts -> MixedAmount -> [WideBuilder]
showMixedAmountLinesB AmountDisplayOpts
displayopts MixedAmount
a'
where
displayopts :: AmountDisplayOpts
displayopts = AmountDisplayOpts
noColour{ displayZeroCommodity :: Bool
displayZeroCommodity=Bool
True, displayAddDecimalMark :: Bool
displayAddDecimalMark=Bool
True }
a' :: MixedAmount
a' = (Amount -> Amount) -> MixedAmount -> MixedAmount
mapMixedAmount Amount -> Amount
amountToBeancount forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p
thisamtwidth :: Int
thisamtwidth = forall a. Ord a => a -> [a] -> a
maximumBound Int
0 forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map WideBuilder -> Int
wbWidth [WideBuilder]
shownAmounts
shownAmountsAssertions :: [(WideBuilder, WideBuilder)]
shownAmountsAssertions = forall a b. [a] -> [b] -> [(a, b)]
zip [WideBuilder]
shownAmounts [WideBuilder]
shownAssertions
where
shownAssertions :: [WideBuilder]
shownAssertions = forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length [WideBuilder]
shownAmounts forall a. Num a => a -> a -> a
- Int
1) forall a. Monoid a => a
mempty forall a. [a] -> [a] -> [a]
++ [WideBuilder
assertion]
where
assertion :: WideBuilder
assertion = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. Monoid a => a
mempty ((Builder -> Int -> WideBuilder
WideBuilder (Char -> Builder
TB.singleton Char
' ') Int
1 forall a. Semigroup a => a -> a -> a
<>)forall b c a. (b -> c) -> (a -> b) -> a -> c
.BalanceAssertion -> WideBuilder
showBalanceAssertion) forall a b. (a -> b) -> a -> b
$ Posting -> Maybe BalanceAssertion
pbalanceassertion Posting
p
statusandaccount :: Text
statusandaccount = Text -> Text
lineIndent forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Int -> Maybe Int -> Bool -> Bool -> Text -> Text
fitText (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Int
2 forall a. Num a => a -> a -> a
+ Int
acctwidth) forall a. Maybe a
Nothing Bool
False Bool
True forall a b. (a -> b) -> a -> b
$ Posting -> Text
pstatusandacct Posting
p
thisacctwidth :: Int
thisacctwidth = forall a. HasChars a => a -> Int
realLength Text
pacct
(Text
samelinecomment, [Text]
newlinecomments) =
case Text -> [Text]
renderCommentLines (Posting -> Text
pcomment Posting
p) of [] -> (Text
"",[])
Text
c:[Text]
cs -> (Text
c,[Text]
cs)
type BeancountAmount = Amount
amountToBeancount :: Amount -> BeancountAmount
amountToBeancount :: Amount -> Amount
amountToBeancount a :: Amount
a@Amount{acommodity :: Amount -> Text
acommodity=Text
c,astyle :: Amount -> AmountStyle
astyle=AmountStyle
s,aprice :: Amount -> Maybe AmountPrice
aprice=Maybe AmountPrice
mp} = Amount
a{acommodity :: Text
acommodity=Text
c', astyle :: AmountStyle
astyle=AmountStyle
s', aprice :: Maybe AmountPrice
aprice=Maybe AmountPrice
mp'}
where
c' :: Text
c' = Text -> Text
T.toUpper forall a b. (a -> b) -> a -> b
$
HasCallStack => Text -> Text -> Text -> Text
T.replace Text
"$" Text
"USD" forall a b. (a -> b) -> a -> b
$
HasCallStack => Text -> Text -> Text -> Text
T.replace Text
"€" Text
"EUR" forall a b. (a -> b) -> a -> b
$
HasCallStack => Text -> Text -> Text -> Text
T.replace Text
"¥" Text
"JPY" forall a b. (a -> b) -> a -> b
$
HasCallStack => Text -> Text -> Text -> Text
T.replace Text
"£" Text
"GBP" forall a b. (a -> b) -> a -> b
$
Text
c
s' :: AmountStyle
s' = AmountStyle
s{ascommodityside :: Side
ascommodityside=Side
R, ascommodityspaced :: Bool
ascommodityspaced=Bool
True}
mp' :: Maybe AmountPrice
mp' = AmountPrice -> AmountPrice
costToBeancount forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe AmountPrice
mp
where
costToBeancount :: AmountPrice -> AmountPrice
costToBeancount (TotalPrice Amount
amt) = Amount -> AmountPrice
TotalPrice forall a b. (a -> b) -> a -> b
$ Amount -> Amount
amountToBeancount Amount
amt
costToBeancount (UnitPrice Amount
amt) = Amount -> AmountPrice
UnitPrice forall a b. (a -> b) -> a -> b
$ Amount -> Amount
amountToBeancount Amount
amt
showAccountNameBeancount :: Maybe Int -> AccountName -> Text
showAccountNameBeancount :: Maybe Int -> Text -> Text
showAccountNameBeancount Maybe Int
w = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. a -> a
id Int -> Text -> Text
T.take Maybe Int
w forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
accountNameToBeancount
renderCommentLines :: Text -> [Text]
Text
t =
case Text -> [Text]
T.lines Text
t of
[] -> []
[Text
l] -> [Text -> Text
commentSpace forall a b. (a -> b) -> a -> b
$ Text -> Text
comment Text
l]
(Text
"":[Text]
ls) -> Text
"" forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text
lineIndent forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
comment) [Text]
ls
(Text
l:[Text]
ls) -> Text -> Text
commentSpace (Text -> Text
comment Text
l) forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text
lineIndent forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
comment) [Text]
ls
where
comment :: Text -> Text
comment = (Text
"; "forall a. Semigroup a => a -> a -> a
<>)
lineIndent :: Text -> Text
lineIndent :: Text -> Text
lineIndent = (Text
" "forall a. Semigroup a => a -> a -> a
<>)
commentSpace :: Text -> Text
= (Text
" "forall a. Semigroup a => a -> a -> a
<>)
isReal :: Posting -> Bool
isReal :: Posting -> Bool
isReal Posting
p = Posting -> PostingType
ptype Posting
p forall a. Eq a => a -> a -> Bool
== PostingType
RegularPosting
isVirtual :: Posting -> Bool
isVirtual :: Posting -> Bool
isVirtual Posting
p = Posting -> PostingType
ptype Posting
p forall a. Eq a => a -> a -> Bool
== PostingType
VirtualPosting
isBalancedVirtual :: Posting -> Bool
isBalancedVirtual :: Posting -> Bool
isBalancedVirtual Posting
p = Posting -> PostingType
ptype Posting
p forall a. Eq a => a -> a -> Bool
== PostingType
BalancedVirtualPosting
hasAmount :: Posting -> Bool
hasAmount :: Posting -> Bool
hasAmount = Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> Bool
isMissingMixedAmount forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> MixedAmount
pamount
hasBalanceAssignment :: Posting -> Bool
hasBalanceAssignment :: Posting -> Bool
hasBalanceAssignment Posting
p = Bool -> Bool
not (Posting -> Bool
hasAmount Posting
p) Bool -> Bool -> Bool
&& forall a. Maybe a -> Bool
isJust (Posting -> Maybe BalanceAssertion
pbalanceassertion Posting
p)
accountNamesFromPostings :: [Posting] -> [AccountName]
accountNamesFromPostings :: [Posting] -> [Text]
accountNamesFromPostings = forall a. Set a -> [a]
S.toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Ord a => [a] -> Set a
S.fromList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Posting -> Text
paccount
sumPostings :: [Posting] -> MixedAmount
sumPostings :: [Posting] -> MixedAmount
sumPostings = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\MixedAmount
amt Posting
p -> MixedAmount -> MixedAmount -> MixedAmount
maPlus MixedAmount
amt forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p) MixedAmount
nullmixedamt
postingStripPrices :: Posting -> Posting
postingStripPrices :: Posting -> Posting
postingStripPrices = (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount MixedAmount -> MixedAmount
mixedAmountStripPrices
postingDate :: Posting -> Day
postingDate :: Posting -> Day
postingDate Posting
p = forall a. a -> Maybe a -> a
fromMaybe Day
nulldate forall a b. (a -> b) -> a -> b
$ 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 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 Posting
p = forall a. a -> Maybe a -> a
fromMaybe Day
nulldate forall a b. (a -> b) -> a -> b
$ 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 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 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Posting -> Maybe Transaction
ptransaction Posting
p
]
postingDateOrDate2 :: WhichDate -> Posting -> Day
postingDateOrDate2 :: WhichDate -> Posting -> Day
postingDateOrDate2 WhichDate
PrimaryDate = Posting -> Day
postingDate
postingDateOrDate2 WhichDate
SecondaryDate = Posting -> Day
postingDate2
postingStatus :: Posting -> Status
postingStatus :: Posting -> Status
postingStatus Posting{pstatus :: Posting -> Status
pstatus=Status
s, ptransaction :: Posting -> Maybe Transaction
ptransaction=Maybe Transaction
mt} = case Status
s of
Status
Unmarked -> forall b a. b -> (a -> b) -> Maybe a -> b
maybe Status
Unmarked Transaction -> Status
tstatus Maybe Transaction
mt
Status
_ -> Status
s
postingAllTags :: Posting -> [Tag]
postingAllTags :: Posting -> [Tag]
postingAllTags Posting
p = Posting -> [Tag]
ptags Posting
p forall a. [a] -> [a] -> [a]
++ 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 Transaction
t = Transaction -> [Tag]
ttags Transaction
t forall a. [a] -> [a] -> [a]
++ 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 Transaction
t} = forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= Posting
p) forall a b. (a -> b) -> a -> b
$ Transaction -> [Posting]
tpostings Transaction
t
relatedPostings Posting
_ = []
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' WhichDate
PrimaryDate DateSpan
s = DateSpan -> Day -> Bool
spanContainsDate DateSpan
s forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> Day
postingDate
isPostingInDateSpan' WhichDate
SecondaryDate DateSpan
s = DateSpan -> Day -> Bool
spanContainsDate DateSpan
s forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> Day
postingDate2
isEmptyPosting :: Posting -> Bool
isEmptyPosting :: Posting -> Bool
isEmptyPosting = MixedAmount -> Bool
mixedAmountLooksZero forall b c a. (b -> c) -> (a -> b) -> a -> c
. Posting -> MixedAmount
pamount
postingApplyAliases :: [AccountAlias] -> Posting -> Either RegexError Posting
postingApplyAliases :: [AccountAlias] -> Posting -> Either FilePath Posting
postingApplyAliases [AccountAlias]
aliases p :: Posting
p@Posting{Text
paccount :: Text
paccount :: Posting -> Text
paccount} =
case [AccountAlias] -> Text -> Either FilePath Text
accountNameApplyAliases [AccountAlias]
aliases Text
paccount of
Right Text
a -> forall a b. b -> Either a b
Right Posting
p{paccount :: Text
paccount=Text
a}
Left FilePath
e -> forall a b. a -> Either a b
Left FilePath
err
where
err :: FilePath
err = FilePath
"problem while applying account aliases:\n" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
pshow [AccountAlias]
aliases
forall a. [a] -> [a] -> [a]
++ FilePath
"\n to account name: "forall a. [a] -> [a] -> [a]
++Text -> FilePath
T.unpack Text
paccountforall a. [a] -> [a] -> [a]
++FilePath
"\n "forall a. [a] -> [a] -> [a]
++FilePath
e
postingAddTags :: Posting -> [Tag] -> Posting
postingAddTags :: Posting -> [Tag] -> Posting
postingAddTags p :: Posting
p@Posting{[Tag]
ptags :: [Tag]
ptags :: Posting -> [Tag]
ptags} [Tag]
tags = Posting
p{ptags :: [Tag]
ptags=[Tag]
ptags forall a. Eq a => [a] -> [a] -> [a]
`union` [Tag]
tags}
postingApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Day -> ValuationType -> Posting -> Posting
postingApplyValuation :: PriceOracle
-> Map Text AmountStyle
-> Day
-> Day
-> ValuationType
-> Posting
-> Posting
postingApplyValuation PriceOracle
priceoracle Map Text AmountStyle
styles Day
periodlast Day
today ValuationType
v Posting
p =
(MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount (PriceOracle
-> Map Text AmountStyle
-> Day
-> Day
-> Day
-> ValuationType
-> MixedAmount
-> MixedAmount
mixedAmountApplyValuation PriceOracle
priceoracle Map Text AmountStyle
styles Day
periodlast Day
today (Posting -> Day
postingDate Posting
p) ValuationType
v) Posting
p
postingToCost :: ConversionOp -> Posting -> Maybe Posting
postingToCost :: ConversionOp -> Posting -> Maybe Posting
postingToCost ConversionOp
NoConversionOp Posting
p = forall a. a -> Maybe a
Just Posting
p
postingToCost ConversionOp
ToCost Posting
p
| Text
"_conversion-matched" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst (Posting -> [Tag]
ptags Posting
p) Bool -> Bool -> Bool
&& Bool
noCost = forall a. Maybe a
Nothing
| Bool
otherwise = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount MixedAmount -> MixedAmount
mixedAmountCost Posting
p
where
noCost :: Bool
noCost = (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Maybe AmountPrice
aprice) forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> [Amount]
amountsRaw) forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p
postingAddInferredEquityPostings :: Bool -> Text -> Posting -> [Posting]
postingAddInferredEquityPostings :: Bool -> Text -> Posting -> [Posting]
postingAddInferredEquityPostings Bool
verbosetags Text
equityAcct Posting
p
| Text
"_price-matched" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst (Posting -> [Tag]
ptags Posting
p) = [Posting
p]
| Bool
otherwise = Posting
taggedPosting forall a. a -> [a] -> [a]
: forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Amount -> [Posting]
conversionPostings [Amount]
priceAmounts
where
taggedPosting :: Posting
taggedPosting
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Amount]
priceAmounts = Posting
p
| Bool
otherwise = Posting
p{ ptags :: [Tag]
ptags = (Text
"_price-matched",Text
"") forall a. a -> [a] -> [a]
: Posting -> [Tag]
ptags Posting
p }
conversionPostings :: Amount -> [Posting]
conversionPostings Amount
amt = case Amount -> Maybe AmountPrice
aprice Amount
amt of
Maybe AmountPrice
Nothing -> []
Just AmountPrice
_ -> [ Posting
cp{ paccount :: Text
paccount = Text
accountPrefix forall a. Semigroup a => a -> a -> a
<> Text
amtCommodity
, pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => a -> a
negate forall a b. (a -> b) -> a -> b
$ Amount -> Amount
amountStripPrices Amount
amt
}
, Posting
cp{ paccount :: Text
paccount = Text
accountPrefix forall a. Semigroup a => a -> a -> a
<> Text
costCommodity
, pamount :: MixedAmount
pamount = Amount -> MixedAmount
mixedAmount Amount
cost
}
]
where
cost :: Amount
cost = Amount -> Amount
amountCost Amount
amt
amtCommodity :: Text
amtCommodity = Amount -> Text
commodity Amount
amt
costCommodity :: Text
costCommodity = Amount -> Text
commodity Amount
cost
cp :: Posting
cp = Posting
p{ pcomment :: Text
pcomment = Posting -> Text
pcomment Posting
p forall a b. a -> (a -> b) -> b
& (if Bool
verbosetags then (Text -> Tag -> Text
`commentAddTag` (Text
"generated-posting",Text
"conversion")) else forall a. a -> a
id)
, ptags :: [Tag]
ptags =
(Text
"_conversion-matched",Text
"") forall a. a -> [a] -> [a]
:
(Text
"_generated-posting",Text
"conversion") forall a. a -> [a] -> [a]
:
(if Bool
verbosetags then [(Text
"generated-posting", Text
"conversion")] else [])
, pbalanceassertion :: Maybe BalanceAssertion
pbalanceassertion = forall a. Maybe a
Nothing
, poriginal :: Maybe Posting
poriginal = forall a. Maybe a
Nothing
}
accountPrefix :: Text
accountPrefix = forall a. Monoid a => [a] -> a
mconcat [ Text
equityAcct, Text
":", Text -> [Text] -> Text
T.intercalate Text
"-" forall a b. (a -> b) -> a -> b
$ forall a. Ord a => [a] -> [a]
sort [Text
amtCommodity, Text
costCommodity], Text
":"]
commodity :: Amount -> Text
commodity = [Text] -> Text
T.unwords forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.words forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Text
acommodity
priceAmounts :: [Amount]
priceAmounts = forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Maybe AmountPrice
aprice) forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> [Amount]
amountsRaw forall a b. (a -> b) -> a -> b
$ Posting -> MixedAmount
pamount Posting
p
postingPriceDirectivesFromCost :: Posting -> [PriceDirective]
postingPriceDirectivesFromCost :: Posting -> [PriceDirective]
postingPriceDirectivesFromCost p :: Posting
p@Posting{MixedAmount
pamount :: MixedAmount
pamount :: Posting -> MixedAmount
pamount} =
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (Day -> Amount -> Maybe PriceDirective
amountPriceDirectiveFromCost forall a b. (a -> b) -> a -> b
$ Posting -> Day
postingDate Posting
p) forall a b. (a -> b) -> a -> b
$ MixedAmount -> [Amount]
amountsRaw MixedAmount
pamount
postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount :: (MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount 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
Text
c1 Text
c2
| Text -> Bool
T.null Text
c1 = Text
c2
| Text -> Bool
T.null Text
c2 = Text
c1
| Bool
otherwise = Text
c1 forall a. Semigroup a => a -> a -> a
<> Text
", " forall a. Semigroup a => a -> a -> a
<> Text
c2
commentAddTag :: Text -> Tag -> Text
Text
c (Text
t,Text
v)
| Text -> Bool
T.null Text
c' = Text
tag
| Bool
otherwise = Text
c' Text -> Text -> Text
`commentJoin` Text
tag
where
c' :: Text
c' = Text -> Text
T.stripEnd Text
c
tag :: Text
tag = Text
t forall a. Semigroup a => a -> a -> a
<> Text
": " forall a. Semigroup a => a -> a -> a
<> Text
v
commentAddTagUnspaced :: Text -> Tag -> Text
Text
c (Text
t,Text
v)
| Text -> Bool
T.null Text
c' = Text
tag
| Bool
otherwise = Text
c' Text -> Text -> Text
`commentJoin` Text
tag
where
c' :: Text
c' = Text -> Text
T.stripEnd Text
c
tag :: Text
tag = Text
t forall a. Semigroup a => a -> a -> a
<> Text
":" forall a. Semigroup a => a -> a -> a
<> Text
v
commentAddTagNextLine :: Text -> Tag -> Text
Text
cmt (Text
t,Text
v) =
Text
cmt forall a. Semigroup a => a -> a -> a
<> (if Text
"\n" Text -> Text -> Bool
`T.isSuffixOf` Text
cmt then Text
"" else Text
"\n") forall a. Semigroup a => a -> a -> a
<> Text
t forall a. Semigroup a => a -> a -> a
<> Text
": " forall a. Semigroup a => a -> a -> a
<> Text
v
tests_Posting :: TestTree
tests_Posting = FilePath -> [TestTree] -> TestTree
testGroup FilePath
"Posting" [
FilePath -> Assertion -> TestTree
testCase FilePath
"accountNamePostingType" forall a b. (a -> b) -> a -> b
$ do
Text -> PostingType
accountNamePostingType Text
"a" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
RegularPosting
Text -> PostingType
accountNamePostingType Text
"(a)" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
VirtualPosting
Text -> PostingType
accountNamePostingType Text
"[a]" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= PostingType
BalancedVirtualPosting
,FilePath -> Assertion -> TestTree
testCase FilePath
"accountNameWithoutPostingType" forall a b. (a -> b) -> a -> b
$ do
Text -> Text
accountNameWithoutPostingType Text
"(a)" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"a"
,FilePath -> Assertion -> TestTree
testCase FilePath
"accountNameWithPostingType" forall a b. (a -> b) -> a -> b
$ do
PostingType -> Text -> Text
accountNameWithPostingType PostingType
VirtualPosting Text
"[a]" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"(a)"
,FilePath -> Assertion -> TestTree
testCase FilePath
"joinAccountNames" forall a b. (a -> b) -> a -> b
$ do
Text
"a" Text -> Text -> Text
`joinAccountNames` Text
"b:c" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"a:b:c"
Text
"a" Text -> Text -> Text
`joinAccountNames` Text
"(b:c)" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"(a:b:c)"
Text
"[a]" Text -> Text -> Text
`joinAccountNames` Text
"(b:c)" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"[a:b:c]"
Text
"" Text -> Text -> Text
`joinAccountNames` Text
"a" forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"a"
,FilePath -> Assertion -> TestTree
testCase FilePath
"concatAccountNames" forall a b. (a -> b) -> a -> b
$ do
[Text] -> Text
concatAccountNames [] forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
""
[Text] -> Text
concatAccountNames [Text
"a",Text
"(b)",Text
"[c:d]"] forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"(a:b:c:d)"
,FilePath -> Assertion -> TestTree
testCase FilePath
"commentAddTag" forall a b. (a -> b) -> a -> b
$ do
Text -> Tag -> Text
commentAddTag Text
"" (Text
"a",Text
"") forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"a: "
Text -> Tag -> Text
commentAddTag Text
"[1/2]" (Text
"a",Text
"") forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"[1/2], a: "
,FilePath -> Assertion -> TestTree
testCase FilePath
"commentAddTagNextLine" forall a b. (a -> b) -> a -> b
$ do
Text -> Tag -> Text
commentAddTagNextLine Text
"" (Text
"a",Text
"") forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"\na: "
Text -> Tag -> Text
commentAddTagNextLine Text
"[1/2]" (Text
"a",Text
"") forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Text
"[1/2]\na: "
]