Skip to content

fix(transactions): re-render form when creating with no account#2777

Open
pro3958 wants to merge 1 commit into
we-promise:mainfrom
pro3958:fix/issue-2566
Open

fix(transactions): re-render form when creating with no account#2777
pro3958 wants to merge 1 commit into
we-promise:mainfrom
pro3958:fix/issue-2566

Conversation

@pro3958

@pro3958 pro3958 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

TransactionsController#create resolved the account with Current.user.accessible_accounts.find(params.dig(:entry, :account_id)). When no account is selected the id is blank, find raises ActiveRecord::RecordNotFound, and StoreLocation's rescue_from renders it as head :not_found — the 404 on /transactions reported here, with the submit button appearing to do nothing.

This switches the lookup to find_by(id:). When it returns nil, the action rebuilds the entry, runs valid?, calls set_new_transaction_form_options, and re-renders :new with 422, mirroring the existing validation-failure branch. That handles a blank, absent, or otherwise invalid account_id the same way, so the user gets the form back with an error instead of a broken request.

Verification

bin/rails test test/controllers/transactions_controller_test.rb passes. The added test posts a transaction with a blank account_id, asserts no Entry/Transaction is created, and expects a 422 — it gets a 404 on the unfixed controller.

Fixes #2566

Notes I made while reviewing this
  • minor app/controllers/transactions_controller.rb:101 — The nil branch duplicates the failure-render logic already present in the else branch (set_new_transaction_form_options + render :new, status: :unprocessable_entity). Minor duplication.
  • minor test/controllers/transactions_controller_test.rb:55 — The test asserts status 422 and no record creation, but does not assert that the re-rendered form actually shows the 'Account must exist' validation error. That distinction (422-with-visible-error vs. 422-with-silent-blank-form) is the real user-facing improvement and isn't locked down.
  • minor app/controllers/valuations_controller.rb:50 — Identical latent bug: valuations#create uses accessible_accounts.find(params.dig(:entry, :account_id)), which will 404 the same way when no account is selected. Not touched by this diff.
  • minor test/controllers/transactions_controller_test.rb:51 — Test uses Category.first.id rather than a named fixture reference (e.g. categories(:food_and_drink)), a soft deviation from the repo's fixtures convention. Non-blocking, and Category.first is only incidental to the code path under test (the entry never persists), but it makes the test order-dependent.
  • minor app/controllers/transactions_controller.rb:101 — The CLAUDE.md pre-PR gates (bin/rails test, rubocop, brakeman, npm lint, importmap audit) could not be executed in this review environment (no Ruby toolchain present), so gate-passing is asserted statically, not demonstrated. The commit/PR must also link the issue with fixes #2566.

Summary by CodeRabbit

  • Bug Fixes

    • Creating a transaction without a valid account now re-renders the form with validation errors instead of causing an error.
    • No transaction records are created when the account is missing.
  • Tests

    • Added coverage to verify the form responds with HTTP 422 for incomplete transaction submissions.

… account

TransactionsController#create looked up the account with
accessible_accounts.find(params.dig(:entry, :account_id)). With no
account selected the id is blank, find raises RecordNotFound, and
StoreLocation's rescue_from turns that into head :not_found — the 404
on /transactions the reporter saw.

Switch to find_by(id:) and, when it returns nil, rebuild the entry,
run validation, and re-render :new with 422, matching the existing
validation-failure branch. This covers a blank, missing, or invalid
account_id, so the user gets the form back with errors instead of a
dead button.

Fixes we-promise#2566
@pro3958

pro3958 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

The 404 came from accessible_accounts.find(account_id) raising RecordNotFound when no account is selected; the fix correctly switches to find_by(id:) and re-renders the form with 422 on nil, matching the existing validation-failure path. Traced by hand it holds for blank, missing, and invalid account_id alike — the error is prevented at the right layer rather than swallowed. The added controller test genuinely fails on the unfixed code (it would get a 404, not 422), so the green is real. One trivial bit of render duplication with the else branch, not worth blocking.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

