Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix schedule split template amounts #4077

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions packages/loot-core/src/server/budget/categoryTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import * as monthUtils from '../../shared/months';
import { amountToInteger } from '../../shared/util';
import { CategoryEntity } from '../../types/models';
import * as db from '../db';

import { getSheetValue, getSheetBoolean } from './actions';
Expand Down Expand Up @@ -29,18 +30,18 @@ export class CategoryTemplate {
// Class interface

// set up the class and check all templates
static async init(templates: Template[], categoryID: string, month) {
static async init(templates: Template[], category: CategoryEntity, month) {
// get all the needed setup values
const lastMonthSheet = monthUtils.sheetForMonth(
monthUtils.subMonths(month, 1),
);
const lastMonthBalance = await getSheetValue(
lastMonthSheet,
`leftover-${categoryID}`,
`leftover-${category.id}`,
);
const carryover = await getSheetBoolean(
lastMonthSheet,
`carryover-${categoryID}`,
`carryover-${category.id}`,
);
let fromLastMonth;
if (lastMonthBalance < 0 && !carryover) {
Expand All @@ -52,7 +53,7 @@ export class CategoryTemplate {
await CategoryTemplate.checkByAndScheduleAndSpend(templates, month);
await CategoryTemplate.checkPercentage(templates);
// call the private constructor
return new CategoryTemplate(templates, categoryID, month, fromLastMonth);
return new CategoryTemplate(templates, category, month, fromLastMonth);
}

getPriorities(): number[] {
Expand Down Expand Up @@ -126,7 +127,7 @@ export class CategoryTemplate {
case 'schedule': {
const budgeted = await getSheetValue(
monthUtils.sheetForMonth(this.month),
`leftover-${this.categoryID}`,
`leftover-${this.category.id}`,
);
const ret = await goalsSchedule(
scheduleFlag,
Expand All @@ -137,7 +138,7 @@ export class CategoryTemplate {
this.fromLastMonth,
toBudget,
[],
this.categoryID,
this.category,
);
toBudget = ret.to_budget;
remainder = ret.remainder;
Expand Down Expand Up @@ -203,7 +204,7 @@ export class CategoryTemplate {

//-----------------------------------------------------------------------------
// Implementation
readonly categoryID: string; //readonly so we can double check the category this is using
readonly category: CategoryEntity; //readonly so we can double check the category this is using
private month: string;
private templates = [];
private remainder = [];
Expand All @@ -223,11 +224,11 @@ export class CategoryTemplate {

private constructor(
templates: Template[],
categoryID: string,
category: CategoryEntity,
month: string,
fromLastMonth: number,
) {
this.categoryID = categoryID;
this.category = category;
this.month = month;
this.fromLastMonth = fromLastMonth;
// sort the template lines into regular template, goals, and remainder templates
Expand Down Expand Up @@ -424,7 +425,7 @@ export class CategoryTemplate {
const sheetName = monthUtils.sheetForMonth(
monthUtils.subMonths(this.month, template.lookBack),
);
return await getSheetValue(sheetName, `budget-${this.categoryID}`);
return await getSheetValue(sheetName, `budget-${this.category.id}`);
}

private runWeek(template): number {
Expand Down Expand Up @@ -472,18 +473,18 @@ export class CategoryTemplate {
//TODO figure out if I already found these values and can pass them in
const spent = await getSheetValue(
sheetName,
`sum-amount-${this.categoryID}`,
`sum-amount-${this.category.id}`,
);
const balance = await getSheetValue(
sheetName,
`leftover-${this.categoryID}`,
`leftover-${this.category.id}`,
);
alreadyBudgeted = balance - spent;
firstMonth = false;
} else {
alreadyBudgeted += await getSheetValue(
sheetName,
`budget-${this.categoryID}`,
`budget-${this.category.id}`,
);
}
}
Expand Down Expand Up @@ -536,7 +537,7 @@ export class CategoryTemplate {
const sheetName = monthUtils.sheetForMonth(
monthUtils.subMonths(this.month, i),
);
sum += await getSheetValue(sheetName, `sum-amount-${this.categoryID}`);
sum += await getSheetValue(sheetName, `sum-amount-${this.category.id}`);
}
return -Math.round(sum / template.numMonths);
}
Expand Down
20 changes: 16 additions & 4 deletions packages/loot-core/src/server/budget/goalsSchedule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ describe('goalsSchedule', () => {
it('should return correct budget when recurring schedule set', async () => {
// Given
const scheduleFlag = false;
const template_lines = [{ type: 'schedule', name: 'Test Schedule' }];
const template_lines = [
{
type: 'schedule',
name: 'Test Schedule',
directive: '#template schedule Test Schedule',
} as const,
];
const current_month = '2024-08-01';
const balance = 0;
const remainder = 0;
const last_month_balance = 0;
const to_budget = 0;
const errors: string[] = [];
const category = { id: 1, name: 'Test Category' };
const category = { id: '1', name: 'Test Category' };

(db.first as jest.Mock).mockResolvedValue({ id: 1, completed: 0 });
(getRuleForSchedule as jest.Mock).mockResolvedValue({
Expand Down Expand Up @@ -86,14 +92,20 @@ describe('goalsSchedule', () => {
it('should return correct budget when yearly recurring schedule set and balance is greater than target', async () => {
// Given
const scheduleFlag = false;
const template_lines = [{ type: 'schedule', name: 'Test Schedule' }];
const template_lines = [
{
type: 'schedule',
name: 'Test Schedule',
directive: '#template schedule Test Schedule',
} as const,
];
const current_month = '2024-09-01';
const balance = 12000;
const remainder = 0;
const last_month_balance = 12000;
const to_budget = 0;
const errors: string[] = [];
const category = { id: 1, name: 'Test Category' };
const category = { id: '1', name: 'Test Category' };

(db.first as jest.Mock).mockResolvedValue({ id: 1, completed: 0 });
(getRuleForSchedule as jest.Mock).mockResolvedValue({
Expand Down
34 changes: 22 additions & 12 deletions packages/loot-core/src/server/budget/goalsSchedule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-strict-ignore
import * as monthUtils from '../../shared/months';
import { extractScheduleConds } from '../../shared/schedules';
import { CategoryEntity } from '../../types/models';
import * as db from '../db';
import {
getRuleForSchedule,
Expand All @@ -9,8 +10,13 @@ import {
} from '../schedules/app';

import { isReflectBudget } from './actions';
import { ScheduleTemplate, Template } from './types/templates';

async function createScheduleList(template, current_month, category) {
async function createScheduleList(
template: ScheduleTemplate[],
current_month: string,
category: CategoryEntity,
) {
const t = [];
const errors = [];

Expand Down Expand Up @@ -186,22 +192,26 @@ async function getSinkingTotal(t) {
}

export async function goalsSchedule(
scheduleFlag,
template_lines,
current_month,
balance,
remainder,
last_month_balance,
to_budget,
errors,
category,
scheduleFlag: boolean,
template_lines: Template[],
current_month: string,
balance: number,
remainder: number,
last_month_balance: number,
to_budget: number,
errors: string[],
category: CategoryEntity,
) {
if (!scheduleFlag) {
scheduleFlag = true;
const template = template_lines.filter(t => t.type === 'schedule');
const scheduleTemplates = template_lines.filter(t => t.type === 'schedule');
//in the case of multiple templates per category, schedules may have wrong priority level

const t = await createScheduleList(template, current_month, category);
const t = await createScheduleList(
scheduleTemplates,
current_month,
category,
);
errors = errors.concat(t.errors);

const isPayMonthOf = c =>
Expand Down
12 changes: 7 additions & 5 deletions packages/loot-core/src/server/budget/goaltemplates.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-strict-ignore
import { Notification } from '../../client/state-types/notifications';
import * as monthUtils from '../../shared/months';
import { CategoryEntity } from '../../types/models';
import * as db from '../db';
import { batchMessages } from '../sync';

Expand Down Expand Up @@ -46,7 +47,7 @@ export function runCheckTemplates() {
return checkTemplates();
}

async function getCategories() {
async function getCategories(): Promise<CategoryEntity[]> {
return await db.all(
`
SELECT categories.* FROM categories
Expand Down Expand Up @@ -150,7 +151,8 @@ async function processTemplate(
const budgetList = [];
const goalList = [];
for (let i = 0; i < categories.length; i++) {
const id = categories[i].id;
const category = categories[i];
const { id } = category;
const sheetName = monthUtils.sheetForMonth(month);
const templates = categoryTemplates[id];
const budgeted = await getSheetValue(sheetName, `budget-${id}`);
Expand All @@ -162,7 +164,7 @@ async function processTemplate(
// gather needed priorities
// gather remainder weights
try {
const obj = await CategoryTemplate.init(templates, id, month);
const obj = await CategoryTemplate.init(templates, category, month);
availBudget += budgeted;
availBudget += obj.getLimitExcess();
const p = obj.getPriorities();
Expand Down Expand Up @@ -230,9 +232,9 @@ async function processTemplate(
// finish
catObjects.forEach(o => {
const ret = o.getValues();
budgetList.push({ category: o.categoryID, budgeted: ret.budgeted });
budgetList.push({ category: o.category.id, budgeted: ret.budgeted });
goalList.push({
category: o.categoryID,
category: o.category.id,
goal: ret.goal,
longGoal: ret.longGoal ? 1 : null,
});
Expand Down
6 changes: 6 additions & 0 deletions upcoming-release-notes/4077.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: Bugfix
authors: [jfdoming]
---

Fix schedule split template amounts
Loading