mirror of
https://github.com/we-promise/sure.git
synced 2026-07-27 12:12:13 +00:00
* Add per-account toggle to disable automatic transaction categorization Adds an `enable_category_matcher` boolean (default: true) to accounts so users can opt out of Plaid's automatic category suggestions on a per-account basis. When disabled, newly synced transactions arrive uncategorized so rules or manual assignment take precedence. - New migration adds `enable_category_matcher` column (default true, null: false) - `PlaidEntry::Processor#matched_category` gates the CategoryMatcher call on the account flag - Toggle rendered in the account edit modal for linked accounts (saves via main form submit) - `AccountableResource#account_params` permits the new field - `AccountsController#toggle_category_matcher` action added for potential API use - i18n strings added for label and hint text - Unit test covers the disabled-matcher path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Only show category matcher toggle for Plaid-linked accounts Only PlaidEntry::Processor honors enable_category_matcher, but the toggle rendered for every linked account, silently doing nothing for other providers. Adds Account::Linkable#supports_category_matcher? (covering both the legacy plaid_account_id link and AccountProvider rows) and gates the form toggle on it. The SimpleFIN TODO now also points at this helper so the toggle appears once SimpleFIN matching lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add controller tests for category matcher toggle persistence and rendering Covers the two paths flagged in review as untested: the flag persisting through the shared AccountableResource#update action via account_params (both disable and re-enable), and the edit form rendering the toggle only for accounts where supports_category_matcher? is true. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Signed-off-by: Mike Lloyd <49411532+mike-lloyd03@users.noreply.github.com> Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
88 lines
2.8 KiB
Ruby
88 lines
2.8 KiB
Ruby
module Account::Linkable
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
# New generic provider association
|
|
has_many :account_providers, dependent: :destroy
|
|
|
|
# Legacy provider associations - kept for backward compatibility during migration
|
|
belongs_to :plaid_account, optional: true
|
|
belongs_to :simplefin_account, optional: true
|
|
|
|
# SQL-level mirror of `linked?`. Use this for set-based checks (e.g. bulk
|
|
# `EXISTS`) so both definitions stay in sync. If `linked?` adds a new
|
|
# provider source, update this scope too.
|
|
scope :linked, -> {
|
|
left_outer_joins(:account_providers)
|
|
.where(
|
|
"account_providers.id IS NOT NULL OR accounts.plaid_account_id IS NOT NULL OR accounts.simplefin_account_id IS NOT NULL"
|
|
)
|
|
.distinct
|
|
}
|
|
end
|
|
|
|
# A "linked" account gets transaction and balance data from a third party like Plaid or SimpleFin
|
|
def linked?
|
|
account_providers.any? || plaid_account.present? || simplefin_account.present?
|
|
end
|
|
|
|
# An "offline" or "unlinked" account is one where the user tracks values and
|
|
# adds transactions manually, without the help of a data provider
|
|
def unlinked?
|
|
!linked?
|
|
end
|
|
alias_method :manual?, :unlinked?
|
|
|
|
# Returns the primary provider adapter for this account
|
|
# If multiple providers exist, returns the first one
|
|
def provider
|
|
return nil unless linked?
|
|
|
|
@provider ||= account_providers.first&.adapter
|
|
end
|
|
|
|
# Returns all provider adapters for this account
|
|
def providers
|
|
@providers ||= account_providers.map(&:adapter).compact
|
|
end
|
|
|
|
# Returns the provider adapter for a specific provider type
|
|
def provider_for(provider_type)
|
|
account_provider = account_providers.find_by(provider_type: provider_type)
|
|
account_provider&.adapter
|
|
end
|
|
|
|
# Convenience method to get the provider name
|
|
def provider_name
|
|
# Try new system first
|
|
return provider&.provider_name if provider.present?
|
|
|
|
# Fall back to legacy system
|
|
return "plaid" if plaid_account.present?
|
|
return "simplefin" if simplefin_account.present?
|
|
|
|
nil
|
|
end
|
|
|
|
# Check if account is linked to a specific provider
|
|
def linked_to?(provider_type)
|
|
account_providers.exists?(provider_type: provider_type)
|
|
end
|
|
|
|
# Whether this account's provider applies the category matcher to imported
|
|
# transactions. Only Plaid honors `enable_category_matcher` today; extend this
|
|
# when other providers (e.g. SimpleFIN) wire up category matching.
|
|
def supports_category_matcher?
|
|
plaid_account.present? || linked_to?("PlaidAccount")
|
|
end
|
|
|
|
# Check if holdings can be deleted
|
|
# If account has multiple providers, returns true only if ALL providers allow deletion
|
|
# This prevents deleting holdings that would be recreated on next sync
|
|
def can_delete_holdings?
|
|
return true if unlinked?
|
|
|
|
providers.all?(&:can_delete_holdings?)
|
|
end
|
|
end
|