TransactionsController#create now handles blank account identifiers by rebuilding the form and returning HTTP 422 instead of raising. An integration test verifies the response and confirms that no entry or transaction records are persisted.

Changes

Transaction creation validation

Layer / File(s) Summary
Handle missing account submissions
app/controllers/transactions_controller.rb, test/controllers/transactions_controller_test.rb
The controller uses find_by, prepares the new transaction form, and renders it with unprocessable_entity when the account is missing. The integration test verifies no records are created and expects HTTP 422.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • we-promise/sure#2189: Both changes update the transaction creation failure path that re-renders the :new form.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: re-rendering the transaction form when no account is selected.
Linked Issues check ✅ Passed The controller now handles blank or missing account selection gracefully and the test confirms 422 with no records created.
Out of Scope Changes check ✅ Passed The changes are limited to the transaction create flow and its test, with no unrelated scope added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pro3958
pro3958 marked this pull request as ready for review July 24, 2026 00:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/controllers/transactions_controller_test.rb (1)

38-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert that the transaction form was rendered.

A 422 response alone would also pass if the controller returned a generic error page. Add a form-specific selector to protect the re-render contract.

Suggested assertion
     assert_response :unprocessable_entity
+    assert_select "form[action=?]", transactions_path
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/controllers/transactions_controller_test.rb` around lines 38 - 57,
Extend the “create without an account” test after the 422 assertion to verify
that the transaction form was rendered, using a form-specific selector targeting
the expected transaction form. Keep the existing no-persistence and
response-status assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/controllers/transactions_controller_test.rb`:
- Around line 38-57: Extend the “create without an account” test after the 422
assertion to verify that the transaction form was rendered, using a
form-specific selector targeting the expected transaction form. Keep the
existing no-persistence and response-status assertions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 012821a4-1840-4031-ae2c-8c65c9de5018

📥 Commits

Reviewing files that changed from the base of the PR and between c6eb7cd and 1c26b55.

📒 Files selected for processing (2)
  • app/controllers/transactions_controller.rb
  • test/controllers/transactions_controller_test.rb

jjmata commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Review

What it does: TransactionsController#create used accessible_accounts.find(...) on account_id, so a blank/missing/invalid id raised ActiveRecord::RecordNotFound, which StoreLocation#handle_not_found turns into a bare head :not_found (confirmed in app/controllers/concerns/store_location.rb) — the reported dead-button 404. The fix swaps to find_by(id:) and, when nil, rebuilds @entry, runs valid?, calls set_new_transaction_form_options, and re-renders :new with 422.

Correctness: Verified this holds up.

  • Current.family.entries.new(entry_params) (a has_many :through build with no account) mirrors the exact pattern already used in EntryableResource#new when no account_id is given, so it's not a novel/risky call.
  • entry_params never includes account_id, so @entry.account stays nil and belongs_to :account (required by default) correctly fails validation ("must exist"), driving the 422 + visible error via shared/_form_errors.
  • Entry's custom validations (cannot_unexclude_split_parent, split_child_date_matches_parent) are no-ops on a fresh record, so valid? won't blow up.
  • find_by(id:) also silently swallows ids for accounts inaccessible to the user (previously a 404, now a 422 form re-render) — a behavior change, but not a security regression since no extra info is leaked either way.

Test coverage: The added test is a real regression test, not tautological — tracing the old code path confirms it would 404 (not 422) before this fix, since find("") raises before any @entry is built.

Minor, non-blocking (already self-flagged in the PR description, and accurate): duplicated render logic between the new nil-branch and the existing failure else branch could be extracted; the test doesn't assert the "Account must exist" error is actually rendered; Category.first.id in the test should probably use a named fixture for consistency with repo convention. None of these block merging.

Looks correct and well-scoped.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Impossible to create transaction without account selected

2 participants