Skip to content

Commit

Permalink
[Goals]: Add a long term goal template (#3012)
Browse files Browse the repository at this point in the history
* update the parser

* can set goal value, but there are errors and it still needs to look for a balance and not month amount

* fix apply budget

* un change

* note

* lint

* working processing, need to set colors based on month somehow

* add long goal option to the gui and db

* note, lint

* fix cleanup

* fix

* make mobile work, lint

* fix bindings

* more proper

* lint

* fix single category run

* don't unset goal values if they don't have a template too

* lint

* more lint

* fix check when no template exists

* rearrange to get around the issue of inconsistent colors

* lint

* typecheck

* add field to aql schema

* fixes

* cleanup

* migration date
  • Loading branch information
youngcw committed Jul 26, 2024
1 parent 1cef0d1 commit 511f677
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type BalanceWithCarryoverProps = Omit<
balance: Binding;
goal: Binding;
budgeted: Binding;
longGoal: Binding;
disabled?: boolean;
carryoverIndicator?: ({ style }: CarryoverIndicatorProps) => JSX.Element;
};
Expand Down Expand Up @@ -54,6 +55,7 @@ export function BalanceWithCarryover({
balance,
goal,
budgeted,
longGoal,
disabled,
carryoverIndicator = DefaultCarryoverIndicator,
...props
Expand All @@ -62,11 +64,12 @@ export function BalanceWithCarryover({
const balanceValue = useSheetValue(balance);
const goalValue = useSheetValue(goal);
const budgetedValue = useSheetValue(budgeted);
const longGoalValue = useSheetValue(longGoal);
const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
const valueStyle = makeBalanceAmountStyle(
balanceValue,
isGoalTemplatesEnabled ? goalValue : null,
budgetedValue,
longGoalValue === 1 ? balanceValue : budgetedValue,
);

return (
Expand All @@ -86,7 +89,7 @@ export function BalanceWithCarryover({
makeBalanceAmountStyle(
value,
isGoalTemplatesEnabled ? goalValue : null,
budgetedValue,
longGoalValue === 1 ? balanceValue : budgetedValue,
)
}
style={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ export const CategoryMonth = memo(function CategoryMonth({
balance={reportBudget.catBalance(category.id)}
goal={reportBudget.catGoal(category.id)}
budgeted={reportBudget.catBudgeted(category.id)}
longGoal={reportBudget.catLongGoal(category.id)}
style={{
':hover': { textDecoration: 'underline' },
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
balance={rolloverBudget.catBalance(category.id)}
goal={rolloverBudget.catGoal(category.id)}
budgeted={rolloverBudget.catBudgeted(category.id)}
longGoal={rolloverBudget.catLongGoal(category.id)}
style={{
':hover': { textDecoration: 'underline' },
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
category,
isHidden,
goal,
longGoal,
budgeted,
spent,
balance,
Expand All @@ -345,8 +346,6 @@ const ExpenseCategory = memo(function ExpenseCategory({
const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
const goalTemp = useSheetValue(goal);
const goalValue = isGoalTemplatesEnabled ? goalTemp : null;
const budgetedTemp = useSheetValue(budgeted);
const budgetedValue = isGoalTemplatesEnabled ? budgetedTemp : null;

const [budgetType = 'rollover'] = useLocalPref('budgetType');
const dispatch = useDispatch();
Expand All @@ -364,6 +363,14 @@ const ExpenseCategory = memo(function ExpenseCategory({
? rolloverBudget.catBalance(category.id)
: reportBudget.catBalance(category.id),
);
const budgetedtmp = useSheetValue(budgeted);
const balancetmp = useSheetValue(balance);
const isLongGoal = useSheetValue(longGoal) === 1;
const budgetedValue = isGoalTemplatesEnabled
? isLongGoal
? balancetmp
: budgetedtmp
: null;

const onTransfer = () => {
dispatch(
Expand Down Expand Up @@ -587,6 +594,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
balance={balance}
goal={goal}
budgeted={budgeted}
longGoal={longGoal}
formatter={value => (
<Button
type="bare"
Expand Down Expand Up @@ -1381,6 +1389,11 @@ const ExpenseGroup = memo(function ExpenseGroup({
? reportBudget.catGoal(category.id)
: rolloverBudget.catGoal(category.id)
}
longGoal={
type === 'report'
? reportBudget.catLongGoal(category.id)
: rolloverBudget.catLongGoal(category.id)
}
budgeted={
type === 'report'
? reportBudget.catBudgeted(category.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function ReportBalanceMenuModal({
balance={reportBudget.catBalance(categoryId)}
goal={reportBudget.catGoal(categoryId)}
budgeted={reportBudget.catBudgeted(categoryId)}
longGoal={reportBudget.catLongGoal(categoryId)}
carryoverIndicator={({ style }) =>
DefaultCarryoverIndicator({
style: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function RolloverBalanceMenuModal({
balance={rolloverBudget.catBalance(categoryId)}
goal={rolloverBudget.catGoal(categoryId)}
budgeted={rolloverBudget.catBudgeted(categoryId)}
longGoal={rolloverBudget.catLongGoal(categoryId)}
carryoverIndicator={({ style }) =>
DefaultCarryoverIndicator({
style: {
Expand Down
6 changes: 6 additions & 0 deletions packages/loot-core/migrations/1720665000000_goal_context.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN TRANSACTION;

ALTER TABLE zero_budgets ADD COLUMN long_goal INTEGER DEFAULT null;
ALTER TABLE reflect_budgets ADD COLUMN long_goal INTEGER DEFAULT null;

COMMIT;
2 changes: 2 additions & 0 deletions packages/loot-core/src/client/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export const rolloverBudget = {
catBalance: id => `leftover-${id}`,
catCarryover: id => `carryover-${id}`,
catGoal: id => `goal-${id}`,
catLongGoal: id => `long-goal-${id}`,
};

export const reportBudget = {
Expand All @@ -263,4 +264,5 @@ export const reportBudget = {
catBalance: id => `leftover-${id}`,
catCarryover: id => `carryover-${id}`,
catGoal: id => `goal-${id}`,
catLongGoal: id => `long-goal-${id}`,
};
2 changes: 2 additions & 0 deletions packages/loot-core/src/server/aql/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const schema = {
amount: f('integer'),
carryover: f('integer'),
goal: f('integer'),
long_goal: f('integer'),
},
zero_budgets: {
id: f('id'),
Expand All @@ -168,6 +169,7 @@ export const schema = {
amount: f('integer'),
carryover: f('integer'),
goal: f('integer'),
long_goal: f('integer'),
},
};

Expand Down
4 changes: 3 additions & 1 deletion packages/loot-core/src/server/budget/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function setBudget({
});
}

export function setGoal({ month, category, goal }): Promise<void> {
export function setGoal({ month, category, goal, long_goal }): Promise<void> {
const table = getBudgetTable();
const existing = db.firstSync(
`SELECT id FROM ${table} WHERE month = ? AND category = ?`,
Expand All @@ -124,13 +124,15 @@ export function setGoal({ month, category, goal }): Promise<void> {
return db.update(table, {
id: existing.id,
goal,
long_goal,
});
}
return db.insert(table, {
id: `${dbMonth(month)}-${category}`,
month: dbMonth(month),
category,
goal,
long_goal,
});
}

Expand Down
3 changes: 3 additions & 0 deletions packages/loot-core/src/server/budget/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ function handleBudgetChange(budget) {
budget.carryover === 1 ? true : false,
);
sheet.get().set(`${sheetName}!goal-${budget.category}`, budget.goal);
sheet
.get()
.set(`${sheetName}!long-goal-${budget.category}`, budget.long_goal);
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/loot-core/src/server/budget/cleanup-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ async function processCleanup(month: string): Promise<Notification> {
category: category.id,
month,
goal: budgeted - balance,
long_goal: 0,
});
num_sources += 1;
} else {
Expand Down
37 changes: 20 additions & 17 deletions packages/loot-core/src/server/budget/goal-template.pegjs
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
// https://peggyjs.org

expr
= priority: priority? _? percentOf:percentOf category: name
{ return { type: 'percentage', percent: +percentOf.percent, previous: percentOf.prev, category, priority: +priority }}
/ priority: priority? _? amount: amount _ repeatEvery _ weeks: weekCount _ starting _ starting: date limit: limit?
{ return { type: 'week', amount, weeks, starting, limit, priority: +priority }}
/ priority: priority? _? amount: amount _ by _ month: month from: spendFrom? repeat: (_ repeatEvery _ repeat)?
= template: template _ percentOf:percentOf category: name
{ return { type: 'percentage', percent: +percentOf.percent, previous: percentOf.prev, category, priority: template.priority, directive: template.directive }}
/ template: template _ amount: amount _ repeatEvery _ weeks: weekCount _ starting _ starting: date limit: limit?
{ return { type: 'week', amount, weeks, starting, limit, priority: template.priority, directive: template.directive }}
/ template: template _ amount: amount _ by _ month: month from: spendFrom? repeat: (_ repeatEvery _ repeat)?
{ return {
type: from ? 'spend' : 'by',
amount,
month,
...(repeat ? repeat[3] : {}),
from,
priority: +priority
priority: template.priority, directive: template.directive
} }
/ priority: priority? _? monthly: amount limit: limit?
{ 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 } }
/ priority: priority? _? remainder: remainder
{ return { type: 'remainder', priority: null, weight: remainder } }
/ priority: priority? _? 'average'i _ amount: positive _ 'months'i?
{ return { type: 'average', amount: +amount, priority: +priority }}
/ template: template _ monthly: amount limit: limit?
{ return { type: 'simple', monthly, limit, priority: template.priority, directive: template.directive } }
/ template: template _ limit: limit
{ return { type: 'simple', limit , priority: template.priority, directive: template.directive } }
/ template: template _ schedule _ full:full? name: name
{ return { type: 'schedule', name, priority: template.priority, directive: template.directive, full } }
/ template: template _ remainder: remainder limit: limit?
{ return { type: 'remainder', priority: null, directive: template.directive, weight: remainder, limit } }
/ template: template _ 'average'i _ amount: positive _ 'months'i?
{ return { type: 'average', amount: +amount, priority: template.priority, directive: template.directive }}
/ goal: goal amount: amount { return {type: 'simple', amount: amount, priority: null, directive: 'goal' }}


repeat 'repeat interval'
Expand Down Expand Up @@ -53,8 +54,10 @@ starting = 'starting'i
upTo = 'up'i _ 'to'i
schedule = 'schedule'i
full = 'full'i _ {return true}
priority = '-'i number: number _ {return number}
priority = '-'i number: number {return number}
remainder = 'remainder'i _? weight: positive? { return +weight || 1 }
template = '#template' priority: priority? {return {priority: +priority, directive: 'template'}}
goal = '#goal'

_ 'space' = ' '+
d 'digit' = [0-9]
Expand Down
Loading

0 comments on commit 511f677

Please sign in to comment.