From 0bcf6ea6f9eb54fdf9cc5a8bafcaaaae76f6a07d Mon Sep 17 00:00:00 2001 From: shall0pass <20625555+shall0pass@users.noreply.github.com> Date: Sat, 22 Apr 2023 10:43:34 -0500 Subject: [PATCH] Change method of calculating 'by' matches to use averaging (#879) I've changed the method of calculating the budgeted amount. There may be a more efficient way of writing the loop, so I'm looking forward to the review. This change implements the mathematics of (Target1+Target2+TargetN-Last_months_category_balance) / (MonthsRemaing1 + MonthsRemaining2 + MonthsRemaingN) * N. This is an averaged approach for multiple templates in the same category. It will appear to overbudget or underbudget some months compared to multiple single targets in different categories, yet there should always be enough saved in the category to satisfy the target due. Setting a target, it's assumed that money will be spent in the appropriate month, When a target reaches maturity, the money in the category associated with that target should be spent or moved so the remaining targets continue to be funded. I don't see an easy way of fixing that, but I hope this change will be of some help. Current method: Notice how the Bills (flexible) category reduces each month, resulting in larger budgeted amounts later in the goal cycle.  Proposed method: **Note: The fact that the initial fill in this example equals the expected fill is a coincidence based on the template values I chose. The initial fills can be different from expected fill.  --- .../src/server/budget/goaltemplates.js | 28 +++++++++++++++---- upcoming-release-notes/879.md | 6 ++++ 2 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 upcoming-release-notes/879.md diff --git a/packages/loot-core/src/server/budget/goaltemplates.js b/packages/loot-core/src/server/budget/goaltemplates.js index 67ecd5c3f..c09253588 100644 --- a/packages/loot-core/src/server/budget/goaltemplates.js +++ b/packages/loot-core/src/server/budget/goaltemplates.js @@ -207,6 +207,9 @@ async function applyCategoryTemplate(category, template_lines, month, force) { let spent = await getSheetValue(sheetName, `sum-amount-${category.id}`); let balance = await getSheetValue(sheetName, `leftover-${category.id}`); let last_month_balance = balance - spent - budgeted; + let totalTarget = 0; + let totalMonths = 0; + let skipMonths = 0; for (let l = 0; l < template_lines.length; l++) { let template = template_lines[l]; switch (template.type) { @@ -230,8 +233,8 @@ async function applyCategoryTemplate(category, template_lines, month, force) { } case 'by': { // by has 'amount' and 'month' params - let target_month = new Date(`${template.month}-01`); - let target = amountToInteger(template.amount); + let N = template_lines.length; + let target_month = new Date(`${template_lines[l].month}-01`); let num_months = differenceInCalendarMonths( target_month, current_month, @@ -242,11 +245,24 @@ async function applyCategoryTemplate(category, template_lines, month, force) { : (template.repeat || 1) * 12; while (num_months < 0 && repeat) { target_month = addMonths(target_month, repeat); - num_months = differenceInCalendarMonths(target_month, current_month); + num_months = differenceInCalendarMonths( + template_lines[l], + current_month, + ); } - let diff = target - last_month_balance; - if (diff >= 0 && num_months > -1) { - to_budget += Math.round(diff / (num_months + 1)); + if (num_months < 0) { + skipMonths++; + } else { + totalTarget += amountToInteger(template_lines[l].amount); + totalMonths += num_months + 1; + } + + let diff = totalTarget - last_month_balance; + if (diff >= 0 && totalMonths > 0 && l === N - 1) { + to_budget += Math.round( + ((totalTarget - last_month_balance) / totalMonths) * + (N - skipMonths), + ); } break; } diff --git a/upcoming-release-notes/879.md b/upcoming-release-notes/879.md new file mode 100644 index 000000000..1fd38d297 --- /dev/null +++ b/upcoming-release-notes/879.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [shall0pass] +--- + +Goal templates: Changed how compounding 'by' matches are filled. Now uses an average across templates. -- GitLab