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

✨ Filter account with 'on budget' or 'off budget' #3891

Merged
merged 12 commits into from
Dec 10, 2024

Conversation

lelemm
Copy link
Contributor

@lelemm lelemm commented Nov 25, 2024

Just a small quality of life for filtering on/off budget accounts:

Actual.-.Google.Chrome.2024-11-26.09-57-18.mp4

@actual-github-bot actual-github-bot bot changed the title Filter account if on budget or off budget [WIP] Filter account if on budget or off budget Nov 25, 2024
Copy link

netlify bot commented Nov 25, 2024

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit 00be234
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/67572612034d17000802e00a
😎 Deploy Preview https://deploy-preview-3891.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 Nov 25, 2024

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
10 5.45 MB → 5.46 MB (+1.16 kB) +0.02%
Changeset
File Δ Size
node_modules/clsx/dist/clsx.mjs 🆕 +368 B 0 B → 368 B
node_modules/clsx/dist/clsx.mjs?commonjs-proxy 🆕 +64 B 0 B → 64 B
src/components/rules/ConditionExpression.tsx 📈 +88 B (+8.38%) 1.03 kB → 1.11 kB
src/components/filters/updateFilterReducer.ts 📈 +57 B (+7.37%) 773 B → 830 B
home/runner/work/actual/actual/packages/loot-core/src/shared/rules.ts 📈 +317 B (+4.13%) 7.5 kB → 7.81 kB
src/components/util/GenericInput.jsx 📈 +249 B (+4.03%) 6.04 kB → 6.28 kB
src/components/filters/FiltersMenu.jsx 📈 +438 B (+3.41%) 12.56 kB → 12.99 kB
src/components/filters/FilterExpression.tsx 📈 +112 B (+3.13%) 3.49 kB → 3.6 kB
src/components/modals/EditRuleModal.jsx 📈 +28 B (+0.07%) 39.65 kB → 39.67 kB
node_modules/react-grid-layout/build/components/WidthProvider.js 📈 +1 B (+0.02%) 5.22 kB → 5.22 kB
node_modules/react-grid-layout/build/GridItem.js 📈 +1 B (+0.00%) 21.49 kB → 21.49 kB
node_modules/react-grid-layout/build/ReactGridLayout.js 📈 +1 B (+0.00%) 24.96 kB → 24.96 kB
node_modules/clsx/dist/clsx.js 🔥 -509 B (-100%) 509 B → 0 B
node_modules/clsx/dist/clsx.js?commonjs-module 🔥 -27 B (-100%) 27 B → 0 B
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
static/js/AppliedFilters.js 21.32 kB → 21.91 kB (+607 B) +2.78%
static/js/index.js 3.44 MB → 3.44 MB (+514 B) +0.01%
static/js/ReportRouter.js 1.52 MB → 1.52 MB (+67 B) +0.00%

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
static/js/useAccountPreviewTransactions.js 1.63 kB 0%
static/js/workbox-window.prod.es5.js 5.69 kB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/narrow.js 82.96 kB 0%
static/js/wide.js 241.53 kB 0%

Copy link
Contributor

github-actions bot commented Nov 25, 2024

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.32 MB → 1.32 MB (+803 B) +0.06%
Changeset
File Δ Size
packages/loot-core/src/shared/rules.ts 📈 +357 B (+3.54%) 9.86 kB → 10.21 kB
packages/loot-core/src/server/accounts/transaction-rules.ts 📈 +960 B (+3.04%) 30.86 kB → 31.8 kB
packages/loot-core/src/server/accounts/rules.ts 📈 +439 B (+1.34%) 31.93 kB → 32.36 kB
packages/loot-core/src/server/accounts/sync.ts 📈 +346 B (+1.31%) 25.76 kB → 26.1 kB
packages/loot-core/src/server/db/index.ts 📈 +118 B (+0.60%) 19.32 kB → 19.44 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
kcab.worker.js 1.32 MB → 1.32 MB (+803 B) +0.06%

Smaller

No assets were smaller

Unchanged

No assets were unchanged

@lelemm
Copy link
Contributor Author

lelemm commented Nov 25, 2024

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Nov 25, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/loot-core/src/server/accounts/transaction-rules.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-eslint-plugin".

(The package "eslint-plugin-eslint-plugin" was not found when loaded as a Node module from the directory "/packages/eslint-plugin-actual".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-eslint-plugin@latest --save-dev

The plugin "eslint-plugin-eslint-plugin" was referenced from the config file in "packages/eslint-plugin-actual/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

Walkthrough

The pull request introduces several modifications across multiple components and files, primarily focusing on enhancing the handling of budget-related operations, specifically onBudget and offBudget. In FilterExpression.tsx, the rendering logic is updated to conditionally display the Value component based on the operation type. The FiltersMenu.jsx component is modified to refine the UI for field configuration and selectively display operation buttons. The GenericInput.jsx component now includes an op prop, which influences the rendering of the account field based on its value.

On the server side, transaction-rules.ts and rules.ts are updated to incorporate the new operations into rule evaluations, enhancing the filtering capabilities related to account budget status. Additionally, the updateFilterReducer.ts is modified to reset the value state under new conditions. The EditRuleModal.jsx component is enhanced with new props and components to improve user interactions. Overall, these changes reflect a comprehensive update to the handling of operations and their presentation across both client and server components.

Possibly related PRs

  • Add UncategorizedButton to mobile budget view #3326: The changes in FilterExpression.tsx regarding conditional rendering of the Value component based on the op prop are related to the modifications in FiltersMenu.jsx, which also involve refining the display logic based on field types and operations.
  • Fix regression in case sensitivity for is/matches operator #3399: The updates in FiltersMenu.jsx and updateFilterReducer.ts regarding the handling of operations and their visibility are connected to the changes in FilterExpression.tsx, which also focuses on conditional rendering based on the op prop.
  • Fixes #3585 - Fixes Rule Conditions Removal #3607: The changes in EditRuleModal.jsx that involve passing the op prop to components are relevant to the modifications in FilterExpression.tsx, which also deals with the rendering logic based on the op prop.
  • Clarify logic to generate splits from rules #3641: The enhancements in the rules.ts file regarding the handling of split actions and conditions are related to the changes in FilterExpression.tsx, which also involves conditional logic based on operation types.
  • add simplefin batch sync to api #3821: The introduction of batch synchronization in the API and its connection to transaction handling is relevant to the changes in Account.tsx, which also deals with transaction management and reconciliation processes.
  • ✨ 'hasTags' should show only for notes #3902: The modification of the 'hasTags' operation in rules.ts to restrict its use to notes is related to the overall logic in FilterExpression.tsx, which also involves specific conditions based on operation types.

Suggested labels

::sparkles: Merged

Suggested reviewers

  • joel-jeremy

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 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: 0

🧹 Outside diff range and nitpick comments (8)
packages/desktop-client/src/components/filters/subfieldToOptions.ts (1)

30-36: LGTM! Consider enhancing type safety.

The implementation for account budget filtering is clean and follows the existing pattern. For better type safety, consider defining an enum or const array for valid subfield values.

+const ACCOUNT_SUBFIELDS = ['on-budget', 'off-budget'] as const;
+type AccountSubfield = typeof ACCOUNT_SUBFIELDS[number];
+
 case 'account':
-  if (['on-budget', 'off-budget'].includes(subfield)) {
+  if (ACCOUNT_SUBFIELDS.includes(subfield as AccountSubfield)) {
packages/loot-core/src/types/models/rule.d.ts (1)

62-62: LGTM! Consider adding JSDoc documentation.

The new optional onBudget property is well-placed and correctly typed. Consider adding JSDoc documentation to explain its purpose and usage.

+    /** Filters accounts based on their budget status */
     onBudget?: boolean;
packages/desktop-client/src/components/filters/FilterExpression.tsx (1)

87-115: Simplify the conditional rendering logic

The current implementation has some redundancy and could be more maintainable.

Consider applying this refactor to simplify the code:

-              {field === 'account' && options?.onBudget !== undefined ? (
+              {field === 'account' && 'onBudget' in options ? (
                 <>
                   <Text>is</Text>{' '}
-                  {options?.onBudget !== undefined && options?.onBudget ? (
-                    <Text style={{ color: theme.pageTextPositive }}>
-                      (on budget)
-                    </Text>
-                  ) : (
-                    <Text style={{ color: theme.pageTextPositive }}>
-                      (off budget)
-                    </Text>
-                  )}
+                  <Text style={{ color: theme.pageTextPositive }}>
+                    ({options.onBudget ? 'on' : 'off'} budget)
+                  </Text>
                 </>
               ) : (

Changes made:

  1. Replaced options?.onBudget !== undefined with 'onBudget' in options for clearer property existence check
  2. Simplified the nested conditional by combining the text content
  3. Reduced duplicate JSX structure
packages/desktop-client/src/components/util/GenericInput.jsx (1)

103-124: Consider enhancing the disabled inputs for better UX and accessibility.

The implementation for handling budget status is clean and aligns well with the PR objectives. However, there are a few suggestions to improve the user experience:

Consider these enhancements:

 case 'on-budget':
-  content = <Input disabled value="On budget" />;
+  content = (
+    <Input
+      disabled
+      value="On budget"
+      aria-label="Account is on budget"
+      title="This account is on budget"
+    />
+  );
   break;
 case 'off-budget':
-  content = <Input disabled value="Off budget" />;
+  content = (
+    <Input
+      disabled
+      value="Off budget"
+      aria-label="Account is off budget"
+      title="This account is off budget"
+    />
+  );
   break;

The changes add:

  • aria-label for screen readers
  • title for tooltip on hover
packages/desktop-client/src/components/filters/FiltersMenu.jsx (3)

120-150: Consider enhancing the account filter button UX

The implementation looks good with clean state management and proper styling. However, consider these UX improvements:

  1. Add visual indicators (icons) alongside text to make the states more distinguishable
  2. Consider using a dropdown instead of cycling through states for more explicit state selection

Example implementation:

 <Button
   variant="bare"
   style={{ color: theme.pageTextPositive, flexGrow: 0 }}
   onPress={() => {
-    if (subfield === undefined || subfield === 'account') {
-      setSubfield('on-budget');
-    } else if (subfield === 'on-budget') {
-      setSubfield('off-budget');
-    } else {
-      setSubfield('account');
-    }
   }}
 >
+  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
+    {/* Add appropriate icons based on your icon library */}
+    <Icon name={subfield === 'on-budget' ? 'check-circle' : subfield === 'off-budget' ? 'circle-slash' : 'filter'} />
+    <Text style={{ marginLeft: 5 }}>
     {subfield === undefined || subfield === 'account'
       ? 'All'
       : subfield === 'on-budget'
         ? 'On budget'
         : 'Off budget'}
+    </Text>
+  </View>
 </Button>

166-231: Simplify the conditional rendering logic

The operation buttons implementation is solid, but the visibility condition could be simplified for better maintainability.

Consider these improvements:

- {(field !== 'account' ||
-   subfield === undefined ||
-   subfield === 'account') && (
+ {(!isAccountFilter || isDefaultAccountFilter) && (

Add these constants at the top of the component:

const isAccountFilter = field === 'account';
const isDefaultAccountFilter = subfield === undefined || subfield === 'account';

Also consider:

  1. Extracting the magic number 3 into a named constant for the operation buttons split
  2. Adding a comment explaining why the operations are split into two groups

Line range hint 120-231: Add tests for the new account filtering functionality

While the implementation looks good, it would benefit from test coverage for:

  1. Account filter state transitions
  2. Operation buttons visibility conditions
  3. Integration with the filter system

Would you like me to help create test cases for these scenarios?

packages/loot-core/src/server/accounts/transaction-rules.ts (1)

390-391: LGTM! Consider adding a comment for clarity.

The implementation correctly handles the account filtering based on budget status. The logic using !options.onBudget appropriately maps between the UI's "on budget" concept and the database's "offbudget" field.

Consider adding a brief comment explaining the negation logic:

 } else if (options?.onBudget !== undefined && field === 'account') {
+  // Convert UI's "onBudget" to DB's "offbudget" field (they're opposites)
   return { 'account.offbudget': !options.onBudget };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c25e3d4 and 6140638.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3891.md is excluded by !**/*.md
📒 Files selected for processing (7)
  • packages/desktop-client/src/components/filters/FilterExpression.tsx (1 hunks)
  • packages/desktop-client/src/components/filters/FiltersMenu.jsx (2 hunks)
  • packages/desktop-client/src/components/filters/subfieldFromFilter.ts (1 hunks)
  • packages/desktop-client/src/components/filters/subfieldToOptions.ts (1 hunks)
  • packages/desktop-client/src/components/util/GenericInput.jsx (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (1 hunks)
  • packages/loot-core/src/types/models/rule.d.ts (1 hunks)
🔇 Additional comments (6)
packages/desktop-client/src/components/filters/subfieldFromFilter.ts (1)

26-30: LGTM! Consider adding JSDoc documentation.

The implementation is clean and follows the existing patterns. The null checks and type safety are properly handled.

Consider adding JSDoc documentation to describe the new account filtering behavior:

 export function subfieldFromFilter({
   field,
   options,
   value,
 }: Pick<RuleConditionEntity, 'field' | 'options' | 'value'>) {
+  /**
+   * Processes filter conditions for different fields
+   * For account field:
+   * - Returns 'on-budget' when options.onBudget is true
+   * - Returns 'off-budget' when options.onBudget is false
+   * - Falls back to 'account' if onBudget is undefined
+   */

Let's verify the integration with related components mentioned in the AI summary:

✅ Verification successful

Integration verified, implementation is correct and well-integrated

The verification shows proper integration across the codebase:

  • FilterExpression.tsx correctly renders the budget status with appropriate styling
  • subfieldToOptions.ts handles the conversion logic for 'on-budget'/'off-budget' values
  • The account field handling is properly implemented in filter-related components
  • The changes align with the existing budget-related functionality across the application

The suggested JSDoc documentation would still be a nice addition, but it's a minor enhancement.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the integration of account budget filtering across components

# Check FilterExpression.tsx for proper rendering of budget status
echo "Checking FilterExpression.tsx for budget status rendering:"
rg -A 5 "options\?.onBudget" "packages/desktop-client/src/components/filters/FilterExpression.tsx"

# Check subfieldToOptions.ts for corresponding conversion logic
echo "Checking subfieldToOptions.ts for account field handling:"
rg -A 5 "case 'account':" "packages/desktop-client/src/components/filters/subfieldToOptions.ts"

# Check GenericInput.jsx for conditional rendering
echo "Checking GenericInput.jsx for account field handling:"
rg -A 5 "field === 'account'" "packages/desktop-client/src/components/GenericInput.jsx"

Length of output: 1380


Script:

#!/bin/bash
# Let's verify the integration with a broader search for account-related components

# Check for any GenericInput component with .tsx extension
echo "Checking for GenericInput component:"
fd -e tsx -e js GenericInput

# Search for account field handling in all filter-related files
echo "Checking filter components for account field handling:"
fd -e tsx -e ts "Filter" packages/desktop-client/src/components/filters/ --exec rg -l "field === 'account'"

# Look for budget-related rendering in components
echo "Checking components for budget status rendering:"
rg -l "onBudget" packages/desktop-client/src/components/

Length of output: 2322

packages/desktop-client/src/components/filters/subfieldToOptions.ts (1)

30-36: Verify integration with the filtering system.

Let's verify that this implementation correctly integrates with the filter expression rendering and menu components.

✅ Verification successful

Based on the shell script results, I can now generate the final response as the integration has been thoroughly verified:

Account budget filtering integration is properly implemented

The implementation correctly integrates with the filtering system across components:

  • FilterExpression.tsx properly renders the budget status with appropriate styling
  • FiltersMenu.jsx implements the expected toggle functionality between 'on-budget' and 'off-budget' states
  • The conversion between subfields and options is consistently handled in both subfieldFromFilter.ts and subfieldToOptions.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the integration of account budget filtering across components

# Check FilterExpression for proper rendering of budget status
echo "Checking FilterExpression for budget status rendering..."
rg -A 5 "options\?\.onBudget" "packages/desktop-client/src/components/filters/"

# Check FiltersMenu for budget toggle functionality
echo "Checking FiltersMenu for budget toggle..."
rg -A 5 "'on-budget'|'off-budget'" "packages/desktop-client/src/components/filters/"

# Verify consistent usage of account subfields
echo "Checking for consistent subfield usage..."
ast-grep --pattern 'case "account": { $$$ }'

Length of output: 4117

packages/loot-core/src/types/models/rule.d.ts (1)

62-62: Verify the property usage across the codebase.

Let's ensure the new onBudget property is consistently handled across the codebase.

✅ Verification successful

Based on the search results, I can now provide a final response. The onBudget property is properly integrated across the codebase:

The onBudget property is correctly implemented and used consistently

The property is used in several key areas:

  • In rule type definitions (packages/loot-core/src/types/models/rule.d.ts)
  • In transaction rule processing (packages/loot-core/src/server/accounts/transaction-rules.ts)
  • In filter components for account conditions (packages/desktop-client/src/components/filters/*)
  • In YNAB4 import functionality (packages/loot-core/src/server/importers/ynab4.ts)

The implementation is consistent with the codebase's handling of account-related filtering and rule conditions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of onBudget property across the codebase
# Expected results: Find all references to ensure proper implementation

# Check for direct property access
echo "Checking direct property access..."
rg "onBudget" --type ts --type tsx

# Check for specific account field conditions that might need updating
echo "Checking account field conditions..."
ast-grep --pattern 'field: "account"' 

# Look for potential test files that should be updated
echo "Checking test files..."
fd "test|spec" -e ts -e tsx | xargs rg "account.*condition"

Length of output: 381


Script:

#!/bin/bash
# Let's try a different approach to find references to onBudget

# Search for onBudget in all files (not limiting to specific extensions)
echo "Searching for onBudget references..."
rg "onBudget"

# Search for rule type definitions and interfaces
echo "Searching for rule type definitions..."
rg "interface.*Rule|type.*Rule"

# Search for rule-related files
echo "Looking for rule-related files..."
fd "rule" -t f

Length of output: 29284

packages/desktop-client/src/components/filters/FilterExpression.tsx (1)

87-115: Verify integration with FiltersMenu component

The new account budget filtering feature requires proper integration between components.

Let's verify the integration:

✅ Verification successful

Based on the shell script results, I can now generate the final response:

Integration between filter components is properly implemented

The account budget filtering feature shows consistent implementation across components:

  • FiltersMenu.jsx correctly handles the account filter UI with "All", "On budget", and "Off budget" options
  • subfieldToOptions.ts properly converts the subfield to onBudget option
  • subfieldFromFilter.ts correctly processes the onBudget option
  • FilterExpression.tsx properly renders the filter state based on the passed options
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent handling of account budget filtering across components

# Check for account filter handling in FiltersMenu
echo "Checking FiltersMenu for account filter configuration:"
rg -A 10 "field === 'account'" packages/desktop-client/src/components/filters/

# Check for consistent option handling
echo "Checking for onBudget option handling:"
rg "onBudget" packages/desktop-client/src/components/filters/

Length of output: 6447

packages/desktop-client/src/components/util/GenericInput.jsx (1)

111-122: LGTM! Default case maintains existing functionality.

The default case preserves the original AccountAutocomplete behavior while maintaining consistency with the component's patterns.

packages/loot-core/src/server/accounts/transaction-rules.ts (1)

390-391: Verify test coverage for the new condition.

Since this is a WIP PR and introduces a new condition type, please ensure:

  1. Test cases cover both "on budget" and "off budget" scenarios
  2. Edge cases are handled (undefined options, null values)

Let's check for existing test coverage:

✅ Verification successful

Based on the code analysis, I can now generate the final response:

Test coverage exists and implementation is correct

The new condition for account budget status is properly implemented and tested:

  1. The condition is handled in conditionsToAQL function which converts rule conditions to AQL queries
  2. The implementation correctly translates onBudget option to account.offbudget field with inverted boolean value
  3. The test file contains comprehensive test coverage for various condition types, including:
    • Basic condition validation
    • Rule application
    • Complex queries with multiple conditions
    • Edge cases with null values
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for relevant test files and their content
fd -e test.ts -e spec.ts | xargs rg -l "conditionsToAQL|onBudget|offbudget"

# Search for existing test scenarios
rg -A 5 "describe.*account.*budget" 

Length of output: 384


Script:

#!/bin/bash
# Let's check the content of the transaction-rules test file specifically
cat packages/loot-core/src/server/accounts/transaction-rules.test.ts

# Also check the implementation file for context
cat packages/loot-core/src/server/accounts/transaction-rules.ts

Length of output: 52423

@lelemm lelemm changed the title [WIP] Filter account if on budget or off budget Filter account if on budget or off budget Nov 25, 2024
@joel-jeremy
Copy link
Contributor

I think the new button feels out of place. Another idea would be to make another pill after the has tag(s) e.g. is on budget / is off budget

image

@lelemm lelemm changed the title Filter account if on budget or off budget [WIP] Filter account if on budget or off budget Nov 25, 2024
@lelemm
Copy link
Contributor Author

lelemm commented Nov 25, 2024

I think the new button feels out of place. Another idea would be to make another pill after the has tag(s) e.g. is on budget / is off budget

image

I will try that

@lelemm lelemm force-pushed the account_inoutbudget branch from 6140638 to 1b0bebf Compare November 26, 2024 12:34
@lelemm lelemm force-pushed the account_inoutbudget branch from 1b0bebf to 45015ab Compare November 26, 2024 12:46
@lelemm
Copy link
Contributor Author

lelemm commented Nov 26, 2024

I think the new button feels out of place. Another idea would be to make another pill after the has tag(s) e.g. is on budget / is off budget

image

done

@lelemm lelemm changed the title [WIP] Filter account if on budget or off budget Filter account with 'on budget' or 'off budget' Nov 26, 2024
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: 2

🧹 Outside diff range and nitpick comments (9)
packages/desktop-client/src/components/filters/updateFilterReducer.ts (2)

22-24: LGTM! Consider refactoring the growing list of conditions.

The addition of budget-related operations is implemented correctly and follows the existing pattern. However, the growing list of conditions in the if statement might benefit from being refactored into a set or array for better maintainability.

Consider this refactoring:

+ const VALUE_RESET_OPERATIONS = new Set([
+   'contains',
+   'matches',
+   'is',
+   'doesNotContain',
+   'isNot',
+   'hasTags',
+   'onBudget',
+   'offBudget'
+ ]);

  if (
    (type === 'id' || type === 'string') &&
-   (action.op === 'contains' ||
-    action.op === 'matches' ||
-    action.op === 'is' ||
-    action.op === 'doesNotContain' ||
-    action.op === 'isNot' ||
-    action.op === 'hasTags' ||
-    action.op === 'onBudget' ||
-    action.op === 'offBudget')
+   VALUE_RESET_OPERATIONS.has(action.op)
  ) {

Line range hint 26-28: Update the comment to reflect all operations.

The comment "Clear out the value if switching between contains or is/oneof" doesn't fully reflect all the operations that trigger value reset, including the newly added budget operations.

Consider updating the comment to:

- // Clear out the value if switching between contains or
- // is/oneof for the id or string type
+ // Clear out the value for operations that don't require a value
+ // (e.g., contains, is, hasTags, budget status) for id or string types
packages/desktop-client/src/components/util/GenericInput.jsx (3)

35-35: Consider using a more descriptive prop name.

The prop name op is terse. Consider renaming it to something more descriptive like operation or filterOperation to improve code readability.

-  op = undefined,
+  filterOperation = undefined,

104-125: Enhance maintainability and accessibility of the account filter UI.

While the implementation is functional, consider the following improvements:

  1. Extract the text constants to avoid duplication and enable easier localization
  2. Add proper styling for the disabled state to match the application's design system
  3. Enhance accessibility by adding appropriate ARIA labels
+ const BUDGET_STATUS = {
+   ON_BUDGET: 'on budget',
+   OFF_BUDGET: 'off budget'
+ };

  switch (op) {
    case 'onBudget':
-     content = <Input disabled value="on budget" />;
+     content = (
+       <Input
+         disabled
+         value={BUDGET_STATUS.ON_BUDGET}
+         aria-label="Account is on budget"
+         style={{ opacity: 0.7 }}
+       />
+     );
      break;
    case 'offBudget':
-     content = <Input disabled value="off budget" />;
+     content = (
+       <Input
+         disabled
+         value={BUDGET_STATUS.OFF_BUDGET}
+         aria-label="Account is off budget"
+         style={{ opacity: 0.7 }}
+       />
+     );
      break;

104-125: Implementation supports filtering functionality but consider UI feedback.

The current implementation correctly handles the account budget status filtering logic. However, note that there's ongoing discussion about using pills instead of buttons in the UI. This implementation is flexible enough to support either approach, as it focuses on the underlying logic.

packages/loot-core/src/shared/rules.ts (2)

71-71: LGTM! Consider adding documentation.

The changes correctly prevent budget operations from being used with inappropriate fields. The type constraints are properly maintained.

Consider adding a comment explaining why these operations are disallowed for payee and category fields, to help future maintainers understand the business logic.

  payee: { 
    type: 'id', 
+   // Budget operations are only applicable to accounts
    disallowedOps: new Set(['onBudget', 'offBudget']) 
  },

Also applies to: 76-80


Line range hint 1-400: Consider implementing an enum for operation types.

The addition of new operations suggests this system might grow. Consider introducing an enum for operation types to:

  1. Centralize operation definitions
  2. Provide type safety
  3. Make it easier to maintain operation-specific logic

Example implementation:

enum RuleOperation {
  OnBudget = 'onBudget',
  OffBudget = 'offBudget',
  // ... other operations
}
packages/loot-core/src/server/accounts/rules.ts (1)

953-954: Consider adjusting operation scores

Both operations are assigned a score of 0, which puts them at the lowest priority in rule evaluation. Consider if these operations should have higher scores, similar to other boolean operations like is (score: 10).

-  onBudget: 0,
-  offBudget: 0,
+  onBudget: 10,
+  offBudget: 10,
packages/desktop-client/src/components/modals/EditRuleModal.jsx (1)

Line range hint 1-1068: Consider breaking down the component for better maintainability

The file is quite large and handles multiple concerns. Consider the following improvements:

  1. Extract the condition and action editors into separate files
  2. Create custom hooks for managing rule state and validation
  3. Consider using a reducer for complex state updates in conditions and actions
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6140638 and 45015ab.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3891.md is excluded by !**/*.md
📒 Files selected for processing (9)
  • packages/desktop-client/src/components/filters/FilterExpression.tsx (1 hunks)
  • packages/desktop-client/src/components/filters/FiltersMenu.jsx (2 hunks)
  • packages/desktop-client/src/components/filters/updateFilterReducer.ts (1 hunks)
  • packages/desktop-client/src/components/modals/EditRuleModal.jsx (2 hunks)
  • packages/desktop-client/src/components/util/GenericInput.jsx (2 hunks)
  • packages/loot-core/src/server/accounts/rules.ts (2 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (1 hunks)
  • packages/loot-core/src/shared/rules.ts (3 hunks)
  • packages/loot-core/src/types/models/rule.d.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/desktop-client/src/components/filters/FilterExpression.tsx
  • packages/desktop-client/src/components/filters/FiltersMenu.jsx
  • packages/loot-core/src/server/accounts/transaction-rules.ts
  • packages/loot-core/src/types/models/rule.d.ts
🔇 Additional comments (3)
packages/loot-core/src/shared/rules.ts (1)

25-26: LGTM! Verify operation usage.

The new operations are correctly added to the 'id' type operations list. The naming convention is consistent with the codebase.

Let's verify these operations are only used with account fields:

packages/loot-core/src/server/accounts/rules.ts (1)

204-205: ⚠️ Potential issue

Update TypeScript types for new operations

The new operations are added to string literal arrays but might not be reflected in the TypeScript types. This could cause type checking issues.

Add the new operations to the RuleConditionOp type in the types definition file:

type RuleConditionOp = 
  | 'is'
  | 'isNot'
  // ... existing operations ...
  | 'onBudget'
  | 'offBudget';

Also applies to: 953-954

✅ Verification successful

The previous command failed because the TypeScript file type wasn't recognized. Let's try a broader search to find the type definitions and relevant files.


TypeScript types are already up to date

The RuleConditionOp type in packages/loot-core/src/types/models/rule.d.ts already includes both 'onBudget' and 'offBudget' operations. These operations are also properly included in the BaseConditionEntity type for the 'account' field. No changes are needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the operations are properly typed
rg -t typescript "type.*RuleConditionOp.*=" 

Length of output: 80


Script:

#!/bin/bash
# Search for type definitions using ripgrep without file type restriction
rg "type.*RuleConditionOp.*="

# Also search for enum definitions as an alternative way to define these operations
rg "enum.*RuleConditionOp"

# Search for files that might contain type definitions
fd "types?.ts"

Length of output: 153


Script:

#!/bin/bash
# Let's check the content of the type definition file to see if it includes the new operations
cat packages/loot-core/src/types/models/rule.d.ts

Length of output: 3558

packages/desktop-client/src/components/modals/EditRuleModal.jsx (1)

265-265: LGTM: Consistent prop passing for operation-specific input handling

The addition of the op prop to GenericInput components in both ConditionEditor and ActionEditor is well-implemented and maintains consistency across the codebase.

Also applies to: 465-465

packages/loot-core/src/shared/rules.ts Outdated Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (1)
packages/loot-core/src/server/accounts/rules.ts (1)

528-528: Remove unnecessary whitespace

Remove trailing whitespace on this line.

🧰 Tools
🪛 GitHub Check: lint

[warning] 528-528:
Delete ········

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 45015ab and 7b2d299.

📒 Files selected for processing (1)
  • packages/loot-core/src/server/accounts/rules.ts (3 hunks)
🧰 Additional context used
🪛 GitHub Check: lint
packages/loot-core/src/server/accounts/rules.ts

[warning] 528-528:
Delete ········

🔇 Additional comments (1)
packages/loot-core/src/server/accounts/rules.ts (1)

204-205: LGTM!

The new operations are correctly added to the CONDITION_TYPES.id.ops array and their scoring in OP_SCORES is consistent with similar operations.

Also applies to: 959-960

packages/loot-core/src/server/accounts/rules.ts Outdated Show resolved Hide resolved
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: 2

🧹 Outside diff range and nitpick comments (4)
packages/loot-core/src/types/models/transaction.d.ts (2)

29-29: LGTM with architectural considerations.

The type definition is correct and follows the existing patterns for internal properties. However, consider these architectural implications:

  1. Storing the full account entity in each transaction increases memory usage.
  2. Having both account ID and _account entity could lead to data synchronization challenges.

If this is a performance optimization to avoid repeated account lookups during rule processing, consider alternative approaches like:

  • Caching account entities separately
  • Using a normalized data structure

29-29: Add JSDoc comments to document the property's purpose.

Since this is an internal property used specifically for rule processing, it would be helpful to add documentation explaining:

  • Its purpose in relation to budget filtering
  • When it gets populated
  • That it's for internal use only

Consider adding a comment like this:

+ /** Internal property populated during rule processing to support budget status filtering.
+  * Contains the full account entity corresponding to the `account` ID.
+  * @internal
+  */
  _account?: AccountEntity;
packages/loot-core/src/server/accounts/transaction-rules.ts (2)

558-563: Add null safety checks for account field access

The implementation looks good but could benefit from additional safety checks to handle cases where the account object might be undefined.

Consider this safer implementation:

 case 'onBudget':
-  return { 'account.offbudget': false };
+  return { 
+    $and: [
+      { account: { $ne: null } },
+      { 'account.offbudget': false }
+    ]
+  };
 case 'offBudget':
-  return { 'account.offbudget': true };
+  return {
+    $and: [
+      { account: { $ne: null } },
+      { 'account.offbudget': true }
+    ]
+  };

858-864: Add error handling for account retrieval

Consider adding error handling for the account retrieval operation to gracefully handle potential database errors.

Consider this implementation:

 if (trans.account) {
-  const account = await getAccount(trans.account);
-  if (account) {
-    r._account = account;
-  }
+  try {
+    const account = await getAccount(trans.account);
+    if (account) {
+      r._account = account;
+    }
+  } catch (error) {
+    console.error('Failed to retrieve account:', error);
+    // Optionally: throw new Error('Failed to prepare transaction: ' + error.message);
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7b2d299 and 0c0ea50.

📒 Files selected for processing (4)
  • packages/loot-core/src/server/accounts/rules.ts (3 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (3 hunks)
  • packages/loot-core/src/server/db/index.ts (1 hunks)
  • packages/loot-core/src/types/models/transaction.d.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/loot-core/src/server/accounts/rules.ts
🔇 Additional comments (2)
packages/loot-core/src/server/db/index.ts (2)

476-478: Implementation aligns well with PR objectives.

The function follows the established patterns in the codebase and provides the necessary functionality for filtering accounts based on budget status.


476-478: Verify error handling in calling code.

Let's verify that all callers of this function properly handle the case when no account is found.

✅ Verification successful

Error handling is properly implemented in the codebase

The verification shows that getAccount is used in two locations:

  1. In transaction-rules.ts, proper error handling is implemented with a null check:
const account = await getAccount(trans.account);
if (account) {
  r._account = account;
}
  1. In TransactionEdit.jsx, the code also handles the null case appropriately:
const account = getAccount(transaction);
const isOffBudget = account && !!account.offbudget;

Both usages properly handle the case when no account is found through null checks, making the implementation safe.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of getAccount function and verify error handling

# Find direct function calls
echo "Finding direct function calls to getAccount:"
rg -A 5 "getAccount\(" --type ts --type tsx --type js --type jsx

# Find potential error handling
echo -e "\nChecking for error handling patterns:"
rg -A 5 "getAccount\(" --type ts --type tsx --type js --type jsx | rg -A 5 "(if.*null|try.*catch|\?\.|\|\|)"

Length of output: 423


Script:

#!/bin/bash
# Let's try a different approach using ast-grep to find function calls
# and ripgrep without file type restrictions

# Find direct function calls to getAccount
echo "Finding direct function calls to getAccount:"
rg "getAccount\(" -A 5

# Use ast-grep to find function calls with potential error handling
echo -e "\nChecking for function calls using AST:"
ast-grep --pattern 'getAccount($$$)'

# Look for await expressions since it's an async function
echo -e "\nChecking for await expressions:"
ast-grep --pattern 'await getAccount($$$)'

Length of output: 2361

packages/loot-core/src/server/db/index.ts Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (1)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)

860-866: Add error handling for account retrieval.

While the account preparation logic is well-structured, consider adding error handling for the getAccount call to gracefully handle potential database errors.

 if (trans.account) {
-  const account = await getAccount(trans.account);
-  if (account) {
-    r._account = account;
-  }
+  try {
+    const account = await getAccount(trans.account);
+    if (account) {
+      r._account = account;
+    }
+  } catch (error) {
+    console.error('Failed to fetch account:', error);
+    // Optionally: r._account = null;
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0ea50 and e84964b.

📒 Files selected for processing (1)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (4 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)
Learnt from: UnderKoen
PR: actualbudget/actual#3619
File: packages/loot-core/src/server/accounts/transaction-rules.ts:795-801
Timestamp: 2024-11-10T16:45:31.225Z
Learning: In the `finalizeTransactionForRules` function within `packages/loot-core/src/server/accounts/transaction-rules.ts`, potential race conditions when inserting payees are handled in the method that calls this function, so additional safeguards within `finalizeTransactionForRules` are unnecessary.
🔇 Additional comments (2)
packages/loot-core/src/server/accounts/transaction-rules.ts (2)

559-564: LGTM: Budget filter conditions are correctly implemented.

The new filter conditions for onBudget and offBudget are properly implemented using the account.offbudget field with appropriate boolean values.


846-846: Type definition properly added as previously suggested.

The _account property has been correctly added to the TransactionForRules type interface as suggested in a previous review.

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

🧹 Outside diff range and nitpick comments (2)
packages/desktop-client/src/components/rules/ConditionExpression.tsx (2)

52-56: Improve type safety for operation handling

The type assertion (op as string) suggests that we should better type the op prop. Consider defining a union type for valid operations.

+type Operation = 'onbudget' | 'offbudget' | string;

 type ConditionExpressionProps = {
   field: unknown;
-  op: unknown;
+  op: Operation;
   value: unknown;
   options: unknown;
   prefix?: string;
   style?: CSSProperties;
   inline?: boolean;
 };

Then simplify the condition:

-      {!['onbudget', 'offbudget'].includes(
-        (op as string)?.toLocaleLowerCase(),
-      ) && (
+      {!['onbudget', 'offbudget'].includes(op.toLowerCase()) && (

52-56: LGTM! The conditional rendering logic is well-implemented

The implementation correctly handles the special cases for budget-related operations, aligning with the PR objectives to support filtering accounts based on budget status.

Consider extracting the budget operation constants to improve maintainability:

+const BUDGET_OPERATIONS = ['onbudget', 'offbudget'] as const;

 export function ConditionExpression({
   // ...
 }: ConditionExpressionProps) {
   return (
     // ...
-      {!['onbudget', 'offbudget'].includes(
+      {!BUDGET_OPERATIONS.includes(
         (op as string)?.toLocaleLowerCase(),
       ) && (
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 179fffa and b61c3bc.

📒 Files selected for processing (1)
  • packages/desktop-client/src/components/rules/ConditionExpression.tsx (1 hunks)

matt-fidd
matt-fidd previously approved these changes Dec 9, 2024
Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Looks great, thank you!

Just one comment on performance but don't want to block it as it doesn't seem to cause any issues in the test deploy.

@lelemm
Copy link
Contributor Author

lelemm commented Dec 9, 2024

Check out the changed code please @matt-fidd

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

🧹 Outside diff range and nitpick comments (2)
packages/loot-core/src/server/accounts/sync.ts (2)

526-528: Consider caching the accounts map.

The accounts map is created by fetching all accounts from the database. Since this operation could be expensive for large datasets, consider caching the map or passing it as a parameter to avoid multiple database calls.


526-528: Consider adding type guards for account entities.

While the code uses TypeScript, it would be beneficial to add runtime type checks for the account entities to ensure data integrity, especially since this is dealing with financial data.

Here's a suggested type guard implementation:

function isValidAccountEntity(account: any): account is AccountEntity {
  return (
    account &&
    typeof account.id === 'string' &&
    typeof account.type === 'string' &&
    typeof account.balances === 'object'
  );
}

function createAccountsMap(accounts: any[]): Map<string, AccountEntity> {
  const validAccounts = accounts.filter(isValidAccountEntity);
  if (validAccounts.length !== accounts.length) {
    console.warn('Some accounts were filtered out due to invalid data');
  }
  return new Map(validAccounts.map(account => [account.id, account]));
}

Also applies to: 679-681

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 199c8eb and 192de30.

📒 Files selected for processing (2)
  • packages/loot-core/src/server/accounts/sync.ts (2 hunks)
  • packages/loot-core/src/shared/rules.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/loot-core/src/shared/rules.ts
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/accounts/sync.ts (1)
Learnt from: matt-fidd
PR: actualbudget/actual#3581
File: packages/loot-core/src/server/accounts/sync.ts:28-46
Timestamp: 2024-11-10T16:45:31.225Z
Learning: Matt prefers to defer adding proper types for transactions interfaces until another PR.
🔇 Additional comments (1)
packages/loot-core/src/server/accounts/sync.ts (1)

536-536: Verify error handling in runRules function.

The runRules function is called with the new accountsMap parameter, but there's no visible error handling if the rules execution fails. Consider adding try-catch blocks to handle potential errors gracefully.

Let's verify the error handling in the runRules function:

Also applies to: 684-684

Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Couple of comments

@matt-fidd matt-fidd self-assigned this Dec 9, 2024
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

🧹 Outside diff range and nitpick comments (3)
packages/loot-core/src/server/accounts/transaction-rules.ts (3)

278-291: Consider memoizing the accounts map.

The accounts map is created on every call to runRules when not provided. Consider caching this map to avoid repeated database queries, especially if this function is called frequently.

+let cachedAccountsMap: Map<string, AccountEntity> | null = null;
+
 export async function runRules(
   trans,
   accounts: Map<string, AccountEntity> = null,
 ) {
   let accountsMap = null;
   if (accounts === null) {
+    if (cachedAccountsMap === null) {
       accountsMap = new Map(
         (await db.getAccounts()).map(account => [account.id, account]),
       );
+      cachedAccountsMap = accountsMap;
+    } else {
+      accountsMap = cachedAccountsMap;
+    }
   } else {
     accountsMap = accounts;
   }

626-633: Consider using Promise.all for better performance.

The code fetches accounts and then maps them into a Map structure sequentially. This could be optimized by running these operations in parallel.

-  const accounts: AccountEntity[] = await db.getAccounts();
-  const transactionsForRules = await Promise.all(
-    transactions.map(transactions =>
-      prepareTransactionForRules(
-        transactions,
-        new Map(accounts.map(account => [account.id, account])),
-      ),
-    ),
-  );
+  const [accounts, transactionsForRules] = await Promise.all([
+    db.getAccounts(),
+    Promise.all(
+      transactions.map(async transactions => {
+        const accountsMap = new Map(
+          (await db.getAccounts()).map(account => [account.id, account])
+        );
+        return prepareTransactionForRules(transactions, accountsMap);
+      }),
+    ),
+  ]);

864-864: Consider adding JSDoc comments for better documentation.

The TransactionForRules type and prepareTransactionForRules function would benefit from JSDoc comments explaining their purpose and parameters.

+/**
+ * Extended transaction type that includes additional properties for rule processing
+ */
 export type TransactionForRules = TransactionEntity & {
   payee_name?: string;
   _account?: AccountEntity;
 };

+/**
+ * Prepares a transaction for rule processing by enriching it with additional data
+ * @param trans The transaction to prepare
+ * @param accounts Optional map of accounts for faster lookups
+ * @returns Promise<TransactionForRules> The prepared transaction
+ */
 export async function prepareTransactionForRules(
   trans: TransactionEntity,
   accounts: Map<string, AccountEntity> | null = null,
 ): Promise<TransactionForRules> {

Also applies to: 869-869

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 192de30 and d96bc90.

📒 Files selected for processing (1)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (6 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/loot-core/src/server/accounts/transaction-rules.ts

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (1)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)

571-576: LGTM: New budget filter conditions are well implemented.

The implementation of onBudget and offBudget cases in conditionsToAQL is clean and straightforward.

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

🧹 Outside diff range and nitpick comments (2)
packages/loot-core/src/server/accounts/transaction-rules.ts (2)

278-291: Consider memoizing the accounts map.

The implementation looks correct, but there's an opportunity for optimization. If this function is called frequently, creating a new Map on each invocation could impact performance.

Consider this optimization:

+ let cachedAccountsMap: Map<string, AccountEntity> | null = null;
+ 
  export async function runRules(
    trans,
    accounts: Map<string, AccountEntity> | null = null,
  ) {
-   let accountsMap = null;
    if (accounts === null) {
+     if (cachedAccountsMap === null) {
        accountsMap = new Map(
          (await db.getAccounts()).map(account => [account.id, account])
        );
+       cachedAccountsMap = accountsMap;
+     } else {
+       accountsMap = cachedAccountsMap;
+     }
    } else {
      accountsMap = accounts;
    }

626-633: Consider batching account retrieval.

The implementation is correct but could be optimized. Currently, we're retrieving all accounts even if we only need a subset.

Consider filtering accounts based on the transaction's account IDs:

- const accounts: AccountEntity[] = await db.getAccounts();
+ const accountIds = new Set(transactions.map(t => t.account));
+ const accounts: AccountEntity[] = await db.all(
+   'SELECT * FROM accounts WHERE id IN (?)',
+   [Array.from(accountIds)]
+ );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d96bc90 and 00be234.

📒 Files selected for processing (1)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (6 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)
Learnt from: UnderKoen
PR: actualbudget/actual#3619
File: packages/loot-core/src/server/accounts/transaction-rules.ts:795-801
Timestamp: 2024-11-10T16:45:31.225Z
Learning: In the `finalizeTransactionForRules` function within `packages/loot-core/src/server/accounts/transaction-rules.ts`, potential race conditions when inserting payees are handled in the method that calls this function, so additional safeguards within `finalizeTransactionForRules` are unnecessary.
🪛 Biome (1.9.4)
packages/loot-core/src/server/accounts/transaction-rules.ts

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (4)
packages/loot-core/src/server/accounts/transaction-rules.ts (4)

16-16: LGTM! Import changes look good.

The addition of AccountEntity type import and getAccount function import aligns well with the new account-related functionality.

Also applies to: 20-20


571-576: LGTM! New budget filter conditions look good.

The implementation of onBudget and offBudget cases in conditionsToAQL is clean and straightforward.


864-864: LGTM! Type definition update looks good.

The addition of the optional _account property to TransactionForRules type is well-documented.


879-886: 🛠️ Refactor suggestion

Use optional chaining for safer property access.

The code could benefit from using optional chaining when accessing properties.

Apply this change:

- if (trans.account) {
-   if (accounts !== null && accounts.has(trans.account)) {
-     r._account = accounts.get(trans.account);
+ if (trans?.account) {
+   if (accounts?.has(trans.account)) {
+     r._account = accounts?.get(trans.account);
    } else {
      r._account = await getAccount(trans.account);
    }
  }

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

@lelemm lelemm requested a review from matt-fidd December 9, 2024 17:26
Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Thanks for the changes :)

LGTM, will leave for a day to see if anyone else has any comments

@matt-fidd matt-fidd merged commit 2b908e9 into actualbudget:master Dec 10, 2024
20 checks passed
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.

3 participants