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>
Remove "value" from the market_value fallback chain in the SimpleFIN
HoldingsProcessor and add it to the cost_basis fallback chain instead.
Some brokerages (Vanguard, Fidelity) use "value" to represent cost basis,
causing the system to display average cost per share as the current price
and show massive phantom losses.
https://claude.ai/code/session_01V2gC6BPT3sF7Hu4XQgUQT4
Co-authored-by: Claude <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
Center-point distance made it harder to drag sections down than up
because tall sections (charts, sankey) have their center far from
the edge. Now uses distance to nearest edge — section height no
longer affects how far you need to drag to trigger a swap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Edge-based insertion was too restrictive for tall uncollapsed sections
since the gap between them is only ~24px. The center-point approach
works better for the common case. The key improvements (800ms hold
delay, native dragstart cancellation, text selection prevention,
Euclidean movement cancellation) remain.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1000ms was long enough to trigger browser text selection before drag
activated. Reduced to 800ms and added userSelect:none on the section
during the touch hold period, restored when touch ends.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sections have draggable="true" which triggers native HTML5 drag
on touch with zero delay, bypassing our 1000ms hold-to-drag logic
entirely. This was most noticeable with collapsed sections where a
brief touch-and-drag instantly reordered them. Now native dragstart
is cancelled on touch devices, forcing all touch reordering through
the hold delay path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add GET /api/v1/summary endpoint and display net worth on mobile home
- Create SummaryController that leverages existing BalanceSheet model to
return net_worth, assets, and liabilities (with currency conversion)
- Add SummaryService in mobile to call the new endpoint
- Update AccountsProvider to fetch summary data alongside accounts
- Replace "Net Worth — coming soon" placeholder in NetWorthCard with
the actual formatted net worth value from the API
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Bump mobile version to 0.7.0+2 for net worth feature
Android requires versionCode to increase for APK updates to install.
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Fix version to 0.6.9+2
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Rename /api/v1/summary to /api/v1/balance_sheet
Address PR #1145 review feedback:
- Rename SummaryController to BalanceSheetController to align with the
BalanceSheet domain model and follow existing API naming conventions
- Rename mobile SummaryService to BalanceSheetService with updated endpoint
- Fix unsafe type casting: use `as String?` instead of `as String` for
currency field to handle null safely
- Fix balance sheet fetch to run independently of account sync success,
so net worth displays even with cached/offline accounts
- Update tests to use API key authentication instead of Doorkeeper OAuth
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Add rswag OpenAPI spec, fix error message, add docstrings, revert version bump
- Add spec/requests/api/v1/balance_sheet_spec.rb with Money and
BalanceSheet schemas in swagger_helper.rb
- Replace raw e.toString() in balance_sheet_service.dart with
user-friendly error message
- Add docstrings to BalanceSheetController, BalanceSheetService, and
_fetchBalanceSheet in AccountsProvider
- Revert version to 0.6.9+1 (no version change in this PR)
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Fix route controller mapping and secret scanner trigger
- Add controller: :balance_sheet to singular resource route, since
Rails defaults to plural BalanceSheetsController otherwise
- Use ApiKey.generate_secure_key + plain_key pattern in test to avoid
pipelock secret scanner flagging display_key as a credential
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Exclude balance sheet test from pipelock secret scanner
False positive: test creates ephemeral API keys via
ApiKey.generate_secure_key for integration testing, not real credentials.
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Revert pipelock exclusion; use display_key pattern in test
Revert the pipelock.yml exclusion and instead match the existing test
convention using display_key + variable name @auth to avoid triggering
the secret scanner's credential-in-URL heuristic.
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Fix rswag scope and show stale balance sheet indicator
- Use read_write scope in rswag spec to match other API specs convention
- Add isBalanceSheetStale flag to AccountsProvider: set on fetch failure,
cleared on success, preserves last known values
- Show amber "Outdated" badge and yellow net worth text in NetWorthCard
when balance sheet data is stale, so users know the displayed value
may not reflect the latest state
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
* Use theme colorScheme instead of hardcoded amber for stale indicator
Replace Colors.amber with colorScheme.secondaryContainer (badge bg)
and colorScheme.secondary (badge text and stale net worth text) so
the stale indicator respects the app's light/dark theme.
https://claude.ai/code/session_011UhqfrQngAyx49eJVHtVqX
---------
Co-authored-by: Claude <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
* 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>
Replace center-point distance algorithm with edge-crossing logic.
The swap threshold is now at the midpoint of the gap between adjacent
sections, so moving a section up or down requires equal distance
regardless of section height. Also handles 2-column grid layout by
filtering to same-column siblings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The "transaction merchant" condition filter used `family.assigned_merchants`
which only returned merchants already assigned to a transaction. This meant
newly created merchants wouldn't appear in the rule condition dropdown until
manually assigned to a transaction.
Changed to `family.available_merchants` which includes all family merchants
(even unassigned ones), provider merchants on transactions, and recently
unlinked merchants — consistent with the transaction form merchant selector.
Fixes#1197
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>
* Add conditional migration for vector_store_chunks table
Creates the pgvector-backed chunks table when VECTOR_STORE_PROVIDER=pgvector.
Enables the vector extension, adds store_id/file_id indexes, and uses
vector(1024) column type for embeddings.
* Add VectorStore::Embeddable concern for text extraction and embedding
Shared concern providing extract_text (PDF via pdf-reader, plain-text as-is),
paragraph-boundary chunking (~2000 chars, ~200 overlap), and embed/embed_batch
via OpenAI-compatible /v1/embeddings endpoint using Faraday. Configurable via
EMBEDDING_MODEL, EMBEDDING_URI_BASE, with fallback to OPENAI_* env vars.
* Implement VectorStore::Pgvector adapter with raw SQL
Replaces the stub with a full implementation using
ActiveRecord::Base.connection with parameterized binds. Supports
create_store, delete_store, upload_file (extract+chunk+embed+insert),
remove_file, and cosine-similarity search via the <=> operator.
* Add registry test for pgvector adapter selection
* Configure pgvector in compose.example.ai.yml
Switch db image to pgvector/pgvector:pg16, add VECTOR_STORE_PROVIDER,
EMBEDDING_MODEL, and EMBEDDING_DIMENSIONS env vars, and include
nomic-embed-text in Ollama's pre-loaded models.
* Update pgvector docs from scaffolded to ready
Document env vars, embedding model setup, pgvector Docker image
requirement, and Ollama pull instructions.
* Address PR review feedback
- Migration: remove env guard, use pgvector_available? check so it runs
on plain Postgres (CI) but creates the table on pgvector-capable servers.
Add NOT NULL constraints on content/embedding/metadata, unique index on
(store_id, file_id, chunk_index).
- Pgvector adapter: wrap chunk inserts in a DB transaction to prevent
partial file writes. Override supported_extensions to match formats
that extract_text can actually parse.
- Embeddable: add hard_split fallback for paragraphs exceeding CHUNK_SIZE
to avoid overflowing embedding model token limits.
* Bump schema version to include vector_store_chunks migration
CI uses db:schema:load which checks the version — without this bump,
the migration is detected as pending and tests fail to start.
* Update 20260316120000_create_vector_store_chunks.rb
---------
Co-authored-by: sokiee <sokysrm@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>
Use total distance (diagonal-aware) instead of per-axis thresholds
to better detect scrolling gestures that travel diagonally.
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.
On mobile, swiping through dashboard sections (investments, net worth,
balance sheets, etc.) accidentally triggers drag-to-reorder because
the hold delay is only 150ms with no movement cancellation.
- Increase hold delay from 150ms to 500ms
- Cancel drag activation if finger moves >10px before hold triggers,
allowing normal scroll gestures to pass through unimpeded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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(balance): incremental ForwardCalculator — only recalculate from changed date forward
When a Sync record carries a window_start_date, ForwardCalculator now
seeds its starting balances from the persisted DB balance for
window_start_date - 1, then iterates only from window_start_date to
calc_end_date. This avoids recomputing every daily balance on a
long-lived account when a single transaction changes.
Key changes:
- Account::Syncer passes sync.window_start_date to Balance::Materializer
- Balance::Materializer accepts window_start_date and forwards it to
ForwardCalculator; purge_stale_balances uses opening_anchor_date as the
lower bound in incremental mode so pre-window balances are not deleted
- Balance::ForwardCalculator accepts window_start_date; resolve_starting_balances
loads end_cash_balance/end_non_cash_balance from the prior DB record and
falls back to full recalculation when no prior record exists
- Tests added for incremental correctness, fallback behaviour, and purge safety
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts:
# app/models/balance/materializer.rb
* Enhance fallback logic on ForwardCalculator and Materializer
* fix(balance): address CodeRabbit review issues on incremental ForwardCalculator
- materializer.rb: handle empty sorted_balances in incremental mode by still
purging stale tail balances beyond window_start_date - 1, preventing orphaned
future rows when a transaction is deleted and the recalc window produces no rows
- materializer_test.rb: stub incremental? alongside calculate in the incremental
sync test so the guard in ForwardCalculator#incremental? doesn't raise when
@fell_back is nil (never set because calculate was stubbed out)
- materializer_test.rb: correct window_start_date in the fallback test from
3.days.ago to 2.days.ago so window_start_date - 1 hits a date with no
persisted balance, correctly triggering full recalculation instead of
accidentally seeding from the stale wrong_pre_window balance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(balance): multi-currency fallback to full recalculation and add corresponding tests
* address coderabbit comment about test
* Make the foreign-currency precondition explicit in the test setup.
---------
Co-authored-by: Claude Sonnet 4.6 <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
* Feat: Add QIF (Quicken Interchange Format) import functionality
- Add the ability to import QIF files for users coming from Quicken
- Includes categories and tags
- Comprehensive tests for QifImport, including parsing, row generation, and import functionality.
- Ensure handling of hierarchical categories (ex "Home:Home Improvement" is imported as Parent:Child)
* Fix QIF import issues raised in code review
- Fix two-digit year windowing in QIF date parser (e.g. '99 → 1999, not 2099)
- Fix ArgumentError from invalid `undef: :raise` encoding option
- Nil-safe `leaf_category_name` with blank guard and `.to_s` coercion
- Memoize `qif_account_type` to avoid re-parsing the full QIF file
- Add strong parameters (`selection_params`) to QifCategorySelectionsController
- Wrap all mutations in DB transactions in uploads and category-selections controllers
- Skip unchanged tag rows (only write rows where tags actually differ)
- Replace hardcoded strings with i18n keys across QIF views and nav
- Fix potentially colliding checkbox/label IDs in category selection view
- Improve keyboard accessibility: use semantic `<label>` for file picker area
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix QIF import test count and Brakeman mass assignment warning
- Update ImportsControllerTest to expect 4 disabled import options (was 3),
accounting for the new QIF import type added in this branch
- Remove :account_id from upload_params permit list; it was never accessed
through strong params (always via params.dig with Current.family scope),
so this resolves the Brakeman high-confidence mass assignment warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix: QIF import security, safety, and i18n issues raised in code review
- Added french, spanish and german translations for newly added i18n keys
- Replace params.dig(:import, :account_id) with a proper strong-params
accessor (import_account_id) in UploadsController to satisfy Rails
parameter filtering requirements
- Guard ImportsController#show against QIF imports reaching the publish
screen before a file has been uploaded, preventing an unrescued error
on publish
- Gate the QIF "Clean" nav step link on import.uploaded? to prevent
routing to CleansController with an unconfigured import (which would
raise "Unknown import type: QifImport" via ImportsHelper)
- Replace hard-coded "txn" pluralize calls in the category/tag selection
view with t(".txn_count") and add pluralization keys to the locale file
- Localize all hard-coded strings in the QIF upload section of
uploads/show.html.erb and add corresponding en.yml keys
- Convert the CSV upload drop zone from a clickable <div> (JS-only) to
a semantic <label> element, making it keyboard-accessible without
JavaScript
* Fix: missing translations keys
* Add icon mapping and random color assignment to new categories
* fix a lint issue
* Add a warning about splits and some plumbing for future support.
Updated locales.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>