diff --git a/hledger-lib/Hledger/Reports/BalanceReport.hs b/hledger-lib/Hledger/Reports/BalanceReport.hs index 0d0a44ee6..5aa6e90a0 100644 --- a/hledger-lib/Hledger/Reports/BalanceReport.hs +++ b/hledger-lib/Hledger/Reports/BalanceReport.hs @@ -72,6 +72,7 @@ balanceReport ropts@ReportOpts{..} q j = dbg1 s = let p = "balanceReport" in Hledger.Utils.dbg1 (p++" "++s) -- add prefix in debug output today = fromMaybe (error' "balanceReport: ReportOpts today_ is unset so could not satisfy --value=now") today_ + multiperiod = interval_ /= NoInterval -- Get all the summed accounts & balances, according to the query, as an account tree. -- If doing cost valuation, amounts will be converted to cost first. @@ -83,10 +84,13 @@ balanceReport ropts@ReportOpts{..} q j = valueaccount a@Account{..} = a{aebalance=val aebalance, aibalance=val aibalance} where val = case value_ of - Just (AtEnd _mc) -> mixedAmountValue prices periodlastday - Just (AtNow _mc) -> mixedAmountValue prices today - Just (AtDate d _mc) -> mixedAmountValue prices d - _ -> id + Nothing -> id + Just (AtCost _mc) -> id + Just (AtEnd _mc) -> mixedAmountValue prices periodlastday + Just (AtNow _mc) -> mixedAmountValue prices today + Just (AtDefault _mc) | multiperiod -> mixedAmountValue prices periodlastday + Just (AtDefault _mc) -> mixedAmountValue prices today + Just (AtDate d _mc) -> mixedAmountValue prices d where -- prices are in parse order - sort into date then parse order, -- & reversed for quick lookup of the latest price. diff --git a/hledger-lib/Hledger/Reports/BudgetReport.hs b/hledger-lib/Hledger/Reports/BudgetReport.hs index a7273019a..5fee82135 100644 --- a/hledger-lib/Hledger/Reports/BudgetReport.hs +++ b/hledger-lib/Hledger/Reports/BudgetReport.hs @@ -273,12 +273,16 @@ budgetReportAsText ropts@ReportOpts{..} budgetr@(PeriodicReport ( _, rows, _)) = title ++ "\n\n" ++ tableAsText ropts showcell (maybetranspose $ budgetReportAsTable ropts budgetr) where + multiperiod = interval_ /= NoInterval title = printf "Budget performance in %s%s:" (showDateSpan $ budgetReportSpan budgetr) (case value_ of Just (AtCost _mc) -> ", valued at cost" Just (AtEnd _mc) -> ", valued at period ends" Just (AtNow _mc) -> ", current value" + -- XXX duplicates the above + Just (AtDefault _mc) | multiperiod -> ", valued at period ends" + Just (AtDefault _mc) -> ", current value" Just (AtDate d _mc) -> ", valued at "++showDate d Nothing -> "") actualwidth = diff --git a/hledger-lib/Hledger/Reports/EntriesReport.hs b/hledger-lib/Hledger/Reports/EntriesReport.hs index 11d60b7fc..bb0a9002e 100644 --- a/hledger-lib/Hledger/Reports/EntriesReport.hs +++ b/hledger-lib/Hledger/Reports/EntriesReport.hs @@ -40,15 +40,21 @@ entriesReport ropts@ReportOpts{..} q j@Journal{..} = datefn = transactionDateFn ropts tvalue t@Transaction{..} = t{tpostings=map pvalue tpostings} pvalue p@Posting{..} = case value_ of - Nothing -> p - Just (AtCost _mc) -> postingToCost (journalCommodityStyles j) p - Just (AtEnd _mc) -> postingValue jmarketprices (fromMaybe (postingDate p) -- XXX shouldn't happen - mperiodorjournallastday) p - Just (AtNow _mc) -> postingValue jmarketprices ( - case today_ of Just d -> d - Nothing -> error' "erValue: ReportOpts today_ is unset so could not satisfy --value=now") p - Just (AtDate d _mc) -> postingValue jmarketprices d p + Nothing -> p + Just (AtCost _mc) -> postingToCost (journalCommodityStyles j) p + Just (AtEnd _mc) -> valueend p + Just (AtNow _mc) -> valuenow p + Just (AtDefault _mc) -> valuenow p + Just (AtDate d _mc) -> postingValue jmarketprices d p where + valueend p = postingValue jmarketprices ( + fromMaybe (postingDate p) -- XXX shouldn't happen + mperiodorjournallastday + ) p + valuenow p = postingValue jmarketprices ( + case today_ of Just d -> d + Nothing -> error' "erValue: ReportOpts today_ is unset so could not satisfy --value=now" + ) p mperiodorjournallastday = mperiodlastday <|> journalEndDate False j where -- The last day of the report period. diff --git a/hledger-lib/Hledger/Reports/MultiBalanceReports.hs b/hledger-lib/Hledger/Reports/MultiBalanceReports.hs index ca878a304..8225236b6 100644 --- a/hledger-lib/Hledger/Reports/MultiBalanceReports.hs +++ b/hledger-lib/Hledger/Reports/MultiBalanceReports.hs @@ -261,6 +261,7 @@ multiBalanceReport ropts@ReportOpts{..} q j@Journal{..} = -- One row per account, with account name info, row amounts, row total and row average. -- Row amounts are converted to value if that has been requested. -- Row total/average are always simply the sum/average of the row amounts. + multiperiod = interval_ /= NoInterval rows :: [MultiBalanceReportRow] = dbg1 "rows" $ [(a, accountLeafName a, accountNameLevel a, valuedrowbals, rowtot, rowavg) @@ -273,12 +274,16 @@ multiBalanceReport ropts@ReportOpts{..} q j@Journal{..} = CumulativeChange -> drop 1 $ scanl (+) 0 changes HistoricalBalance -> drop 1 $ scanl (+) (startingBalanceFor a) changes -- The row amounts valued according to --value if needed. + , let rowbalsendvalue = [mixedAmountValue prices periodlastday amt | (amt,periodlastday) <- zip rowbals lastdays] + , let rowbalsdatevalue d = [mixedAmountValue prices d amt | amt <- rowbals] , let valuedrowbals = dbg1 "valuedrowbals" $ case value_ of - Just (AtCost _mc) -> rowbals -- cost valuation was handled earlier - Just (AtEnd _mc) -> [mixedAmountValue prices periodlastday amt | (amt,periodlastday) <- zip rowbals lastdays] - Just (AtNow _mc) -> [mixedAmountValue prices today amt | amt <- rowbals] - Just (AtDate d _mc) -> [mixedAmountValue prices d amt | amt <- rowbals] - Nothing -> rowbals + Nothing -> rowbals + Just (AtCost _mc) -> rowbals -- cost valuation was handled earlier + Just (AtEnd _mc) -> rowbalsendvalue + Just (AtNow _mc) -> rowbalsdatevalue today + Just (AtDefault _mc) | multiperiod -> rowbalsendvalue + Just (AtDefault _mc) -> rowbalsdatevalue today + Just (AtDate d _mc) -> rowbalsdatevalue d -- The total and average for the row, and their values. -- Total for a cumulative/historical report is always zero. diff --git a/hledger-lib/Hledger/Reports/PostingsReport.hs b/hledger-lib/Hledger/Reports/PostingsReport.hs index d7298b64b..1ad46bb83 100644 --- a/hledger-lib/Hledger/Reports/PostingsReport.hs +++ b/hledger-lib/Hledger/Reports/PostingsReport.hs @@ -87,36 +87,44 @@ postingsReport ropts@ReportOpts{..} q j@Journal{..} = today = fromMaybe (error' "postingsReport: ReportOpts today_ is unset so could not satisfy --value=now") today_ + multiperiod = interval_ /= NoInterval + -- Postings or summary pseudo postings to be displayed. displayps = - let - multiperiod = interval_ /= NoInterval - in - if multiperiod then - let - showempty = empty_ || average_ - summaryps = summarisePostingsByInterval interval_ whichdate depth showempty reportspan reportps - in case value_ of - Just (AtEnd _mc) -> [(postingValue jmarketprices periodlastday p , periodend) | (p,periodend) <- summaryps - ,let periodlastday = maybe - (error' "postingsReport: expected a subperiod end date") -- XXX shouldn't happen - (addDays (-1)) - periodend - ] - Just (AtNow _mc) -> [(postingValue jmarketprices today p , periodend) | (p,periodend) <- summaryps] - Just (AtDate d _mc) -> [(postingValue jmarketprices d p , periodend) | (p,periodend) <- summaryps] - Just (AtCost _mc) -> summaryps -- conversion to cost was done earlier - _ -> summaryps - else - let reportperiodlastday = - fromMaybe (error' "postingsReport: expected a non-empty journal") -- XXX shouldn't happen - $ reportPeriodOrJournalLastDay ropts j - in case value_ of - Nothing -> [(p , Nothing) | p <- reportps] - Just (AtCost _mc) -> [(p , Nothing) | p <- reportps] -- conversion to cost was done earlier - Just (AtEnd _mc) -> [(postingValue jmarketprices reportperiodlastday p, Nothing) | p <- reportps] - Just (AtNow _mc) -> [(postingValue jmarketprices today p , Nothing) | p <- reportps] - Just (AtDate d _mc) -> [(postingValue jmarketprices d p , Nothing) | p <- reportps] + if multiperiod then + let + showempty = empty_ || average_ + summaryps = summarisePostingsByInterval interval_ whichdate depth showempty reportspan reportps + summarypsendvalue = [(postingValue jmarketprices periodlastday p, periodend) | (p,periodend) <- summaryps + ,let periodlastday = maybe + (error' "postingsReport: expected a subperiod end date") -- XXX shouldn't happen + (addDays (-1)) + periodend + ] + summarypsdatevalue d = [(postingValue jmarketprices d p, periodend) | (p,periodend) <- summaryps] + in case value_ of + Nothing -> summaryps + Just (AtCost _mc) -> summaryps -- conversion to cost was done earlier + Just (AtEnd _mc) -> summarypsendvalue + Just (AtNow _mc) -> summarypsdatevalue today + Just (AtDefault _mc) | multiperiod -> summarypsendvalue + Just (AtDefault _mc) -> summarypsdatevalue today + Just (AtDate d _mc) -> summarypsdatevalue d + else + let + reportperiodlastday = + fromMaybe (error' "postingsReport: expected a non-empty journal") -- XXX shouldn't happen + $ reportPeriodOrJournalLastDay ropts j + reportpsdatevalue d = [(postingValue jmarketprices d p, Nothing) | p <- reportps] + reportpsnovalue = [(p, Nothing) | p <- reportps] + in case value_ of + Nothing -> reportpsnovalue + Just (AtCost _mc) -> reportpsnovalue -- conversion to cost was done earlier + Just (AtEnd _mc) -> reportpsdatevalue reportperiodlastday + Just (AtNow _mc) -> reportpsdatevalue today + Just (AtDefault _mc) | multiperiod -> reportpsdatevalue reportperiodlastday + Just (AtDefault _mc) -> reportpsdatevalue today + Just (AtDate d _mc) -> reportpsdatevalue d -- posting report items ready for display items = dbg1 "postingsReport items" $ postingsReportItems displayps (nullposting,Nothing) whichdate depth valuedstartbal runningcalc startnum @@ -128,20 +136,24 @@ postingsReport ropts@ReportOpts{..} q j@Journal{..} = startbal | average_ = if historical then precedingavg else 0 | otherwise = if historical then precedingsum else 0 -- For --value=end/now/DATE, convert the initial running total/average to value. + startbaldatevalue d = mixedAmountValue prices d startbal + where + -- prices are in parse order - sort into date then parse order, + -- & reversed for quick lookup of the latest price. + prices = reverse $ sortOn mpdate jmarketprices valuedstartbal = case value_ of - Nothing -> startbal - Just (AtCost _mc) -> startbal -- conversion to cost was done earlier - Just (AtEnd _mc) -> mixedAmountValue prices daybeforereportstart startbal - Just (AtNow _mc) -> mixedAmountValue prices today startbal - Just (AtDate d _mc) -> mixedAmountValue prices d startbal + Nothing -> startbal + Just (AtCost _mc) -> startbal -- conversion to cost was done earlier + Just (AtEnd _mc) -> startbaldatevalue daybeforereportstart + Just (AtNow _mc) -> startbaldatevalue today + Just (AtDefault _mc) | multiperiod -> startbaldatevalue daybeforereportstart + Just (AtDefault _mc) -> startbaldatevalue today + Just (AtDate d _mc) -> startbaldatevalue d where daybeforereportstart = maybe (error' "postingsReport: expected a non-empty journal") -- XXX shouldn't happen (addDays (-1)) $ reportPeriodOrJournalStart ropts j - -- prices are in parse order - sort into date then parse order, - -- & reversed for quick lookup of the latest price. - prices = reverse $ sortOn mpdate jmarketprices startnum = if historical then length precedingps + 1 else 1 runningcalc = registerRunningCalculationFn ropts diff --git a/hledger-lib/Hledger/Reports/ReportOptions.hs b/hledger-lib/Hledger/Reports/ReportOptions.hs index a4b85a5ed..013849a95 100644 --- a/hledger-lib/Hledger/Reports/ReportOptions.hs +++ b/hledger-lib/Hledger/Reports/ReportOptions.hs @@ -85,6 +85,7 @@ data ValuationType = | AtEnd (Maybe CommoditySymbol) -- ^ convert to default valuation commodity or given commodity, using market prices at period end(s) | AtNow (Maybe CommoditySymbol) -- ^ convert to default valuation commodity or given commodity, using current market prices | AtDate Day (Maybe CommoditySymbol) -- ^ convert to default valuation commodity or given commodity, using market prices on some date + | AtDefault (Maybe CommoditySymbol) -- ^ works like AtNow in single period reports, like AtEnd in multiperiod reports deriving (Show,Data,Eq) -- Typeable -- instance Default ValuationType where def = AtNow Nothing @@ -349,7 +350,7 @@ valuationTypeFromRawOpts = lastDef Nothing . filter isJust . map valuationfromra where valuationfromrawopt (n,v) | n == "B" = Just $ AtCost Nothing - | n == "V" = Just $ AtNow Nothing -- TODO: if multiperiod then AtEnd Nothing + | n == "V" = Just $ AtDefault Nothing | n == "value" = Just $ valuation v | otherwise = Nothing valuation v diff --git a/hledger/Hledger/Cli/Commands/Balance.hs b/hledger/Hledger/Cli/Commands/Balance.hs index 12301c4f1..03d7a5ae0 100644 --- a/hledger/Hledger/Cli/Commands/Balance.hs +++ b/hledger/Hledger/Cli/Commands/Balance.hs @@ -576,6 +576,7 @@ multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String multiBalanceReportAsText ropts@ReportOpts{..} r = title ++ "\n\n" ++ (balanceReportTableAsText ropts $ balanceReportAsTable ropts r) where + multiperiod = interval_ /= NoInterval title = printf "%s in %s%s:" (case balancetype_ of PeriodChange -> "Balance changes" @@ -586,6 +587,9 @@ multiBalanceReportAsText ropts@ReportOpts{..} r = Just (AtCost _mc) -> ", valued at cost" Just (AtEnd _mc) -> ", valued at period ends" Just (AtNow _mc) -> ", current value" + -- XXX duplicates the above + Just (AtDefault _mc) | multiperiod -> ", valued at period ends" + Just (AtDefault _mc) -> ", current value" Just (AtDate d _mc) -> ", valued at "++showDate d Nothing -> "") diff --git a/hledger/Hledger/Cli/CompoundBalanceCommand.hs b/hledger/Hledger/Cli/CompoundBalanceCommand.hs index b980e3e66..6bfe7fbd2 100644 --- a/hledger/Hledger/Cli/CompoundBalanceCommand.hs +++ b/hledger/Hledger/Cli/CompoundBalanceCommand.hs @@ -140,10 +140,13 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=r PeriodChange -> "(Balance Changes)" CumulativeChange -> "(Cumulative Ending Balances)" HistoricalBalance -> "(Historical Ending Balances)" + multiperiod = interval_ /= NoInterval valuation = case value_ of Just (AtCost _mc) -> ", valued at cost" Just (AtEnd _mc) -> ", valued at period ends" Just (AtNow _mc) -> ", current value" + Just (AtDefault _mc) | multiperiod -> ", valued at period ends" + Just (AtDefault _mc) -> ", current value" Just (AtDate d _mc) -> ", valued at "++showDate d Nothing -> "" diff --git a/hledger/hledger_options.m4.md b/hledger/hledger_options.m4.md index e45e7d8df..33bf90a99 100644 --- a/hledger/hledger_options.m4.md +++ b/hledger/hledger_options.m4.md @@ -447,13 +447,18 @@ $ hledger balance --pivot member acct:. The `-B/--cost` flag converts amounts to their cost at transaction time, if they have a [transaction price](/journal.html#transaction-prices) specified. +This flag is equivalent to `--value=cost`, described below. ## Market value The `-V/--market` flag converts reported amounts to their market value in another commodity. -It uses the commodity referenced in the latest [market price](journal.html#market-prices) (P directive) -dated on or before the valuation date. The default valuation date is today. -For example: +It uses the default valuation commodity referenced in the latest [market price](journal.html#market-prices) (P directive) +dated on or before the default valuation date, which is: + +- today for single period reports (equivalent to `--value=now`) +- the end of each subperiod for multiperiod reports (ie, reports with a [report interval](#report-intervals)) (equivalent to `--value=end`). + +A quick example of `-V`: ```journal # one euro is worth this many dollars from nov 1 @@ -495,8 +500,8 @@ You can control valuation more precisely with the `--value` option. --value=TYPE which type of valuation should be done ? cost|end|now|YYYY-MM-DD -The argument is one of the keywords shown, or their first letter, or a custom date. -The precise effect of the keywords is command-specific, but here is their general meaning: +TYPE is one of the keywords shown, or their first letter, or a custom date. +Their meanings: `--value=cost` (or `c`) : Convert amounts to cost, using the prices recorded in transactions. diff --git a/tests/budget/budget.test b/tests/budget/budget.test index 6fcd965df..af5311f81 100644 --- a/tests/budget/budget.test +++ b/tests/budget/budget.test @@ -338,7 +338,7 @@ P 2018/01/26 SHARE €10 assets:bank $ hledger -f - bal -M --budget --cumulative --forecast -V -Budget performance in 2018/05/01-2018/06/30, current value: +Budget performance in 2018/05/01-2018/06/30, valued at period ends: || May Jun ================++========================================== diff --git a/tests/journal/valuation.test b/tests/journal/valuation.test index f27bc5f35..3a49bee55 100644 --- a/tests/journal/valuation.test +++ b/tests/journal/valuation.test @@ -223,7 +223,7 @@ $ hledger -f- reg --value=now 2000/02/01 (a) 4 B 8 B 2000/03/01 (a) 4 B 12 B -# 18. register report valued at default date (same as above) +# 18. single-period register report valued at default date (same as --value=now) $ hledger -f- reg -V 2000/01/01 (a) 4 B 4 B 2000/02/01 (a) 4 B 8 B @@ -314,11 +314,11 @@ $ hledger -f- reg --value=now -M 2000/02 a 4 B 8 B 2000/03 a 4 B 12 B -# 26. periodic register report valued at default date (same as above) +# 26. periodic register report valued at default date (same as --value=end) $ hledger -f- reg -V -M -2000/01 a 4 B 4 B -2000/02 a 4 B 8 B -2000/03 a 4 B 12 B +2000/01 a 5 B 5 B +2000/02 a 2 B 7 B +2000/03 a 3 B 10 B # balance @@ -338,7 +338,7 @@ $ hledger -f- bal -N --value=2000-01-15 $ hledger -f- bal -N --value=now 12 B a -# 31. single column balance report valued at default date (same as above) +# 31. single column balance report valued at default date (same as --value=now) $ hledger -f- bal -N -V 12 B a @@ -394,15 +394,15 @@ Balance changes in 2000q1, current value: ---++--------------- || 4 B 4 B 4 B -# 37. multicolumn balance report valued at default date (same as above) +# 37. multicolumn balance report valued at default date (same as --value=end) $ hledger -f- bal -M -V -Balance changes in 2000q1, current value: +Balance changes in 2000q1, valued at period ends: || Jan Feb Mar ===++=============== - a || 4 B 4 B 4 B + a || 5 B 2 B 3 B ---++--------------- - || 4 B 4 B 4 B + || 5 B 2 B 3 B # balance, periodic, with -H (starting balance and accumulating across periods)