From 98c17bd5e0f13e27a09a7f6ac176510530572be7 Mon Sep 17 00:00:00 2001
From: shall0pass <20625555+shall0pass@users.noreply.github.com>
Date: Mon, 4 Mar 2024 14:46:08 -0600
Subject: [PATCH] [Goals] Schedule top off amount (#2404)

* add log messages to schedule templates

* log the included schedules

* more log output

* use a negate filter for sinking funds

* carve out a top out exception if no sinking funds

* note
---
 .../src/server/budget/goals/goalsSchedule.ts  | 46 ++++++++-----------
 upcoming-release-notes/2404.md                |  6 +++
 2 files changed, 25 insertions(+), 27 deletions(-)
 create mode 100644 upcoming-release-notes/2404.md

diff --git a/packages/loot-core/src/server/budget/goals/goalsSchedule.ts b/packages/loot-core/src/server/budget/goals/goalsSchedule.ts
index ac7d65200..6efdd323c 100644
--- a/packages/loot-core/src/server/budget/goals/goalsSchedule.ts
+++ b/packages/loot-core/src/server/budget/goals/goalsSchedule.ts
@@ -186,36 +186,22 @@ export async function goalsSchedule(
     const t = await createScheduleList(template, current_month, category);
     errors = errors.concat(t.errors);
 
-    const t_payMonthOf = t.t.filter(
-      c =>
-        c.full ||
-        (c.target_frequency === 'monthly' &&
-          c.target_interval === 1 &&
-          c.num_months === 0) ||
-        (c.target_frequency === 'weekly' &&
-          c.target_interval >= 0 &&
-          c.num_months === 0) ||
-        c.target_frequency === 'daily' ||
-        isReflectBudget(),
-    );
+    const isPayMonthOf = c =>
+      c.full ||
+      (c.target_frequency === 'monthly' &&
+        c.target_interval === 1 &&
+        c.num_months === 0) ||
+      (c.target_frequency === 'weekly' &&
+        c.target_interval >= 0 &&
+        c.num_months === 0) ||
+      c.target_frequency === 'daily' ||
+      isReflectBudget();
 
+    const t_payMonthOf = t.t.filter(isPayMonthOf);
     const t_sinking = t.t
-      .filter(
-        c =>
-          (!c.full &&
-            c.target_frequency === 'monthly' &&
-            c.target_interval > 1) ||
-          (!c.full &&
-            c.target_frequency === 'monthly' &&
-            c.num_months > 0 &&
-            c.target_interval === 1) ||
-          (!c.full && c.target_frequency === 'yearly') ||
-          (!c.full && c.target_frequency === undefined),
-      )
+      .filter(c => !isPayMonthOf(c))
       .sort((a, b) => a.next_date_string.localeCompare(b.next_date_string));
-
     const totalPayMonthOf = await getPayMonthOfTotal(t_payMonthOf);
-
     const totalSinking = await getSinkingTotal(t_sinking);
     const totalSinkingBaseContribution =
       await getSinkingBaseContributionTotal(t_sinking);
@@ -228,7 +214,13 @@ export async function goalsSchedule(
         remainder,
         last_month_balance,
       );
-      to_budget += Math.round(totalPayMonthOf + totalSinkingContribution);
+      if (t_sinking.length === 0) {
+        to_budget +=
+          Math.round(totalPayMonthOf + totalSinkingContribution) -
+          last_month_balance;
+      } else {
+        to_budget += Math.round(totalPayMonthOf + totalSinkingContribution);
+      }
     }
   }
   return { to_budget, errors, remainder, scheduleFlag };
diff --git a/upcoming-release-notes/2404.md b/upcoming-release-notes/2404.md
new file mode 100644
index 000000000..03a3ed91b
--- /dev/null
+++ b/upcoming-release-notes/2404.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [shall0pass]
+---
+
+[Goals] If no sinking funds are used, apply existing category balance to simple schedules to 'top off' the category.
-- 
GitLab