* feat: add Binance support (Items, Accounts, Importers, Processor, and Sync)
* refactor: deduplicate 'stablecoins' constant and push stale_rate filter to SQL
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Extract Entry.uncategorized_transactions scope, remove Family#uncategorized_transaction_count
Adds a single Entry.uncategorized_transactions scope containing the
shared conditions (transactions join, active accounts, category nil,
not transfer kinds, not excluded). All callers now use this scope:
- Entry.uncategorized_matching builds on it
- Transaction::Grouper::ByMerchantOrName#uncategorized_entries uses it
- categorizes_controller#uncategorized_entries_for uses it (also fixes
missing status/excluded filters that were silently absent before)
- Both controllers replace Current.family.uncategorized_transaction_count
with Current.accessible_entries.uncategorized_transactions.count so
the button count and wizard count both respect account sharing
Family#uncategorized_transaction_count removed as it is now unused and
was family-scoped rather than user-scoped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Scope assign_entry write to Current.accessible_entries
Replaces unscoped Entry.where(id:) with Current.accessible_entries.where(id:)
so the write path is consistent with the find above it. Not exploitable
given the find would 404 first, but removes the pattern inconsistency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add privacy-sensitive class to amounts in categorize wizard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Extract uncategorized_count helper in CategorizesController
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix comment on uncategorized_transactions scope to mention draft accounts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Use uncategorized_count helper in assign_entry action
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: allow high precision for security prices in trade forms (to solve #1323)
* fix: prevent race conditions on currency selection in money field
* fix: silently ignore currency fetch errors in money field
* Add production-ready Polish localization and reusable locale audit tooling
- add and update Polish locale files across models, views, mailers, and shared translations
- add runtime rails-i18n dependency and Polish locale support in language helper
- add regression coverage for Polish pluralization and locale-aware money formatting
- introduce reusable locale audit script for any locale plus backward-compatible PL wrapper
- add localization audit docs and generated PL readiness/pluralization reports
- resolve one/few/many/other pluralization consistency for Polish locales
* Fix Polish locale review feedback
* Fix locale compatibility regressions
* Polish locale typo pass and wrapper cleanup
* Final language improvements and test isolation for Polish locales
- Improved partial_success wording in SnapTrade with proper noun inflection
- Fixed typos: Pomin → Pomiń in Mercury and LunchFlow items
- Isolated I18n backend state in polish_pluralization_test to prevent test coupling
* Fix code review comments in locale audit scripts
- Use RbConfig.ruby instead of 'ruby' to ensure consistent interpreter
- Remove Symbol from permitted_classes and explicitly allow CLDR plural symbols (one, few, many, other) in YAML loading
* Simplify i18n flow and align locale interpolation keys
* Remove locale audit scripts and localization docs
* Fixed crypto and loan add menus
* Changed unspecified crypto account type to none for consistency
* Added default add message for loan subtype
* Made the short form of non-mortgage loans in loans.rb match the long form
* Edited the crypto tooltip to be country generic
* Update config/locales/views/loans/en.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Geoffrey <79559478+CYBRXT@users.noreply.github.com>
* Update app/views/loans/_form.html.erb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Geoffrey <79559478+CYBRXT@users.noreply.github.com>
* Following Dosu's comment on my issue for consistency
---------
Signed-off-by: Geoffrey <79559478+CYBRXT@users.noreply.github.com>
Co-authored-by: Geoffrey <geoffrey@github.worker>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Add Quick Categorize Wizard (iteration 1)
Adds a step-by-step wizard for bulk-categorizing uncategorized transactions
and optionally creating auto-categorization rules, reducing friction after
connecting a new bank account.
New files:
- Transaction::Grouper abstraction + ByMerchantOrName strategy (groups by
merchant name when present, falls back to entry name; sorted by count desc)
- Transactions::CategorizesController (GET show / POST create)
- Wizard view at app/views/transactions/categorizes/show.html.erb
- Stimulus categorize_controller.js (Enter-key-to-select-first)
- Tests for grouper and controller
Modified files:
- routes.rb: resource :categorize inside namespace :transactions
- transactions_controller.rb: expose @uncategorized_count to index
- transactions/index.html.erb: Categorize (N) button in header
- family.rb: uncategorized_transaction_count query
- rules_controller.rb: return_to param support for wizard → rule editor flow
- rules/_form.html.erb, rules/new.html.erb: pass return_to through form
- i18n: categorizes show/create keys + rules.create.success
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Quick Categorize Wizard — iteration 2 polish
Six improvements from live testing:
- Breadcrumb: Home > Transactions > Categorize
- Layout: category picker + confirmation dialog above transaction list
- Inline confirmation dialog: clicking a category pill shows a <dialog>
summarising what will happen (N transactions → category, rule if checked)
with Confirm and Cancel buttons — no redirect to rule editor
- Direct rule creation: rule created with active: true in the controller
instead of redirecting to the rule editor; revert return_to plumbing from
RulesController, rules/_form, rules/new, rules/en.yml
- Individual row assignment: per-row category <select> submits via
PATCH /transactions/categorize/assign_entry and removes the row via
Turbo Stream (assign_entry action + route)
- Enter key guard: selectFirst only fires when exactly 1 pill is visible
after filtering
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Quick Categorize Wizard — iteration 3 reliability fixes and UX polish
- Fix Stimulus controller not loading: remove invalid `@hotwired/turbo` named
import (not in importmap); use global `Turbo.renderStreamMessage` instead
- Fix Enter key submitting form with wrong category when search field is
unfocused: move keydown listener to document so it fires regardless of focus
- Prevent Enter from submitting when multiple categories are visible
- Clear search filter after bulk category assignment (pill click or Enter),
but not after individual row dropdown assignment
- Update group transaction count and total amount live as entries are assigned
via row dropdown or partial bulk assignment
- Add turbo frames for remaining count and group summary so they update
without a full page reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Quick categorization polish
* refactoring
* Remove unused GROUPS_PER_BATCH constant, fix ERB self-closing tags
Wizard only ever uses one group at a time so limit: 1 is correct and
more honest than fetching 20 and discarding 19. ERB linter fixes are
whitespace/void-element corrections with no functional change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Move Categorize button into ... menu on transactions index
Reduces header clutter by putting it in the overflow menu at the bottom,
where it only appears when there are uncategorized transactions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Scope categorize wizard to accessible entries only
Fixes a security issue where users with restricted account access via
account sharing could view and categorize transactions from accounts
they cannot access through normal transaction flows.
- Pass Current.accessible_entries to Transaction::Grouper so the wizard
only displays groups from accounts the user can see
- Use Current.accessible_entries on all write paths in create and
assign_entry, matching the pattern in TransactionCategoriesController
- Refactor Grouper to accept an entries scope instead of a family object,
keeping authorization concerns in the controller
- Add tests verifying inaccessible entries are hidden from the wizard
and cannot be categorized via forged POST/PATCH params
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Clamp position param to >= 0 to guard against negative offset
Prevents ArgumentError from Array#drop when a negative position is
passed via a tampered query string or form value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Surface rule creation failure and add accessible names to entry row
- Capture Rule.create_from_grouping! return value; set flash[:alert] when
nil so users who checked "Create Rule" know it wasn't created (e.g. a
duplicate already exists); stream the notification for partial updates
- Add aria-label to the per-row checkbox and category select in
_entry_row so screen readers can identify which transaction each
control belongs to
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Localize breadcrumb labels in categorizes controller
Follows the pattern used by FamilyExportsController and ImportsController.
Adds 'transactions' and 'categorize' keys to the breadcrumbs locale file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add error handling to categorize controller fetch calls
Check response.ok before parsing the body and add .catch handlers
so network failures and non-2xx responses are logged rather than
silently swallowed. On assignment failure the per-row select is
reset to empty so the user can retry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Scope preview_rule to accessible entries only
Entry.uncategorized_matching now accepts an entries scope instead of a
family object, matching the same pattern used for Transaction::Grouper.
The preview_rule action passes Current.accessible_entries so rule
previews respect account sharing permissions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Scope remaining count to accessible entries
Adds Entry.uncategorized_count(entries) following the same pattern as
uncategorized_matching. Replaces all three uses of
Current.family.uncategorized_transaction_count in the categorize
controller so the remaining-count badge reflects only the transactions
the current user can actually access and categorize.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Comments got separated from their function
* Remove quick-categorize-wizard dev notes
This was a planning document used during development, not intended
for the final branch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Recompute remaining entries from server state after writes
Adds uncategorized_entries_for helper that reloads remaining entries
from the DB with a category_id IS NULL filter after each write, so
the partial-update Turbo Stream reflects server-side state rather than
trusting the client-provided remaining_ids. This handles the case where
a concurrent request has categorized one of the remaining entries
between page render and form submit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Rename create_from_grouping! to create_from_grouping
The method rescues RecordInvalid and returns nil, which contradicts
the bang convention. Dropping the ! correctly signals that callers
should check the return value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Clamp offset in grouper to guard against negative values
The controller already clamps position before passing it as offset,
but clamping in the grouper itself prevents ArgumentError from
Array#drop if the grouper is ever called directly with a negative offset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
* feat: improve QIF import date format selection
- Added a reusable date format auto-detection method.
- Show a live preview of the first parsed date that updates client-side
as the user changes the dropdown selection, via a new
qif-date-format Stimulus controller.
- Show an error alert and disable the submit button when no supported
date format can parse the file's dates.
* A few polishing fixes:
- Missing return on redirects
Stale REASONABLE_DATE_RANGE constant.
- Replaced the frozen constant with a class method
Bare inline rescue — Replaced Date.strptime(s, fmt) rescue nil with an explicit begin/rescue catching.
- save!(validate: false) in controller — Changed to update_column(:column_mappings, ...) in qif_category_selections_controller.rb:22, matching the pattern used in detect_and_set_qif_date_format!.
- Unescaped JSON in HTML attribute — Replaced the raw <div> with tag.div ... do block in show.html.erb:16, letting Rails properly escape the data attribute value.
* fix: address review feedback for QIF date format feature
- Add missing `return` after redirect for non-QIF imports
- Pass date_format to parse_opening_balance in will_adjust_opening_anchor?
- Return empty array when no usable date sample exists for format preview
- Add sr-only label to date format select for accessibility
- Consolidate duplicate try_parse_date/parse_qif_date into single method
- Remove misleading ambiguity scoring comment from detect_date_format
- Skip redundant sync_mappings when date format already triggered a sync
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Use %{product_name} interpolation in locale strings
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Make category selection menus opaque for excluded transactions
* Allow keyboard navigation in category selection menu
* Fix category transparency on mobile
* Make checkbox opaque
* Remove text-secondary from amount container
* Submit form directly
* Handle aria labels
- Added server-side validation to Category model to enforce 6-digit hex format for colors.
- Added HTML pattern attribute to category form for client-side validation.
- Updated tests to cover validation and fixed existing tests using shorthand hex colors.
* Fix NoMethodError when entry has nil entryable
Guard against orphaned entries where the entryable record has been
deleted but the entry still exists, preventing a crash on the
account show page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add dependent: :destroy to Entryable has_one :entry
The polymorphic has_one :entry association lacked a dependent option,
meaning if a Transaction/Trade/Valuation was ever deleted directly
(bypassing the Entry), the Entry would be left orphaned with a nil
entryable — causing NoMethodError in the activity feed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add nil entryable guard to _split_group.html.erb
Same defensive check as _entry.html.erb for consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Record dividends and interest as Trades in investment accounts
All investment income (dividends and interest) is now modeled as a
Trade with qty: 0 and price: 0, keeping security_id NOT NULL on trades
intact. Dividends require a security; interest falls back to a
per-account synthetic cash security (kind: "cash", offline: true) when
none is selected, matching how brokerages handle uninvested cash
internally.
- Add `kind` column to securities ("standard" | "cash") with DB check
constraint; `Security.cash_for(account)` lazily finds or creates the
synthetic cash security; `scope :standard` excludes synthetic
securities from user-facing pickers
- Trade::CreateForm: new `dividend` type (security required); `interest`
now creates a Trade instead of a Transaction
- Trade form: Dividend and Interest in the type dropdown with a security
combobox (required for dividend, optional for interest)
- transactions table: untouched
* UI fixes
* HealthChecker — both scopes now chain .standard to exclude cash securities from provider health checks.
DB query moved to model — Account#traded_standard_securities in app/models/account.rb, view uses account.traded_standard_securities.
DRY income creation — create_income_trade(sec:, label:, name:) extracted as shared private method; create_dividend_income and create_interest_income delegate to it.
show.html.erb blocks merged — single unless trade.qty.zero? block covers qty/price/fee fields.
Test extended — assert_response :unprocessable_entity added after the assert_no_difference block.
* Hide cash account ticker from no-security trade detail
* Fix CodeRabbit review issues from PR #1311
- Remove duplicate YAML keys in translation files (de, es, fr)
- Add error handling for security resolution in create_dividend_income
- Extract income trade check to reduce duplication in header template
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* Include holdings in dividend/interest security picker
The security picker for dividend/interest trades should include all securities
in holdings, not just those with trade history. This fixes the issue where
accounts with imported holdings (e.g., SimpleFIN) but no trades would have an
empty picker and be unable to record dividends.
Uses UNION to combine securities from both trades and holdings.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* scope picker to holdings only (a trade creates a holding anyway)
---------
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Add an optional fee field (decimal, precision: 19, scale: 4) to trades.
Fee is included in the total amount calculation (qty * price + fee) for
both create and update flows. The fee field appears on both the create
and edit forms, defaults to 0, and auto-submits like other trade fields.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Show inflow/outflow totals when filtering by transfers
When filtering transactions by "Transfer" type, the summary bar previously
showed $0 for both Income and Expenses because transfers were excluded from
those sums. Now computes transfer inflow/outflow in the same SQL pass and
switches labels to "Inflow"/"Outflow" when transfer amounts are non-zero.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add mixed filter comment and transfer-only test coverage
Document the intentional mixed filter behavior where transfer amounts
are excluded from the summary bar when non-transfer types are present.
Add test exercising Inflow/Outflow label switching for transfer-only results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* third party provider scoping
* Simplify logic and allow only admins to mange providers
* Broadcast fixes
* FIX tests and build
* Fixes
* Reviews
* Scope merchants
* DRY fixes
isTouchDevice() blocked mouse/trackpad drag on touch-capable laptops
(e.g. Windows with touchscreen). Now checks if a touch interaction is
actually in progress instead.
Also adds touchcancel binding to clean up hold timer state when the
OS interrupts a touch (notifications, palm rejection, etc).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Adapt holdings to number inputs
* Reviews
* FIX a small provider hardcoded name
* PR l10n request
---------
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Reorganize import UI with Financial Tools / Raw Data tabs
Split the flat list of import sources into two tabbed sections using
DS::Tabs: "Financial Tools" (Mint, Quicken/QIF, YNAB coming soon) and
"Raw Data" (transactions, investments, accounts, categories, rules,
documents). This prepares for adding more tool-specific importers
without cluttering the list.
https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3
* Fix import controller test to account for YNAB coming soon entry
The new YNAB "coming soon" disabled entry adds a 5th aria-disabled
element to the import dialog.
https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3
* Fix system tests to click Raw Data tab before selecting import type
Transaction, trade, and account imports are now under the Raw Data tab
and need an explicit tab click before the buttons are visible.
https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3
* feat: Add bulk import for NDJSON export files
Implements an import flow that accepts the full all.ndjson file from data exports,
allowing users to restore their complete data including:
- Accounts with accountable types
- Categories with parent relationships
- Tags and merchants
- Transactions with category, merchant, and tag references
- Trades with securities
- Valuations
- Budgets and budget categories
- Rules with conditions and actions (including compound conditions)
Key changes:
- Add BulkImport model extending Import base class
- Add Family::DataImporter to handle NDJSON parsing and import logic
- Update imports controller and views to support NDJSON workflow
- Skip configuration/mapping steps for structured NDJSON imports
- Add i18n translations for bulk import UI
- Add tests for BulkImport and DataImporter
* fix: Fix category import and test query issues
- Add default lucide_icon ("shapes") for categories when not provided
- Fix valuation test to use proper ActiveRecord joins syntax
* Linter errors
* fix: Add default color for tags when not provided in import
* fix: Add default kind for transactions when not provided in import
* Fix test
* Fix tests
* Fix remaining merge conflicts from PR 766 cherry-pick
Resolve conflict markers in test fixtures and clean up BulkImport
entry in new.html.erb to use the _import_option partial consistently.
https://claude.ai/code/session_01BM4SBWNhATqoKTEvy3qTS3
* Import Sure `.ndjson`
* Remove `.ndjson` import from raw data
* Fix support for Sure "bulk" import from old branch
* Linter
* Fix CI test
* Fix more CI tests
* Fix tests
* Fix tests / move PDF import to first tab
* Remove redundant title
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Fix infinite sync loop on SnapTrade setup accounts page
* Address PR feedback: behavior assertions & stale sync recovery
- Uses `latest_sync.completed?` so we don't drop dropped/failed syncs
- Replaces `assigns` checks with `assert_select` DOM checks
- Adds required IDs/classes to the html template for assertions
* Add AI merchant enhancement and dedup
* Enhancements
Add error if job is already running
add note that we also merge merchants
* Allow updating provider website
* Review fixes
* Update provider_merchant.rb
* Linter and fixes
* FIX transaction quick menu modal
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
* Add split ui
* Add settings configuration for split transactions
- Adds a new settings section for appearance changes
- Also adds extra checks for delete and API calls
- Also adds checks for parent/child changes
* fixes
- split transactions dark mode fix
- add split transactions to context menu
* Update entry.rb
1. New validation split_child_date_matches_parent — prevents saving a split child with a date different from its parent. This is the root-cause fix that
protects all flows at once.
2. Bulk update guard — bulk_update! now strips :date from attributes when processing split children, preventing the validation from raising and silently
skipping the date change instead.
* N+1 fix for split_parent?
* Update entry.rb
Problem: In bulk_update!, when a split child has :date removed from attrs (line 432) and the remaining attrs is empty (e.g., the bulk update only
changed the date), entry.update! {} still ran as a no-op. But lock_saved_attributes! and mark_user_modified! at lines 443-444 executed unconditionally,
incorrectly marking untouched split children as user-modified and opting them out of future syncs.
Fix:
1. Added a changed flag to track whether any actual modification happened
2. Wrapped entry.update! in an if attrs.present? check so no-op updates are skipped
3. Gated lock_saved_attributes! and mark_user_modified! behind if changed, so they only run when the entry was actually modified (either via attribute
update or tag update)
* fixes
1. Indentation in show.html.erb Settings section — The split button block and delete block had extra indentation making them appear nested inside guard
blocks they weren't part of. Fixed to match actual nesting.
2. Skip @split_parents query when grouping is off — The controller now only loads split parent entries when show_split_grouped? is true, saving a query
with joins when the feature is disabled.
* Refactor report and dashboard tables from div grids to semantic HTML
Convert div-based grid layouts to proper <table>/<thead>/<tbody>/<tr>/<th>/<td>
elements in report views and the dashboard investment summary:
- reports/_breakdown_table + _category_row (income/expense breakdown)
- reports/_trends_insights (monthly trends)
- reports/_net_worth (asset/liability summaries)
- reports/_investment_performance (top holdings)
- pages/dashboard/_investment_summary (top holdings)
Replaces shared/ruler dividers with border-b border-divider on <tr> elements.
Updates test selectors from div[data-category] to tr[data-category] and from
[role="columnheader"] to thead/th.
Closes#1121
* Address PR review feedback
- Restore w-max sm:w-full wrapper on report tables to preserve horizontal
scroll behavior on narrow screens
- Add sr-only accessible header for net worth amount columns
- Use border-divider instead of border-primary in dashboard investment summary
* Fix rounded corners on semantic table body containers
Move rounded-lg, shadow-border-xs, and bg-container from tbody
(where border-radius and box-shadow don't apply) to a wrapper div
with overflow-hidden. Add bg-container-inset on thead to preserve
the two-tone card design.
Extend privacy mode coverage to remaining financial views
Transfers, trades, valuations, and holdings detail views were missing
the privacy-sensitive class, leaving amounts visible when privacy mode
was enabled. Also adds blur to the summary card partial (used by credit
cards, loans, etc.), account chart balances, and time series chart
containers (dashboard net worth and per-account charts).
Resolve merge conflicts in investment summary/performance views where main's
grid layout refactoring conflicted with privacy-sensitive class additions.
Also add privacy-sensitive to transaction list amounts, transaction detail
header, and sankey cashflow chart containers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Touch events on the full section meant any touch on the header could
trigger hold-to-drag. Now touch events are scoped to the grip handle
button, which is made visible on mobile (was hidden lg:block before).
Desktop drag-and-drop via draggable attribute is unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add scheduled demo family refresh job
Rebuild demo data daily at 5am UTC by anonymizing and enqueueing deletion of the existing demo family while immediately generating new sample data. Add super-admin email notifications with 24-hour session and signup metrics, plus tests for the new job and mailer.
* Delete demo monitoring key before family refresh
Ensure DemoFamilyRefreshJob removes ApiKey::DEMO_MONITORING_KEY from the old demo family before enqueueing async family destruction and generating replacement sample data. Adds a regression assertion that the key is gone before generator execution.
DeveloperMessage was a debug-only STI subclass of Message that was never
created by any production code. Remove the model, view partial, test,
and fixtures, and simplify Chat#conversation_messages accordingly.
https://claude.ai/code/session_012pm5HKGKFs1tpAsvXMr4Tp
Co-authored-by: Claude <noreply@anthropic.com>
* feat: Add default account for manual transaction entries (#1061)
Allow users to designate a default account that auto-selects
in the transaction creation form. Also consolidates account list
actions (edit, link/unlink, enable/disable) into a meatball menu.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* - handle context menu width on mobile
- restrict default account to depository types only
- added FR, ES and DE i18n files
* - Add credit card accounts can also be used as default
- Moved logic into controller
* Scope context menu max-width to accounts menu only
- decouples the width constraint from the shared DS::Menu component by introducing an optional max_width param
* fix ci test and address issues raised by coderabbit and codex
* Address CodeRabbit review feedback
- Use .present? for institution_name guards to avoid empty UI artifacts
- Align "Set default" menu visibility with actual preselection eligibility
(active + unlinked + supports_default?) to prevent drift between UI and model
- Keep disabled star visible when account is already default but now ineligible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add eligible_for_transaction_default? predicate to Account model
Consolidates active + unlinked + supports_default? checks into a single
shared predicate used by the controller, view, and user model guard,
preventing a direct PATCH from bypassing UI eligibility rules.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Added "Unset default" option
Added negative test for default account
Removed duplicated logic for account.eligible_for_transaction_default
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Document admin-only reset auth in OpenAPI docs
The DELETE /api/v1/users/reset endpoint now requires admin role
(ensure_admin). Update the rswag spec to:
- Set default user role to admin so the 200 test passes
- Add a 403 response case for non-admin users with read_write scope
- Clarify the description notes admin requirement
- Add SuccessMessage schema and users paths to openapi.yaml
https://claude.ai/code/session_01Tj8ToLRmVg5HLmHwq9KKDY
* Consolidate duplicate 403 responses for reset endpoint
OpenAPI keys responses by status code, so two 403 blocks caused the
first (insufficient scope) to be silently overwritten by the second
(non-admin). Merge into a single 403 whose description covers both
causes: requires read_write scope and admin role. The test exercises
the read-only key path which hits 403 via scope check.
https://claude.ai/code/session_01Tj8ToLRmVg5HLmHwq9KKDY
* Em-dash out of messages.
* Fix tests
* Fix tests
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(transaction): add support for file attachments using Active Storage
* feat(attachments): implement transaction attachments with upload, show, and delete functionality
* feat(attachments): enhance attachment upload functionality to support multiple files and improved error handling
* feat(attachments): add attachment upload form and display functionality in transaction views
* feat(attachments): implement attachment validation for count, size, and content type; enhance upload form with validation hints
* fix(attachments): use correct UI components
* feat(attachments): Implement Turbo Stream responses for creating and deleting transaction attachments.
* fix(attachments): include auth in activestorage controller
* test(attachments): add test coverage for turbostream and auth
* feat(attachments): extract strings to i18n
* fix(attachments): ensure only newly added attachments are purged when transaction validation fails.
* fix(attachments): validate attachment params
* refactor(attachments): use stimulus declarative actions
* fix(attachments): add auth for other representations
* refactor(attachments): use Browse component for attachment uploads
* fix(attachments): reject empty values on attachment upload
* fix(attachments): hide the upload form if reached max uploads
* fix(attachments): correctly purge only newly added attachments on upload failure
* fix(attachments): ensure attachment count limit is respected within a transaction lock
* fix(attachments): update attachment parameter handling to avoid `ParameterMissing` errors.
* fix(components): adjust icon_only logic for buttonish
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* refactor: use a map of providers that support pending transactions
* feat: add pending transaction manual merging tool
* fix(coderabbit): validate posted_entry_id against eligible posted candidates server-side
* fix(coderabbit): validate offset for negative numbers
* fix(coderabbit): check if pending_duplicate_candidates has_more in one transaction
* refactor: use list of radio buttons for better pagination
* chore: show current transaction range in paginated view
* chore: whitespace
chore: whitespace