mirror of
https://github.com/simonmichael/hledger.git
synced 2024-12-28 04:46:31 +03:00
lib!: modifyTransactions now takes a Map of commodity styles, and will
style amounts according to that argument. journalAddForecast and journalTransform now return an Either String Journal. This improves efficiency, as we no longer have to restyle all amounts in the journal after generating auto postings or periodic transactions. Changing the return type of journalAddForecast and journalTransform reduces partiality. To get the previous behaviour for modifyTransaction, use modifyTransaction mempty.
This commit is contained in:
parent
2da0e67141
commit
fc8aa602cf
@ -723,7 +723,7 @@ journalUntieTransactions t@Transaction{tpostings=ps} = t{tpostings=map (\p -> p{
|
|||||||
-- relative dates in transaction modifier queries.
|
-- relative dates in transaction modifier queries.
|
||||||
journalModifyTransactions :: Day -> Journal -> Either String Journal
|
journalModifyTransactions :: Day -> Journal -> Either String Journal
|
||||||
journalModifyTransactions d j =
|
journalModifyTransactions d j =
|
||||||
case modifyTransactions d (jtxnmodifiers j) (jtxns j) of
|
case modifyTransactions (journalCommodityStyles j) d (jtxnmodifiers j) (jtxns j) of
|
||||||
Right ts -> Right j{jtxns=ts}
|
Right ts -> Right j{jtxns=ts}
|
||||||
Left err -> Left err
|
Left err -> Left err
|
||||||
|
|
||||||
@ -1066,20 +1066,12 @@ checkBalanceAssignmentUnassignableAccountB p = do
|
|||||||
-- amounts in each commodity (see journalCommodityStyles).
|
-- amounts in each commodity (see journalCommodityStyles).
|
||||||
-- Can return an error message eg if inconsistent number formats are found.
|
-- Can return an error message eg if inconsistent number formats are found.
|
||||||
journalApplyCommodityStyles :: Journal -> Either String Journal
|
journalApplyCommodityStyles :: Journal -> Either String Journal
|
||||||
journalApplyCommodityStyles j@Journal{jtxns=ts, jpricedirectives=pds} =
|
journalApplyCommodityStyles = fmap fixjournal . journalInferCommodityStyles
|
||||||
case journalInferCommodityStyles j of
|
|
||||||
Left e -> Left e
|
|
||||||
Right j' -> Right j''
|
|
||||||
where
|
where
|
||||||
styles = journalCommodityStyles j'
|
fixjournal j@Journal{jpricedirectives=pds} =
|
||||||
j'' = j'{jtxns=map fixtransaction ts
|
journalMapPostings (postingApplyCommodityStyles styles) j{jpricedirectives=map fixpricedirective pds}
|
||||||
,jpricedirectives=map fixpricedirective pds
|
where
|
||||||
}
|
styles = journalCommodityStyles j
|
||||||
fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
|
|
||||||
fixposting p = p{pamount=styleMixedAmount styles $ pamount p
|
|
||||||
,pbalanceassertion=fixbalanceassertion <$> pbalanceassertion p}
|
|
||||||
-- balance assertion amounts are always displayed (by print) at full precision, per docs
|
|
||||||
fixbalanceassertion ba = ba{baamount=styleAmountExceptPrecision styles $ baamount ba}
|
|
||||||
fixpricedirective pd@PriceDirective{pdamount=a} = pd{pdamount=styleAmountExceptPrecision styles a}
|
fixpricedirective pd@PriceDirective{pdamount=a} = pd{pdamount=styleAmountExceptPrecision styles a}
|
||||||
|
|
||||||
-- | Get the canonical amount styles for this journal, whether (in order of precedence):
|
-- | Get the canonical amount styles for this journal, whether (in order of precedence):
|
||||||
|
@ -38,6 +38,7 @@ module Hledger.Data.Posting (
|
|||||||
relatedPostings,
|
relatedPostings,
|
||||||
postingStripPrices,
|
postingStripPrices,
|
||||||
postingApplyAliases,
|
postingApplyAliases,
|
||||||
|
postingApplyCommodityStyles,
|
||||||
-- * date operations
|
-- * date operations
|
||||||
postingDate,
|
postingDate,
|
||||||
postingDate2,
|
postingDate2,
|
||||||
@ -298,6 +299,14 @@ postingApplyAliases aliases p@Posting{paccount} =
|
|||||||
err = "problem while applying account aliases:\n" ++ pshow aliases
|
err = "problem while applying account aliases:\n" ++ pshow aliases
|
||||||
++ "\n to account name: "++T.unpack paccount++"\n "++e
|
++ "\n to account name: "++T.unpack paccount++"\n "++e
|
||||||
|
|
||||||
|
-- | Choose and apply a consistent display style to the posting
|
||||||
|
-- amounts in each commodity (see journalCommodityStyles).
|
||||||
|
postingApplyCommodityStyles :: M.Map CommoditySymbol AmountStyle -> Posting -> Posting
|
||||||
|
postingApplyCommodityStyles styles p = p{pamount=styleMixedAmount styles $ pamount p
|
||||||
|
,pbalanceassertion=fixbalanceassertion <$> pbalanceassertion p}
|
||||||
|
where
|
||||||
|
fixbalanceassertion ba = ba{baamount=styleAmountExceptPrecision styles $ baamount ba}
|
||||||
|
|
||||||
-- | Rewrite an account name using all matching aliases from the given list, in sequence.
|
-- | Rewrite an account name using all matching aliases from the given list, in sequence.
|
||||||
-- Each alias sees the result of applying the previous aliases.
|
-- Each alias sees the result of applying the previous aliases.
|
||||||
-- Or, return any error arising from a bad regular expression in the aliases.
|
-- Or, return any error arising from a bad regular expression in the aliases.
|
||||||
|
@ -13,6 +13,7 @@ module Hledger.Data.TransactionModifier (
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Control.Applicative ((<|>), liftA2)
|
import Control.Applicative ((<|>), liftA2)
|
||||||
|
import qualified Data.Map as M
|
||||||
import Data.Maybe (catMaybes)
|
import Data.Maybe (catMaybes)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Time.Calendar (Day)
|
import Data.Time.Calendar (Day)
|
||||||
@ -22,7 +23,7 @@ import Hledger.Data.Amount
|
|||||||
import Hledger.Data.Transaction (txnTieKnot)
|
import Hledger.Data.Transaction (txnTieKnot)
|
||||||
import Hledger.Query (Query, filterQuery, matchesAmount, matchesPosting,
|
import Hledger.Query (Query, filterQuery, matchesAmount, matchesPosting,
|
||||||
parseQuery, queryIsAmt, queryIsSym, simplifyQuery)
|
parseQuery, queryIsAmt, queryIsSym, simplifyQuery)
|
||||||
import Hledger.Data.Posting (commentJoin, commentAddTag)
|
import Hledger.Data.Posting (commentJoin, commentAddTag, postingApplyCommodityStyles)
|
||||||
import Hledger.Utils (dbg6, wrap)
|
import Hledger.Utils (dbg6, wrap)
|
||||||
|
|
||||||
-- $setup
|
-- $setup
|
||||||
@ -35,9 +36,9 @@ import Hledger.Utils (dbg6, wrap)
|
|||||||
-- Or if any of them fails to be parsed, return the first error. A reference
|
-- Or if any of them fails to be parsed, return the first error. A reference
|
||||||
-- date is provided to help interpret relative dates in transaction modifier
|
-- date is provided to help interpret relative dates in transaction modifier
|
||||||
-- queries.
|
-- queries.
|
||||||
modifyTransactions :: Day -> [TransactionModifier] -> [Transaction] -> Either String [Transaction]
|
modifyTransactions :: M.Map CommoditySymbol AmountStyle -> Day -> [TransactionModifier] -> [Transaction] -> Either String [Transaction]
|
||||||
modifyTransactions d tmods ts = do
|
modifyTransactions styles d tmods ts = do
|
||||||
fs <- mapM (transactionModifierToFunction d) tmods -- convert modifiers to functions, or return a parse error
|
fs <- mapM (transactionModifierToFunction styles d) tmods -- convert modifiers to functions, or return a parse error
|
||||||
let
|
let
|
||||||
modifytxn t = t''
|
modifytxn t = t''
|
||||||
where
|
where
|
||||||
@ -61,7 +62,7 @@ modifyTransactions d tmods ts = do
|
|||||||
-- >>> import qualified Data.Text.IO as T
|
-- >>> import qualified Data.Text.IO as T
|
||||||
-- >>> t = nulltransaction{tpostings=["ping" `post` usd 1]}
|
-- >>> t = nulltransaction{tpostings=["ping" `post` usd 1]}
|
||||||
-- >>> tmpost acc amt = TMPostingRule (acc `post` amt) False
|
-- >>> tmpost acc amt = TMPostingRule (acc `post` amt) False
|
||||||
-- >>> test = either putStr (T.putStr.showTransaction) . fmap ($ t) . transactionModifierToFunction nulldate
|
-- >>> test = either putStr (T.putStr.showTransaction) . fmap ($ t) . transactionModifierToFunction mempty nulldate
|
||||||
-- >>> test $ TransactionModifier "" ["pong" `tmpost` usd 2]
|
-- >>> test $ TransactionModifier "" ["pong" `tmpost` usd 2]
|
||||||
-- 0000-01-01
|
-- 0000-01-01
|
||||||
-- ping $1.00
|
-- ping $1.00
|
||||||
@ -77,11 +78,11 @@ modifyTransactions d tmods ts = do
|
|||||||
-- pong $3.00 ; generated-posting: = ping
|
-- pong $3.00 ; generated-posting: = ping
|
||||||
-- <BLANKLINE>
|
-- <BLANKLINE>
|
||||||
--
|
--
|
||||||
transactionModifierToFunction :: Day -> TransactionModifier -> Either String (Transaction -> Transaction)
|
transactionModifierToFunction :: M.Map CommoditySymbol AmountStyle -> Day -> TransactionModifier -> Either String (Transaction -> Transaction)
|
||||||
transactionModifierToFunction refdate TransactionModifier{tmquerytxt, tmpostingrules} = do
|
transactionModifierToFunction styles refdate TransactionModifier{tmquerytxt, tmpostingrules} = do
|
||||||
q <- simplifyQuery . fst <$> parseQuery refdate tmquerytxt
|
q <- simplifyQuery . fst <$> parseQuery refdate tmquerytxt
|
||||||
let
|
let
|
||||||
fs = map (tmPostingRuleToFunction q tmquerytxt) tmpostingrules
|
fs = map (tmPostingRuleToFunction styles q tmquerytxt) tmpostingrules
|
||||||
generatePostings ps = concatMap (\p -> p : map ($p) (if q `matchesPosting` p then fs else [])) ps
|
generatePostings ps = concatMap (\p -> p : map ($p) (if q `matchesPosting` p then fs else [])) ps
|
||||||
Right $ \t@(tpostings -> ps) -> txnTieKnot t{tpostings=generatePostings ps}
|
Right $ \t@(tpostings -> ps) -> txnTieKnot t{tpostings=generatePostings ps}
|
||||||
|
|
||||||
@ -93,9 +94,9 @@ transactionModifierToFunction refdate TransactionModifier{tmquerytxt, tmpostingr
|
|||||||
-- and a hidden _generated-posting: tag which does not.
|
-- and a hidden _generated-posting: tag which does not.
|
||||||
-- The TransactionModifier's query text is also provided, and saved
|
-- The TransactionModifier's query text is also provided, and saved
|
||||||
-- as the tags' value.
|
-- as the tags' value.
|
||||||
tmPostingRuleToFunction :: Query -> T.Text -> TMPostingRule -> (Posting -> Posting)
|
tmPostingRuleToFunction :: M.Map CommoditySymbol AmountStyle -> Query -> T.Text -> TMPostingRule -> (Posting -> Posting)
|
||||||
tmPostingRuleToFunction query querytxt tmpr =
|
tmPostingRuleToFunction styles query querytxt tmpr =
|
||||||
\p -> renderPostingCommentDates $ pr
|
\p -> postingApplyCommodityStyles styles . renderPostingCommentDates $ pr
|
||||||
{ pdate = pdate pr <|> pdate p
|
{ pdate = pdate pr <|> pdate p
|
||||||
, pdate2 = pdate2 pr <|> pdate2 p
|
, pdate2 = pdate2 pr <|> pdate2 p
|
||||||
, pamount = amount' p
|
, pamount = amount' p
|
||||||
|
@ -362,8 +362,6 @@ journalFinalise InputOpts{auto_,balancingopts_,strict_} f txt pj' = do
|
|||||||
-- (Note adding auto postings after balancing means #893b fails;
|
-- (Note adding auto postings after balancing means #893b fails;
|
||||||
-- adding them before balancing probably means #893a, #928, #938 fail.)
|
-- adding them before balancing probably means #893a, #928, #938 fail.)
|
||||||
>>= journalModifyTransactions d
|
>>= journalModifyTransactions d
|
||||||
-- then apply commodity styles once more, to style the auto posting amounts. (XXX inefficient ?)
|
|
||||||
>>= journalApplyCommodityStyles
|
|
||||||
-- then check balance assertions.
|
-- then check balance assertions.
|
||||||
>>= journalBalanceTransactions balancingopts_
|
>>= journalBalanceTransactions balancingopts_
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ hledgerWebTest = do
|
|||||||
Right rs -> rs
|
Right rs -> rs
|
||||||
copts = defcliopts{reportspec_=rspec, file_=[""]} -- non-empty, see file_ note above
|
copts = defcliopts{reportspec_=rspec, file_=[""]} -- non-empty, see file_ note above
|
||||||
wopts = defwebopts{cliopts_=copts}
|
wopts = defwebopts{cliopts_=copts}
|
||||||
j <- fmap (journalTransform copts) $ readJournal' (T.pack $ unlines -- PARTIAL: readJournal' should not fail
|
j <- fmap (either error id . journalTransform copts) $ readJournal' (T.pack $ unlines -- PARTIAL: readJournal' should not fail
|
||||||
["~ monthly"
|
["~ monthly"
|
||||||
," assets 10"
|
," assets 10"
|
||||||
," income"
|
," income"
|
||||||
|
@ -41,7 +41,7 @@ rewrite opts@CliOpts{rawopts_=rawopts,reportspec_=rspec} j@Journal{jtxns=ts} = d
|
|||||||
-- rewrite matched transactions
|
-- rewrite matched transactions
|
||||||
d <- getCurrentDay
|
d <- getCurrentDay
|
||||||
let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j
|
let modifiers = transactionModifierFromOpts opts : jtxnmodifiers j
|
||||||
let j' = j{jtxns=either error' id $ modifyTransactions d modifiers ts} -- PARTIAL:
|
let j' = j{jtxns=either error' id $ modifyTransactions mempty d modifiers ts} -- PARTIAL:
|
||||||
-- run the print command, showing all transactions, or show diffs
|
-- run the print command, showing all transactions, or show diffs
|
||||||
printOrDiff rawopts opts{reportspec_=rspec{_rsQuery=Any}} j j'
|
printOrDiff rawopts opts{reportspec_=rspec{_rsQuery=Any}} j j'
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ withJournalDo opts cmd = do
|
|||||||
-- to let the add command work.
|
-- to let the add command work.
|
||||||
journalpaths <- journalFilePathFromOpts opts
|
journalpaths <- journalFilePathFromOpts opts
|
||||||
files <- readJournalFiles (inputopts_ opts) journalpaths
|
files <- readJournalFiles (inputopts_ opts) journalpaths
|
||||||
let transformed = journalTransform opts <$> files
|
let transformed = journalTransform opts =<< files
|
||||||
either error' cmd transformed -- PARTIAL:
|
either error' cmd transformed -- PARTIAL:
|
||||||
|
|
||||||
-- | Apply some extra post-parse transformations to the journal, if
|
-- | Apply some extra post-parse transformations to the journal, if
|
||||||
@ -86,12 +86,14 @@ withJournalDo opts cmd = do
|
|||||||
-- - pivoting account names (--pivot)
|
-- - pivoting account names (--pivot)
|
||||||
-- - anonymising (--anonymise).
|
-- - anonymising (--anonymise).
|
||||||
--
|
--
|
||||||
journalTransform :: CliOpts -> Journal -> Journal
|
-- This will return an error message if the query in any auto posting rule fails
|
||||||
|
-- to parse, or the generated transactions are not balanced.
|
||||||
|
journalTransform :: CliOpts -> Journal -> Either String Journal
|
||||||
journalTransform opts =
|
journalTransform opts =
|
||||||
anonymiseByOpts opts
|
fmap (anonymiseByOpts opts)
|
||||||
-- - converting amounts to market value (--value)
|
-- - converting amounts to market value (--value)
|
||||||
-- . journalApplyValue ropts
|
-- . journalApplyValue ropts
|
||||||
. pivotByOpts opts
|
. fmap (pivotByOpts opts)
|
||||||
. journalAddForecast opts
|
. journalAddForecast opts
|
||||||
|
|
||||||
-- | Apply the pivot transformation on a journal, if option is present.
|
-- | Apply the pivot transformation on a journal, if option is present.
|
||||||
@ -113,26 +115,28 @@ anonymiseByOpts opts =
|
|||||||
--
|
--
|
||||||
-- When --auto is active, auto posting rules will be applied to the
|
-- When --auto is active, auto posting rules will be applied to the
|
||||||
-- generated transactions. If the query in any auto posting rule fails
|
-- generated transactions. If the query in any auto posting rule fails
|
||||||
-- to parse, this function will raise an error.
|
-- to parse, or the generated transactions are not balanced, this function will
|
||||||
|
-- return an error message.
|
||||||
--
|
--
|
||||||
-- The start & end date for generated periodic transactions are determined in
|
-- The start & end date for generated periodic transactions are determined in
|
||||||
-- a somewhat complicated way; see the hledger manual -> Periodic transactions.
|
-- a somewhat complicated way; see the hledger manual -> Periodic transactions.
|
||||||
--
|
--
|
||||||
journalAddForecast :: CliOpts -> Journal -> Journal
|
journalAddForecast :: CliOpts -> Journal -> Either String Journal
|
||||||
journalAddForecast CliOpts{inputopts_=iopts, reportspec_=rspec} j =
|
journalAddForecast CliOpts{inputopts_=iopts, reportspec_=rspec} j =
|
||||||
case forecast_ ropts of
|
case forecast_ ropts of
|
||||||
Nothing -> j
|
Nothing -> return j
|
||||||
Just _ -> either error id $ do -- PARTIAL:
|
Just _ -> do
|
||||||
forecasttxns <- addAutoTxns =<< mapM (balanceTransaction (balancingopts_ iopts))
|
forecasttxns <- addAutoTxns =<< mapM (balanceTransaction (balancingopts_ iopts))
|
||||||
[ txnTieKnot t | pt <- jperiodictxns j
|
[ txnTieKnot $ transactionTransformPostings (postingApplyCommodityStyles styles) t
|
||||||
|
| pt <- jperiodictxns j
|
||||||
, t <- runPeriodicTransaction pt forecastspan
|
, t <- runPeriodicTransaction pt forecastspan
|
||||||
, spanContainsDate forecastspan (tdate t)
|
, spanContainsDate forecastspan (tdate t)
|
||||||
]
|
]
|
||||||
journalBalanceTransactions (balancingopts_ iopts) j{ jtxns = concat [jtxns j, forecasttxns] }
|
journalBalanceTransactions (balancingopts_ iopts) j{ jtxns = concat [jtxns j, forecasttxns] }
|
||||||
>>= journalApplyCommodityStyles
|
|
||||||
where
|
where
|
||||||
today = _rsDay rspec
|
today = _rsDay rspec
|
||||||
ropts = _rsReportOpts rspec
|
ropts = _rsReportOpts rspec
|
||||||
|
styles = journalCommodityStyles j
|
||||||
|
|
||||||
-- "They can start no earlier than: the day following the latest normal transaction in the journal (or today if there are none)."
|
-- "They can start no earlier than: the day following the latest normal transaction in the journal (or today if there are none)."
|
||||||
mjournalend = dbg2 "journalEndDate" $ journalEndDate False j -- ignore secondary dates
|
mjournalend = dbg2 "journalEndDate" $ journalEndDate False j -- ignore secondary dates
|
||||||
@ -147,7 +151,7 @@ journalAddForecast CliOpts{inputopts_=iopts, reportspec_=rspec} j =
|
|||||||
(fromMaybe nulldatespan $ dbg2 "forecastspan flag" $ forecast_ ropts)
|
(fromMaybe nulldatespan $ dbg2 "forecastspan flag" $ forecast_ ropts)
|
||||||
(DateSpan (Just forecastbeginDefault) (Just forecastendDefault))
|
(DateSpan (Just forecastbeginDefault) (Just forecastendDefault))
|
||||||
|
|
||||||
addAutoTxns = if auto_ iopts then modifyTransactions today (jtxnmodifiers j) else return
|
addAutoTxns = if auto_ iopts then modifyTransactions styles today (jtxnmodifiers j) else return
|
||||||
|
|
||||||
-- | Write some output to stdout or to a file selected by --output-file.
|
-- | Write some output to stdout or to a file selected by --output-file.
|
||||||
-- If the file exists it will be overwritten.
|
-- If the file exists it will be overwritten.
|
||||||
@ -196,7 +200,7 @@ journalReload :: CliOpts -> IO (Either String Journal)
|
|||||||
journalReload opts = do
|
journalReload opts = do
|
||||||
journalpaths <- dbg6 "reloading files" <$> journalFilePathFromOpts opts
|
journalpaths <- dbg6 "reloading files" <$> journalFilePathFromOpts opts
|
||||||
files <- readJournalFiles (inputopts_ opts) journalpaths
|
files <- readJournalFiles (inputopts_ opts) journalpaths
|
||||||
return $ journalTransform opts <$> files
|
return $ journalTransform opts =<< files
|
||||||
|
|
||||||
-- | Has the specified file changed since the journal was last read ?
|
-- | Has the specified file changed since the journal was last read ?
|
||||||
-- Typically this is one of the journal's journalFilePaths. These are
|
-- Typically this is one of the journal's journalFilePaths. These are
|
||||||
|
Loading…
Reference in New Issue
Block a user