mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-25 08:09:38 +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
35
app/controllers/budget_categories_controller.rb
Normal file
35
app/controllers/budget_categories_controller.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
class BudgetCategoriesController < ApplicationController
|
||||
def index
|
||||
@budget = Current.family.budgets.find(params[:budget_id])
|
||||
render layout: "wizard"
|
||||
end
|
||||
|
||||
def show
|
||||
@budget = Current.family.budgets.find(params[:budget_id])
|
||||
|
||||
@recent_transactions = @budget.entries
|
||||
|
||||
if params[:id] == BudgetCategory.uncategorized.id
|
||||
@budget_category = @budget.uncategorized_budget_category
|
||||
@recent_transactions = @recent_transactions.where(account_transactions: { category_id: nil })
|
||||
else
|
||||
@budget_category = Current.family.budget_categories.find(params[:id])
|
||||
@recent_transactions = @recent_transactions.joins("LEFT JOIN categories ON categories.id = account_transactions.category_id")
|
||||
.where("categories.id = ? OR categories.parent_id = ?", @budget_category.category.id, @budget_category.category.id)
|
||||
end
|
||||
|
||||
@recent_transactions = @recent_transactions.order("account_entries.date DESC, ABS(account_entries.amount) DESC").take(3)
|
||||
end
|
||||
|
||||
def update
|
||||
@budget_category = Current.family.budget_categories.find(params[:id])
|
||||
@budget_category.update!(budget_category_params)
|
||||
|
||||
redirect_to budget_budget_categories_path(@budget_category.budget)
|
||||
end
|
||||
|
||||
private
|
||||
def budget_category_params
|
||||
params.require(:budget_category).permit(:budgeted_spending)
|
||||
end
|
||||
end
|
55
app/controllers/budgets_controller.rb
Normal file
55
app/controllers/budgets_controller.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
class BudgetsController < ApplicationController
|
||||
before_action :set_budget, only: %i[show edit update]
|
||||
|
||||
def index
|
||||
redirect_to_current_month_budget
|
||||
end
|
||||
|
||||
def show
|
||||
@next_budget = @budget.next_budget
|
||||
@previous_budget = @budget.previous_budget
|
||||
@latest_budget = Budget.find_or_bootstrap(Current.family)
|
||||
render layout: with_sidebar
|
||||
end
|
||||
|
||||
def edit
|
||||
render layout: "wizard"
|
||||
end
|
||||
|
||||
def update
|
||||
@budget.update!(budget_params)
|
||||
redirect_to budget_budget_categories_path(@budget)
|
||||
end
|
||||
|
||||
def create
|
||||
start_date = Date.parse(budget_create_params[:start_date])
|
||||
@budget = Budget.find_or_bootstrap(Current.family, date: start_date)
|
||||
redirect_to budget_path(@budget)
|
||||
end
|
||||
|
||||
def picker
|
||||
render partial: "budgets/picker", locals: {
|
||||
family: Current.family,
|
||||
year: params[:year].to_i || Date.current.year
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def budget_create_params
|
||||
params.require(:budget).permit(:start_date)
|
||||
end
|
||||
|
||||
def budget_params
|
||||
params.require(:budget).permit(:budgeted_spending, :expected_income)
|
||||
end
|
||||
|
||||
def set_budget
|
||||
@budget = Current.family.budgets.find(params[:id])
|
||||
@budget.sync_budget_categories
|
||||
end
|
||||
|
||||
def redirect_to_current_month_budget
|
||||
current_budget = Budget.find_or_bootstrap(Current.family)
|
||||
redirect_to budget_path(current_budget)
|
||||
end
|
||||
end
|
|
@ -19,9 +19,15 @@ class CategoriesController < ApplicationController
|
|||
if @category.save
|
||||
@transaction.update(category_id: @category.id) if @transaction
|
||||
|
||||
redirect_back_or_to categories_path, notice: t(".success")
|
||||
flash[:notice] = t(".success")
|
||||
|
||||
redirect_target_url = request.referer || categories_path
|
||||
respond_to do |format|
|
||||
format.html { redirect_back_or_to categories_path, notice: t(".success") }
|
||||
format.turbo_stream { render turbo_stream: turbo_stream.action(:redirect, redirect_target_url) }
|
||||
end
|
||||
else
|
||||
@categories = Current.family.categories.alphabetically.where(parent_id: nil)
|
||||
@categories = Current.family.categories.alphabetically.where(parent_id: nil).where.not(id: @category.id)
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
@ -60,6 +66,6 @@ class CategoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def category_params
|
||||
params.require(:category).permit(:name, :color, :parent_id)
|
||||
params.require(:category).permit(:name, :color, :parent_id, :classification, :lucide_icon)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ class TransactionsController < ApplicationController
|
|||
|
||||
def index
|
||||
@q = search_params
|
||||
search_query = Current.family.transactions.search(@q).includes(:entryable).reverse_chronological
|
||||
search_query = Current.family.transactions.search(@q).reverse_chronological
|
||||
@pagy, @transaction_entries = pagy(search_query, limit: params[:per_page] || "50")
|
||||
|
||||
totals_query = search_query.incomes_and_expenses
|
||||
|
|
|
@ -8,6 +8,7 @@ class TransfersController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@categories = Current.family.categories.expenses
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -37,7 +38,11 @@ class TransfersController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
@transfer.update!(transfer_update_params)
|
||||
Transfer.transaction do
|
||||
@transfer.update!(transfer_update_params.except(:category_id))
|
||||
@transfer.outflow_transaction.update!(category_id: transfer_update_params[:category_id])
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_back_or_to transactions_url, notice: t(".success") }
|
||||
format.turbo_stream
|
||||
|
@ -61,6 +66,6 @@ class TransfersController < ApplicationController
|
|||
end
|
||||
|
||||
def transfer_update_params
|
||||
params.require(:transfer).permit(:notes, :status)
|
||||
params.require(:transfer).permit(:notes, :status, :category_id)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue