From ad00da244be52705b96b8f4908ab2bac81d6f8ee Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Sun, 5 May 2019 17:47:38 -0700 Subject: [PATCH] reg: support --value-at with -H (#329) --- hledger-lib/Hledger/Reports/PostingsReport.hs | 43 +++++++++++++-- hledger-lib/Hledger/Reports/ReportOptions.hs | 38 ++++++++++--- tests/journal/market-prices.test | 54 +++++++++++++------ 3 files changed, 109 insertions(+), 26 deletions(-) diff --git a/hledger-lib/Hledger/Reports/PostingsReport.hs b/hledger-lib/Hledger/Reports/PostingsReport.hs index ac048b5d5..c1ed28082 100644 --- a/hledger-lib/Hledger/Reports/PostingsReport.hs +++ b/hledger-lib/Hledger/Reports/PostingsReport.hs @@ -71,13 +71,26 @@ postingsReport ropts@ReportOpts{..} q j = -- postings to be included in the report, and similarly-matched postings before the report start date (precedingps, reportps) = matchedPostingsBeforeAndDuring ropts q j reportspan + -- We may be converting amounts to value, according to --value-at, as follows: + -- (keep synced with hledger_options.m4.md) + -- register -M --value-at + -- transaction: convert each summary posting to value at posting date ; convert -H starting balance to value at day before report start + -- period: convert each summary posting to value at period end ; convert -H starting balance to value at day before report start + -- date: convert each summary posting to value at date ; convert -H starting balance to value at date + -- register --value-at + -- transaction: convert each posting to value at posting date ; convert -H starting balance to value at day before report start + -- period: convert each posting to value at report end ; convert -H starting balance to value at day before report start + -- date: convert each posting to value at date ; convert -H starting balance to value at date + -- in all cases, the running total/average is calculated from the above numbers. + -- "Day before report start" is a bit arbitrary. + + mvalueat = if value_ then Just value_at_ else Nothing + today = fromMaybe (error' "postingsReport: ReportOpts today_ is unset so could not satisfy --value-at=now") today_ + -- Postings or summary pseudo postings to be displayed. - -- If --value-at is present, we'll need to convert them to value as of various dates. displayps = let multiperiod = interval_ /= NoInterval - mvalueat = if value_ then Just value_at_ else Nothing - today = fromMaybe (error' "postingsReport: ReportOpts today_ is unset so could not satisfy --value-at=now") today_ in if multiperiod then let @@ -86,6 +99,7 @@ postingsReport ropts@ReportOpts{..} q j = in case mvalueat of Nothing -> [(p , periodend) | (p,periodend) <- summaryps] Just AtTransaction -> [(postingValueAtDate j (postingDate p) p , periodend) | (p,periodend) <- summaryps] + -- ^ XXX shouldn't this value the individual ps at postingdate before summarising Just AtPeriod -> [(postingValueAtDate j periodlastday p , periodend) | (p,periodend) <- summaryps ,let periodlastday = maybe (error' "postingsReport: expected a subperiod end date") -- XXX shouldn't happen @@ -105,8 +119,10 @@ postingsReport ropts@ReportOpts{..} q j = Just AtNow -> [(postingValueAtDate j today p , Nothing) | p <- reportps] Just (AtDate d) -> [(postingValueAtDate j d p , Nothing) | p <- reportps] + -- For -H: postings preceding the report period, to calculate the initial running total/average. + -- posting report items ready for display - items = dbg1 "postingsReport items" $ postingsReportItems displayps (nullposting,Nothing) whichdate depth startbal runningcalc startnum + items = dbg1 "postingsReport items" $ postingsReportItems displayps (nullposting,Nothing) whichdate depth valuedstartbal runningcalc startnum where historical = balancetype_ == HistoricalBalance precedingsum = sumPostings precedingps @@ -114,6 +130,25 @@ postingsReport ropts@ReportOpts{..} q j = | otherwise = divideMixedAmount (fromIntegral $ length precedingps) precedingsum startbal | average_ = if historical then precedingavg else 0 | otherwise = if historical then precedingsum else 0 + -- For --value-at: convert the initial running total/average to value. + -- For --value-at=transaction, we don't bother valuing each + -- preceding posting at posting date - how useful would that + -- be ? Just value the initial sum/average at report start date. + valuedstartbal = case mvalueat of + Nothing -> startbal + Just AtTransaction -> mixedAmountValue prices daybeforereportstart startbal + Just AtPeriod -> mixedAmountValue prices daybeforereportstart startbal + Just AtNow -> mixedAmountValue prices today startbal + Just (AtDate d) -> mixedAmountValue prices d startbal + 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 j + 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 ca084cfbc..95598f767 100644 --- a/hledger-lib/Hledger/Reports/ReportOptions.hs +++ b/hledger-lib/Hledger/Reports/ReportOptions.hs @@ -33,6 +33,8 @@ module Hledger.Reports.ReportOptions ( specifiedStartEndDates, specifiedStartDate, specifiedEndDate, + reportPeriodStart, + reportPeriodOrJournalStart, reportPeriodLastDay, reportPeriodOrJournalLastDay, @@ -431,6 +433,8 @@ queryOptsFromOpts d ReportOpts{..} = flagsqopts ++ argsqopts flagsqopts = [] argsqopts = snd $ parseQuery d (T.pack query_) +-- Report dates. + -- | The effective report span is the start and end dates specified by -- options or queries, or otherwise the earliest and latest transaction or -- posting dates in the journal. If no dates are specified by options/queries @@ -470,10 +474,32 @@ specifiedStartDate ropts = fst <$> specifiedStartEndDates ropts specifiedEndDate :: ReportOpts -> IO (Maybe Day) specifiedEndDate ropts = snd <$> specifiedStartEndDates ropts --- Get the last day of the overall report period. +-- Some pure alternatives to the above. XXX review/clean up + +-- Get the report's start date. -- If no report period is specified, will be Nothing. -- Will also be Nothing if ReportOpts does not have today_ set, --- since we need that to get the report period robustly. +-- since we need that to get the report period robustly +-- (unlike reportStartDate, which looks up the date with IO.) +reportPeriodStart :: ReportOpts -> Maybe Day +reportPeriodStart ropts@ReportOpts{..} = do + t <- today_ + queryStartDate False $ queryFromOpts t ropts + +-- Get the report's start date, or if no report period is specified, +-- the journal's start date (the earliest posting date). If there's no +-- report period and nothing in the journal, will be Nothing. +reportPeriodOrJournalStart :: ReportOpts -> Journal -> Maybe Day +reportPeriodOrJournalStart ropts@ReportOpts{..} j = + reportPeriodStart ropts <|> journalStartDate False j + +-- Get the last day of the overall report period. +-- This the inclusive end date (one day before the +-- more commonly used, exclusive, report end date). +-- If no report period is specified, will be Nothing. +-- Will also be Nothing if ReportOpts does not have today_ set, +-- since we need that to get the report period robustly +-- (unlike reportEndDate, which looks up the date with IO.) reportPeriodLastDay :: ReportOpts -> Maybe Day reportPeriodLastDay ropts@ReportOpts{..} = do t <- today_ @@ -481,10 +507,10 @@ reportPeriodLastDay ropts@ReportOpts{..} = do qend <- queryEndDate False q return $ addDays (-1) qend --- Get the last day of the overall report period, --- or if no report period is specified, the last day of the journal --- (ie the latest posting date). --- If there's no report period and nothing in the journal, will be Nothing. +-- Get the last day of the overall report period, or if no report +-- period is specified, the last day of the journal (ie the latest +-- posting date). If there's no report period and nothing in the +-- journal, will be Nothing. reportPeriodOrJournalLastDay :: ReportOpts -> Journal -> Maybe Day reportPeriodOrJournalLastDay ropts@ReportOpts{..} j = reportPeriodLastDay ropts <|> journalEndDate False j diff --git a/tests/journal/market-prices.test b/tests/journal/market-prices.test index 4aa471a17..e0f876ce1 100644 --- a/tests/journal/market-prices.test +++ b/tests/journal/market-prices.test @@ -231,33 +231,55 @@ $ hledger -f- reg -V 2000/02/01 (a) 4 B 8 B 2000/03/01 (a) 4 B 12 B +# register with -H (starting balance) + +# 19. register with starting balance, valued at transaction. +# Shows the running total of the posting amount values (not the values of the running total). +# The starting balance is 1 A, valued at 2000/1/31 (day before report start), which is 5 B. +$ hledger -f- reg --value-at=transaction -b 200002 -H +2000/02/01 (a) 2 B 7 B +2000/03/01 (a) 3 B 10 B + +# 20. register with starting balance, valued at period end. +# That is unspecified so the last posting date is used, ie 2000/3/1, so the price is 3 B. +# Starting balance is 5 B as above. +$ hledger -f- reg --value-at=period -b 200002 -H +2000/02/01 (a) 3 B 8 B +2000/03/01 (a) 3 B 11 B + +# 21. register with starting balance, valued at specified date (when the price is 5 B). +# Starting balance is 5 B as above. +$ hledger -f- reg --value-at=2000-01-15 -b 200002 -H +2000/02/01 (a) 5 B 10 B +2000/03/01 (a) 5 B 15 B + # register, periodic -# 19. periodic register report valued at transaction +# 22. periodic register report valued at transaction $ hledger -f- reg --value-at=transaction -M 2000/01 a 1 B 1 B 2000/02 a 2 B 3 B 2000/03 a 3 B 6 B -# 20. periodic register report valued at period end +# 23. periodic register report valued at period end $ hledger -f- reg --value-at=period -M 2000/01 a 5 B 5 B 2000/02 a 2 B 7 B 2000/03 a 3 B 10 B -# 21. periodic register report valued at specified date +# 24. periodic register report valued at specified date $ hledger -f- reg --value-at=2000-01-15 -M 2000/01 a 5 B 5 B 2000/02 a 5 B 10 B 2000/03 a 5 B 15 B -# 22. periodic register report valued today +# 25. periodic register report valued today $ hledger -f- reg --value-at=now -M 2000/01 a 4 B 4 B 2000/02 a 4 B 8 B 2000/03 a 4 B 12 B -# 23. periodic register report valued at default date (same as above) +# 26. periodic register report valued at default date (same as above) $ hledger -f- reg -V -M 2000/01 a 4 B 4 B 2000/02 a 4 B 8 B @@ -265,31 +287,31 @@ $ hledger -f- reg -V -M # balance -# 24. single column balance report valued at transaction +# 27. single column balance report valued at transaction $ hledger -f- bal --value-at=transaction 6 B a -------------------- 6 B -# 25. single column balance report valued at period end +# 28. single column balance report valued at period end $ hledger -f- bal --value-at=period 9 B a -------------------- 9 B -# 26. single column balance report valued at specified date +# 29. single column balance report valued at specified date $ hledger -f- bal --value-at=2000-01-15 15 B a -------------------- 15 B -# 27. single column balance report valued today +# 30. single column balance report valued today $ hledger -f- bal --value-at=now 12 B a -------------------- 12 B -# 28. single column balance report valued at default date (same as above) +# 31. single column balance report valued at default date (same as above) $ hledger -f- bal -V 12 B a -------------------- @@ -297,7 +319,7 @@ $ hledger -f- bal -V # balance, periodic -# 29. multicolumn balance report valued at transaction +# 32. multicolumn balance report valued at transaction $ hledger -f- bal -MTA --value-at=transaction Balance changes in 2000q1: @@ -307,7 +329,7 @@ Balance changes in 2000q1: ---++--------------------------------- || 1 B 2 B 3 B 6 B 2 B -# 30. multicolumn balance report valued at period end +# 33. multicolumn balance report valued at period end $ hledger -f- bal -M --value-at=period Balance changes in 2000q1: @@ -317,7 +339,7 @@ Balance changes in 2000q1: ---++--------------- || 5 B 2 B 3 B -# 31. multicolumn balance report valued at period end with -T or -A +# 34. multicolumn balance report valued at period end with -T or -A $ hledger -f- bal -M --value-at=period -TA Balance changes in 2000q1: @@ -329,7 +351,7 @@ Balance changes in 2000q1: # >2 /not yet supported/ # >=1 -# 32. multicolumn balance report valued at other date +# 35. multicolumn balance report valued at other date $ hledger -f- bal -MTA --value-at=2000-01-15 Balance changes in 2000q1: @@ -339,7 +361,7 @@ Balance changes in 2000q1: ---++--------------------------------- || 5 B 5 B 5 B 15 B 5 B -# 33. multicolumn balance report valued today (with today >= 2000-04-01) +# 36. multicolumn balance report valued today (with today >= 2000-04-01) $ hledger -f- bal -M --value-at=now Balance changes in 2000q1: @@ -349,7 +371,7 @@ Balance changes in 2000q1: ---++--------------- || 4 B 4 B 4 B -# 34. multicolumn balance report valued at default date (same as above) +# 37. multicolumn balance report valued at default date (same as above) $ hledger -f- bal -M -V Balance changes in 2000q1: