From 7068517f2c463713a2a146e845c6b0332e8f0948 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Tue, 12 Jul 2022 10:43:58 +0100 Subject: [PATCH] fix: bal: budget goals were ignoring rule-specified start date --- hledger-lib/Hledger/Reports/BudgetReport.hs | 32 +++++++++++++++------ hledger/test/balance/budget.test | 22 +++++++++++--- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/hledger-lib/Hledger/Reports/BudgetReport.hs b/hledger-lib/Hledger/Reports/BudgetReport.hs index 575f8caa8..a2e48e17a 100644 --- a/hledger-lib/Hledger/Reports/BudgetReport.hs +++ b/hledger-lib/Hledger/Reports/BudgetReport.hs @@ -26,7 +26,7 @@ import Data.Decimal (roundTo) import Data.Function (on) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM -import Data.List (find, partition, transpose, foldl') +import Data.List (find, partition, transpose, foldl', maximumBy) import Data.List.Extra (nubSort) import Data.Maybe (fromMaybe, catMaybes, isJust) import Data.Map (Map) @@ -36,7 +36,7 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Builder as TB -import Safe (maximumDef, minimumDef) +import Safe (minimumDef) --import System.Console.CmdArgs.Explicit as C --import Lucid as L import qualified Text.Tabular.AsciiWide as Tab @@ -46,6 +46,7 @@ import Hledger.Utils import Hledger.Reports.ReportOptions import Hledger.Reports.ReportTypes import Hledger.Reports.MultiBalanceReport +import Data.Ord (comparing) type BudgetGoal = Change @@ -113,19 +114,34 @@ journalAddBudgetGoalTransactions bopts ropts reportspan j = where mbudgetgoalsstartdate = -- We want to also generate budget goal txns before the report start date, in case -H is used. - -- What should the actual starting date for goal txns be ? This gets a little tricky: consider a - -- journal with a "~ monthly" periodic transaction rule, where the first transaction is on 1/5. + -- What should the actual starting date for goal txns be ? This gets tricky. + -- Consider a journal with a "~ monthly" periodic transaction rule, where the first transaction is on 1/5. -- Users will certainly expect a budget goal for january, but "~ monthly" generates transactions -- on the first of month, and starting from 1/5 would exclude 1/1. - -- Hopefully the following procedure will produce intuitive behaviour in general: + -- Secondly, consider a rule like "~ every february 2nd from 2020/01"; we should not start that + -- before 2020-02-02. + -- Hopefully the following algorithm produces intuitive behaviour in general: -- from the earlier of the journal start date and the report start date, -- move backward to the nearest natural start date of the largest period seen among the - -- active periodic transactions (so here: monthly, 1/5 -> 1/1). + -- active periodic transactions, unless that is disallowed by a start date in the periodic rule. + -- (Do we need to pay attention to an end date in the rule ? Don't think so.) + -- (So with "~ monthly", the journal start date 1/5 is adjusted to 1/1.) case minimumDef Nothing $ filter isJust [journalStartDate False j, spanStart reportspan] of Nothing -> Nothing - Just d -> Just $ intervalStartBefore biggestinterval d + Just d -> Just d' where - biggestinterval = maximumDef (Days 1) $ map ptinterval budgetpts + -- the interval and any date span of the periodic transaction with longest period + (interval, span) = + case budgetpts of + [] -> (Days 1, nulldatespan) + pts -> (ptinterval pt, ptspan pt) + where pt = maximumBy (comparing ptinterval) pts -- PARTIAL: maximumBy won't fail + -- the natural start of this interval on or before the journal/report start + intervalstart = intervalStartBefore interval d + -- the natural interval start before the journal/report start, + -- or the rule-specified start if later, + -- but no later than the journal/report start. + d' = min d $ maybe intervalstart (max intervalstart) $ spanStart span -- select periodic transactions matching a pattern -- (the argument of the (final) --budget option). diff --git a/hledger/test/balance/budget.test b/hledger/test/balance/budget.test index 67f2ddc08..553ea146b 100644 --- a/hledger/test/balance/budget.test +++ b/hledger/test/balance/budget.test @@ -620,7 +620,7 @@ Budget performance in 2020Q1: ---------------++----------------------------------------------------------- || 0 [ 0% of $500] 0 [0% of $500] 0 [ 0% of $500] -# 32. Select from multiple named budgets. +# 32, 33. Select from multiple named budgets. < ~ weekly weekly budget (aaa) 1 @@ -636,7 +636,7 @@ $ hledger -f- bal --budget=monthly -p 2021-01 > !/aaa/ >= -# 33. Cumulative budget report. +# 34. Cumulative budget report. < ~ monthly (a) 10 @@ -656,8 +656,8 @@ Budget performance in 2022-01-01..2022-02-28: ---++--------------------------------- || 10 [100% of 10] 15 [75% of 20] -# 34. Historical budget report. -$ hledger -f- bal --budget -M --historical -b 2022-02-01 +# 35. Historical budget report. +$ hledger -f- bal --budget -MH -b 2022-02-01 Budget performance in 2022-02: || 2022-02-28 @@ -665,3 +665,17 @@ Budget performance in 2022-02: a || 15 [75% of 20] ---++---------------- || 15 [75% of 20] + +# 36. Historical budget report where the periodic transaction has date bounds. +< +~ every february 2nd from 2020/01 + (a) 1 + +$ hledger -f- bal --budget -MH -p 2020q1 +Budget performance in 2020Q1: + + || 2020-01-31 2020-02-29 2020-03-31 +===++====================================== + a || 0 [0] 0 [0% of 1] 0 [0% of 1] +---++-------------------------------------- + || 0 [0] 0 [0% of 1] 0 [0% of 1]