Files
sure/app/controllers/splits_controller.rb
CrossDrain 0b7fa732ae feat(splits): add exclusion support for splits and improve rendering (#1661)
* feat(splits): add excluded attribute support for split children and improve rendering of split transactions

* address coderabbitai suggestions to improve code quality

* Fix split excluded coercion, DRY helpers, and clean up view partials

Fix boolean coercion bug where string "false" from form params was
truthy in Ruby, causing all split children to be marked excluded.
Use ActiveModel::Type::Boolean for explicit casting in Entry#split!.

Additional changes addressing code review feedback:

- Extract duplicated in_split_group logic from TransactionsController
  and TransactionCategoriesController into TransactionsHelper
- Remove redundant local_assigns.fetch calls in partials that already
  declare defaults via the Rails 7.1 locals: magic comment
- Simplify ternary in _transaction.html.erb to pass grouped directly
- Guard hidden_field_tag :grouped to only emit when value is "true"
- Add model tests for excluded on split children (boolean and string)
- Add controller test for excluded param through full HTTP stack
- Add test confirming excluded children are dropped from balance queries

* fix(splits): simplify excluded attribute boolean check

* refactor(splits): extract truthy values constant for excluded check

Extract the array of truthy values used for excluded attribute check
into a private constant to improve code maintainability and avoid
duplication of the magic array.

* refactor: simplify split grouping link generation and add test coverage for excluded split parameters
2026-05-09 12:36:41 +02:00

101 lines
2.6 KiB
Ruby

class SplitsController < ApplicationController
before_action :set_entry
before_action :require_split_write_permission!, only: %i[create update destroy]
def new
@categories = Current.family.categories.alphabetically
end
def create
unless @entry.transaction.splittable?
redirect_back_or_to transactions_path, alert: t("splits.create.not_splittable")
return
end
raw_splits = split_params[:splits]
raw_splits = raw_splits.values if raw_splits.respond_to?(:values)
splits = raw_splits.map do |s|
{ name: s[:name], amount: s[:amount].to_d * -1, category_id: s[:category_id].presence, excluded: s[:excluded] }
end
@entry.split!(splits)
@entry.sync_account_later
redirect_back_or_to transactions_path, notice: t("splits.create.success")
rescue ActiveRecord::RecordInvalid => e
redirect_back_or_to transactions_path, alert: e.message
end
def edit
resolve_to_parent!
unless @entry.split_parent?
redirect_to transactions_path, alert: t("splits.edit.not_split")
return
end
@categories = Current.family.categories.alphabetically
@children = @entry.child_entries.includes(:entryable)
end
def update
resolve_to_parent!
unless @entry.split_parent?
redirect_to transactions_path, alert: t("splits.edit.not_split")
return
end
raw_splits = split_params[:splits]
raw_splits = raw_splits.values if raw_splits.respond_to?(:values)
splits = raw_splits.map do |s|
{ name: s[:name], amount: s[:amount].to_d * -1, category_id: s[:category_id].presence, excluded: s[:excluded] }
end
Entry.transaction do
@entry.unsplit!
@entry.split!(splits)
end
@entry.sync_account_later
redirect_to transactions_path, notice: t("splits.update.success")
rescue ActiveRecord::RecordInvalid => e
redirect_to transactions_path, alert: e.message
end
def destroy
resolve_to_parent!
unless @entry.split_parent?
redirect_to transactions_path, alert: t("splits.edit.not_split")
return
end
@entry.unsplit!
@entry.sync_account_later
redirect_to transactions_path, notice: t("splits.destroy.success")
end
private
def set_entry
@entry = Current.accessible_entries.find(params[:transaction_id])
end
def require_split_write_permission!
require_account_permission!(@entry.account, redirect_path: transactions_path)
end
def resolve_to_parent!
@entry = @entry.parent_entry if @entry.split_child?
end
def split_params
params.require(:split).permit(splits: [ :name, :amount, :category_id, :excluded ])
end
end