cli: --forecast adds periodic transactions to reports
Ledger-style periodic transactions, previously supported only by hledger-budget, have landed as a first-class feature. The --forecast flag activates them, so that any transactions they generate are included in reports.
This commit is contained in:
parent
50b4d76ce9
commit
f101d5b515
@ -13,7 +13,6 @@ import Data.List
|
||||
import Data.String.Here
|
||||
import System.Console.CmdArgs
|
||||
import Hledger.Cli
|
||||
import Hledger.Data.AutoTransaction
|
||||
|
||||
-- hledger-budget REPORT-COMMAND [--no-offset] [--no-buckets] [OPTIONS...]
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ module Hledger.Data (
|
||||
module Hledger.Data.StringFormat,
|
||||
module Hledger.Data.Timeclock,
|
||||
module Hledger.Data.Transaction,
|
||||
module Hledger.Data.AutoTransaction,
|
||||
module Hledger.Data.Types,
|
||||
tests_Hledger_Data
|
||||
)
|
||||
@ -42,6 +43,7 @@ import Hledger.Data.RawOptions
|
||||
import Hledger.Data.StringFormat
|
||||
import Hledger.Data.Timeclock
|
||||
import Hledger.Data.Transaction
|
||||
import Hledger.Data.AutoTransaction
|
||||
import Hledger.Data.Types
|
||||
|
||||
tests_Hledger_Data :: Test
|
||||
|
||||
@ -104,6 +104,7 @@ data ReportOpts = ReportOpts {
|
||||
-- eg in the income section of an income statement, this helps --sort-amount know
|
||||
-- how to sort negative numbers.
|
||||
,color_ :: Bool
|
||||
,forecast_ :: Bool
|
||||
} deriving (Show, Data, Typeable)
|
||||
|
||||
instance Default ReportOpts where def = defreportopts
|
||||
@ -134,6 +135,7 @@ defreportopts = ReportOpts
|
||||
def
|
||||
def
|
||||
def
|
||||
def
|
||||
|
||||
rawOptsToReportOpts :: RawOpts -> IO ReportOpts
|
||||
rawOptsToReportOpts rawopts = checkReportOpts <$> do
|
||||
@ -164,6 +166,7 @@ rawOptsToReportOpts rawopts = checkReportOpts <$> do
|
||||
,sort_amount_ = boolopt "sort-amount" rawopts'
|
||||
,pretty_tables_ = boolopt "pretty-tables" rawopts'
|
||||
,color_ = color
|
||||
,forecast_ = boolopt "forecast" rawopts'
|
||||
}
|
||||
|
||||
-- | Do extra validation of raw option values, raising an error if there's a problem.
|
||||
|
||||
@ -155,6 +155,7 @@ reportflags = [
|
||||
,flagNone ["empty","E"] (setboolopt "empty") "show items with zero amount, normally hidden"
|
||||
,flagNone ["cost","B"] (setboolopt "cost") "convert amounts to their cost at transaction time (using the transaction price, if any)"
|
||||
,flagNone ["value","V"] (setboolopt "value") "convert amounts to their market value on the report end date (using the most recent applicable market price, if any)"
|
||||
,flagNone ["forecast"] (\opts -> setboolopt "forecast" opts) "generate forecast transactions"
|
||||
]
|
||||
|
||||
-- | Common output-related flags: --output-file, --output-format...
|
||||
|
||||
@ -31,7 +31,7 @@ import Data.List
|
||||
import Data.Maybe
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import Data.Time (Day)
|
||||
import Data.Time (Day, addDays)
|
||||
import Data.Word
|
||||
import Numeric
|
||||
import Safe (readMay)
|
||||
@ -70,6 +70,7 @@ withJournalDo opts cmd = do
|
||||
. anonymiseByOpts opts
|
||||
. journalApplyAliases (aliasesFromOpts opts)
|
||||
<=< journalApplyValue (reportopts_ opts)
|
||||
<=< journalAddForecast opts
|
||||
either error' f ej
|
||||
|
||||
-- | Apply the pivot transformation on a journal, if option is present.
|
||||
@ -117,6 +118,29 @@ journalApplyValue ropts j = do
|
||||
= id
|
||||
return $ convert j
|
||||
|
||||
-- | Run PeriodicTransactions from journal from today or journal end to requested end day.
|
||||
-- Add generated transactions to the journal
|
||||
journalAddForecast :: CliOpts -> Journal -> IO Journal
|
||||
journalAddForecast opts j = do
|
||||
today <- getCurrentDay
|
||||
-- Create forecast starting from end of journal + 1 day, and until the end of requested reporting period
|
||||
-- If end is not provided, do 180 days of forecast.
|
||||
-- Note that jdatespan already returns last day + 1
|
||||
let startDate = fromMaybe today $ spanEnd (jdatespan j)
|
||||
endDate = fromMaybe (addDays 180 today) $ periodEnd (period_ ropts)
|
||||
dates = DateSpan (Just startDate) (Just endDate)
|
||||
withForecast = [makeForecast t | pt <- jperiodictxns j, t <- runPeriodicTransaction pt dates, spanContainsDate dates (tdate t) ] ++ (jtxns j)
|
||||
makeForecast t = txnTieKnot $ t { tdescription = T.pack "Forecast transaction" }
|
||||
ropts = reportopts_ opts
|
||||
if forecast_ ropts
|
||||
then return $ journalBalanceTransactions' opts j { jtxns = withForecast }
|
||||
else return j
|
||||
where
|
||||
journalBalanceTransactions' opts j =
|
||||
let assrt = not . ignore_assertions_ $ inputopts_ opts
|
||||
in
|
||||
either error' id $ journalBalanceTransactions assrt j
|
||||
|
||||
-- | Write some output to stdout or to a file selected by --output-file.
|
||||
-- If the file exists it will be overwritten.
|
||||
writeOutput :: CliOpts -> String -> IO ()
|
||||
|
||||
102
tests/budget/forecast.test
Normal file
102
tests/budget/forecast.test
Normal file
@ -0,0 +1,102 @@
|
||||
# Test --forecast switch
|
||||
hledger bal -M -b 2016-11 -e 2017-02 -f - --forecast
|
||||
<<<
|
||||
2016/12/31
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
~ monthly from 2016/1
|
||||
income $-1000
|
||||
expenses:food $20
|
||||
expenses:leisure $15
|
||||
expenses:grocery $30
|
||||
assets:cash
|
||||
>>>
|
||||
Balance changes in 2016/12/01-2017/01/31:
|
||||
|
||||
|| 2016/12 2017/01
|
||||
==================++==================
|
||||
assets:cash || $-600 $935
|
||||
expenses:food || 0 $20
|
||||
expenses:grocery || 0 $30
|
||||
expenses:housing || $600 0
|
||||
expenses:leisure || 0 $15
|
||||
income || 0 $-1000
|
||||
------------------++------------------
|
||||
|| 0 0
|
||||
|
||||
>>>2
|
||||
>>>=0
|
||||
|
||||
|
||||
hledger print -b 2016-11 -e 2017-02 -f - --forecast
|
||||
<<<
|
||||
2016/12/31
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
~ monthly from 2016/1
|
||||
income $-1000
|
||||
expenses:food $20
|
||||
expenses:leisure $15
|
||||
expenses:grocery $30
|
||||
assets:cash
|
||||
>>>
|
||||
2016/12/31
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
2017/01/01 Forecast transaction
|
||||
income $-1000
|
||||
expenses:food $20
|
||||
expenses:leisure $15
|
||||
expenses:grocery $30
|
||||
assets:cash
|
||||
|
||||
>>>2
|
||||
>>>=0
|
||||
|
||||
|
||||
hledger register -b 2016-11 -e 2017-02 -f - --forecast
|
||||
<<<
|
||||
2016/12/31
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
~ monthly from 2016/1
|
||||
income $-1000
|
||||
expenses:food $20
|
||||
expenses:leisure $15
|
||||
expenses:grocery $30
|
||||
assets:cash
|
||||
>>>
|
||||
2016/12/31 expenses:housing $600 $600
|
||||
assets:cash $-600 0
|
||||
2017/01/01 Forecast transact.. income $-1000 $-1000
|
||||
expenses:food $20 $-980
|
||||
expenses:leisure $15 $-965
|
||||
expenses:grocery $30 $-935
|
||||
assets:cash $935 0
|
||||
>>>2
|
||||
>>>=0
|
||||
|
||||
# Check that --forecast generates transactions only after last transaction in journal
|
||||
hledger register -b 2015-12 -e 2017-02 -f - assets:cash --forecast
|
||||
<<<
|
||||
2016/01/01
|
||||
expenses:fun $10 ; more fireworks
|
||||
assets:cash
|
||||
|
||||
2016/12/02
|
||||
expenses:housing $600
|
||||
assets:cash
|
||||
|
||||
~ yearly from 2016
|
||||
income $-10000 ; bonus
|
||||
assets:cash
|
||||
>>>
|
||||
2016/01/01 assets:cash $-10 $-10
|
||||
2016/12/02 assets:cash $-600 $-610
|
||||
2017/01/01 Forecast transact.. assets:cash $10000 $9390
|
||||
>>>2
|
||||
>>>=0
|
||||
Loading…
Reference in New Issue
Block a user