mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 12:04:08 +00:00
Add support to unlink lunch flow accounts (#318)
* Add support to unlink lunch flow accounts * add support to link and unlink to any provider * Fix tests and query * Let's keep Amr happy about his brand * Wrap unlink operations in a transaction and add error handling. * Fix tests --------- Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
class AccountsController < ApplicationController
|
||||
before_action :set_account, only: %i[sync sparkline toggle_active show destroy]
|
||||
before_action :set_account, only: %i[sync sparkline toggle_active show destroy unlink confirm_unlink select_provider]
|
||||
include Periodable
|
||||
|
||||
def index
|
||||
@@ -17,7 +17,7 @@ class AccountsController < ApplicationController
|
||||
|
||||
def sync_all
|
||||
family.sync_later
|
||||
redirect_to accounts_path, notice: "Syncing accounts..."
|
||||
redirect_to accounts_path, notice: t("accounts.sync_all.syncing")
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -71,10 +71,93 @@ class AccountsController < ApplicationController
|
||||
|
||||
def destroy
|
||||
if @account.linked?
|
||||
redirect_to account_path(@account), alert: "Cannot delete a linked account"
|
||||
redirect_to account_path(@account), alert: t("accounts.destroy.cannot_delete_linked")
|
||||
else
|
||||
@account.destroy_later
|
||||
redirect_to accounts_path, notice: "Account scheduled for deletion"
|
||||
redirect_to accounts_path, notice: t("accounts.destroy.success", type: @account.accountable_type)
|
||||
end
|
||||
end
|
||||
|
||||
def confirm_unlink
|
||||
unless @account.linked?
|
||||
redirect_to account_path(@account), alert: t("accounts.unlink.not_linked")
|
||||
end
|
||||
end
|
||||
|
||||
def unlink
|
||||
unless @account.linked?
|
||||
redirect_to account_path(@account), alert: t("accounts.unlink.not_linked")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
Account.transaction do
|
||||
# Remove new system links (account_providers join table)
|
||||
@account.account_providers.destroy_all
|
||||
|
||||
# Remove legacy system links (foreign keys)
|
||||
@account.update!(plaid_account_id: nil, simplefin_account_id: nil)
|
||||
end
|
||||
|
||||
redirect_to accounts_path, notice: t("accounts.unlink.success")
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
redirect_to account_path(@account), alert: t("accounts.unlink.error", error: e.message)
|
||||
rescue StandardError => e
|
||||
Rails.logger.error "Failed to unlink account #{@account.id}: #{e.message}"
|
||||
redirect_to account_path(@account), alert: t("accounts.unlink.error", error: t("accounts.unlink.generic_error"))
|
||||
end
|
||||
end
|
||||
|
||||
def select_provider
|
||||
if @account.linked?
|
||||
redirect_to account_path(@account), alert: t("accounts.select_provider.already_linked")
|
||||
return
|
||||
end
|
||||
|
||||
@available_providers = []
|
||||
|
||||
# Check SimpleFIN
|
||||
if family.can_connect_simplefin?
|
||||
@available_providers << {
|
||||
name: "SimpleFIN",
|
||||
key: "simplefin",
|
||||
description: "Connect to your bank via SimpleFIN",
|
||||
path: select_existing_account_simplefin_items_path(account_id: @account.id)
|
||||
}
|
||||
end
|
||||
|
||||
# Check Plaid US
|
||||
if family.can_connect_plaid_us?
|
||||
@available_providers << {
|
||||
name: "Plaid",
|
||||
key: "plaid_us",
|
||||
description: "Connect to your US bank via Plaid",
|
||||
path: select_existing_account_plaid_items_path(account_id: @account.id, region: "us")
|
||||
}
|
||||
end
|
||||
|
||||
# Check Plaid EU
|
||||
if family.can_connect_plaid_eu?
|
||||
@available_providers << {
|
||||
name: "Plaid (EU)",
|
||||
key: "plaid_eu",
|
||||
description: "Connect to your EU bank via Plaid",
|
||||
path: select_existing_account_plaid_items_path(account_id: @account.id, region: "eu")
|
||||
}
|
||||
end
|
||||
|
||||
# Check Lunch Flow
|
||||
if family.can_connect_lunchflow?
|
||||
@available_providers << {
|
||||
name: "Lunch Flow",
|
||||
key: "lunchflow",
|
||||
description: "Connect to your bank via Lunch Flow",
|
||||
path: select_existing_account_lunchflow_items_path(account_id: @account.id)
|
||||
}
|
||||
end
|
||||
|
||||
if @available_providers.empty?
|
||||
redirect_to account_path(@account), alert: t("accounts.select_provider.no_providers")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class LunchflowItemsController < ApplicationController
|
||||
|
||||
# Create or find lunchflow_item for this family
|
||||
lunchflow_item = Current.family.lunchflow_items.first_or_create!(
|
||||
name: "Lunchflow Connection"
|
||||
name: "Lunch Flow Connection"
|
||||
)
|
||||
|
||||
# Fetch account details from API
|
||||
@@ -279,7 +279,7 @@ class LunchflowItemsController < ApplicationController
|
||||
|
||||
# Create or find lunchflow_item for this family
|
||||
lunchflow_item = Current.family.lunchflow_items.first_or_create!(
|
||||
name: "Lunchflow Connection"
|
||||
name: "Lunch Flow Connection"
|
||||
)
|
||||
|
||||
# Fetch account details from API
|
||||
@@ -338,7 +338,7 @@ class LunchflowItemsController < ApplicationController
|
||||
|
||||
def create
|
||||
@lunchflow_item = Current.family.lunchflow_items.build(lunchflow_params)
|
||||
@lunchflow_item.name = "Lunchflow Connection"
|
||||
@lunchflow_item.name = "Lunch Flow Connection"
|
||||
|
||||
if @lunchflow_item.save
|
||||
# Trigger initial sync to fetch accounts
|
||||
|
||||
@@ -48,6 +48,48 @@ class PlaidItemsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def select_existing_account
|
||||
@account = Current.family.accounts.find(params[:account_id])
|
||||
@region = params[:region] || "us"
|
||||
|
||||
# Get all Plaid accounts from this family's Plaid items for the specified region
|
||||
# that are not yet linked to any account
|
||||
@available_plaid_accounts = Current.family.plaid_items
|
||||
.where(plaid_region: @region)
|
||||
.includes(:plaid_accounts)
|
||||
.flat_map(&:plaid_accounts)
|
||||
.select { |pa| pa.account_provider.nil? && pa.account.nil? } # Not linked via new or legacy system
|
||||
|
||||
if @available_plaid_accounts.empty?
|
||||
redirect_to account_path(@account), alert: "No available Plaid accounts to link. Please connect a new Plaid account first."
|
||||
end
|
||||
end
|
||||
|
||||
def link_existing_account
|
||||
@account = Current.family.accounts.find(params[:account_id])
|
||||
plaid_account = PlaidAccount.find(params[:plaid_account_id])
|
||||
|
||||
# Verify the Plaid account belongs to this family's Plaid items
|
||||
unless Current.family.plaid_items.include?(plaid_account.plaid_item)
|
||||
redirect_to account_path(@account), alert: "Invalid Plaid account selected"
|
||||
return
|
||||
end
|
||||
|
||||
# Verify the Plaid account is not already linked
|
||||
if plaid_account.account_provider.present? || plaid_account.account.present?
|
||||
redirect_to account_path(@account), alert: "This Plaid account is already linked"
|
||||
return
|
||||
end
|
||||
|
||||
# Create the link via AccountProvider
|
||||
AccountProvider.create!(
|
||||
account: @account,
|
||||
provider: plaid_account
|
||||
)
|
||||
|
||||
redirect_to accounts_path, notice: "Account successfully linked to Plaid"
|
||||
end
|
||||
|
||||
private
|
||||
def set_plaid_item
|
||||
@plaid_item = Current.family.plaid_items.find(params[:id])
|
||||
|
||||
@@ -186,6 +186,46 @@ class SimplefinItemsController < ApplicationController
|
||||
redirect_to accounts_path, notice: t(".success")
|
||||
end
|
||||
|
||||
def select_existing_account
|
||||
@account = Current.family.accounts.find(params[:account_id])
|
||||
|
||||
# Get all SimpleFIN accounts from this family's SimpleFIN items
|
||||
# that are not yet linked to any account
|
||||
@available_simplefin_accounts = Current.family.simplefin_items
|
||||
.includes(:simplefin_accounts)
|
||||
.flat_map(&:simplefin_accounts)
|
||||
.select { |sa| sa.account_provider.nil? && sa.account.nil? } # Not linked via new or legacy system
|
||||
|
||||
if @available_simplefin_accounts.empty?
|
||||
redirect_to account_path(@account), alert: "No available SimpleFIN accounts to link. Please connect a new SimpleFIN account first."
|
||||
end
|
||||
end
|
||||
|
||||
def link_existing_account
|
||||
@account = Current.family.accounts.find(params[:account_id])
|
||||
simplefin_account = SimplefinAccount.find(params[:simplefin_account_id])
|
||||
|
||||
# Verify the SimpleFIN account belongs to this family's SimpleFIN items
|
||||
unless Current.family.simplefin_items.include?(simplefin_account.simplefin_item)
|
||||
redirect_to account_path(@account), alert: "Invalid SimpleFIN account selected"
|
||||
return
|
||||
end
|
||||
|
||||
# Verify the SimpleFIN account is not already linked
|
||||
if simplefin_account.account_provider.present? || simplefin_account.account.present?
|
||||
redirect_to account_path(@account), alert: "This SimpleFIN account is already linked"
|
||||
return
|
||||
end
|
||||
|
||||
# Create the link via AccountProvider
|
||||
AccountProvider.create!(
|
||||
account: @account,
|
||||
provider: simplefin_account
|
||||
)
|
||||
|
||||
redirect_to accounts_path, notice: "Account successfully linked to SimpleFIN"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_simplefin_item
|
||||
|
||||
Reference in New Issue
Block a user