Files
sure/app/models/lunchflow_account/processor.rb
soky srm ca4fb7995c Implement holdings for lunch flow (#590)
* Implement holdings for lunch flow

* Implement holdings function call
2026-01-09 13:14:14 +01:00

90 lines
3.0 KiB
Ruby

class LunchflowAccount::Processor
include CurrencyNormalizable
attr_reader :lunchflow_account
def initialize(lunchflow_account)
@lunchflow_account = lunchflow_account
end
def process
unless lunchflow_account.current_account.present?
Rails.logger.info "LunchflowAccount::Processor - No linked account for lunchflow_account #{lunchflow_account.id}, skipping processing"
return
end
Rails.logger.info "LunchflowAccount::Processor - Processing lunchflow_account #{lunchflow_account.id} (account #{lunchflow_account.account_id})"
begin
process_account!
rescue StandardError => e
Rails.logger.error "LunchflowAccount::Processor - Failed to process account #{lunchflow_account.id}: #{e.message}"
Rails.logger.error "Backtrace: #{e.backtrace.join("\n")}"
report_exception(e, "account")
raise
end
process_transactions
process_investments
end
private
def process_account!
if lunchflow_account.current_account.blank?
Rails.logger.error("Lunchflow account #{lunchflow_account.id} has no associated Account")
return
end
# Update account balance from latest Lunchflow data
account = lunchflow_account.current_account
balance = lunchflow_account.current_balance || 0
# LunchFlow balance convention matches our app convention:
# - Positive balance = debt (you owe money)
# - Negative balance = credit balance (bank owes you, e.g., overpayment)
# No sign conversion needed - pass through as-is (same as Plaid)
#
# Exception: CreditCard and Loan accounts return inverted signs
# Provider returns negative for positive balance, so we negate it
if account.accountable_type == "CreditCard" || account.accountable_type == "Loan"
balance = -balance
end
# Normalize currency with fallback chain: parsed lunchflow currency -> existing account currency -> USD
currency = parse_currency(lunchflow_account.currency) || account.currency || "USD"
# Update account balance
account.update!(
balance: balance,
cash_balance: balance,
currency: currency
)
end
def process_transactions
LunchflowAccount::Transactions::Processor.new(lunchflow_account).process
rescue => e
report_exception(e, "transactions")
end
def process_investments
# Only process holdings for investment/crypto accounts with holdings support
return unless lunchflow_account.holdings_supported?
return unless [ "Investment", "Crypto" ].include?(lunchflow_account.current_account&.accountable_type)
LunchflowAccount::Investments::HoldingsProcessor.new(lunchflow_account).process
rescue => e
report_exception(e, "holdings")
end
def report_exception(error, context)
Sentry.capture_exception(error) do |scope|
scope.set_tags(
lunchflow_account_id: lunchflow_account.id,
context: context
)
end
end
end