1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-05 05:25:24 +02:00

Transfer and Payment auto-matching, model and UI improvements (#1585)

* Transfer data model migration

* Transfers and payment modeling and UI improvements

* Fix CI

* Transfer matching flow

* Better UI for transfers

* Auto transfer matching, approve, reject flow

* Mark transfers created from form as confirmed

* Account filtering

* Excluded rejected transfers from calculations

* Calculation tweaks with transfer exclusions

* Clean up migration
This commit is contained in:
Zach Gollwitzer 2025-01-07 09:41:24 -05:00 committed by GitHub
parent 46e129308f
commit 307a3687e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 1161 additions and 682 deletions

View file

@ -30,7 +30,15 @@ class Account::Entry < ApplicationRecord
)
}
scope :without_transfers, -> { where(marked_as_transfer: false) }
# All entries that are not part of a pending/approved transfer (rejected transfers count as normal entries, so are included)
scope :incomes_and_expenses, -> {
joins(
'LEFT JOIN transfers AS inflow_transfers ON inflow_transfers.inflow_transaction_id = account_entries.entryable_id
LEFT JOIN transfers AS outflow_transfers ON outflow_transfers.outflow_transaction_id = account_entries.entryable_id'
)
.where("(inflow_transfers.id IS NULL AND outflow_transfers.id IS NULL) OR inflow_transfers.status = 'rejected' OR outflow_transfers.status = 'rejected'")
}
scope :with_converted_amount, ->(currency) {
# Join with exchange rates to convert the amount to the given currency
# If no rate is available, exclude the transaction from the results
@ -59,6 +67,15 @@ class Account::Entry < ApplicationRecord
enriched_name.presence || name
end
def transfer_match_candidates
account.family.entries
.where.not(account_id: account_id)
.where.not(id: id)
.where(amount: -amount)
.where(currency: currency)
.where(date: (date - 4.days)..(date + 4.days))
end
class << self
def search(params)
Account::EntrySearch.new(params).build_query(all)
@ -98,13 +115,6 @@ class Account::Entry < ApplicationRecord
select("*").from(rolling_totals).where("date >= ?", period.date_range.first)
end
def mark_transfers!
update_all marked_as_transfer: true
# Attempt to "auto match" and save a transfer if 2 transactions selected
Account::Transfer.new(entries: all).save if all.count == 2
end
def bulk_update!(bulk_update_params)
bulk_attributes = {
date: bulk_update_params[:date],
@ -128,7 +138,7 @@ class Account::Entry < ApplicationRecord
end
def income_total(currency = "USD")
total = without_transfers.account_transactions.includes(:entryable)
total = account_transactions.includes(:entryable).incomes_and_expenses
.where("account_entries.amount <= 0")
.map { |e| e.amount_money.exchange_to(currency, date: e.date, fallback_rate: 0) }
.sum
@ -137,29 +147,12 @@ class Account::Entry < ApplicationRecord
end
def expense_total(currency = "USD")
total = without_transfers.account_transactions.includes(:entryable)
total = account_transactions.includes(:entryable).incomes_and_expenses
.where("account_entries.amount > 0")
.map { |e| e.amount_money.exchange_to(currency, date: e.date, fallback_rate: 0) }
.sum
Money.new(total, currency)
end
private
def entryable_search(params)
entryable_ids = []
entryable_search_performed = false
Account::Entryable::TYPES.map(&:constantize).each do |entryable|
next unless entryable.requires_search?(params)
entryable_search_performed = true
entryable_ids += entryable.search(params).pluck(:id)
end
return nil unless entryable_search_performed
entryable_ids
end
end
end