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

Add percentage adjustments to schedule templates (#4098) #4257

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

MattFaz
Copy link

@MattFaz MattFaz commented Jan 30, 2025

Addresses feature request #4098

This adds the ability to increase or decrease schedule amounts by a percentage using square bracket modifiers. The syntax allows adjusting schedule amounts without modifying the original schedule.

Example usage:

  • #template schedule Car Insurance [increase 10%]
  • #template schedule Rent [decrease 5%]

Detailed Explanation:

Yearly expenses such as insurance, property rates, etc. increase year on year. You often don't know the new amount until ~1-2 months before it's due. This creates a budget crunch - if your $1,000 insurance jumps 20% ($1,200), you need to make up that extra $200 in just a month or two.

This adds percentage adjustments to templates, letting you gradually save the expected increase throughout the year:
#template schedule Car Insurance [increase 20%].

By proactively budgeting a percentage change for these yearly increases, you avoid last-minute scrambling when renewal notices arrive with higher amounts.

@actual-github-bot actual-github-bot bot changed the title Add percentage adjustments to schedule templates (#4098) [WIP] Add percentage adjustments to schedule templates (#4098) Jan 30, 2025
Copy link

netlify bot commented Jan 30, 2025

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit 58a64c1
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/679c0852bb680400089d061d
😎 Deploy Preview https://deploy-preview-4257.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

github-actions bot commented Jan 30, 2025

Bundle Stats — desktop-client

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
15 6.71 MB 0%

Changeset

No files were changed

View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

No assets were bigger

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/nl.js 79.76 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/workbox-window.prod.es5.js 5.69 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/uk.js 111.11 kB 0%
static/js/AppliedFilters.js 10.52 kB 0%
static/js/en-GB.js 99.33 kB 0%
static/js/pt-BR.js 103.29 kB 0%
static/js/wide.js 102.8 kB 0%
static/js/useAccountPreviewTransactions.js 1.69 kB 0%
static/js/en.js 99.34 kB 0%
static/js/narrow.js 84.94 kB 0%
static/js/ReportRouter.js 1.59 MB 0%
static/js/index.js 4.29 MB 0%

Copy link
Contributor

github-actions bot commented Jan 30, 2025

Bundle Stats — loot-core

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
1 1.33 MB → 1.33 MB (+2.12 kB) +0.16%
Changeset
File Δ Size
packages/loot-core/src/server/budget/template-notes.ts 📈 +714 B (+25.82%) 2.7 kB → 3.4 kB
packages/loot-core/src/server/budget/goalsSchedule.ts 📈 +247 B (+3.23%) 7.47 kB → 7.71 kB
packages/loot-core/src/server/budget/goal-template.pegjs 📉 -787 B (-1.25%) 61.33 kB → 60.56 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
kcab.worker.js 1.33 MB → 1.33 MB (+2.12 kB) +0.16%

Smaller

No assets were smaller

Unchanged

No assets were unchanged

@MattFaz MattFaz changed the title [WIP] Add percentage adjustments to schedule templates (#4098) Add percentage adjustments to schedule templates (#4098) Jan 30, 2025
Copy link
Contributor

coderabbitai bot commented Jan 30, 2025

Walkthrough

The pull request introduces modifications to the budget schedule templates, focusing on enhancing the grammar rules and parsing logic to accommodate percentage-based adjustments for recurring transactions. Key changes include the addition of a new modifiers rule in the grammar, which allows for optional adjustments to be specified alongside schedule names. The parsing logic now captures these adjustments and incorporates them into the scheduleAmount calculation within the createScheduleList function. Additionally, an optional adjustment property has been added to the ScheduleTemplate interface, enabling the representation of these adjustments in the template structure. The overall changes improve the handling of schedule adjustments and ensure robust parsing of names and values.

Possibly related PRs

  • enhance: allow negatives in the budget template #4028: The changes in this PR allow negative values in the budget template, which relates to the modifications in the goal-template.pegjs file in the main PR that enhance parsing rules for amounts, including adjustments for negative values.
  • [Goals] fix how goal values are calculated #3817: This PR addresses the calculation of goal values, which is relevant to the changes in the main PR that involve adjustments and modifiers in schedule templates, impacting how budgeted amounts are derived.
  • 🐛 [Goals]: Fix inconsistent schedule amounts #4265: This PR focuses on fixing inconsistent schedule amounts, which aligns with the main PR's changes that enhance the parsing and handling of schedule adjustments and budget calculations.

Suggested labels

enhancement, budget, feature

Suggested reviewers

  • youngcw
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b09d800 and 131fc6c.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/4257.md is excluded by !**/*.md
📒 Files selected for processing (3)
  • packages/loot-core/src/server/budget/goal-template.pegjs (3 hunks)
  • packages/loot-core/src/server/budget/goalsSchedule.ts (1 hunks)
  • packages/loot-core/src/server/budget/types/templates.d.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/budget/goal-template.pegjs (2)
Learnt from: youngcw
PR: actualbudget/actual#3754
File: packages/loot-core/src/server/budget/goal-template.pegjs:38-43
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/server/budget/goal-template.pegjs`, within the `limit` rule, the optional space `_?` is always placed before the `hold` option, ensuring consistency.
Learnt from: youngcw
PR: actualbudget/actual#3754
File: packages/loot-core/src/server/budget/goal-template.pegjs:38-43
Timestamp: 2024-11-10T16:45:25.627Z
Learning: Validations for limit rules, such as checking start date validity and ensuring amounts are positive, are performed during template processing, not in the parser defined in `goal-template.pegjs`.
🔇 Additional comments (4)
packages/loot-core/src/server/budget/types/templates.d.ts (1)

48-48: LGTM! The type definition aligns with the feature requirements.

The optional adjustment property is correctly typed as number and maintains backward compatibility.

packages/loot-core/src/server/budget/goal-template.pegjs (3)

31-37: LGTM! Clean implementation of the adjustment modifiers.

The implementation correctly:

  • Parses both increase/decrease operations
  • Handles the percentage value
  • Returns the adjustment with the correct sign (+/- based on increase/decrease)

21-22: Add test cases for the schedule rule.

While the implementation looks correct, it would be beneficial to add test cases to verify:

  1. Schedule templates without modifiers still work
  2. Various percentage values (including decimals)
  3. Error cases (invalid modifiers)

Would you like me to help create a comprehensive test suite for the grammar changes?


90-100: Verify the regex pattern for schedule names.

The complex regex pattern for rawScheduleName needs validation to ensure it correctly handles all edge cases:

  1. Names with multiple spaces
  2. Names containing special characters
  3. Names followed by valid/invalid modifiers
✅ Verification successful

Schedule name pattern is correctly implemented and well-tested

The regex pattern for rawScheduleName is verified to handle all required cases:

  • Names with multiple spaces (e.g., "Test Schedule", "Mock Schedule 1")
  • Special characters and numbers
  • Proper handling of [increase]/[decrease] modifiers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Create a test file with various schedule names
cat << 'EOF' > test_cases.txt
Car Insurance [increase 10%]
Rent [decrease 5%]
Complex Name With Spaces [increase 15%]
Name.With.Dots [decrease 7%]
Name-With-Dashes [increase 3%]
Invalid[increase 10%]
Invalid [invalid 10%]
EOF

# Use ast-grep to verify the grammar can parse these cases
ast-grep --pattern 'schedule $name [$op $value%]'

Length of output: 57


Script:

#!/bin/bash
# Search for test files related to goal templates
echo "=== Test Files ==="
fd -e test.js -e test.ts -e spec.js -e spec.ts | rg -i "goal.*template|template.*goal"

echo -e "\n=== Usage in Tests ==="
rg -l "(\[increase|\[decrease)" $(fd -e test.js -e test.ts -e spec.js -e spec.ts)

echo -e "\n=== Documentation ==="
fd -e md | rg -i "goal.*template|template.*goal"

echo -e "\n=== Real Usage Examples ==="
# Look for actual schedule patterns in the codebase
rg "(\[increase|\[decrease)" --type ts --type js -A 1 -B 1

Length of output: 3368


Script:

#!/bin/bash
echo "=== Goal Schedule Tests ==="
cat packages/loot-core/src/server/budget/goalsSchedule.test.ts || true

echo -e "\n=== Template Notes Tests ==="
cat packages/loot-core/src/server/budget/template-notes.test.ts || true

echo -e "\n=== Implementation Files ==="
fd . packages/loot-core/src/server/budget/ -t f --exclude "*.pegjs" --exclude "*.test.ts"

echo -e "\n=== Grammar Usage ==="
rg "rawScheduleName" packages/loot-core/src/server/budget/

Length of output: 12097

packages/loot-core/src/server/budget/goalsSchedule.ts Outdated Show resolved Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 131fc6c and 43d0e0f.

📒 Files selected for processing (1)
  • packages/loot-core/src/server/budget/goalsSchedule.ts (1 hunks)
🧰 Additional context used
🪛 ESLint
packages/loot-core/src/server/budget/goalsSchedule.ts

[error] 41-41: Replace Invalid·adjustment·percentage·for·schedule·${template[ll].name} with ⏎··········Invalid·adjustment·percentage·for·schedule·${template[ll].name},⏎········

(prettier/prettier)

🪛 GitHub Check: lint
packages/loot-core/src/server/budget/goalsSchedule.ts

[failure] 41-41:
Replace Invalid·adjustment·percentage·for·schedule·${template[ll].name} with ⏎··········Invalid·adjustment·percentage·for·schedule·${template[ll].name},⏎········

🪛 GitHub Actions: Test
packages/loot-core/src/server/budget/goalsSchedule.ts

[error] 41-41: Prettier formatting error: Incorrect template literal formatting

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Functional
  • GitHub Check: Visual regression
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (2)
packages/loot-core/src/server/budget/goalsSchedule.ts (2)

32-36: LGTM! Good use of mutable declaration.

The change from const to let is appropriate here as it enables the new adjustment feature while maintaining the existing calculation logic.


38-46: Consider additional edge cases in adjustment calculation.

While the validation and calculation logic is sound, consider these edge cases:

  1. When scheduleAmount is 0, any adjustment will still result in 0
  2. Very small amounts might round to 0 after adjustment

Would you like me to add validation for these edge cases?

🧰 Tools
🪛 ESLint

[error] 41-41: Replace Invalid·adjustment·percentage·for·schedule·${template[ll].name} with ⏎··········Invalid·adjustment·percentage·for·schedule·${template[ll].name},⏎········

(prettier/prettier)

🪛 GitHub Check: lint

[failure] 41-41:
Replace Invalid·adjustment·percentage·for·schedule·${template[ll].name} with ⏎··········Invalid·adjustment·percentage·for·schedule·${template[ll].name},⏎········

🪛 GitHub Actions: Test

[error] 41-41: Prettier formatting error: Incorrect template literal formatting

packages/loot-core/src/server/budget/goalsSchedule.ts Outdated Show resolved Hide resolved
@youngcw
Copy link
Member

youngcw commented Jan 30, 2025

Are the brackets necessary?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/loot-core/src/server/budget/goal-template.pegjs (2)

31-37: Consider adding grammar documentation.

The modifiers implementation is clean and well-structured. Consider adding a comment explaining the syntax and valid ranges for future maintainers.

+// Modifiers allow percentage adjustments to schedule amounts
+// Syntax: [increase X%] or [decrease X%]
+// Valid range: -100% to 1000%
 modifiers = _ '[' modifier:modifier ']' { return modifier }

90-100: Enhance documentation for the complex name parsing logic.

While the implementation is correct, the complex lookahead logic would benefit from more detailed documentation explaining the edge cases it handles.

-// Match schedule name including spaces up until we see a [, looking ahead to make sure it's followed by increase/decrease
+// Parses schedule names that may contain spaces, stopping at modifier brackets.
+// Examples:
+//   "Car Insurance [increase 10%]" -> name: "Car Insurance"
+//   "Home Rent [decrease 5%]" -> name: "Home Rent"
+//   "No Modifier" -> name: "No Modifier"
+// The lookahead ensures spaces are included in the name only if they're not
+// followed by a modifier bracket, preventing incorrect parsing of modifiers.
 rawScheduleName = $(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43d0e0f and 105a2ef.

📒 Files selected for processing (3)
  • packages/loot-core/src/server/budget/goal-template.pegjs (3 hunks)
  • packages/loot-core/src/server/budget/goalsSchedule.ts (1 hunks)
  • packages/loot-core/src/server/budget/template-notes.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/loot-core/src/server/budget/goalsSchedule.ts
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/budget/goal-template.pegjs (2)
Learnt from: youngcw
PR: actualbudget/actual#3754
File: packages/loot-core/src/server/budget/goal-template.pegjs:38-43
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In `packages/loot-core/src/server/budget/goal-template.pegjs`, within the `limit` rule, the optional space `_?` is always placed before the `hold` option, ensuring consistency.
Learnt from: youngcw
PR: actualbudget/actual#3754
File: packages/loot-core/src/server/budget/goal-template.pegjs:38-43
Timestamp: 2024-11-10T16:45:25.627Z
Learning: Validations for limit rules, such as checking start date validity and ensuring amounts are positive, are performed during template processing, not in the parser defined in `goal-template.pegjs`.
🪛 ESLint
packages/loot-core/src/server/budget/template-notes.ts

[error] 46-46: Replace ${name}:·${template.line}${template.error·?·'\nError:·'·+·template.error·:·''} with ⏎··········${name}:·${template.line}${template.error·?·'\nError:·'·+·template.error·:·''},⏎········

(prettier/prettier)


[error] 95-95: Replace parsedTemplate.type·===·'schedule'·&&·parsedTemplate.adjustment·!==·undefined with ⏎··········parsedTemplate.type·===·'schedule'·&&⏎··········parsedTemplate.adjustment·!==·undefined⏎········

(prettier/prettier)


[error] 96-96: Replace parsedTemplate.adjustment·<=·-100·||·parsedTemplate.adjustment·>·1000 with ⏎············parsedTemplate.adjustment·<=·-100·||⏎············parsedTemplate.adjustment·>·1000⏎··········

(prettier/prettier)


[error] 97-97: Replace Invalid·adjustment·percentage·(${parsedTemplate.adjustment}%).·Must·be·between·-100%·and·1000% with ⏎··············Invalid·adjustment·percentage·(${parsedTemplate.adjustment}%).·Must·be·between·-100%·and·1000%,⏎············

(prettier/prettier)


[error] 124-124: Insert

(prettier/prettier)

🪛 GitHub Check: lint
packages/loot-core/src/server/budget/template-notes.ts

[failure] 46-46:
Replace ${name}:·${template.line}${template.error·?·'\nError:·'·+·template.error·:·''} with ⏎··········${name}:·${template.line}${template.error·?·'\nError:·'·+·template.error·:·''},⏎········


[failure] 95-95:
Replace parsedTemplate.type·===·'schedule'·&&·parsedTemplate.adjustment·!==·undefined with ⏎··········parsedTemplate.type·===·'schedule'·&&⏎··········parsedTemplate.adjustment·!==·undefined⏎········


[failure] 96-96:
Replace parsedTemplate.adjustment·<=·-100·||·parsedTemplate.adjustment·>·1000 with ⏎············parsedTemplate.adjustment·<=·-100·||⏎············parsedTemplate.adjustment·>·1000⏎··········


[failure] 97-97:
Replace Invalid·adjustment·percentage·(${parsedTemplate.adjustment}%).·Must·be·between·-100%·and·1000% with ⏎··············Invalid·adjustment·percentage·(${parsedTemplate.adjustment}%).·Must·be·between·-100%·and·1000%,⏎············


[failure] 124-124:
Insert

🪛 GitHub Actions: Test
packages/loot-core/src/server/budget/template-notes.ts

[error] 46-46: Prettier formatting error: Incorrect template literal formatting

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (ubuntu-latest)
🔇 Additional comments (2)
packages/loot-core/src/server/budget/goal-template.pegjs (2)

21-22: LGTM! Well-structured schedule rule update.

The schedule rule has been cleanly extended to support modifiers while maintaining backward compatibility.


76-77: LGTM! Clear whitespace handling.

The distinction between optional and mandatory whitespace improves the grammar's robustness.

packages/loot-core/src/server/budget/template-notes.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/budget/template-notes.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/budget/template-notes.ts Outdated Show resolved Hide resolved
@MattFaz MattFaz changed the title Add percentage adjustments to schedule templates (#4098) [WIP] Add percentage adjustments to schedule templates (#4098) Jan 30, 2025
@MattFaz
Copy link
Author

MattFaz commented Jan 30, 2025

Are the brackets necessary?

It could be done without the square brackets however they are helpful for a few reasons:

  • The brackets help make the parsing unambiguous, the parser can clearly look for [indecrease/decrease X%] after the schedule name
  • It provides a clear visual separation between schedule name and adjustment modifier (particularly important because schedule names can contain spaces)
  • Bracket syntax could potentially be used in the future for other modifiers without changing the base template structure

I could look at removing the bracket syntax however it would make it more challenging to determine where the schedule name ends and the modifier begins.

@MattFaz MattFaz changed the title [WIP] Add percentage adjustments to schedule templates (#4098) Add percentage adjustments to schedule templates (#4098) Jan 30, 2025
@MattFaz MattFaz requested a review from youngcw January 30, 2025 23:27
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/loot-core/src/server/budget/template-notes.test.ts (1)

230-261: Add test cases for valid adjustment scenarios.

The error cases are well covered, but consider adding test cases for:

  • Valid adjustments at boundary values (-100%, 1000%)
  • Valid adjustments within range (e.g., [increase 50%], [decrease 25%])

Here's a suggested test case structure:

     },
+    {
+      description: 'Accepts valid schedule adjustments at boundaries',
+      mockTemplateNotes: [
+        {
+          id: 'cat1',
+          name: 'Category 1',
+          note: '#template schedule Mock Schedule 1 [increase 1000%]',
+        },
+        {
+          id: 'cat2',
+          name: 'Category 2',
+          note: '#template schedule Mock Schedule 1 [decrease 100%]',
+        },
+      ],
+      mockSchedules: mockSchedules(),
+      expected: {
+        type: 'message',
+        message: 'All templates passed! 🎉',
+      },
+    },
+    {
+      description: 'Accepts valid schedule adjustments within range',
+      mockTemplateNotes: [
+        {
+          id: 'cat1',
+          name: 'Category 1',
+          note: '#template schedule Mock Schedule 1 [increase 50%]',
+        },
+        {
+          id: 'cat2',
+          name: 'Category 2',
+          note: '#template schedule Mock Schedule 1 [decrease 25%]',
+        },
+      ],
+      mockSchedules: mockSchedules(),
+      expected: {
+        type: 'message',
+        message: 'All templates passed! 🎉',
+      },
+    },
packages/loot-core/src/server/budget/template-notes.ts (2)

46-51: Improve error handling robustness.

Consider these enhancements:

  1. Use optional chaining for safer error checking
  2. Make the adjustment error check more explicit
-        if (template.error && template.error.includes('adjustment')) {
-          errors.push(`${name}: ${template.line}\nError: ${template.error}`);
+        if (template.error?.includes('Invalid adjustment percentage')) {
+          errors.push(`${name}: ${template.line}\nError: ${template.error}`);
🧰 Tools
🪛 Biome (1.9.4)

[error] 47-47: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


99-113: Extract validation logic to a separate function.

Consider moving the validation logic to a dedicated function and using constants for the bounds.

+const MIN_ADJUSTMENT_PERCENTAGE = -100;
+const MAX_ADJUSTMENT_PERCENTAGE = 1000;
+
+function validateScheduleAdjustment(adjustment: number): void {
+  if (adjustment <= MIN_ADJUSTMENT_PERCENTAGE || adjustment > MAX_ADJUSTMENT_PERCENTAGE) {
+    throw new Error(
+      `Invalid adjustment percentage (${adjustment}%). Must be between ${MIN_ADJUSTMENT_PERCENTAGE}% and ${MAX_ADJUSTMENT_PERCENTAGE}%`,
+    );
+  }
+}

 async function getCategoriesWithTemplates(): Promise<CategoryWithTemplates[]> {
   // ...
   try {
     const parsedTemplate: Template = parse(trimmedLine);
-    if (
-      parsedTemplate.type === 'schedule' &&
-      parsedTemplate.adjustment !== undefined
-    ) {
-      if (
-        parsedTemplate.adjustment <= -100 ||
-        parsedTemplate.adjustment > 1000
-      ) {
-        throw new Error(
-          `Invalid adjustment percentage (${parsedTemplate.adjustment}%). Must be between -100% and 1000%`,
-        );
-      }
+    if (parsedTemplate.type === 'schedule' && parsedTemplate.adjustment !== undefined) {
+      validateScheduleAdjustment(parsedTemplate.adjustment);
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 105a2ef and 58a64c1.

📒 Files selected for processing (2)
  • packages/loot-core/src/server/budget/template-notes.test.ts (1 hunks)
  • packages/loot-core/src/server/budget/template-notes.ts (2 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/loot-core/src/server/budget/template-notes.ts

[error] 47-47: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

@youngcw
Copy link
Member

youngcw commented Jan 31, 2025

Are the brackets necessary?

It could be done without the square brackets however they are helpful for a few reasons:

  • The brackets help make the parsing unambiguous, the parser can clearly look for [indecrease/decrease X%] after the schedule name
  • It provides a clear visual separation between schedule name and adjustment modifier (particularly important because schedule names can contain spaces)
  • Bracket syntax could potentially be used in the future for other modifiers without changing the base template structure

I could look at removing the bracket syntax however it would make it more challenging to determine where the schedule name ends and the modifier begins.

Mostly I would like to avoid special syntax that is unique to just one kind of template outside of the semi-human readable word style. Its not the end of the world if you aren't able to do it without the brackets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants