Skip to content

Conversation

@Himmelschmidt
Copy link

@Himmelschmidt Himmelschmidt commented Aug 7, 2025

As mentioned in Discussion #93

Summary by CodeRabbit

  • New Features

    • SimpleFin integration: add/manage connections, sync, and view linked accounts.
    • Accounts page now displays SimpleFin connections alongside others.
    • Settings gains a “SimpleFin” section with connection list and details.
    • Guided flow to add a connection via setup token and configure account types/subtypes.
    • Dynamic subtype selector in account setup; institution labels shown on accounts.
  • Style

    • Updated hover/focus styles for method selectors.
  • Tests

    • Added tests for SimpleFin models, controller actions, and related flows.
  • Chores

    • Added HTTP client dependency and database changes to support SimpleFin and external transaction IDs.

- Add HTTParty gem for making HTTP requests to SimpleFin API
- Required for SimpleFin protocol implementation
- Create simplefin_items table for SimpleFin connections
- Create simplefin_accounts table for account metadata
- Add simplefin_account_id to accounts table for linking
- Add external_id to transactions for deduplication
- Enable encrypted storage of SimpleFin access URLs
- Add SimplefinItem model with sync capabilities and encryption
- Add SimplefinAccount model for account data mapping
- Implement Provider::Simplefin API client with token exchange
- Add SimpleFin protocol support with proper error handling
- Include sync jobs, importers, and processors for data flow
- Add family SimpleFin connectivity mixin
- Add SimpleFin account creation methods to Account model
- Implement intelligent account type mapping from names
- Add SimpleFin linkable functionality to Account
- Include SimpleFin items in Family model associations
- Support account creation with user-selected types
- Create SimplefinItemsController with CRUD operations
- Add account setup flow with user type selection
- Include sync management and error handling
- Update AccountsController to display SimpleFin items
- Add routes for SimpleFin item management and setup
- Create SimpleFin connection management views
- Add account setup modal with type selection
- Include connection form with token input and instructions
- Update accounts index to display SimpleFin items
- Add SimpleFin option to account method selector
- Include SimpleFin in settings navigation
- Add pending_account_setup field to SimpleFin items
- Enable pausing sync for user account type selection
- Allow users to choose account types during import
- Prevent automatic account creation until user confirms
- Add SimplefinItem model tests with fixtures
- Add SimplefinAccount model tests
- Add SimplefinItemsController tests
- Include test coverage for sync and account creation
- Update sync button routing to handle SimpleFin accounts
- Add SimpleFin item sync path alongside existing Plaid support
- Prevent NoMethodError when viewing SimpleFin-linked accounts
- Support proper sync routing for Plaid, SimpleFin, and manual accounts
- Add subtype database columns to all accountable models
- Create Stimulus controller for dynamic subtype dropdown interaction
- Add delegation from Account to accountable subtype for clean API access
- Update SimpleFin account setup form with working subtype selection
- Fix account display to show proper subtype labels instead of generic "Cash"

Users can now select both account type and subtype during SimpleFin import,
and the selected subtypes are properly saved and displayed in the UI.
- Replace hardcoded colors with design system tokens throughout SimpleFin views
- Fix method selector hover states to use bg-surface instead of bg-gray-50
- Update SimpleFin form to use styled_form_with and standard form patterns
- Replace custom button styling with design system button components
- Fix info boxes and containers to use bg-surface and border-primary
- Replace hardcoded green/blue colors with text-primary, text-secondary, text-link
- Remove custom text area styling to allow form builder defaults (dark mode support)

All SimpleFin components now properly adapt to both light and dark themes
with correct contrast and visibility.
- Fix upsert method to handle string/symbol keys with indifferent access
- Add missing show route and view for SimpleFin items
- Fix test fixtures to use correct user references
- Update test data to match real-world JSON format (string keys, BigDecimal)
- Apply code formatting and linting fixes (rubocop, erb_lint)
- Ensure all SimpleFin tests pass (16/16 passing)
- Remove map_simplefin_type_to_accountable_type method (no longer needed)
- Remove create_from_simplefin_account method (manual setup only)
- Simplify account type selection UI to not pre-select defaults
- Update processor to log error if account missing (safety check)
- All account creation now goes through manual user selection flow
SimpleFin is primarily for North American financial institutions,
so only show the option when US banking connections are available.
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @Himmelschmidt, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've implemented the initial integration for SimpleFin, a new way for users to connect their bank accounts and automatically import financial data. This feature provides an alternative to existing methods, expanding the range of financial institutions and data sources that can be linked to the application. The changes include new backend models and services for handling SimpleFin API interactions, as well as new UI components for managing these connections and setting up imported accounts.

Highlights

  • SimpleFin Integration: I've introduced a comprehensive integration with SimpleFin, an open-source financial data bridge. This allows users to connect their bank accounts and automatically sync account balances and transactions, similar to the existing Plaid integration. This feature enhances the application's ability to provide automated financial tracking.
  • Enhanced Account Management: I've updated the core Account model to support linking with SimplefinAccount records. The AccountsController and associated views have been modified to display these new SimpleFin connections alongside manual and Plaid-linked accounts, providing a unified view of all user financial data.
  • Dedicated SimpleFin UI: New controllers and views have been added specifically for managing SimpleFin items. Users can now add new SimpleFin connections by providing a setup token, initiate manual syncs, and go through a guided process to categorize their imported SimpleFin accounts by type and subtype (e.g., checking, savings, credit card).
  • Database Schema Expansion: To support the new integration, I've added several new database tables (simplefin_items, simplefin_accounts) and columns. This includes linking accounts to simplefin_accounts, adding an external_id to transactions for deduplication, and introducing a subtype column to various accountable types for more detailed categorization.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant new feature: integration with SimpleFin for bank account syncing. The changes are extensive, touching models, controllers, views, and adding new infrastructure like migrations and background jobs. Overall, the implementation is solid and follows existing patterns in the codebase. I've provided several suggestions to improve maintainability, reduce code duplication, and enhance error handling, particularly around the new controller logic and data processing classes. Great work on adding this new provider integration!

- Move SimpleFin item creation logic from controller to Family#create_simplefin_item!
- Remove duplication between controller and model
- Simplify controller to focus on web request/response handling
- Remove unused simplefin_provider method
- Follow Rails best practices for fat models, skinny controllers
- Remove fallback to Date.current when transaction dates fail to parse
- Raise ArgumentError instead to ensure data integrity
- Log detailed error messages for debugging
- Skip transactions with invalid dates rather than using incorrect dates
- Prevents hard-to-debug issues with balances and financial reports
- Remove debug console.log statements from JavaScript controller
- Consolidate duplicate SimpleFin account creation methods into single method
- Refactor SimplefinItemsController to reduce complexity with helper methods
- Fix HTTParty thread-safety by moving SSL options to class level
- Remove redundant HTTParty options from individual requests
- Add proper error logging for invalid currency URIs
- Extract sync button path logic to AccountsHelper#sync_path_for method
- DRY up repeated subtype dropdown code with reusable partial and data structure

All SimpleFin tests passing (16/16). Code quality improvements maintain
backward compatibility while following Rails best practices.
The subtype attribute was moved from Account to individual accountable models
to enable users to select specific subtypes during SimpleFin account import.
This change allows for better account categorization and more precise display
of account types (e.g., "HSA" instead of generic "Cash").

However, tests and the PlaidAccount processor weren't updated to work with
the new delegation pattern. This commit fixes:

- PlaidAccount::Processor now sets subtype on accountable and uses enrichable
  pattern to respect user locks
- PropertiesController updated to handle subtype via accountable_attributes
- Test fixtures corrected to set subtype on accountable models not Account
- Tests updated to work with the delegated subtype pattern

All originally failing tests now pass:
- PropertiesControllerTest#test_updates_property_overview
- PlaidAccount::ProcessorTest (2 failing tests)
- AccountTest#test_gets_short/long_subtype_label
@jjmata jjmata added this to the v0.7.0 milestone Aug 7, 2025
@jjmata
Copy link
Collaborator

jjmata commented Aug 7, 2025

Super smooth experience until I added my accounts from BofA ... hitting this error:

ActiveModel::UnknownAttributeError in SimplefinItemsController#complete_account_setup
Screenshot 2025-08-07 at 2 50 14 PM

Will debug some more.

@jjmata
Copy link
Collaborator

jjmata commented Aug 7, 2025

I also don't see an obvious way to add new accounts that have been added to a "connection" (the UI asks me to create a new connection/token)

@Himmelschmidt
Copy link
Author

I reverted the gate behind us commit as this was mistakenly tied to plaid being setup due to a confusingly labeled variable

- Change date parameters from string format to Unix timestamps as required by SimpleFin API
- Add better error handling for 400 Bad Request responses
- Add more detailed error logging for debugging failed API calls

This fixes the issue where SimpleFin was only returning recent transactions
instead of historical data when start_date was provided.
- Add start_date parameter to SimpleFin API calls for historical data
- Use 100-year lookback for first sync to capture all available history
- Use 7-day buffer for incremental syncs to catch late-posting transactions
- Fix transaction storage to prevent data loss during account updates
- Remove verbose logging for cleaner output

This ensures users get all their historical transactions on first sync,
not just recent ones.
- Negate SimpleFin amounts to convert from banking convention to Maybe's format
- SimpleFin: expenses negative, income positive (banking convention)
- Maybe: expenses positive, income negative (internal convention)
- Improve date parsing to handle multiple date formats (Unix timestamps, strings, Date objects)

This fixes the issue where expenses showed as negative in the UI instead of positive.
…ities

- Add belongs_to :simplefin_account association to Account model
- Fix balance handling for credit cards and loans (use absolute value)
- SimpleFin returns negative balances for liabilities, but Maybe expects positive

This enables displaying organization names and ensures correct balance display.
- Show institution names under SimpleFin connection titles
- Display organization names next to account names (e.g., "360 Checking • Capital One")
- Add organization info to all SimpleFin account displays:
  - Account setup page
  - SimpleFin item details page
  - Regular account lists for SimpleFin accounts
- Use org_data from SimpleFin accounts with fallback to institution_name

This improves account identification by showing which financial institution
each account belongs to throughout the SimpleFin workflow.
- Replace custom styles with DS components (DS::FilledIcon, DS::Link, DS::Button)
- Use proper design system tokens instead of hardcoded colors
- Fix form select styling to match design system patterns
- Update empty states to use consistent styling
- Ensure all SimpleFin views follow the app's design system

This makes the SimpleFin interface consistent with the rest of the app.
@jjmata jjmata moved this to In progress in Rebrand to Sure (v0.7.0) Aug 11, 2025
@jjmata jjmata requested a review from Copilot August 11, 2025 19:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces SimpleFin integration as an alternative to Plaid for financial data connectivity. SimpleFin is a financial data aggregation service that allows users to connect their bank accounts and automatically sync transactions and balances.

  • Adds complete SimpleFin integration including models, controllers, views, and API client
  • Refactors account subtype handling by moving subtype field from Account to accountable models
  • Adds account setup flow for manually selecting account types when importing from SimpleFin

Reviewed Changes

Copilot reviewed 49 out of 50 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/models/simplefin_*.rb Core SimpleFin models for items, accounts, syncing, and data processing
app/models/provider/simplefin.rb HTTP client for SimpleFin API communication
app/controllers/simplefin_items_controller.rb Controller for managing SimpleFin connections and account setup
app/views/simplefin_items/ Complete UI for SimpleFin connection management and account setup
db/migrate/ Database migrations for SimpleFin tables and subtype refactoring
app/models/family/simplefin_connectable.rb Family concern for SimpleFin integration
test/ Comprehensive test coverage for SimpleFin functionality
Comments suppressed due to low confidence (1)

# Then save transactions separately (so they don't get overwritten)
if transactions && transactions.any?
simplefin_account.update!(raw_transactions_payload: transactions)
else
Copy link

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

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

Empty else block should be removed or contain a comment explaining why it's intentionally empty

Suggested change
else

Copilot uses AI. Check for mistakes.
class: "block text-sm font-medium text-primary mb-2" %>
<% selected_value = account_type == "Depository" ?
(simplefin_account.name.downcase.include?("checking") ? "checking" :
simplefin_account.name.downcase.include?("savings") ? "savings" : "") : "" %>
Copy link

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

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

Complex logic for determining selected_value should be extracted to a helper method for better readability and testability

Suggested change
simplefin_account.name.downcase.include?("savings") ? "savings" : "") : "" %>
<%# Helper method for determining selected_value %>
<% def selected_subtype_value(account_type, account) -%>
if account_type == "Depository"
name = account.name.downcase
if name.include?("checking")
"checking"
elsif name.include?("savings")
"savings"
else
""
end
else
""
end
end -%>
<div class="subtype-select" data-type="<%= account_type %>" style="display: none;">
<% if subtype_config[:options].present? %>
<%= label_tag "account_subtypes[#{simplefin_account.id}]", subtype_config[:label],
class: "block text-sm font-medium text-primary mb-2" %>
<% selected_value = selected_subtype_value(account_type, simplefin_account) %>

Copilot uses AI. Check for mistakes.
# Handle both string and symbol keys
data = transaction_data.with_indifferent_access


Copy link

Copilot AI Aug 11, 2025

Choose a reason for hiding this comment

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

[nitpick] Empty line after variable assignment should be removed for consistent code formatting

Suggested change

Copilot uses AI. Check for mistakes.
jjmata and others added 2 commits August 11, 2025 21:45
Co-authored-by: Copilot <[email protected]>
Signed-off-by: Juan José Mata <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Aug 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds SimpleFin integration across the app: new provider client, models, syncer/importer pipeline, controller and views, routes, and migrations. Updates Accounts and Family to link SimpleFin data, adjusts properties strong params, introduces a Stimulus controller for subtype selection, and centralizes sync path logic.

Changes

Cohort / File(s) Summary
Dependencies
Gemfile
Add httparty runtime dependency.
Controllers
app/controllers/accounts_controller.rb, app/controllers/properties_controller.rb, app/controllers/simplefin_items_controller.rb
Expose @simplefin_items in Accounts#index; move properties subtype under accountable_attributes; add full CRUD/sync/setup flow for SimpleFin items.
Helpers
app/helpers/accounts_helper.rb
Add sync_path_for(account) to route syncs for Plaid/SimpleFin/manual.
Frontend (Stimulus)
app/javascript/controllers/account_type_selector_controller.js
New controller to show/hide subtype selects per chosen account type.
Core Models Updates
app/models/account.rb, app/models/account/linkable.rb, app/models/family.rb, app/models/plaid_account/processor.rb, app/models/family/simplefin_connectable.rb
Link accounts to SimpleFin, update manual scope and linked? logic, delegate subtype to accountable, adjust Plaid processing, add Family::SimplefinConnectable with associations and creation helper.
SimpleFin Provider & Domain
app/models/provider/simplefin.rb
New HTTP client for SimpleFin (claim access URL, fetch accounts/info, errors).
SimpleFin Models
app/models/simplefin_item.rb, app/models/simplefin_item/*
Add SimplefinItem with statuses, encryption, associations, scopes; Importer, Provided, Syncer, SyncCompleteEvent helpers.
SimpleFin Account
app/models/simplefin_account.rb, app/models/simplefin_account/processor.rb
New SimplefinAccount model with snapshot upserts and transaction processing into internal records.
Views: Accounts
app/views/accounts/_account.html.erb, app/views/accounts/index.html.erb, app/views/accounts/new/_method_selector.html.erb, app/views/accounts/show/_header.html.erb
Show institution badge for SimpleFin, render SimpleFin items on index, minor style tweaks, centralize refresh path via helper.
Views: Settings and SimpleFin UI
app/views/settings/_settings_nav.html.erb, app/views/simplefin_items/*
Add SimpleFin nav; new pages/partials for listing, creating, showing, and setting up accounts; subtype select partial.
Transactions View
app/views/transactions/show.html.erb
Formatting-only change.
Routing
config/routes.rb
Add resources :simplefin_items with member routes: sync, setup_accounts, complete_account_setup.
Migrations
db/migrate/20250807143728_create_simplefin_items.rb, 20250807143819_create_simplefin_accounts.rb, 20250807144230_add_simplefin_account_id_to_accounts.rb, 20250807144857_add_external_id_to_transactions.rb, 20250807163541_add_pending_account_setup_to_simplefin_items.rb, 20250808141424_add_balance_date_to_simplefin_accounts.rb, 20250808143007_add_extra_simplefin_account_fields.rb, 20250807170943_add_subtype_to_accountables.rb
Create SimpleFin tables and relationships; add links to accounts, external_id on transactions; add pending_account_setup; add balance_date/extra/org_data; add subtype columns across accountables.
Schema
db/schema.rb
Reflects new tables, columns, indexes, and FKs for SimpleFin and subtype additions.
Tests
test/controllers/*, test/models/*, test/fixtures/*
Add controller/model tests for SimpleFin; adjust properties/plaid processor tests for subtype-on-accountable; update fixtures for subtype location.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant A as App (UI)
  participant C as SimplefinItemsController
  participant F as Family
  participant P as Provider::Simplefin
  participant I as SimplefinItem
  U->>A: Enter setup token
  A->>C: POST /simplefin_items
  C->>F: create_simplefin_item!(setup_token, item_name)
  F->>P: claim_access_url(token)
  P-->>F: access_url
  F->>I: create! (name, access_url)
  I->>I: sync_later
  C-->>A: Redirect with notice
Loading
sequenceDiagram
  participant U as User
  participant A as App (UI)
  participant C as SimplefinItemsController
  participant I as SimplefinItem
  participant SY as SimplefinItem::Syncer
  participant IM as SimplefinItem::Importer
  participant P as Provider::Simplefin
  participant SA as SimplefinAccount
  participant AC as Account

  U->>A: Click Sync
  A->>C: POST /simplefin_items/:id/sync
  C->>I: sync_later
  I->>SY: perform_sync(sync)
  SY->>I: import_latest_simplefin_data
  I->>IM: import
  IM->>P: get_accounts(access_url, window)
  P-->>IM: accounts snapshot
  IM->>I: upsert_simplefin_snapshot!(...)
  IM->>SA: upsert_simplefin_snapshot!/transactions
  SY->>I: detect unlinked accounts?
  alt Unlinked exist
    SY->>I: update!(pending_account_setup: true)
    SY-->>C: return
  else None
    SY->>I: process_accounts
    I->>AC: schedule_account_syncs
  end
Loading
sequenceDiagram
  participant U as User
  participant A as App (UI)
  participant C as SimplefinItemsController
  participant I as SimplefinItem
  participant SA as SimplefinAccount
  participant AC as Account

  U->>A: Open Setup Accounts
  A->>C: GET /simplefin_items/:id/setup_accounts
  C-->>A: Render form (types/subtypes)
  U->>A: Submit selections
  A->>C: POST /.../complete_account_setup
  loop each selected SA
    C->>AC: Account.create_from_simplefin_account(SA, type, subtype)
    C->>SA: link to Account
  end
  C->>I: update!(pending_account_setup: false)
  C-->>A: Redirect with success
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90 minutes

Poem

A bridge of bytes, a SimpleFin spin,
I twitch my ears—new routes begin!
Tokens claimed and accounts align,
Subtypes hop in tidy line.
Syncs now hum, a gentle thrum—
With paws on keys, I nudge “become.”
Carrots cached; the ledgers run. 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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

@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: 16

♻️ Duplicate comments (10)
app/views/accounts/show/_header.html.erb (1)

35-35: Excellent refactoring to use helper method.

This addresses the previous review feedback by centralizing the sync path logic into the sync_path_for(account) helper method, significantly improving code maintainability and readability.

app/views/simplefin_items/_subtype_select.html.erb (1)

5-7: Extract selected_value logic to a helper for readability and reuse.

Inline inference from account name is brittle and hard to test. Move it to a helper and call it here.

Apply this diff:

-    <% selected_value = account_type == "Depository" ?
-         (simplefin_account.name.downcase.include?("checking") ? "checking" :
-          simplefin_account.name.downcase.include?("savings") ? "savings" : "") : "" %>
+    <% selected_value = selected_simplefin_subtype(account_type, simplefin_account.name) %>

Add helper (app/helpers/simplefin_items_helper.rb):

module SimplefinItemsHelper
  def selected_simplefin_subtype(account_type, account_name)
    return "" unless account_type.to_s.casecmp("Depository").zero?
    name = account_name.to_s.downcase
    return "checking" if name.include?("checking")
    return "savings" if name.include?("savings")
    ""
  end
end
app/models/simplefin_item/importer.rb (3)

13-15: Remove empty conditional block

The empty if-else block serves no purpose and should be removed for cleaner code.

    # Determine start date based on sync history
    start_date = determine_sync_start_date

-    if start_date
-    else
-    end
-
    accounts_data = simplefin_provider.get_accounts(

43-43: Typo in comment

-        return 100.years.ago  # Set to 100 years for first sync to get everything just to be sure
+        return 100.years.ago  # Set to 100 years for first sync to get everything just to be sure

68-71: Remove empty else block

The empty else block after checking for transactions should be removed.

      # Then save transactions separately (so they don't get overwritten)
      if transactions && transactions.any?
        simplefin_account.update!(raw_transactions_payload: transactions)
-      else
      end
app/models/provider/simplefin.rb (3)

4-5: Thread-safety concern with class-level configuration

The default_options.merge! modifies shared class-level state, which can lead to thread-safety issues in multi-threaded environments. Consider using class-level configuration properly.

 class Provider::Simplefin
   include HTTParty
 
   headers "User-Agent" => "Sure Finance SimpleFin Client"
-  default_options.merge!(verify: true, ssl_verify_mode: :peer)
+  
+  # Set default options at class level
+  default_options[:verify] = true
+  default_options[:ssl_verify_mode] = :peer

49-49: Add explicit SSL/TLS options for consistency

For consistency and to ensure secure connections, add SSL/TLS options explicitly.

     # The access URL already contains HTTP Basic Auth credentials
-    response = HTTParty.get(accounts_url)
+    response = HTTParty.get(accounts_url, verify: true, ssl_verify_mode: :peer)

14-14: Add explicit SSL/TLS options for security

The HTTParty request doesn't specify SSL/TLS options explicitly. Since the class-level defaults might not be applied correctly, consider adding them explicitly to ensure secure connections.

-    response = HTTParty.post(claim_url)
+    response = HTTParty.post(claim_url, verify: true, ssl_verify_mode: :peer)
app/models/simplefin_account/processor.rb (1)

38-38: Remove unnecessary empty line

      # Handle both string and symbol keys
      data = transaction_data.with_indifferent_access

-
      # Convert SimpleFin transaction to internal Transaction format
app/controllers/simplefin_items_controller.rb (1)

16-42: Consider extracting the SimpleFin connection logic into a service object

The create action contains significant business logic including token validation, API calls, object creation, and comprehensive error handling. This violates the "fat model, skinny controller" principle and makes the code harder to test and maintain.

Consider creating a service object like Simplefin::ConnectionCreator that encapsulates all this logic:

def create
  setup_token = simplefin_params[:setup_token]
  
  return render_error("Please enter a SimpleFin setup token.") if setup_token.blank?
  
  result = Simplefin::ConnectionCreator.new(
    family: Current.family,
    setup_token: setup_token
  ).call
  
  if result.success?
    redirect_to simplefin_items_path, 
                notice: "SimpleFin connection added successfully! Your accounts will appear shortly as they sync in the background."
  else
    render_error(result.error_message, setup_token)
  end
end
🧹 Nitpick comments (24)
test/fixtures/depositories.yml (1)

2-2: Add missing newline at end of file.

The static analysis tool correctly identified a formatting issue. YAML files should end with a newline character.

Apply this diff to add the required newline:

-two: { subtype: checking }
+two: { subtype: checking }
+
db/migrate/20250807170943_add_subtype_to_accountables.rb (1)

3-11: Consider adding database indexes for subtype columns.

Since subtype is likely to be used for filtering and querying operations (especially in reports and account categorization), adding indexes on frequently queried subtype columns would improve performance.

Consider adding indexes for the most commonly queried accountable types:

   add_column :depositories, :subtype, :string
+  add_index :depositories, :subtype
   add_column :investments, :subtype, :string
+  add_index :investments, :subtype
   add_column :loans, :subtype, :string
+  add_index :loans, :subtype
   add_column :credit_cards, :subtype, :string
+  add_index :credit_cards, :subtype
db/migrate/20250807143728_create_simplefin_items.rb (1)

10-11: Consider using an enum for status field.

The status field with a default of "good" suggests multiple status values. Consider defining an enum in the corresponding model to ensure type safety and better maintainability.

-      t.string :status, default: "good"
+      t.integer :status, default: 0

Then in the SimplefinItem model, add:

enum status: { good: 0, error: 1, pending: 2 }
app/views/settings/_settings_nav.html.erb (1)

13-13: Consider conditional rendering for SimpleFin navigation item.

The SimpleFin navigation item is always visible, but you might want to conditionally show it based on feature flags or user permissions, similar to how billing/self-hosting items are handled.

-      { label: "SimpleFin", path: simplefin_items_path, icon: "building-2" },
+      { label: "SimpleFin", path: simplefin_items_path, icon: "building-2", if: can_connect_simplefin? },
app/views/accounts/_account.html.erb (1)

21-23: Consider extracting organization name logic to a helper method.

While the current implementation works, consider extracting this logic to improve readability and reusability.

In app/helpers/accounts_helper.rb:

def simplefin_org_name(account)
  account.simplefin_account&.org_data&.dig('name')
end

Then update the view:

-            <% if account.simplefin_account&.org_data&.dig('name') %>
-              <span class="text-secondary">• <%= account.simplefin_account.org_data["name"] %></span>
-            <% end %>
+            <% if simplefin_org_name(account) %>
+              <span class="text-secondary">• <%= simplefin_org_name(account) %></span>
+            <% end %>
db/migrate/20250808141424_add_balance_date_to_simplefin_accounts.rb (1)

3-3: Consider precision and indexing for balance_date (temporal queries).

If you’ll query or sort by balance_date (e.g., “latest snapshot”), add microsecond precision and an index.

Apply this diff:

-    add_column :simplefin_accounts, :balance_date, :datetime
+    add_column :simplefin_accounts, :balance_date, :datetime, precision: 6
+    add_index :simplefin_accounts, :balance_date
app/models/account/linkable.rb (1)

9-12: linked? logic is correct; consider using associations for clarity.

Minor: Using associations (plaid_account.present? || simplefin_account.present?) avoids stale foreign key presence if associations change.

-  def linked?
-    plaid_account_id.present? || simplefin_account_id.present?
-  end
+  def linked?
+    plaid_account.present? || simplefin_account.present?
+  end
app/views/simplefin_items/index.html.erb (1)

6-8: Consider i18n for static UI strings.

Hard-coded strings make localization harder. Extract to I18n keys (e.g., simplefin.items.index.title, subtitle).

app/views/simplefin_items/_subtype_select.html.erb (2)

8-10: Option label text reads well; consider i18n and placeholder clarity.

Use I18n for “Select subtype/type” and consider a disabled placeholder option to avoid accidental empty submission.

Apply this diff:

-                  options_for_select([["Select #{account_type == 'Depository' ? 'subtype' : 'type'}", ""]] + subtype_config[:options], selected_value),
+                  options_for_select([[t('simplefin.subtypes.placeholder', default: "Select #{account_type == 'Depository' ? 'subtype' : 'type'}"), "", { disabled: true }]] + subtype_config[:options], selected_value),

12-13: Ensure subtype_config[:message] is user-safe and localized.

ERB escapes by default, but prefer I18n-managed messages to centralize copy.

test/controllers/simplefin_items_controller_test.rb (2)

30-38: Soft-delete test is good; add an assertion on side-effects if any background cleanup is queued.

If destroy triggers background cleanup (e.g., canceling tokens or removing webhooks), assert the job enqueue to better cover behavior.

If you have a job, add:

assert_enqueued_with(job: Simplefin::CleanupItemJob, args: [@simplefin_item.id]) do
  delete simplefin_item_url(@simplefin_item)
end

40-44: Sync test: consider asserting job enqueue to improve coverage.

Verifying redirect and flash is good; asserting the sync job enqueued will better validate behavior.

If applicable:

assert_enqueued_with(job: Simplefin::SyncItemJob, args: [@simplefin_item.id]) do
  post sync_simplefin_item_url(@simplefin_item)
end
app/models/simplefin_item/sync_complete_event.rb (1)

15-20: Prefer passing the record as the Turbo target over stringly-typed DOM ids

Using the model as the target lets Turbo compute dom_id internally and avoids coupling to the id format.

-    simplefin_item.broadcast_replace_to(
-      simplefin_item.family,
-      target: "simplefin_item_#{simplefin_item.id}",
-      partial: "simplefin_items/simplefin_item",
-      locals: { simplefin_item: simplefin_item }
-    )
+    simplefin_item.broadcast_replace_to(
+      simplefin_item.family,
+      target: simplefin_item,
+      partial: "simplefin_items/simplefin_item",
+      locals: { simplefin_item: simplefin_item }
+    )
app/javascript/controllers/account_type_selector_controller.js (1)

18-27: Avoid shadowing variable names inside the loop

The inner selectElement variable shadows the outer one, which is easy to misread. Rename for clarity.

-    const subtypeSelects = container.querySelectorAll('.subtype-select')
-    subtypeSelects.forEach(select => {
-      select.style.display = 'none'
-      // Clear the name attribute so it doesn't get submitted
-      const selectElement = select.querySelector('select')
-      if (selectElement) {
-        selectElement.removeAttribute('name')
-      }
-    })
+    const subtypeSelects = container.querySelectorAll('.subtype-select')
+    subtypeSelects.forEach(wrapper => {
+      wrapper.style.display = 'none'
+      // Clear the name attribute so it doesn't get submitted
+      const innerSelect = wrapper.querySelector('select')
+      if (innerSelect) {
+        innerSelect.removeAttribute('name')
+      }
+    })
app/views/simplefin_items/show.html.erb (2)

13-16: Use Turbo’s confirm attribute for consistency

When using Turbo, prefer data-turbo-confirm over the legacy data-confirm to ensure the confirm dialog is honored uniformly.

-    <%= button_to simplefin_item_path(@simplefin_item), method: :delete, data: { confirm: "Are you sure?" }, class: "inline-flex items-center gap-2 px-4 py-2 bg-destructive border border-destructive rounded-lg text-white font-medium hover:bg-destructive-hover focus:ring-2 focus:ring-destructive focus:ring-offset-2" do %>
+    <%= button_to simplefin_item_path(@simplefin_item), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "inline-flex items-center gap-2 px-4 py-2 bg-destructive border border-destructive rounded-lg text-white font-medium hover:bg-destructive-hover focus:ring-2 focus:ring-destructive focus:ring-offset-2" do %>

63-66: Unify currency formatting and honor per-account currency codes

This uses default currency (likely USD). Elsewhere (setup view) you pass unit: simplefin_account.currency. Prefer a dedicated helper to consistently format amounts given an ISO code, avoiding mixing symbols and codes.

-              <p class="text-sm font-medium text-primary">
-                <%= number_to_currency(simplefin_account.current_balance || 0) %>
-              </p>
+              <p class="text-sm font-medium text-primary">
+                <%= format_currency(simplefin_account.current_balance, simplefin_account.currency) %>
+              </p>

Example helper to add (outside this file):

# app/helpers/currency_helper.rb
module CurrencyHelper
  # Simple formatter that shows ISO code prefix for clarity across currencies.
  # Swap to MoneyRails or a real formatter if available.
  def format_currency(amount, currency_code)
    value = amount || 0
    number_to_currency(value, unit: (currency_code.presence || "USD"), format: "%u %n")
  end
end

Want me to wire this helper everywhere we show SimpleFin balances?

app/views/simplefin_items/setup_accounts.html.erb (1)

51-52: Align currency formatting with a consistent helper

To keep currency display consistent with the show page recommendation and respect ISO codes, use a helper.

-                  Balance: <%= number_to_currency(simplefin_account.current_balance || 0, unit: simplefin_account.currency) %>
+                  Balance: <%= format_currency(simplefin_account.current_balance, simplefin_account.currency) %>
test/models/simplefin_account_test.rb (2)

11-18: Use BigDecimal in fixtures/setup to avoid float rounding in tests

Initialize monetary values with BigDecimal to prevent subtle float precision issues.

-    @simplefin_account = SimplefinAccount.create!(
+    @simplefin_account = SimplefinAccount.create!(
       simplefin_item: @simplefin_item,
       name: "Test Checking Account",
       account_id: "test_checking_123",
       currency: "USD",
       account_type: "checking",
-      current_balance: 1500.50
+      current_balance: BigDecimal("1500.50")
     )

47-72: Consider asserting currency and subtype persistence in snapshot upsert

You already validate balances, dates, name, extra, and org_data. Adding assertions that currency and subtype are persisted would tighten coverage for data mapping.

app/models/simplefin_account/processor.rb (1)

82-83: Clarify the application name in comment

The comment mentions "Maybe" as the application name, but based on the repository context (we-promise/sure), this appears to be the "Sure" application. Please verify and update the comment for clarity.

       # SimpleFin uses banking convention (expenses negative, income positive)
-      # Maybe expects opposite convention (expenses positive, income negative)
-      # So we negate the amount to convert from SimpleFin to Maybe format
+      # Sure expects opposite convention (expenses positive, income negative)
+      # So we negate the amount to convert from SimpleFin to Sure format
       -parsed_amount
app/models/simplefin_account.rb (1)

89-92: Missing space before method definition

Add a blank line before the method definition for consistent formatting.

       nil
     end
+
     def has_balance
       return if current_balance.present? || available_balance.present?
app/controllers/simplefin_items_controller.rb (1)

55-90: Add null checks for associated records to prevent NoMethodError

The setup_accounts action loads SimpleFin accounts that don't have associated Account records, but doesn't verify that the SimplefinItem actually has any accounts to set up. This could lead to confusing UX if the view doesn't handle empty states properly.

Consider adding a check and redirect if there are no accounts to set up:

 def setup_accounts
   @simplefin_accounts = @simplefin_item.simplefin_accounts.includes(:account).where(accounts: { id: nil })
+  
+  if @simplefin_accounts.empty?
+    redirect_to simplefin_items_path, notice: "All accounts have already been set up."
+    return
+  end
+  
   @account_type_options = [
app/models/simplefin_item.rb (2)

52-58: Add error handling for upsert operations

The upsert_simplefin_snapshot! method uses save! which will raise an exception if validation fails. Since this is called during the import process, a validation failure could leave the sync in an inconsistent state.

Consider adding validation error handling or using a transaction:

 def upsert_simplefin_snapshot!(accounts_snapshot)
+  transaction do
     assign_attributes(
       raw_payload: accounts_snapshot,
     )
 
     save!
+  end
+rescue ActiveRecord::RecordInvalid => e
+  Rails.logger.error "Failed to save SimpleFin snapshot: #{e.message}"
+  raise
 end

27-30: Consider adding a state machine for deletion workflow

The destroy_later method sets a flag and enqueues a job, but there's no tracking of the deletion state (e.g., pending, in_progress, failed). This could lead to orphaned records if the job fails.

Consider using a state machine or enum for tracking deletion status:

+  enum :deletion_status, { 
+    active: "active",
+    pending_deletion: "pending_deletion", 
+    deleting: "deleting",
+    deleted: "deleted"
+  }, default: :active
+
   def destroy_later
-    update!(scheduled_for_deletion: true)
+    update!(deletion_status: :pending_deletion)
     DestroyJob.perform_later(self)
   end
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6d4a5dd and d2cbea7.

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (49)
  • Gemfile (1 hunks)
  • app/controllers/accounts_controller.rb (1 hunks)
  • app/controllers/properties_controller.rb (1 hunks)
  • app/controllers/simplefin_items_controller.rb (1 hunks)
  • app/helpers/accounts_helper.rb (1 hunks)
  • app/javascript/controllers/account_type_selector_controller.js (1 hunks)
  • app/models/account.rb (3 hunks)
  • app/models/account/linkable.rb (1 hunks)
  • app/models/family.rb (1 hunks)
  • app/models/family/simplefin_connectable.rb (1 hunks)
  • app/models/plaid_account/processor.rb (1 hunks)
  • app/models/provider/simplefin.rb (1 hunks)
  • app/models/simplefin_account.rb (1 hunks)
  • app/models/simplefin_account/processor.rb (1 hunks)
  • app/models/simplefin_item.rb (1 hunks)
  • app/models/simplefin_item/importer.rb (1 hunks)
  • app/models/simplefin_item/provided.rb (1 hunks)
  • app/models/simplefin_item/sync_complete_event.rb (1 hunks)
  • app/models/simplefin_item/syncer.rb (1 hunks)
  • app/views/accounts/_account.html.erb (1 hunks)
  • app/views/accounts/index.html.erb (1 hunks)
  • app/views/accounts/new/_method_selector.html.erb (2 hunks)
  • app/views/accounts/show/_header.html.erb (1 hunks)
  • app/views/settings/_settings_nav.html.erb (1 hunks)
  • app/views/simplefin_items/_simplefin_item.html.erb (1 hunks)
  • app/views/simplefin_items/_subtype_select.html.erb (1 hunks)
  • app/views/simplefin_items/index.html.erb (1 hunks)
  • app/views/simplefin_items/new.html.erb (1 hunks)
  • app/views/simplefin_items/setup_accounts.html.erb (1 hunks)
  • app/views/simplefin_items/show.html.erb (1 hunks)
  • app/views/transactions/show.html.erb (1 hunks)
  • config/routes.rb (1 hunks)
  • db/migrate/20250807143728_create_simplefin_items.rb (1 hunks)
  • db/migrate/20250807143819_create_simplefin_accounts.rb (1 hunks)
  • db/migrate/20250807144230_add_simplefin_account_id_to_accounts.rb (1 hunks)
  • db/migrate/20250807144857_add_external_id_to_transactions.rb (1 hunks)
  • db/migrate/20250807163541_add_pending_account_setup_to_simplefin_items.rb (1 hunks)
  • db/migrate/20250807170943_add_subtype_to_accountables.rb (1 hunks)
  • db/migrate/20250808141424_add_balance_date_to_simplefin_accounts.rb (1 hunks)
  • db/migrate/20250808143007_add_extra_simplefin_account_fields.rb (1 hunks)
  • db/schema.rb (13 hunks)
  • test/controllers/properties_controller_test.rb (1 hunks)
  • test/controllers/simplefin_items_controller_test.rb (1 hunks)
  • test/fixtures/accounts.yml (0 hunks)
  • test/fixtures/depositories.yml (1 hunks)
  • test/models/account_test.rb (1 hunks)
  • test/models/plaid_account/processor_test.rb (1 hunks)
  • test/models/simplefin_account_test.rb (1 hunks)
  • test/models/simplefin_item_test.rb (1 hunks)
💤 Files with no reviewable changes (1)
  • test/fixtures/accounts.yml
🧰 Additional context used
🧬 Code Graph Analysis (26)
test/models/simplefin_item_test.rb (2)
test/models/simplefin_account_test.rb (1)
  • setup (3-84)
test/controllers/simplefin_items_controller_test.rb (1)
  • setup (3-45)
app/controllers/accounts_controller.rb (3)
app/controllers/accountable_sparklines_controller.rb (1)
  • family (26-28)
app/models/assistant/function.rb (1)
  • family (78-80)
app/models/sync.rb (1)
  • family (190-196)
app/helpers/accounts_helper.rb (1)
app/models/plaid_item/webhook_processor.rb (1)
  • plaid_item (40-42)
test/models/simplefin_account_test.rb (2)
app/models/simplefin_account.rb (2)
  • upsert_simplefin_snapshot! (9-27)
  • upsert_simplefin_transactions_snapshot! (29-35)
app/models/account/current_balance_manager.rb (1)
  • current_balance (16-23)
db/migrate/20250807143819_create_simplefin_accounts.rb (3)
db/migrate/20250807143728_create_simplefin_items.rb (2)
  • change (1-20)
  • change (2-19)
db/migrate/20250807144230_add_simplefin_account_id_to_accounts.rb (2)
  • change (1-5)
  • change (2-4)
db/migrate/20250807144857_add_external_id_to_transactions.rb (2)
  • change (1-6)
  • change (2-5)
db/migrate/20250807143728_create_simplefin_items.rb (2)
db/migrate/20250807143819_create_simplefin_accounts.rb (2)
  • change (1-20)
  • change (2-19)
db/migrate/20250807144230_add_simplefin_account_id_to_accounts.rb (2)
  • change (1-5)
  • change (2-4)
app/models/simplefin_item/syncer.rb (5)
app/models/simplefin_account/processor.rb (2)
  • attr_reader (1-109)
  • initialize (4-6)
app/models/simplefin_item/importer.rb (2)
  • attr_reader (1-107)
  • initialize (4-7)
app/models/provider/simplefin.rb (3)
  • attr_reader (79-86)
  • initialize (7-8)
  • initialize (82-85)
app/controllers/simplefin_items_controller.rb (1)
  • sync (49-52)
app/models/simplefin_item.rb (3)
  • import_latest_simplefin_data (32-34)
  • process_accounts (36-40)
  • schedule_account_syncs (42-50)
app/models/family.rb (2)
app/models/account.rb (1)
  • include (1-212)
app/models/plaid_item.rb (1)
  • include (1-120)
test/models/account_test.rb (1)
app/models/account.rb (2)
  • short_subtype_label (185-187)
  • long_subtype_label (190-192)
app/javascript/controllers/account_type_selector_controller.js (1)
app/javascript/controllers/multi_select_controller.js (1)
  • event (17-17)
app/models/family/simplefin_connectable.rb (2)
app/models/provider/simplefin.rb (1)
  • claim_access_url (10-25)
app/models/concerns/syncable.rb (1)
  • sync_later (14-35)
app/models/simplefin_item/provided.rb (1)
app/controllers/simplefin_items_controller.rb (1)
  • new (12-14)
test/models/plaid_account/processor_test.rb (9)
app/models/plaid_account/investments/transactions_processor.rb (1)
  • account (22-24)
app/models/plaid_account/investments/holdings_processor.rb (1)
  • account (43-45)
app/models/plaid_account/liabilities/credit_processor.rb (1)
  • account (18-20)
app/models/plaid_account/liabilities/mortgage_processor.rb (1)
  • account (18-20)
app/models/plaid_account/liabilities/student_loan_processor.rb (1)
  • account (20-22)
app/models/plaid_account/transactions/processor.rb (1)
  • account (41-43)
app/models/plaid_entry/processor.rb (1)
  • account (52-54)
app/controllers/accountable_sparklines_controller.rb (1)
  • accountable (30-32)
app/models/concerns/enrichable.rb (1)
  • lock_attr! (61-63)
test/controllers/properties_controller_test.rb (1)
app/controllers/accountable_sparklines_controller.rb (1)
  • accountable (30-32)
app/models/simplefin_item/importer.rb (4)
app/models/provider/simplefin.rb (4)
  • attr_reader (79-86)
  • initialize (7-8)
  • initialize (82-85)
  • get_accounts (27-66)
app/models/simplefin_item/provided.rb (1)
  • simplefin_provider (4-6)
app/models/simplefin_item.rb (2)
  • upsert_simplefin_snapshot! (52-58)
  • upsert_simplefin_institution_snapshot! (60-69)
app/models/concerns/syncable.rb (1)
  • last_synced_at (53-55)
app/models/simplefin_account/processor.rb (2)
app/models/simplefin_item/importer.rb (2)
  • attr_reader (1-107)
  • initialize (4-7)
app/models/provider/simplefin.rb (3)
  • attr_reader (79-86)
  • initialize (7-8)
  • initialize (82-85)
test/controllers/simplefin_items_controller_test.rb (2)
test/models/simplefin_account_test.rb (1)
  • setup (3-84)
test/models/simplefin_item_test.rb (1)
  • setup (3-64)
db/migrate/20250807144230_add_simplefin_account_id_to_accounts.rb (2)
db/migrate/20250807143728_create_simplefin_items.rb (2)
  • change (1-20)
  • change (2-19)
db/migrate/20250807143819_create_simplefin_accounts.rb (2)
  • change (1-20)
  • change (2-19)
app/models/provider/simplefin.rb (3)
app/models/account.rb (1)
  • start_date (166-169)
app/models/simplefin_account/processor.rb (2)
  • initialize (4-6)
  • attr_reader (1-109)
app/models/simplefin_item/importer.rb (2)
  • initialize (4-7)
  • attr_reader (1-107)
app/models/plaid_account/processor.rb (2)
app/models/plaid_account/type_mappable.rb (1)
  • map_accountable (6-17)
app/models/concerns/enrichable.rb (1)
  • enrich_attributes (34-51)
app/models/simplefin_item.rb (5)
app/models/provider/simplefin.rb (1)
  • include (1-87)
app/models/simplefin_account.rb (1)
  • upsert_simplefin_snapshot! (9-27)
app/models/simplefin_item/provided.rb (1)
  • simplefin_provider (4-6)
app/models/simplefin_item/importer.rb (1)
  • import (9-35)
app/models/concerns/syncable.rb (1)
  • sync_later (14-35)
app/models/simplefin_item/sync_complete_event.rb (4)
app/models/provider/simplefin.rb (3)
  • attr_reader (79-86)
  • initialize (7-8)
  • initialize (82-85)
app/models/simplefin_item/syncer.rb (2)
  • attr_reader (1-34)
  • initialize (4-6)
app/models/plaid_item/accounts_snapshot.rb (1)
  • accounts (10-12)
app/models/concerns/syncable.rb (1)
  • broadcast_sync_complete (45-47)
app/models/simplefin_account.rb (2)
app/models/simplefin_item.rb (1)
  • upsert_simplefin_snapshot! (52-58)
app/models/account/current_balance_manager.rb (1)
  • current_balance (16-23)
app/controllers/simplefin_items_controller.rb (3)
app/models/family/simplefin_connectable.rb (1)
  • create_simplefin_item! (12-24)
app/models/account.rb (1)
  • create_from_simplefin_account (78-99)
app/models/concerns/syncable.rb (1)
  • sync_later (14-35)
app/models/account.rb (2)
app/models/simplefin_account.rb (1)
  • belongs_to (1-93)
lib/money/arithmetic.rb (1)
  • abs (41-43)
db/schema.rb (2)
app/controllers/simplefin_items_controller.rb (1)
  • index (4-7)
app/controllers/accounts_controller.rb (1)
  • index (5-11)
🪛 YAMLlint (1.37.1)
test/fixtures/depositories.yml

[error] 2-2: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (41)
db/migrate/20250807143819_create_simplefin_accounts.rb (1)

8-9: Review decimal precision for new balance fields

The new columns in db/migrate/20250807143819_create_simplefin_accounts.rb use:

t.decimal :current_balance,   precision: 19, scale: 4
t.decimal :available_balance, precision: 19, scale: 4

• This matches the existing definitions in

  • account_balances.balance (precision: 19, scale: 4)
  • security_prices.price (precision: 19, scale: 4)

• With precision 19 and scale 4 you get up to 15 integer digits and 4 decimals (max value ≈ 9 999 999 999 999 999.9999).

Please confirm that:

  • This range covers your largest expected balances (including any high-value or crypto use cases).
  • It aligns with the SimpleFin API’s currency precision requirements.
db/migrate/20250807163541_add_pending_account_setup_to_simplefin_items.rb (1)

3-3: LGTM!

The migration properly adds a boolean column with appropriate defaults and null constraints. The default: false, null: false ensures data consistency.

app/models/plaid_account/processor.rb (3)

37-41: LGTM! Improved accountable initialization.

The explicit accountable creation and assignment improves code clarity and ensures the accountable exists before enrichment operations. This pattern aligns well with the subtype delegation changes mentioned in the AI summary.


45-50: LGTM! Consistent use of enrichable pattern.

The refactor to use enrich_attributes for the name field maintains consistency with the enrichable pattern and properly respects locked attributes. The source attribution to "plaid" is appropriate for auditing.


52-58: Verify accountable exists before enrichment.

While the code creates the accountable at lines 37-41, there's a potential race condition or edge case where account.accountable could still be nil when reaching this enrichment call.

Consider adding a safety check:

 # Enrich subtype on the accountable, respecting locks
-account.accountable.enrich_attributes(
+if account.accountable
+  account.accountable.enrich_attributes(
     {
       subtype: map_subtype(plaid_account.plaid_type, plaid_account.plaid_subtype)
     },
     source: "plaid"
-  )
+  )
+end
db/migrate/20250807144857_add_external_id_to_transactions.rb (1)

3-4: LGTM! Proper external ID implementation.

The migration correctly adds an external_id column with an index, which is essential for efficient lookups when syncing with external systems like SimpleFin. The index will improve performance for deduplication queries.

db/migrate/20250807143728_create_simplefin_items.rb (2)

4-4: LGTM!

The foreign key relationship to family using UUID type is correctly implemented and aligns with the existing database schema patterns.


14-15: LGTM!

Using jsonb columns for raw payloads is appropriate for storing flexible API response data from SimpleFin.

app/views/transactions/show.html.erb (1)

87-87: Minor formatting improvement applied.

The ERB tag closing was moved to the same line for consistency. This is a good cleanup change.

app/controllers/accounts_controller.rb (1)

8-8: LGTM!

The addition of @simplefin_items follows the same pattern as @plaid_items and properly integrates the SimpleFin data into the accounts index view.

test/models/plaid_account/processor_test.rb (2)

52-52: LGTM! Subtype delegation change implemented correctly.

The test correctly updates the accountable's subtype instead of the account's subtype, aligning with the architectural change where subtype is now delegated to the accountable.


55-55: LGTM! Lock attribute call updated correctly.

The lock_attr! call has been correctly moved from the account to the accountable, maintaining consistency with the subtype delegation pattern.

test/controllers/properties_controller_test.rb (2)

42-45: LGTM! Nested attributes update implemented correctly.

The test correctly uses accountable_attributes to update the subtype through the nested relationship, which aligns with the controller changes that moved subtype handling to the accountable level.


52-52: LGTM! Test expectation updated correctly.

The test expectation correctly reflects that account.subtype now delegates to the accountable's subtype, and the value "condominium" matches what was set in the nested attributes.

app/views/accounts/_account.html.erb (1)

19-24: LGTM! Safe navigation prevents nil errors.

The use of safe navigation (&.) throughout the chain (account.simplefin_account&.org_data&.dig('name')) properly handles cases where SimpleFin data might not be present, preventing potential nil reference errors.

db/migrate/20250808143007_add_extra_simplefin_account_fields.rb (1)

1-6: LGTM!

The migration correctly adds JSONB columns for storing extra data and organization information from SimpleFin. The column types and names align with the usage in the SimpleFin integration codebase.

config/routes.rb (1)

254-261: LGTM!

The SimpleFin routes are properly structured with all necessary actions for the SimpleFin integration workflow. The member routes for sync, setup_accounts, and complete_account_setup align with the controller implementation and match the pattern used for Plaid items.

app/views/accounts/index.html.erb (2)

24-24: LGTM!

The empty state check now correctly includes all three connection types (manual, Plaid, and SimpleFin) to determine when to show the empty state.


32-35: LGTM!

SimpleFin items are properly integrated into the accounts index view, following the same pattern as Plaid items with consistent sorting by created_at.

app/models/simplefin_item/provided.rb (1)

1-7: LGTM!

The module correctly provides a memoized SimpleFin provider instance following the same pattern as the existing Plaid integration. The implementation is clean and follows Rails conventions.

app/helpers/accounts_helper.rb (1)

7-15: LGTM!

The centralized sync path logic correctly handles all three account types (Plaid, SimpleFin, and manual) with proper fallback behavior. This eliminates code duplication and provides a consistent interface for determining sync paths.

test/models/account_test.rb (2)

18-24: LGTM!

The test setup correctly creates an Investment instance as the accountable and properly wires it to the account. This aligns with the delegation pattern where subtype is delegated to the accountable.


30-30: LGTM!

The test correctly updates the subtype on the accountable object rather than the account itself, which aligns with the delegation pattern where subtype is delegated to the accountable.

db/migrate/20250807144230_add_simplefin_account_id_to_accounts.rb (1)

1-5: LGTM!

The migration correctly adds a nullable UUID foreign key reference to link accounts with SimpleFin accounts. The use of add_reference with proper options (null: true, foreign_key: true, type: :uuid) follows Rails best practices and maintains referential integrity.

app/models/family.rb (1)

2-2: LGTM!

The SimplefinConnectable mixin is correctly added to the Family model, enabling SimpleFin integration capabilities alongside the existing Plaid integration. The placement in the include list follows a logical order.

app/controllers/properties_controller.rb (1)

90-93: No action needed: nested attributes for accountable are already enabled
The Account model declares
accepts_nested_attributes_for :accountable, update_only: true
(app/models/account.rb:33), so permitting accountable_attributes (including :subtype) in property_params will take effect as expected.

app/views/simplefin_items/index.html.erb (1)

10-15: Nice use of DS::Link for primary action.

Clear CTA and consistent styling with design system.

app/views/simplefin_items/_subtype_select.html.erb (1)

1-1: Ignore account_type casing mismatch suggestion
The account_type string is sourced from the static @account_type_options in SimplefinItemsController (e.g. "Depository"), not from upstream payloads—so the equality check against "Depository" will always match. No change required.

Likely an incorrect or invalid review comment.

app/views/simplefin_items/new.html.erb (2)

9-15: Form setup is sound; double-check Turbo opt-out is necessary.

If the create action responds with standard redirects/stream, you can likely keep Turbo enabled. If the provider flow depends on a full reload, leaving data: { turbo: false } is fine.

Would you like a follow-up commit to enable Turbo and adjust the controller response if needed?


17-22: Good guidance UX and external link handling.

Clear instructions and target: "_blank" link are appropriate.

app/models/account/linkable.rb (1)

9-12: manual? now correctly includes SimpleFin linkage
Verified that manual? (alias for !linked?) reflects both plaid_account_id and simplefin_account_id. The sole usage of account.manual? in app/components/UI/account/activity_feed.html.erb will now behave as expected, and there are no ActiveRecord scopes relying on the old “lack of Plaid” logic. No further changes needed.

db/migrate/20250808141424_add_balance_date_to_simplefin_accounts.rb (1)

1-5: Migration is straightforward and reversible via schema rollback.

No defaults or backfills—safe for online migration.

app/javascript/controllers/account_type_selector_controller.js (1)

29-43: No changes needed for subtype matching
The selectedType values ("Depository", "CreditCard", "Investment", "Loan", "OtherAsset", "Skip") are defined in @account_type_options and used verbatim in your data-type attributes via the same Ruby keys. This guarantees exact matches between the select’s value and the container’s data-type. The initial if (selectedType === 'Skip') return check correctly handles the Skip option, and there’s no variance in casing or format to normalize.

Likely an incorrect or invalid review comment.

app/models/family/simplefin_connectable.rb (1)

12-24: LGTM: Centralizes SimpleFin item creation and schedules initial sync

Creation logic is appropriately moved here (per earlier feedback), with a sensible default name and immediate sync scheduling.

app/models/simplefin_item/syncer.rb (1)

20-29: LGTM: Post-import processing and downstream account sync scheduling

Processing and fan-out to account-level syncs aligns with the existing sync pipeline.

app/models/simplefin_item/importer.rb (1)

98-100: Verified requires_update status: enum defined correctly
The SimplefinItem model declares:

enum :status, { good: "good", requires_update: "requires_update" }, default: :good

and the status column exists in db/schema.rb. No further changes needed.

app/models/simplefin_account/processor.rb (1)

90-108: parse_date errors are already isolated to a single transaction
The ArgumentError you see in parse_date is caught by the broad rescue in process_transaction (lines 66–69), so only that one transaction is skipped—the overall sync continues. By contrast, parse_amount quietly falls back to zero but still lets you know via a log. If you really want consistency, you could have parse_amount raise and let the outer rescue skip bad‐amount transactions; otherwise, no changes to parse_date are required here.

Likely an incorrect or invalid review comment.

app/models/simplefin_account.rb (1)

63-65: Logging improvement already addressed

Good to see the warning log was added as suggested in the previous review. This properly tracks malformed currency URLs from SimpleFin.

app/controllers/simplefin_items_controller.rb (2)

28-41: Improve error handling specificity

The generic rescue => e block might catch and suppress important errors that should bubble up to the application error handler (like database errors or programming errors). It's better to be specific about which exceptions you want to handle.

Consider catching only expected exceptions:

-    rescue => e
+    rescue StandardError => e
+      # Only catch StandardError and its descendants, not system errors
+      raise unless e.is_a?(Net::HTTPError) || e.is_a?(Timeout::Error) || e.is_a?(JSON::ParserError)
       Rails.logger.error("SimpleFin connection error: #{e.message}")
       render_error("An unexpected error occurred. Please try again or contact support.", setup_token)

Likely an incorrect or invalid review comment.


107-112: No nested balance is passed to CreditCard
The create_from_simplefin_account workflow merges the top-level balance into the Account’s own cash_balance and only places { subtype, available_credit } into accountable_attributes. At no point does balance enter the nested CreditCard attributes, so you won’t see an UnknownAttributeError: balance on the CreditCard model.

db/schema.rb (1)

807-809: Good addition of external_id for transaction deduplication

The addition of external_id with an index on the transactions table is a good practice for preventing duplicate transaction imports from SimpleFin. This will help maintain data integrity when syncing transactions.

Comment on lines +92 to +98
def complete_account_setup
account_types = params[:account_types] || {}
account_subtypes = params[:account_subtypes] || {}

account_types.each do |simplefin_account_id, selected_type|
# Skip accounts that the user chose not to add
next if selected_type == "Skip"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the constant reference error that causes crashes when accessing credit card accounts

Based on the PR comments, users are encountering uninitialized constant Skip errors. The issue is on line 98 where selected_type == "Skip" is being checked as a string, but the error suggests the code might be trying to reference it as a constant elsewhere.

The error is likely not in this exact line but related to how the Skip value is being processed. Let me verify if there's an issue with the account creation logic that might be causing the reported errors.

#!/bin/bash
# Check for any references to Skip as a constant in the codebase
rg "Skip[^'\"]" --type ruby

# Check the Account model's create_from_simplefin_account method implementation
ast-grep --pattern 'def create_from_simplefin_account($$$)'

# Look for any Skip-related constant definitions
rg "^\s*(class|module)\s+Skip" --type ruby

Comment on lines +84 to +86
if account_type == "CreditCard" || account_type == "Loan"
balance = balance.abs
end
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify balance conversion logic

The comment states SimpleFin returns negative balances for credit cards, but the logic uses abs for both CreditCard and Loan types. Verify this is correct for loans as well.


🌐 Web query:

Does SimpleFin API return negative balances for loan accounts similar to credit cards, or only for credit card accounts?

💡 Result:

Short answer: SimpleFIN itself does not guarantee that loan account balances will be returned as negative. The SimpleFIN protocol documents the balance and transaction-sign conventions but does not mandate “loan = negative” semantics — institutions/servers can and do return either sign. (simplefin.org)

Supporting details and recommendations

  • The SimpleFIN spec defines "balance" as a numeric string and states that transaction amounts use the convention "positive = money deposited into the account" (so signs are relative to cash flow), but it does not spell out that loans must be negative like some other APIs do. That means how a loan or credit-card balance is signed is up to the server implementation. (simplefin.org)
  • In practice developers have reported inconsistent signs for liability accounts (credit cards, car loans, mortgages) when importing via SimpleFIN — some institutions return positive balances for loans or credit cards, which can make them look like assets unless you normalize them. (github.com, reddit.com)
  • By contrast, APIs such as Plaid explicitly document sign semantics for credit and loan accounts (credit: positive = amount owed; negative = lender owes you; loans: typically positive = principal remaining, negative only if lender owes you). If you need that behavior, you should normalize SimpleFIN data in your app. (plaid.com)

Practical approach (recommended)

  • Treat the value from SimpleFIN as “raw.” Detect whether the account is a liability (credit card / loan / mortgage) from whatever metadata your SimpleFIN server provides (account name/type in extra fields or org conventions). Then convert to the sign convention your app expects (for example, if you want liabilities to appear negative for net-worth calculations, set normalized_balance = -abs(raw_balance) for liabilities).
  • Always test with the specific institutions you plan to support — SimpleFIN servers vary by bank and you’ll see different behavior across providers. (simplefin.org, github.com)

If you want, tell me:

  • which institutions or account types you’re integrating and what sign convention your app expects, and I’ll suggest specific normalization logic or sample code.

Citations:


Verify liability account sign normalization

The SimpleFIN spec does not guarantee that loan balances will be returned as negative—implementations vary by institution. Right now you’re calling balance.abs for both credit-card and loan accounts, which forces a positive value whether SimpleFIN returns + or -. That’s fine if your app treats liabilities as positive “debt” amounts, but if you need liabilities to appear negative (e.g. for net-worth calculations), you should instead normalize to -balance.abs.

Please confirm your intended sign convention and adjust accordingly:

  • If you want liabilities (CreditCard and Loan) to be negative:
    if %w[CreditCard Loan].include?(account_type)
      balance = -balance.abs
    end
  • If you want liabilities to be shown as positive debt amounts, leaving the code as-is (balance.abs) is correct.
🤖 Prompt for AI Agents
In app/models/account.rb around lines 84 to 86, the code currently forces
liabilities (CreditCard and Loan) to be positive by calling balance.abs, but the
app’s sign convention for liabilities must be explicit; if you want liabilities
represented as negative amounts for net-worth calculations, change the
normalization to set balance = -balance.abs for those account types, otherwise
leave as balance.abs if you intend liabilities to be shown as positive debt
amounts—update the conditional accordingly (use a clear %w[...] include? check)
and ensure tests reflect the chosen convention.

Comment on lines +112 to +113
if simplefin_account.available_balance.present? && simplefin_account.available_balance > 0
attributes[:available_credit] = simplefin_account.available_balance
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Potential bug with balance logic for credit cards

Based on the PR comments, there's an issue with credit card accounts showing "unknown attribute 'balance' for CreditCard". The condition checks if available_balance > 0 before setting available_credit, but for credit cards, the available balance from SimpleFin might be negative (representing available credit). This logic might need adjustment.

         when "CreditCard"
           # For credit cards, available_balance often represents available credit
-          if simplefin_account.available_balance.present? && simplefin_account.available_balance > 0
-            attributes[:available_credit] = simplefin_account.available_balance
+          # SimpleFin may return negative values for available credit
+          if simplefin_account.available_balance.present?
+            attributes[:available_credit] = simplefin_account.available_balance.abs
           end

🏁 Script executed:

#!/bin/bash
# Check if CreditCard model has a balance attribute or if it's only on Account
echo "Checking CreditCard model attributes..."
rg "class CreditCard" app/models --type ruby -A 20

# Check schema for credit_cards table
rg "create_table.*credit_cards" db/schema.rb -A 15

# Check if there's a balance attribute definition
rg "attribute.*balance|attr_.*balance" app/models/credit_card.rb

Length of output: 1838


Update credit card available_credit logic to handle negative balances

For CreditCard accounts, SimpleFin may return a negative available_balance to represent available credit. Remove the > 0 guard and assign the absolute value instead.

• File: app/models/account.rb (around lines 112–115)

         when "CreditCard"
           # SimpleFin may return negative values for available credit
-          if simplefin_account.available_balance.present? && simplefin_account.available_balance > 0
-            attributes[:available_credit] = simplefin_account.available_balance
+          if simplefin_account.available_balance.present?
+            attributes[:available_credit] = simplefin_account.available_balance.abs
           end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if simplefin_account.available_balance.present? && simplefin_account.available_balance > 0
attributes[:available_credit] = simplefin_account.available_balance
when "CreditCard"
# SimpleFin may return negative values for available credit
if simplefin_account.available_balance.present?
attributes[:available_credit] = simplefin_account.available_balance.abs
end
🤖 Prompt for AI Agents
In app/models/account.rb around lines 112–113, the current guard only assigns
available_credit when simplefin_account.available_balance > 0; update it so
CreditCard accounts handle negative balances by removing the `> 0` guard and
assigning the absolute value instead: when
simplefin_account.available_balance.present? and the account is a CreditCard,
set attributes[:available_credit] = simplefin_account.available_balance.abs; for
non-credit-card accounts preserve the existing behavior (only assign when
balance > 0).


included do
belongs_to :plaid_account, optional: true
belongs_to :simplefin_account, optional: true
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Association looks good; add exclusivity guard to prevent double-linking (Plaid + SimpleFin).

Right now an account could potentially have both plaid_account_id and simplefin_account_id set. Enforce mutual exclusivity to avoid ambiguous data sources.

Apply this diff:

   included do
     belongs_to :plaid_account, optional: true
     belongs_to :simplefin_account, optional: true
+    validate :provider_link_exclusivity
   end
+
+  private
+    def provider_link_exclusivity
+      if plaid_account_id.present? && simplefin_account_id.present?
+        errors.add(:base, "Account cannot be linked to multiple providers (Plaid and SimpleFin)")
+      end
+    end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
belongs_to :simplefin_account, optional: true
# app/models/account/linkable.rb
module Account
module Linkable
extend ActiveSupport::Concern
included do
belongs_to :plaid_account, optional: true
belongs_to :simplefin_account, optional: true
validate :provider_link_exclusivity
end
private
def provider_link_exclusivity
if plaid_account_id.present? && simplefin_account_id.present?
errors.add(
:base,
"Account cannot be linked to multiple providers (Plaid and SimpleFin)"
)
end
end
end
end
🤖 Prompt for AI Agents
In app/models/account/linkable.rb around line 6, currently the model allows both
plaid_account_id and simplefin_account_id to be set; add a validation to enforce
mutual exclusivity so an account cannot be linked to both sources
simultaneously. Implement a custom validator method (e.g., validate
:ensure_single_account_source) that adds an error on :base when both
plaid_account_id and simplefin_account_id are present, and ensure the model
returns invalid in that case; update or add specs to cover the validation.

end

def get_info(base_url)
response = HTTParty.get("#{base_url}/info")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add explicit SSL/TLS options for consistency

For consistency with other requests, add SSL/TLS options explicitly.

-    response = HTTParty.get("#{base_url}/info")
+    response = HTTParty.get("#{base_url}/info", verify: true, ssl_verify_mode: :peer)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = HTTParty.get("#{base_url}/info")
response = HTTParty.get("#{base_url}/info", verify: true, ssl_verify_mode: :peer)
🤖 Prompt for AI Agents
In app/models/provider/simplefin.rb around line 69, the HTTParty.get call does
not supply explicit SSL/TLS options; update the call to include the same SSL
options used elsewhere in the codebase for consistency (for example include
verify: true, ssl_version: 'TLSv1_2' and the project's ssl_ca_file or cert store
setting, or mirror the exact options used in neighboring requests), ensuring you
pass the SSL options as HTTParty request parameters so the request enforces peer
verification and the desired TLS version.

create_table :simplefin_accounts, id: :uuid do |t|
t.references :simplefin_item, null: false, foreign_key: true, type: :uuid
t.string :name
t.string :account_id
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add unique constraint on account_id.

The account_id field appears to be an external identifier from SimpleFin. Without a unique constraint, duplicate accounts could be created, leading to data integrity issues.

Apply this diff to add uniqueness constraint:

-      t.string :account_id
+      t.string :account_id, null: false

And update the index to be unique:

-      t.index :account_id
+      t.index :account_id, unique: true
🤖 Prompt for AI Agents
In db/migrate/20250807143819_create_simplefin_accounts.rb around line 6, the
account_id column lacks a uniqueness constraint; modify the migration to enforce
uniqueness by adding a unique index on account_id (for example add_index
:simplefin_accounts, :account_id, unique: true or t.index :account_id, unique:
true) and ensure the column definition and index are applied together in the
migration (and if this migration already ran in environments, create a new
migration to add the unique index instead).

Comment on lines 193 to +196
t.date "expiration_date"
t.decimal "annual_fee", precision: 10, scale: 2
t.jsonb "locked_attributes", default: {}
t.string "subtype"
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Credit card schema doesn't have a balance attribute

According to the PR comments, users are encountering "unknown attribute 'balance' for CreditCard" errors. The schema confirms that the credit_cards table indeed doesn't have a balance column - it only has available_credit, while the balance is stored in the parent accounts table.

This confirms the issue reported in the PR comments. The error occurs because the code is trying to set balance directly on the CreditCard model instead of on the Account model.

Let me verify how the polymorphic relationship is structured:


🏁 Script executed:

#!/bin/bash
# Check how CreditCard relates to Account
rg "class CreditCard" -A 20 --type ruby

# Check if Account uses accepts_nested_attributes_for
rg "accepts_nested_attributes_for.*accountable" --type ruby

# Look for the specific line causing the balance assignment issue
rg "accountable_attributes.*balance" --type ruby

Length of output: 3754


Use initial_balance instead of undefined balance on CreditCard creation

The credit_cards table (and the CreditCard model) does not have a balance column, and the parent accounts table expects its opening balance in the initial_balance field via nested attributes. Update any code or tests that pass a balance param to use initial_balance (or add explicit mapping for balanceinitial_balance in your controller/concern).

• In test/controllers/credit_cards_controller_test.rb, change:

  post credit_cards_path, params: {
    account: {
-       balance: 1000,
+       initial_balance: 1000,
      …  
    }
  }

• Review your AccountableResource concern or controller strong params—ensure only initial_balance is permitted (or add logic to translate a balance key into initial_balance).
• Confirm any API clients/docs are updated to reflect the correct initial_balance attribute.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In db/schema.rb around lines 193 to 196 the credit_cards table defines no
`balance` column (accounts expect an opening balance via `initial_balance`), so
tests and controller logic that pass `balance` must be updated: change any test
data in test/controllers/credit_cards_controller_test.rb to use
`initial_balance` in nested account attributes, and in your AccountableResource
concern or the credit cards controller permit `initial_balance` (or add explicit
mapping that translates incoming `balance` => `initial_balance` before
strong-params filtering); finally update API docs/clients to reference
`initial_balance` instead of `balance`.

Comment on lines +685 to +703
create_table "simplefin_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "simplefin_item_id", null: false
t.string "name"
t.string "account_id"
t.string "currency"
t.decimal "current_balance", precision: 19, scale: 4
t.decimal "available_balance", precision: 19, scale: 4
t.string "account_type"
t.string "account_subtype"
t.jsonb "raw_payload"
t.jsonb "raw_transactions_payload"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "balance_date"
t.jsonb "extra"
t.jsonb "org_data"
t.index ["account_id"], name: "index_simplefin_accounts_on_account_id"
t.index ["simplefin_item_id"], name: "index_simplefin_accounts_on_simplefin_item_id"
end
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing index on simplefin_accounts.account_id foreign key reference

The simplefin_accounts table has an account_id column with an index, but based on the foreign key relationships in the schema, this appears to be linking to the accounts table. However, there's no foreign key constraint defined for simplefin_accounts.account_id to reference accounts.id.

This missing foreign key constraint could lead to data integrity issues. Add the foreign key constraint:

add_foreign_key "simplefin_accounts", "accounts"
🤖 Prompt for AI Agents
In db/schema.rb around lines 685 to 703, the simplefin_accounts.account_id
column lacks a foreign key constraint to accounts.id; add a migration that adds
the foreign key (e.g. add_foreign_key "simplefin_accounts", "accounts", column:
"account_id") so the column references accounts.id (keep or reuse the existing
index), and run migrations to update the schema.rb accordingly.

gem "redcarpet"
gem "stripe"
gem "plaid"
gem "httparty"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider pinning HTTParty to a specific version range.

Adding httparty without version constraints could lead to unexpected behavior if breaking changes are introduced in future releases. For production stability, it's recommended to specify a version constraint.

Apply this diff to pin HTTParty to a stable version range:

-gem "httparty"
+gem "httparty", "~> 0.22.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
gem "httparty"
-gem "httparty"
+gem "httparty", "~> 0.22.0"
🤖 Prompt for AI Agents
In Gemfile around line 72, the httparty gem is added without a version
constraint; update that line to pin httparty to a stable version range (for
example "~> 0.18" or another vetted range used by the project), then run bundle
install (or bundle update httparty) to update Gemfile.lock and run the test
suite to ensure compatibility.

Comment on lines +46 to +63
test "scopes work correctly" do
# Create one for deletion
item_for_deletion = SimplefinItem.create!(
family: @family,
name: "Delete Me",
access_url: "https://example.com/delete_token",
scheduled_for_deletion: true
)

active_items = SimplefinItem.active
ordered_items = SimplefinItem.ordered

assert_includes active_items, @simplefin_item
refute_includes active_items, item_for_deletion

assert_equal [ @simplefin_item, item_for_deletion ].sort_by(&:created_at).reverse,
ordered_items.to_a
end
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Stabilize ordering test to avoid flakiness on same-second timestamps.

If the DB/time resolution collapses timestamps to the same second, order can be non-deterministic.

Apply this diff to ensure distinct created_at:

   test "scopes work correctly" do
     # Create one for deletion
     item_for_deletion = SimplefinItem.create!(
       family: @family,
       name: "Delete Me",
       access_url: "https://example.com/delete_token",
       scheduled_for_deletion: true
     )
 
+    # Ensure created_at differs for deterministic ordering
+    travel 1.second
+    @simplefin_item.touch
+    travel_back
+
     active_items = SimplefinItem.active
     ordered_items = SimplefinItem.ordered

And include TimeHelpers if not already:

# Add at top of this test file or in test_helper.rb
include ActiveSupport::Testing::TimeHelpers
🤖 Prompt for AI Agents
In test/models/simplefin_item_test.rb around lines 46 to 63, the ordering
assertion is flaky when multiple records share the same-second created_at; make
created_at values deterministic by using ActiveSupport::Testing::TimeHelpers to
advance time between creations (e.g., travel or travel 1.second forward before
creating item_for_deletion) so each record has a distinct timestamp, and add
include ActiveSupport::Testing::TimeHelpers at the top of this test file (or in
test_helper.rb) if not already present.

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

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants