From ed285e9ac5731af20035c09c20520b4e821d4482 Mon Sep 17 00:00:00 2001 From: youngcw <calebyoung94@gmail.com> Date: Tue, 6 Jun 2023 13:41:09 -0700 Subject: [PATCH] Goals: Remainder option (#1101) Added the option to add a remainder goal template. This will use the remaining available funds and dump them into the respective category. There is optional weighting. The remainder templates will be forced to the lowest priority as to run after all other templates. Usage: `#template remainder <weight>` Add the template line to any categories you want to catch any remaining funds such as savings. The amount added to the category will equal `remaining_budget/total_of_weights*weight`. The default weight is 1. --- .../src/server/budget/goal-template.pegjs | 9 +++-- .../src/server/budget/goaltemplates.ts | 35 +++++++++++++++++++ upcoming-release-notes/1101.md | 6 ++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 upcoming-release-notes/1101.md diff --git a/packages/loot-core/src/server/budget/goal-template.pegjs b/packages/loot-core/src/server/budget/goal-template.pegjs index 9c3fd1b80..e0b6d97c8 100644 --- a/packages/loot-core/src/server/budget/goal-template.pegjs +++ b/packages/loot-core/src/server/budget/goal-template.pegjs @@ -15,12 +15,14 @@ expr priority: +priority } } / priority: priority? _? monthly: amount limit: limit? - { return { type: 'simple', monthly, limit, priority: +priority } } + { return { type: 'simple', monthly, limit, priority: +priority } } / priority: priority? _? limit: limit { return { type: 'simple', limit , priority: +priority } } / priority: priority? _? schedule _ full:full? name: name - { return { type: 'schedule', name, priority: +priority, full } } - + { return { type: 'schedule', name, priority: +priority, full } } + / priority: priority? _? remainder: remainder + { return { type: 'remainder', priority: null, weight: remainder } } + repeat 'repeat interval' = 'month'i { return { annual: false } } @@ -48,6 +50,7 @@ upTo = 'up'i _ 'to'i schedule = 'schedule'i full = 'full'i _ {return true} priority = '-'i number: number _ {return number} +remainder = 'remainder'i _? weight: positive? { return +weight || 1 } _ 'space' = ' '+ d 'digit' = [0-9] diff --git a/packages/loot-core/src/server/budget/goaltemplates.ts b/packages/loot-core/src/server/budget/goaltemplates.ts index 66644df6e..94a1e4b1a 100644 --- a/packages/loot-core/src/server/budget/goaltemplates.ts +++ b/packages/loot-core/src/server/budget/goaltemplates.ts @@ -80,8 +80,35 @@ async function processTemplate(month, force) { }); } } + // find all remainder templates, place them after all other templates + let remainder_found; + let remainder_priority = lowestPriority + 1; + let remainder_weight_total = 0; + for (let c = 0; c < categories.length; c++) { + let category = categories[c]; + let templates = category_templates[category.id]; + if (templates) { + for (let i = 0; i < templates.length; i++) { + if (templates[i].type === 'remainder') { + templates[i].priority = remainder_priority; + remainder_weight_total += templates[i].weight; + remainder_found = true; + } + } + } + } + // so the remainders don't get skiped + if (remainder_found) lowestPriority = remainder_priority; for (let priority = 0; priority <= lowestPriority; priority++) { + // setup scaling for remainder + let remainder_scale = 1; + if (priority === lowestPriority) { + let sheetName = monthUtils.sheetForMonth(month); + let budgetAvailable = await getSheetValue(sheetName, `to-budget`); + remainder_scale = Math.round(budgetAvailable / remainder_weight_total); + } + for (let c = 0; c < categories.length; c++) { let category = categories[c]; let template = category_templates[category.id]; @@ -132,6 +159,7 @@ async function processTemplate(month, force) { template, month, priority, + remainder_scale, force, ); if (to_budget != null) { @@ -235,6 +263,7 @@ async function applyCategoryTemplate( template_lines, month, priority, + remainder_scale, force, ) { let current_month = getCorrectedDate(`${month}-01`); @@ -549,6 +578,12 @@ async function applyCategoryTemplate( } break; } + case 'remainder': { + to_budget = Math.round(remainder_scale * template.weight); + // can over budget with the rounding, so checking that + if (to_budget > budgetAvailable) to_budget = budgetAvailable; + break; + } case 'error': return { errors }; default: diff --git a/upcoming-release-notes/1101.md b/upcoming-release-notes/1101.md new file mode 100644 index 000000000..70ad7e55d --- /dev/null +++ b/upcoming-release-notes/1101.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [youngcw] +--- + +Goals: Add remainder option to budget all extra funds automatically. -- GitLab