mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 05:25:24 +02:00
Budgeting V1 (#1609)
* Budgeting V1 * Basic UI template * Fully scaffolded budgeting v1 * Basic working budget * Finalize donut chart for budgets * Allow categorization of loan payments for budget * Include loan payments in incomes_and_expenses scope * Add budget allocations progress * Empty states * Clean up budget methods * Category aggregation queries * Handle overage scenarios in form * Finalize budget donut chart controller * Passing tests * Fix allocation naming * Add income category migration * Native support for uncategorized budget category * Formatting * Fix subcategory sort order, padding * Fix calculation for category rollups in budget
This commit is contained in:
parent
413ec6cbed
commit
195ec85d96
61 changed files with 2044 additions and 140 deletions
|
@ -17,7 +17,7 @@ class Account::Entry < ApplicationRecord
|
|||
scope :chronological, -> {
|
||||
order(
|
||||
date: :asc,
|
||||
Arel.sql("CASE WHEN entryable_type = 'Account::Valuation' THEN 1 ELSE 0 END") => :asc,
|
||||
Arel.sql("CASE WHEN account_entries.entryable_type = 'Account::Valuation' THEN 1 ELSE 0 END") => :asc,
|
||||
created_at: :asc
|
||||
)
|
||||
}
|
||||
|
@ -25,18 +25,27 @@ class Account::Entry < ApplicationRecord
|
|||
scope :reverse_chronological, -> {
|
||||
order(
|
||||
date: :desc,
|
||||
Arel.sql("CASE WHEN entryable_type = 'Account::Valuation' THEN 1 ELSE 0 END") => :desc,
|
||||
Arel.sql("CASE WHEN account_entries.entryable_type = 'Account::Valuation' THEN 1 ELSE 0 END") => :desc,
|
||||
created_at: :desc
|
||||
)
|
||||
}
|
||||
|
||||
# All entries that are not part of a pending/approved transfer (rejected transfers count as normal entries, so are included)
|
||||
# All non-transfer entries, rejected transfers, and the outflow of a loan payment transfer are incomes/expenses
|
||||
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'")
|
||||
joins("INNER JOIN account_transactions ON account_transactions.id = account_entries.entryable_id AND account_entries.entryable_type = 'Account::Transaction'")
|
||||
.joins("LEFT JOIN transfers ON transfers.inflow_transaction_id = account_transactions.id OR transfers.outflow_transaction_id = account_transactions.id")
|
||||
.joins("LEFT JOIN account_transactions inflow_txns ON inflow_txns.id = transfers.inflow_transaction_id")
|
||||
.joins("LEFT JOIN account_entries inflow_entries ON inflow_entries.entryable_id = inflow_txns.id AND inflow_entries.entryable_type = 'Account::Transaction'")
|
||||
.joins("LEFT JOIN accounts inflow_accounts ON inflow_accounts.id = inflow_entries.account_id")
|
||||
.where("transfers.id IS NULL OR transfers.status = 'rejected' OR (account_entries.amount > 0 AND inflow_accounts.accountable_type = 'Loan')")
|
||||
}
|
||||
|
||||
scope :incomes, -> {
|
||||
incomes_and_expenses.where("account_entries.amount <= 0")
|
||||
}
|
||||
|
||||
scope :expenses, -> {
|
||||
incomes_and_expenses.where("account_entries.amount > 0")
|
||||
}
|
||||
|
||||
scope :with_converted_amount, ->(currency) {
|
||||
|
@ -137,18 +146,16 @@ class Account::Entry < ApplicationRecord
|
|||
all.size
|
||||
end
|
||||
|
||||
def income_total(currency = "USD")
|
||||
total = account_transactions.includes(:entryable).incomes_and_expenses
|
||||
.where("account_entries.amount <= 0")
|
||||
def income_total(currency = "USD", start_date: nil, end_date: nil)
|
||||
total = incomes.where(date: start_date..end_date)
|
||||
.map { |e| e.amount_money.exchange_to(currency, date: e.date, fallback_rate: 0) }
|
||||
.sum
|
||||
|
||||
Money.new(total, currency)
|
||||
end
|
||||
|
||||
def expense_total(currency = "USD")
|
||||
total = account_transactions.includes(:entryable).incomes_and_expenses
|
||||
.where("account_entries.amount > 0")
|
||||
def expense_total(currency = "USD", start_date: nil, end_date: nil)
|
||||
total = expenses.where(date: start_date..end_date)
|
||||
.map { |e| e.amount_money.exchange_to(currency, date: e.date, fallback_rate: 0) }
|
||||
.sum
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue