* Replace text-tertiary with text-subdued in views
Replace usages of the text-tertiary utility with text-subdued across several view partials to standardize subdued text styling because text-tertiary does not exist in the design system (reports, doorkeeper auth, simplefin items). Also adjust the net worth empty-liabilities markup to use a grid layout for consistent spacing, and update the related controller test selector to match the new CSS class.
* Standardize empty net worth message markup
Replace inconsistent markup and classes for empty asset/liability sections in the net worth partial. Swap text-secondary/p-2/text-center for text-subdued with unified padding (py-3 px-4 lg:px-6), and simplify the liabilities block from a grid/div to a single paragraph for consistent styling and spacing.
* Fix separators in breakdown table view
Correct conditional logic for rendering column separators (rulers) in the reports breakdown table. The top-level check now compares idx to groups.size instead of group.size, and the subcategory check compares idx to group[:subcategories].size. This ensures separators are shown between categories and subcategories correctly, avoiding missing or extra rulers.
* Fix subcategory index variable name in partial
Rename the inner loop index from `idx` to `sub_idx` in app/views/reports/_breakdown_table.html.erb to avoid shadowing the outer `idx`. This ensures the conditional that renders the separator (`shared/ruler`) uses the correct index for subcategories, preventing incorrect rendering of separators between subcategory rows.
* Fix conditional block order in breakdown table
Reorder ERB tags to properly nest the subcategory conditional and the ruler render in the reports breakdown partial. This ensures the divider is only rendered between subcategories and prevents mismatched ERB/end tags that could break template rendering.
* fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically.
* fix: Initialize sankey tooltip with top-0 to avoid overflow
* fix: add fallback to HTML height
* fix: properly set bottom spacing and use position fixed for bottom navbar
* fix: move viewport controller initialization
* fix: locale-dependent category duplication bug
* fix: use family locale for investment contributions category to prevent duplicates and handle legacy data
* Remove v* tag trigger from flutter-build to fix double-runs
publish.yml already calls flutter-build via workflow_call on v* tags,
so the direct push trigger was causing duplicate workflow runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Refactor mobile release asset flow
* fix: category uniqueness and workflow issues
* fix: fix test issue
* fix: solve test issue
* fix: resolve legacy problem
* fix: solve lint test issue
* fix: revert unrelated changes
---------
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent value overflow in Assets vs Liabilities card
Fixes issue where large asset/liability values overflow the container
when AI side panel is open and reduces horizontal space.
Changes:
- Added flex-wrap to allow values to wrap to next line if needed
- Added break-all to both asset and liability values for long numbers
- Added shrink-0 to minus sign to prevent it from shrinking
Fixes#976
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* refactor: use break-words instead of break-all per code review
Changed from break-all to break-words for currency values to prevent
awkward mid-number breaks (e.g., $1,234,5 / 67.89). break-words only
breaks when content overflows and keeps values intact when possible,
providing cleaner line breaks while still preventing overflow.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* Show disabled import options before accounts exist
Keep account-dependent import choices visible on /imports/new and render them as disabled with guidance when no accounts are available.
* Refactor disabled import options: extract partial, fix accessibility (#986)
- Extract _import_option partial to eliminate duplicated enabled/disabled
markup across TransactionImport, TradeImport, and MintImport (also
used by AccountImport, CategoryImport, RuleImport for consistency)
- Replace misleading chevron-right with lock icon in disabled state
- Add aria-disabled="true" for screen reader accessibility
- Remove redundant default: parameter from t() call
- Fix locale key ordering (requires_account after import_* keys)
- Fix extra blank line in test file
- Add assertion for aria-disabled attribute in test
https://claude.ai/code/session_016j9tDYEBfWX9Dzd99rAYjX
Co-authored-by: Claude <noreply@anthropic.com>
* Tailwind fixes
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Wire ui layout and AI flags into mobile auth
Include ui_layout and ai_enabled in mobile login/signup/SSO payloads,
add an authenticated endpoint to enable AI from Flutter, and gate
mobile navigation based on intro layout and AI consent flow.
* Linter
* Ensure write scope on enable_ai
* Make sure AI is available before enabling it
* Test improvements
* PR comment
* Fix review issues: test assertion bug, missing coverage, and Dart defaults (#985)
- Fix login test to use ai_enabled? (method) instead of ai_enabled (column)
to match what mobile_user_payload actually serializes
- Add test for enable_ai when ai_available? returns false (403 path)
- Default aiEnabled to false when user is null in AuthProvider to avoid
showing AI as available before authentication completes
- Remove extra blank lines in auth_provider.dart and auth_service.dart
https://claude.ai/code/session_01LEYYmtsDBoqizyihFtkye4
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Handle empty compound conditions on rules index
* fix: avoid contradictory rule condition summary on /rules
* refactor: move rules condition display logic from view to model
* fix: localize rule title fallback and preload conditions in rules index
Add rswag request specs for merchants index/show and define a MerchantDetail schema used by the docs. Update the generated OpenAPI document with merchants paths and schema.
* Add family moniker selection and dynamic UI labels
Introduce a Family moniker persisted in the database with allowed values Family/Group, add required onboarding selection for it, and thread moniker-aware copy through key user-facing views and locales. Also add helper methods and tests for onboarding form presence and family moniker behavior.
* Small copy edits/change moniker question order
* Conditional Group/Family onboarding flow fixes
* Fix label
* Grouping of fields
* Profile Info page Group/Family changes
* Only admins can change Group/Family moniker
* Repetitive defaults
* Moniker in Account model
* Moniker in User model
* Auth fix
* Sure product is also a moniker
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Hovering over category links in the outflow donut chart triggered
Turbo 8's default link prefetching, which made a real request to the
transactions controller. The controller's store_params! before_action
saved those filter params (category + date range) to the session.
When the user later navigated to /transactions via the nav menu,
the stored params were restored, showing an unexpected filtered view.
Adding data-turbo-prefetch="false" prevents the prefetch on hover
while preserving the intended click-to-navigate behavior.
https://claude.ai/code/session_01Na7AF1wyidPwFdPq5w8oaw
* Add SearchFamilyImportedFiles assistant function with vector store support
Implement per-Family document search using OpenAI vector stores, allowing
the AI assistant to search through uploaded financial documents (tax returns,
statements, contracts, etc.). The architecture is modular with a provider-
agnostic VectorStoreConcept interface so other RAG backends can be added.
Key components:
- Assistant::Function::SearchFamilyImportedFiles - tool callable from any LLM
- Provider::VectorStoreConcept - abstract vector store interface
- Provider::Openai vector store methods (create, upload, search, delete)
- Family::VectorSearchable concern with document management
- FamilyDocument model for tracking uploaded files
- Migration adding vector_store_id to families and family_documents table
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* Extract VectorStore adapter layer for swappable backends
Replace the Provider::VectorStoreConcept mixin with a standalone adapter
architecture under VectorStore::. This cleanly separates vector store
concerns from the LLM provider and makes it trivial to swap backends.
Components:
- VectorStore::Base — abstract interface (create/delete/upload/remove/search)
- VectorStore::Openai — uses ruby-openai gem's native vector_stores.search
- VectorStore::Pgvector — skeleton for local pgvector + embedding model
- VectorStore::Qdrant — skeleton for Qdrant vector DB
- VectorStore::Registry — resolves adapter from VECTOR_STORE_PROVIDER env
- VectorStore::Response — success/failure wrapper (like Provider::Response)
Consumers updated to go through VectorStore.adapter:
- Family::VectorSearchable
- Assistant::Function::SearchFamilyImportedFiles
- FamilyDocument
Removed: Provider::VectorStoreConcept, vector store methods from Provider::Openai
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* Add Vector Store configuration docs to ai.md
Documents how to configure the document search feature, covering all
three supported backends (OpenAI, pgvector, Qdrant), environment
variables, Docker Compose examples, supported file types, and privacy
considerations.
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* No need to specify `imported` in code
* Missed a couple more places
* Tiny reordering for the human OCD
* Update app/models/assistant/function/search_family_files.rb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Juan José Mata <jjmata@jjmata.com>
* PR comments
* More PR comments
---------
Signed-off-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Fix property subtype not persisting on edit
* Add regression test for property subtype persistence
This change introduces model specs and factories to cover
property subtype persistence on update.
FactoryBot setup and test dependencies were adjusted to
support the new specs.
* Add regression test for property subtype persistence
* remove unused FactoryBot factories and test
* remove FactoryBot in Gemfile.lock
* Fix no-op regression test for property subtype update
* Delete no-op property_test
* add pimary_residence in properties fixtures
* add capybara system test for property subtype persistence
* fix spelling and indent
* rename test to "can persist property subtype"
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.com>
---------
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.com>
* feat: Add responsive dialog behavior for transaction modals
Add responsive option to DS::Dialog component that switches between:
- Mobile (< 1024px): Modal style (centered) with inline close button
- Desktop (≥ 1024px): Drawer style (right side panel) with header close button
Update transaction, transfer, holding, trade, and valuation views to use
responsive behavior, maintaining mobile experience while reverting desktop
to drawer style like budget categories.
Changes:
- app/components/DS/dialog.rb: Add responsive parameter and helper methods
- app/components/DS/dialog.html.erb: Apply responsive styling
- app/views/*/show.html.erb: Add responsive: true and hide close icons on mobile
* fix: Enhance close button accessibility in dialog components
* fix: Refactor dialog component to improve close button handling and accessibility
* Include newer providers in automatic family sync
Coinbase, CoinStats, Mercury, and SnapTrade all implement Syncable
and have Syncer classes but were not listed in child_syncables,
meaning their data only refreshed on manual sync button clicks.
* refactor(syncer): Open/Closed principle for provider sync
- Adding new providers requires modifying child_syncables (violates O/C)
- plaid_items missing .active scope (bug: syncs deleted items)
- snaptrade_items can exist without user registration → fails on sync
- Scattered knowledge about 'ready to sync' logic
1. **Registry pattern**: SYNCABLE_ITEM_ASSOCIATIONS constant lists all
provider associations that participate in family sync
2. **Encapsulated sync-readiness**: Each item model defines its own
`syncable` scope that knows when it's ready for auto-sync:
- Most providers: `syncable = active` (not scheduled for deletion)
- SnapTrade: `syncable = active + user_registered` (has API creds)
3. **Single loop**: child_syncables iterates the registry, calling
`.syncable` on each association
- Adding a provider = add to registry + define syncable scope
- Each model owns its 'ready to sync' business logic
- Fixes plaid_items bug (now uses .active via .syncable)
- Fixes snaptrade auto-sync failures (filters unregistered items)
- Easy to extend with new conditions per provider
- family/syncer.rb: Registry + dynamic collection
- *_item.rb (7 files): Add `scope :syncable, -> { active }`
- snaptrade_item.rb: Add syncable with user_registered filter
* Fix rubocop bracket spacing in SnaptradeItem syncable scope
* fix: keep nav bar sticky at top
* fix: sticky on settings page
* fix: keep padding in settings page
* fix: make all settings page title sticky
* fix: make buttons sticky with title
* fix: set header bar min height
* fix: mobile responsive
* fix: reduce header bar
* Add LLM prompt for API endpoint consistency (fixes#944)
- Add .cursor/rules/api-endpoint-consistency.mdc: checklist that applies
when editing app/controllers/api/v1, spec/requests/api/v1, or
test/controllers/api/v1. Enforces (1) Minitest-only behavioral coverage
for new endpoints, (2) rswag docs-only (no expect/assert), (3) same
API key auth pattern in all rswag specs.
- Reference the rule in AGENTS.md under API Development Guidelines.
* Add tests for API endpoint consistency implementation
- Minitest: test/api_endpoint_consistency_rule_test.rb checks rule file
exists, globs, and all three sections (Minitest, rswag docs-only,
API key auth) plus AGENTS.md reference.
- Standalone: test/support/verify_api_endpoint_consistency.rb runs
same checks without loading Rails (use when app fails to boot).
- Rule: add mdc: links for Cursor, note valuations_spec OAuth outlier.
* Address review: add --compliance check, CLAUDE.md section
- Verification script: --compliance scans current APIs and reports
rswag OAuth vs API key, missing Minitest for controllers, expect/assert.
- CLAUDE.md: add Post-commit API consistency subsection under
API Development Guidelines (links to rule, documents script + --compliance).
---------
Co-authored-by: mkdev11 <jaysmth689+github@users.noreply.github.com>
* First cut of a simplified "intro" UI layout
* Linter
* Add guest role and intro-only access
* Fix guest role UI defaults (#940)
Use enum predicate to avoid missing role helper.
* Remove legacy user role mapping (#941)
Drop the unused user role references in role normalization
and SSO role mapping forms to avoid implying a role that
never existed.
Refs: #0
* Remove role normalization (#942)
Remove role normalization
Roles are now stored directly without legacy mappings.
* Revert role mapping logic
* Remove `normalize_role_settings`
* Remove unnecessary migration
* Make `member` the default
* Broken `.erb`
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
* Add Indexa Capital provider scaffold
Generate Indexa Capital provider scaffolding and align credential fields with the API authentication requirements.
* Fix PR 926 lint and schema CI failures
* Implement Indexa Capital provider with real API integration
- Rewrite all broken view templates (were meta-ERB from code generator)
- Create missing select_accounts.html.erb template
- Implement real API calls: list_accounts via /users/me, get_holdings
via /accounts/{number}/fiscal-results, get_account_balance via
/accounts/{number}/performance
- Add API token auth support (stored token > env token > credentials)
- Add api_token column with encryption support
- Redesign settings panel: API token prominent, credentials collapsible
- Fix account balances display using performance endpoint portfolios
- Fix accounts index empty-state guard missing indexa_capital_items
- Simplify activities fetch job (no activities API endpoint exists)
- Fix i18n interpolation (%%{ -> %{) throughout locale file
* Add tests for Indexa Capital provider integration
- IndexaCapitalItemTest: validations, credentials, scopes, sync status
- IndexaCapitalAccountTest: upsert, holdings, account provider linking
- Provider::IndexaCapitalTest: auth modes, API stubs, error handling
- IndexaCapitalItemsControllerTest: CRUD, setup, linking, authorization
- Fixtures for items (token + credentials) and accounts (mutual + pension)
52 tests, 98 assertions, 0 failures
* Address code review feedback from PR #933
- Fix zero balance bug: use `nil?` instead of `present?` so 0 is stored
- Fix has_indexa_capital_credentials? to check api_token (was ignored)
- Fix build_provider to delegate to Provided concern (was ignoring token)
- Fix IndexaCapital section outside encryption_error guard in settings
- Add account_number sanitization to prevent path traversal in API URLs
- Replace all skipped processor tests with real working tests
- Add zero-balance and path-traversal test coverage
61 tests, 107 assertions, 0 failures
* Address code review round 2: credentials validation, RuboCop, test quality
- Fix RuboCop SpaceInsideArrayLiteralBrackets in credentials check
- Chain where.not calls so all three username/document/password must be present
- Require all three credentials (||) instead of any one (&&) in validate_configuration!
- Move attr_reader to private to avoid exposing credentials publicly
- Parse dates with Date.parse in extract_balance for robustness
- Remove stale TODO and Crypto from supported_account_types
- Order build_provider query deterministically by created_at
- Replace no-op holdings assertion with meaningful assert_difference
* Address code review round 3: JSON parse safety and test precision
- Rescue JSON::ParserError on 2xx responses for clearer error messages
- Fix weak balance assertion: set balance to 0 before processing, assert
expected value (27093.01 = sum of holdings amounts)
* Include Indexa Capital in automatic family sync
Add indexa_capital_items to Family::Syncer#child_syncables so balances
and holdings refresh on daily auto-sync and login sync, not only on
manual sync button clicks.
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Add REST API for holdings and trades (Discussion #905)
- Trades: GET index (filter by account_id, account_ids, start_date, end_date),
GET show, POST create (buy/sell with security_id or ticker), PATCH update,
DELETE destroy. Create restricted to accounts that support trades (investment
or crypto exchange). Uses existing Trade::CreateForm for creation.
- Holdings: GET index (filter by account_id, account_ids, date, start_date,
end_date, security_id), GET show. Read-only; scoped to family.
- Auth: read scope for index/show; write scope for create/update/destroy.
- Responses: JSON via jbuilder (trade: id, date, amount, qty, price, account,
security, category; holding: id, date, qty, price, amount, account, security,
avg_cost). Pagination for index endpoints (page, per_page).
Co-authored-by: Cursor <cursoragent@cursor.com>
* API v1 holdings & trades: validation, docs, specs
- Holdings: validate date params, return 400 for invalid dates (parse_date!)
- Trades: validate start_date/end_date, return 422 for invalid dates
- Trades: accept buy/sell and inflow/outflow in update (trade_sell_from_type_or_nature?)
- Trades view: nil guard for trade.security
- Trades apply_filters: single join(:entry) when filtering
- OpenAPI: add Trade/TradeCollection schemas, ErrorResponse.errors
- Add spec/requests/api/v1/holdings_spec.rb and trades_spec.rb (rswag)
- Regenerate docs/api/openapi.yaml
Co-authored-by: Cursor <cursoragent@cursor.com>
* CI: fix Brakeman and test rate-limit failures
- Disable Rack::Attack in test (use existing enabled flag) so parallel
API tests no longer hit 429 from shared api_ip throttle
- Add Brakeman ignore for trades_controller trade_params mass-assignment
(account_id/security_id validated in create/update)
- Trades/holdings API and OpenAPI spec updates
Co-authored-by: Cursor <cursoragent@cursor.com>
* Trades: partial qty/price update fallback; fix PATCH OpenAPI schema
- Fall back to existing trade qty/price when only one is supplied so sign
normalisation and amount recalculation always run
- OpenAPI: remove top-level qty, price, investment_activity_label,
category_id from PATCH body; document entryable_attributes only
Co-authored-by: Cursor <cursoragent@cursor.com>
* Trades: fix update/DELETE OpenAPI and avoid sell-trade corruption
- Only run qty/price normalisation when client sends qty or price; preserve
existing trade direction when type/nature omitted
- OpenAPI: remove duplicate PATCH path param; add 422 for PATCH; document
DELETE 200 body (DeleteResponse)
Co-authored-by: Cursor <cursoragent@cursor.com>
* API: flat trade update params, align holdings errors, spec/OpenAPI fixes
- Trades update: accept flat params (qty, price, type, etc.), build
entryable_attributes in build_entry_params_for_update (match transactions)
- Holdings: ArgumentError → 422 validation_failed; parse_date!(value, name)
with safe message; extract render_validation_error, log_and_render_error
- Specs: path id required (trades, holdings); trades delete 200 DeleteResponse;
remove holdings 500; trades update body flat; holdings 422 invalid date
- OpenAPI: PATCH trade request body flat
Co-authored-by: Cursor <cursoragent@cursor.com>
* OpenAPI: add 422 invalid date filter to holdings index
Co-authored-by: Cursor <cursoragent@cursor.com>
* API consistency and RSwag doc-only fixes
- Trades: use render_validation_error in all 4 validation paths; safe_per_page_param case/when
- Holdings: set_holding to family.holdings.find; price as Money.format in API; safe_per_page_param case/when
- Swagger: Holding qty/price descriptions (Quantity of shares held, Formatted price per share)
- RSwag: trades delete and valuations 201 use bare run_test! (documentation only, no expect)
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix index-vs-show visibility inconsistencies and preserve custom activity labels
- Add account status filter to set_holding to match index behavior
- Add visible scope to set_trade to match index behavior
- Preserve existing investment_activity_label when updating qty/price
Co-authored-by: Cursor <cursoragent@cursor.com>
* Trades: clearer validation for non-numeric qty/price
Return 'must be valid numbers' when qty or price is non-numeric (e.g. abc)
instead of misleading 'must be present and positive'.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: mkdev11 <jaysmth689+github@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add account linking functionality for SnapTrade items
- Introduced UI to link existing accounts when setting up SnapTrade items, preventing duplicate account creation.
- Updated controller to fetch linkable accounts.
- Added tests to verify proper filtering of accounts and linking behavior.
* Add `snaptrade_item_id` to account linking flow for SnapTrade items
- Updated controller to allow specifying `snaptrade_item_id` when linking accounts.
- Adjusted form and views to include `snaptrade_item_id` as a hidden field.
- Enhanced tests to validate behavior with the new parameter.
- Introduced new tests to cover SnapTrade decryption and connection errors in `SnaptradeItemsControllerTest`.
- Updated error messages for improved user clarity.
- Modified `unlink` functionality to preserve `SnaptradeAccount` records while ensuring proper detachment of associated holdings.
* feat: Protect demo monitoring API key from deletion
- Add DEMO_MONITORING_KEY constant to ApiKey model
- Add `demo_monitoring_key?` method to identify the monitoring key
- Add `visible` scope to exclude monitoring key from UI queries
- Update controller to use `visible` scope, hiding the monitoring key
- Prevent revocation of the monitoring key with explicit error handling
- Update Demo::Generator to use the shared constant
Users on the demo instance can still create their own API keys,
but cannot see or delete the monitoring key used for uptime checks.
https://claude.ai/code/session_01RQFsw39K7PB5kztboVdBdB
* Linter
* Protect demo monitoring API key from deletion
* Use monitoring source for demo API key
* Add test for demo monitoring revoke guard
* Disable Rack::Attack in test and development
---------
Co-authored-by: Claude <noreply@anthropic.com>