Skip to content

Commit

Permalink
[Bugfix] Cleanup tool: Add balance check (actualbudget#2295)
Browse files Browse the repository at this point in the history
* add balance check

* lint

* fill rollover categories after non-rollover

* allow partial fills of non-rollover

* update

* release note and youngcw suggestion

* warnings

* remove commented coded
  • Loading branch information
shall0pass authored Feb 5, 2024
1 parent b2f538d commit fea4576
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 18 deletions.
113 changes: 95 additions & 18 deletions packages/loot-core/src/server/budget/cleanup-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ async function processCleanup(month: string): Promise<Notification> {
let num_sinks = 0;
let total_weight = 0;
const errors = [];
const warnings = [];
const sinkCategory = [];
const sourceWithRollover = [];
const db_month = parseInt(month.replace('-', ''));

const category_templates = await getCategoryTemplates();
const categories = await db.all(
Expand All @@ -35,21 +38,35 @@ async function processCleanup(month: string): Promise<Notification> {
sheetName,
`budget-${category.id}`,
);
const spent = await getSheetValue(
sheetName,
`sum-amount-${category.id}`,
if (balance >= 0) {
const spent = await getSheetValue(
sheetName,
`sum-amount-${category.id}`,
);
await setBudget({
category: category.id,
month,
amount: budgeted - balance,
});
await setGoal({
category: category.id,
month,
goal: -spent,
});
num_sources += 1;
} else {
warnings.push(category.name + ' does not have available funds.');
}
const carryover = await db.first(
`SELECT carryover FROM zero_budgets WHERE month = ? and category = ?`,
[db_month, category.id],
);
await setBudget({
category: category.id,
month,
amount: budgeted - balance,
});
await setGoal({
category: category.id,
month,
goal: -spent,
});
num_sources += 1;
if (carryover !== null) {
//keep track of source categories with rollover enabled
if (carryover.carryover === 1) {
sourceWithRollover.push({ cat: category, temp: template });
}
}
}
if (template.filter(t => t.type === 'sink').length > 0) {
sinkCategory.push({ cat: category, temp: template });
Expand All @@ -60,7 +77,6 @@ async function processCleanup(month: string): Promise<Notification> {
}

//funds all underfunded categories first unless the overspending rollover is checked
const db_month = parseInt(month.replace('-', ''));
for (let c = 0; c < categories.length; c++) {
const category = categories[c];
const budgetAvailable = await getSheetValue(sheetName, `to-budget`);
Expand Down Expand Up @@ -88,13 +104,65 @@ async function processCleanup(month: string): Promise<Notification> {
month,
amount: to_budget,
});
} else if (
balance < 0 &&
!category.is_income &&
carryover.carryover === 0 &&
Math.abs(balance) > budgetAvailable
) {
await setBudget({
category: category.id,
month,
amount: budgeted + budgetAvailable,
});
}
}

const budgetAvailable = await getSheetValue(sheetName, `to-budget`);
//fund rollover categories after non-rollover categories
for (let c = 0; c < categories.length; c++) {
const category = categories[c];
const budgetAvailable = await getSheetValue(sheetName, `to-budget`);
const balance = await getSheetValue(sheetName, `leftover-${category.id}`);
const budgeted = await getSheetValue(sheetName, `budget-${category.id}`);
const to_budget = budgeted + Math.abs(balance);
const categoryId = category.id;
let carryover = await db.first(
`SELECT carryover FROM zero_budgets WHERE month = ? and category = ?`,
[db_month, categoryId],
);

if (carryover === null) {
carryover = { carryover: 0 };
}

if (
balance < 0 &&
Math.abs(balance) <= budgetAvailable &&
!category.is_income &&
carryover.carryover === 1
) {
await setBudget({
category: category.id,
month,
amount: to_budget,
});
} else if (
balance < 0 &&
!category.is_income &&
carryover.carryover === 1 &&
Math.abs(balance) > budgetAvailable
) {
await setBudget({
category: category.id,
month,
amount: budgeted + budgetAvailable,
});
}
}

const budgetAvailable = await getSheetValue(sheetName, `to-budget`);
if (budgetAvailable <= 0) {
errors.push('No funds are available to reallocate.');
warnings.push('No funds are available to reallocate.');
}

for (let c = 0; c < sinkCategory.length; c++) {
Expand Down Expand Up @@ -131,8 +199,17 @@ async function processCleanup(month: string): Promise<Notification> {
message: `There were errors interpreting some templates:`,
pre: errors.join('\n\n'),
};
} else if (warnings.length) {
return {
type: 'warning',
message: 'Funds not available:',
pre: warnings.join('\n\n'),
};
} else {
return { type: 'message', message: 'All categories were up to date.' };
return {
type: 'message',
message: 'All categories were up to date.',
};
}
} else {
const applied = `Successfully returned funds from ${num_sources} ${
Expand Down
6 changes: 6 additions & 0 deletions upcoming-release-notes/2295.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [shall0pass]
---

Monthly cleanup tool: Adjust behavior with category roll-over and allow partial fills

0 comments on commit fea4576

Please sign in to comment.