Skip to content

feat(app): record a manual transaction from the Transactions screen#84

Merged
jakewan merged 3 commits into
mainfrom
feature/record-transaction-form
Jun 23, 2026
Merged

feat(app): record a manual transaction from the Transactions screen#84
jakewan merged 3 commits into
mainfrom
feature/record-transaction-form

Conversation

@jakewan

@jakewan jakewan commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Overview

Manual transactions could previously only be entered through the MCP tools, so every ad-hoc financial event meant opening Claude instead of the app. This adds an in-app form on the Transactions screen to record a manual transaction against an account — enter a name, amount, and Expense/Income, with the date defaulting to today and a live signed-amount preview showing the effect before submit.

The form is seated above the master-detail content and fed by the screen's existing account selector, so there is no second account picker — the selected account is injected into a standalone, independently-testable component. Status is always Reconciled; recurring and projected entries belong to the recurring/projection work.

How it works

The Expense/Income toggle is the sole source of the amount's sign: the magnitude field's validator forbids a typed sign, so a user can't enter a value that fights the toggle. The daemon does not validate the amount at all, so the form's canSubmit is the sole gate on amount quality — it rejects zero, blank, and non-numeric magnitudes, with the preview, the predicate, and the submit button all reading one shared derived-state chain so they can't disagree at a boundary.

formatAmount/statusLabel move into a shared qml/txformat.js so the new preview and the existing transactions panel read one source. FinchClient gains a recordTransaction path mirroring the existing createAccount path (re-entrancy guard, off-thread call, verbatim daemon validation message). The form, the client path, and the panel embedding are each covered by Qt Quick Test specs driving the real components against a mock client.

Closes #38

jakewan and others added 2 commits June 22, 2026 21:40
Manual transactions could only be entered via MCP tools — the app had no
way to record one, so every ad-hoc financial event meant opening Claude
instead of the app. This adds an in-app record form on the Transactions
screen, seated above the master-detail content and fed by that screen's
existing account selector (no second account picker).

The form sends the magnitude with its sign chosen solely by an
Expense/Income toggle — the amount field's validator forbids a typed
sign, so the toggle is the one source of direction. A live signed-amount
preview shows the effect before submit. The daemon does not validate the
amount at all, so the form is the sole gate on amount quality (rejecting
zero, blank, and non-numeric). Status is always Reconciled; recurring and
projected entries belong to the recurring/projection work.

Shared formatAmount/statusLabel move into qml/txformat.js so the preview
and the existing panel read one source, resolving the lift-to-shared-helper
TODO. FinchClient gains a recordTransaction RPC path mirroring createAccount
(re-entrancy guard, QFutureWatcher, verbatim InvalidArgument message).

Closes #38

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The record form cleared a stale daemon error only when the name or amount
changed. Editing the date or description, or flipping Expense/Income, left
a rejected-field message visible while the user fixed the very field that
caused it. Clear the error on every editable input, matching the
clear-on-edit behavior the name and amount fields already had.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an in-app “manual transaction” recording workflow to the Qt Transactions screen, introducing a reusable record form component, shared transaction formatting helpers, and a new async FinchClient::recordTransaction RPC path (with tests around the form + embedding).

Changes:

  • Embed a new RecordTransactionForm above the Transactions master/detail view, wired to the screen’s selected account and re-fetching transactions after a successful record.
  • Add FinchClient::recordTransaction(...) with an in-progress guard, background RPC execution, and user-facing error propagation.
  • Add shared QML formatting helpers (txformat.js) and Qt Quick Tests for the form and panel integration.

PR description checks (repo conventions):

  • Overview section: Pass
  • Scope cohesion: Pass
  • No private tooling references in tracked files: Pass

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
CHANGELOG.md Notes the new Transactions-screen manual transaction recording feature.
app/tests/tst_recordtransactionform.cpp Adds a Qt Quick Test entrypoint for the new form’s QML specs.
app/tests/transactionspanel/tst_TransactionsPanel.qml Extends panel tests to cover embedded form wiring + post-record refetch.
app/tests/recordtransactionform/tst_RecordTransactionForm.qml Adds direct form behavior tests (validation, sign handling, submit lifecycle).
app/tests/MockFinchClient.qml Extends the mock client with record-transaction lifecycle and signals.
app/src/finchclient.h Introduces record-transaction API surface, signals, watcher, and in-progress state.
app/src/finchclient.cpp Implements async RecordTransaction RPC call + result handling and error mapping.
app/qml/txformat.js Centralizes transaction status/amount formatting used by both panel and form.
app/qml/TransactionsPanel.qml Imports shared formatting and embeds the new record form tied to selected account.
app/qml/RecordTransactionForm.qml New self-contained transaction recording form component with validation + preview.
app/CMakeLists.txt Adds QML resources and new test target/module for the record form.

Comment thread app/qml/RecordTransactionForm.qml Outdated
// validate amount at all, so this form is the sole gate on amount quality.
readonly property bool amountValid: !isNaN(magnitude) && magnitude > 0
// Math.round avoids float drift (12.34 * 100 -> 1233.9999...); the toggle applies the sign.
readonly property int signedCents: amountValid ? Math.round(magnitude * 100) * (expense ? -1 : 1) : 0

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 15cf462signedCents is now a JS number rather than a 32-bit int, so amounts past ~$21.5M marshal to the qlonglong exactly; added a ~$30M spec to guard it.

The form's signedCents was a QML int, which is 32-bit, while the client's
amount and the proto field are 64-bit. An amount above ~$21.5M would
overflow and record a truncated value — well within reach for a personal
finance tool (a home purchase, a large brokerage transfer). Type it as a
JS number so it marshals to the qlonglong amount exactly, and add a spec
recording ~$30M that overflows a 32-bit int.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jakewan jakewan marked this pull request as ready for review June 23, 2026 05:10
@jakewan jakewan merged commit 7617fcd into main Jun 23, 2026
4 checks passed
@jakewan jakewan deleted the feature/record-transaction-form branch June 23, 2026 05:11
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.

No UI for transaction recording

2 participants