parent
							
								
									cbf4029b8b
								
							
						
					
					
						commit
						b7413edf22
					
				| @ -22,6 +22,7 @@ module Hledger.Reports.ReportOptions ( | ||||
|   whichDateFromOpts, | ||||
|   journalSelectingAmountFromOpts, | ||||
|   intervalFromRawOpts, | ||||
|   forecastPeriodFromRawOpts, | ||||
|   queryFromOpts, | ||||
|   queryFromOptsOnly, | ||||
|   queryOptsFromOpts, | ||||
| @ -124,7 +125,7 @@ data ReportOpts = ReportOpts { | ||||
|       --   sign normalisation, converting normally negative subreports to | ||||
|       --   normally positive for a more conventional display. | ||||
|     ,color_          :: Bool | ||||
|     ,forecast_       :: Bool | ||||
|     ,forecast_       :: Maybe DateSpan | ||||
|     ,transpose_      :: Bool | ||||
|  } deriving (Show, Data, Typeable) | ||||
| 
 | ||||
| @ -192,7 +193,7 @@ rawOptsToReportOpts rawopts = checkReportOpts <$> do | ||||
|     ,invert_      = boolopt "invert" rawopts' | ||||
|     ,pretty_tables_ = boolopt "pretty-tables" rawopts' | ||||
|     ,color_       = color | ||||
|     ,forecast_    = boolopt "forecast" rawopts' | ||||
|     ,forecast_    = forecastPeriodFromRawOpts d rawopts' | ||||
|     ,transpose_   = boolopt "transpose" rawopts' | ||||
|     } | ||||
| 
 | ||||
| @ -313,6 +314,18 @@ intervalFromRawOpts = lastDef NoInterval . collectopts intervalfromrawopt | ||||
|       | n == "yearly"    = Just $ Years 1 | ||||
|       | otherwise = Nothing | ||||
| 
 | ||||
| -- | get period expression from --forecast option | ||||
| forecastPeriodFromRawOpts :: Day -> RawOpts -> Maybe DateSpan | ||||
| forecastPeriodFromRawOpts d opts = | ||||
|   case  | ||||
|     dbg2 "forecastopt" $ maybestringopt "forecast" opts | ||||
|   of | ||||
|     Nothing -> Nothing | ||||
|     Just "" -> Just nulldatespan | ||||
|     Just str -> | ||||
|       either (\e -> usageError $ "could not parse forecast period : "++customErrorBundlePretty e) (Just . snd) $  | ||||
|       parsePeriodExpr d $ stripquotes $ T.pack str | ||||
|      | ||||
| -- | Extract the interval from the parsed -p/--period expression. | ||||
| -- Return Nothing if an interval is not explicitly defined. | ||||
| extractIntervalOrNothing :: (Interval, DateSpan) -> Maybe Interval | ||||
|  | ||||
| @ -1414,7 +1414,7 @@ This can be used to match transactions generated "just now", | ||||
| rather than generated in the past and saved to the journal. | ||||
| 
 | ||||
| Forecast transactions start on the first occurrence, and end on the last occurrence, | ||||
| of their interval within the forecast period. The forecast period: | ||||
| of their interval within the forecast period. The default forecast period: | ||||
| 
 | ||||
| - begins on the later of | ||||
|   - the report start date if specified with -b/-p/date: | ||||
| @ -1425,8 +1425,9 @@ of their interval within the forecast period. The forecast period: | ||||
|   or 180 days from today. | ||||
| 
 | ||||
| where "today" means the current date at report time. | ||||
| The "later of" rule ensures that forecast transactions do not overlap normal transactions in time; | ||||
| they will begin only after normal transactions end. | ||||
| The "later of" rule ensures that by default forecast transactions do not overlap normal transactions in time; | ||||
| they will begin only after normal transactions end. If you wish to use your own forecast period, | ||||
| you can provied it via `--forecast=PERIODICEXPR`. | ||||
| 
 | ||||
| Forecasting can be useful for estimating balances into the future, | ||||
| and experimenting with different scenarios. | ||||
|  | ||||
| @ -86,8 +86,8 @@ asInit d reset ui@UIState{ | ||||
|     q = And [queryFromOpts d ropts, excludeforecastq (forecast_ ropts)] | ||||
|       where | ||||
|         -- Except in forecast mode, exclude future/forecast transactions. | ||||
|         excludeforecastq True = Any | ||||
|         excludeforecastq False =  -- not:date:tomorrow- not:tag:generated-transaction | ||||
|         excludeforecastq (Just _) = Any | ||||
|         excludeforecastq Nothing  =  -- not:date:tomorrow- not:tag:generated-transaction | ||||
|           And [ | ||||
|              Not (Date $ DateSpan (Just $ addDays 1 d) Nothing) | ||||
|             ,Not (Tag "generated-transaction" Nothing) | ||||
| @ -218,7 +218,7 @@ asDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | ||||
|               ,("-+", str "depth") | ||||
|               ,("T", renderToggle (tree_ ropts) "flat" "tree") | ||||
|               ,("H", renderToggle (not ishistorical) "end-bals" "changes") | ||||
|               ,("F", renderToggle1 (forecast_ ropts) "forecast") | ||||
|               ,("F", renderToggle1 (isJust $ forecast_ ropts) "forecast") | ||||
|               --,("/", "filter") | ||||
|               --,("DEL", "unfilter") | ||||
|               --,("ESC", "cancel/top") | ||||
| @ -339,7 +339,7 @@ asHandle ui0@UIState{ | ||||
|         VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui | ||||
|         VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui | ||||
|         VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui | ||||
|         VtyEvent (EvKey (KChar 'F') []) -> continue $ regenerateScreens j d $ toggleForecast ui | ||||
|         VtyEvent (EvKey (KChar 'F') []) -> continue $ regenerateScreens j d $ toggleForecast d ui | ||||
| 
 | ||||
|         VtyEvent (EvKey (KDown)     [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui | ||||
|         VtyEvent (EvKey (KUp)       [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui | ||||
|  | ||||
| @ -66,7 +66,7 @@ main = do | ||||
| 
 | ||||
|   -- always include forecasted periodic transactions when loading data; | ||||
|   -- they will be toggled on and off in the UI. | ||||
|   let copts' = copts{reportopts_=ropts{forecast_=True}} | ||||
|   let copts' = copts{reportopts_=ropts{forecast_=Just $ fromMaybe nulldatespan (forecast_ ropts)}} | ||||
| 
 | ||||
|   case True of | ||||
|     _ | "help"            `inRawOpts` rawopts -> putStr (showModeUsage uimode) | ||||
|  | ||||
| @ -72,8 +72,8 @@ rsInit d reset ui@UIState{aopts=_uopts@UIOpts{cliopts_=CliOpts{reportopts_=ropts | ||||
|     q = And [queryFromOpts d ropts', excludeforecastq (forecast_ ropts)] | ||||
|       where | ||||
|         -- Except in forecast mode, exclude future/forecast transactions. | ||||
|         excludeforecastq True = Any | ||||
|         excludeforecastq False =  -- not:date:tomorrow- not:tag:generated-transaction | ||||
|         excludeforecastq (Just _) = Any | ||||
|         excludeforecastq Nothing  =  -- not:date:tomorrow- not:tag:generated-transaction | ||||
|           And [ | ||||
|              Not (Date $ DateSpan (Just $ addDays 1 d) Nothing) | ||||
|             ,Not (Tag "generated-transaction" Nothing) | ||||
| @ -237,7 +237,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | ||||
| --              ,("RIGHT", str "transaction") | ||||
|               ,("T", renderToggle (tree_ ropts) "flat(-subs)" "tree(+subs)") -- rsForceInclusive may override, but use tree_ to ensure a visible toggle effect | ||||
|               ,("H", renderToggle (not ishistorical) "historical" "period") | ||||
|               ,("F", renderToggle1 (forecast_ ropts) "forecast") | ||||
|               ,("F", renderToggle1 (isJust $ forecast_ ropts) "forecast") | ||||
| --               ,("a", "add") | ||||
| --               ,("g", "reload") | ||||
| --               ,("q", "quit") | ||||
| @ -336,7 +336,7 @@ rsHandle ui@UIState{ | ||||
|         VtyEvent (EvKey (KChar 'U') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui | ||||
|         VtyEvent (EvKey (KChar 'P') []) -> rsCenterAndContinue $ regenerateScreens j d $ togglePending ui | ||||
|         VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui | ||||
|         VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleForecast ui | ||||
|         VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleForecast d ui | ||||
| 
 | ||||
|         VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui | ||||
|         VtyEvent (EvKey (KDown)     [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui | ||||
|  | ||||
| @ -164,11 +164,15 @@ toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts | ||||
| -- overkill, probably we should just hide/show the periodic | ||||
| -- transactions with a query for their special tag. | ||||
| -- | ||||
| toggleForecast :: UIState -> UIState | ||||
| toggleForecast ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | ||||
| toggleForecast :: Day -> UIState -> UIState | ||||
| toggleForecast d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = | ||||
|   ui{aopts=uopts{cliopts_=copts'}} | ||||
|   where | ||||
|     copts' = copts{reportopts_=ropts{forecast_=not $ forecast_ ropts}} | ||||
|     copts' = copts{reportopts_=ropts{forecast_=forecast'}} | ||||
|     forecast' = | ||||
|       case forecast_ ropts of | ||||
|         Just _  -> Nothing | ||||
|         Nothing -> forecastPeriodFromRawOpts d $ rawopts_ copts | ||||
| 
 | ||||
| -- | Toggle between showing all and showing only real (non-virtual) items. | ||||
| toggleReal :: UIState -> UIState | ||||
|  | ||||
| @ -181,8 +181,12 @@ reportflags = [ | ||||
| 
 | ||||
|   -- generated postings/transactions | ||||
|  ,flagNone ["auto"]          (setboolopt "auto") "apply automated posting rules to modify transactions" | ||||
|  ,flagNone ["forecast"]      (setboolopt "forecast") "generate future transactions from periodic transaction rules, for the next 6 months or till report end date. In hledger-ui, also make ordinary future transactions visible." | ||||
| 
 | ||||
|  ,flagOpt "" ["forecast"]    (\s opts -> Right $ setopt "forecast" s opts) "PERIODEXP"  | ||||
|   (unlines | ||||
|    [ "Generate periodic transactions (from periodic transaction rules). By default these begin after the latest recorded transaction, and end 6 months from today, or at the report end date." | ||||
|    , "Also, in hledger-ui, make future transactions visible." | ||||
|    , "Note that = (and not a space) is required before PERIODEXP if you wish to supply it." | ||||
|    ]) | ||||
|  ] | ||||
| 
 | ||||
| -- | Common flags that are accepted but not shown in --help, | ||||
|  | ||||
| @ -114,18 +114,22 @@ anonymiseByOpts opts = | ||||
| -- from today if unspecified. | ||||
| -- | ||||
| journalAddForecast :: CliOpts -> Journal -> IO Journal | ||||
| journalAddForecast opts@CliOpts{inputopts_=iopts, reportopts_=ropts} j = do | ||||
| journalAddForecast CliOpts{inputopts_=iopts, reportopts_=ropts} j = do | ||||
|   today <- getCurrentDay | ||||
| 
 | ||||
|   -- "They can start no earlier than: the day following the latest normal transaction in the journal (or today if there are none)." | ||||
|   let mjournalend   = dbg2 "journalEndDate" $ journalEndDate False j  -- ignore secondary dates | ||||
|       forecaststart = dbg2 "forecaststart" $ fromMaybe today mjournalend | ||||
|       forecastbeginDefault = dbg2 "forecastbeginDefault" $ fromMaybe today mjournalend | ||||
| 
 | ||||
|   -- "They end on or before the specified report end date, or 180 days from today if unspecified." | ||||
|   mspecifiedend <-  snd . dbg2 "specifieddates" <$> specifiedStartEndDates ropts | ||||
|   let forecastend = dbg2 "forecastend" $ fromMaybe (addDays 180 today) mspecifiedend | ||||
|   let forecastendDefault = dbg2 "forecastendDefault" $ fromMaybe (addDays 180 today) mspecifiedend | ||||
|        | ||||
|   let forecastspan = dbg2 "forecastspan" $ | ||||
|         spanDefaultsFrom | ||||
|           (fromMaybe nulldatespan $ dbg2 "forecastspan flag" $ forecast_ ropts) | ||||
|           (DateSpan (Just forecastbeginDefault) (Just forecastendDefault)) | ||||
|            | ||||
|   let forecastspan = DateSpan (Just forecaststart) (Just forecastend) | ||||
|       forecasttxns = | ||||
|         [ txnTieKnot t | pt <- jperiodictxns j | ||||
|                        , t <- runPeriodicTransaction pt forecastspan | ||||
| @ -135,12 +139,12 @@ journalAddForecast opts@CliOpts{inputopts_=iopts, reportopts_=ropts} j = do | ||||
|       forecasttxns' = (if auto_ iopts then modifyTransactions (jtxnmodifiers j) else id) forecasttxns | ||||
| 
 | ||||
|   return $ | ||||
|     if forecast_ ropts | ||||
|       then journalBalanceTransactions' opts j{ jtxns = concat [jtxns j, forecasttxns'] } | ||||
|       else j | ||||
|     case forecast_ ropts of | ||||
|       Just _  -> journalBalanceTransactions' iopts j{ jtxns = concat [jtxns j, forecasttxns'] } | ||||
|       Nothing -> j | ||||
|   where | ||||
|     journalBalanceTransactions' opts j = | ||||
|       let assrt = not . ignore_assertions_ $ inputopts_ opts | ||||
|     journalBalanceTransactions' iopts j = | ||||
|       let assrt = not . ignore_assertions_ $ iopts | ||||
|       in | ||||
|        either error' id $ journalBalanceTransactions assrt j | ||||
| 
 | ||||
|  | ||||
| @ -156,3 +156,48 @@ Y 2000 | ||||
| 
 | ||||
| >>>2 | ||||
| >>>=0 | ||||
| 
 | ||||
| 
 | ||||
| # 8. A balance report with forecast-begin enabling transaction before report end | ||||
| hledger bal -M -b 2016-10 -e 2017-02 -f - --forecast=20160801- | ||||
| <<< | ||||
| 2016/12/31 | ||||
|     expenses:housing  $600 | ||||
|     assets:cash | ||||
| 
 | ||||
| ~ monthly from 2016/1  salary | ||||
|     income  $-1000 | ||||
|     assets:cash | ||||
| >>> | ||||
| Balance changes in 2016-10-01-2017-01-31: | ||||
| 
 | ||||
|                   ||    Oct     Nov     Dec     Jan  | ||||
| ==================++================================ | ||||
|  assets:cash      ||  $1000   $1000    $400   $1000  | ||||
|  expenses:housing ||      0       0    $600       0  | ||||
|  income           || $-1000  $-1000  $-1000  $-1000  | ||||
| ------------------++-------------------------------- | ||||
|                   ||      0       0       0       0  | ||||
| >>>2 | ||||
| >>>=0 | ||||
| 
 | ||||
| # 9. Parse error in malformed forecast period expression | ||||
| hledger bal -M -b 2016-10 -e 2017-02 -f - --forecast=20160801-foobar | ||||
| <<< | ||||
| 2016/12/31 | ||||
|     expenses:housing  $600 | ||||
|     assets:cash | ||||
| 
 | ||||
| ~ monthly from 2016/1  salary | ||||
|     income  $-1000 | ||||
|     assets:cash | ||||
| >>> | ||||
| >>>2 | ||||
| hledger: could not parse forecast period : 1:10: | ||||
|   | | ||||
| 1 | 20160801-foobar | ||||
|   |          ^ | ||||
| unexpected 'f' | ||||
| expecting end of input | ||||
|  (use -h to see usage) | ||||
| >>>=1 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user