whisper-money/tests/Feature
Víctor Falcón 4c83cb8b33
feat(import): persist per-account import configuration on the backend (#698)
## What & why

Import configuration (the CSV/Excel column mapping and date format a
user
sets up per account) was stored in the browser's `localStorage`, so it
never
followed the user to another device. Importing the same account's
statement on
mobile — or on a new machine — meant reconfiguring the mapping from
scratch.

This moves that configuration to the backend, keyed per account and per
import
type, so it's preconfigured and loaded automatically wherever the user
imports.

## Demo

Cross-device QA: import transactions on "device 1" with a custom column
mapping, then clear **all** cookies + local/session storage (simulating
a
fresh device), log back in, and start a new import — the mapping
(Description → *Movement*, Amount → *How Much*, date format
*DD-MM-YYYY*)
is auto-loaded from the backend with no manual setup.


https://github.com/user-attachments/assets/e449dfe9-3970-48d4-97ca-9e415c73835b



## How

- New `account_import_configs` table: one row per `(account_id, type)`
where
`type` is `transaction` or `balance`. The mapping + date format are
stored as
  an opaque `config` JSON blob (exactly what the client sends).
- `GET/PUT /api/accounts/{account}/import-config`
(`AccountImportConfigController`),
authorized via the existing `AccountPolicy` (`view`/`update`) — mirrors
  `AccountBalanceController`. `PUT` upserts on `(account_id, type)`.
- Frontend: the two import-config storage helpers now read/write the
endpoint
via `axios` instead of `localStorage` (merged into a single module — the
  transaction and balance variants shared the same logic).
- The saved config is fetched **off the file-parse critical path**: the
parsed
file shows immediately with auto-detected columns, and the saved mapping
is
applied when it arrives (guarded so a slow load can't clobber a file
picked
afterwards). A slow or hanging request never blocks the preview or Next
  button. When no config exists or the request fails, it falls back to
  auto-detection exactly as before.

## Notes / decisions

- **No localStorage migration.** Existing per-device configs aren't
migrated;
on the next import the mapping is auto-detected and re-saved to the
backend,
so it self-heals after one import. The old `import_config_account_*`
keys are
  simply no longer read.
- The mapping-validity check against the actual file headers stays
client-side
  (it depends on the just-parsed file).

## Testing

- `tests/Feature/AccountImportConfigTest.php` (9 tests): auth required,
  cross-user 403 on read and write, save→persist→load round-trip, upsert
  de-duplication, transaction/balance independence, and validation
  (unknown type, missing column mapping).
- Sibling suites green (`AccountBalanceControllerTest`,
`SavedFilterTest`) —
no regression from the new `Account::importConfigs()` relation or
routes.
2026-07-18 16:10:40 +02:00
..
Ai fix(ai): don't report expected transient provider overloads (PHP-LARAVEL-44) (#655) 2026-07-07 10:55:02 +00:00
Api feat(transactions): serve import dedup and account ledger from the backend (#631) 2026-07-03 16:49:59 +02:00
Auth feat(users): track last login and last active timestamps (#516) 2026-06-10 11:01:30 +02:00
Commands feat(leads): add user lead re-invite campaign (#432) 2026-05-26 08:35:31 +02:00
Console feat(encryption): report count of users still holding encrypted data (#687) 2026-07-17 09:29:33 +00:00
Events fix: stop double-dispatching transaction listeners (N+1 insert into jobs) (#620) 2026-07-02 13:26:45 +00:00
Jobs feat(drip): email users stuck on the paywall a day after onboarding (#562) 2026-06-19 14:11:12 +00:00
Listeners feat(budgets): track multiple categories and labels per budget (#466) 2026-06-01 12:32:23 +02:00
Mail feat(leads): add user lead re-invite campaign (#432) 2026-05-26 08:35:31 +02:00
Mcp feat(mcp): add OAuth 2.1 for Claude Desktop & ChatGPT connectors (Phase 3) (#691) 2026-07-17 19:10:48 +02:00
Onboarding feat(ai): defer per-transaction categorization until onboarding completes (#536) 2026-06-15 17:33:26 +02:00
OpenBanking fix(banking): treat EnableBanking upstream 5xx as transient, not reportable (#678) 2026-07-15 06:40:55 +00:00
Services fix(balances): stop historical-balance generator OOM on ancient purchase/loan dates (PHP-LARAVEL-49) (#661) 2026-07-08 21:34:43 +00:00
Settings feat(mcp): add write tools (Phase 2) (#690) 2026-07-17 15:25:03 +00:00
Spaces feat(spaces): phase 0 — multi-tenant Space foundation (no behaviour change) (#650) 2026-07-09 14:26:07 +02:00
Sync refactor: Simplify transaction endpoints architecture (#76) 2026-01-25 16:15:17 +01:00
AccountBalanceControllerTest.php fix(balances): allow saving a zero balance (#664) 2026-07-10 15:02:38 +02:00
AccountControllerTest.php chore: harden Inertia boundary, CI type-check, and test isolation (#640) 2026-07-04 18:57:58 +00:00
AccountImportConfigTest.php feat(import): persist per-account import configuration on the backend (#698) 2026-07-18 16:10:40 +02:00
AccountUserCurrencyServiceTest.php fix(open-banking): stop storing the XXX no-currency placeholder on accounts (#602) 2026-06-27 16:01:21 +00:00
AiConsentSettingsTest.php refactor(ai): remove AiConsentSettings feature flag (#619) 2026-07-01 09:47:55 +02:00
AiConsentTest.php feat(ai): dismissable AI consent banner that stops after the first decision (#617) 2026-07-01 07:26:36 +00:00
AlignAccountsEncryptedFlagMigrationTest.php refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
ApplyRealEstateRevaluationTest.php fix(real-estate): compound annual revaluation monthly (#337) 2026-04-27 07:35:51 +01:00
AuthenticatedLayoutSafeAreaTest.php fix(layout): keep bottom padding while floating nav is visible (#537) 2026-06-15 18:23:26 +02:00
AutomationRuleApplicationTest.php fix(security): scope job-status endpoints to owner + feature-area fixes (#627) 2026-07-03 14:49:32 +02:00
AutomationRuleEvaluationTest.php feat(transactions): add counterparty fields (#440) 2026-05-27 16:20:55 +02:00
AutomationRuleTest.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
BackfillAccountIbansCommandTest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
BackfillXxxAccountCurrenciesTest.php fix(open-banking): stop storing the XXX no-currency placeholder on accounts (#602) 2026-06-27 16:01:21 +00:00
BalanceLookupTest.php feat: investment benefits — show gains/losses on investment accounts (#140) 2026-02-23 13:59:10 +01:00
BudgetHistoricalAssignmentTest.php feat(budgets): track multiple categories and labels per budget (#466) 2026-06-01 12:32:23 +02:00
BudgetPeriodDateTest.php Remove budgets feature flag (#108) 2026-02-12 09:58:01 +01:00
BudgetPeriodServiceTest.php fix(budgets): make period generation idempotent (#533) 2026-06-15 12:44:44 +02:00
BudgetTest.php feat: add catch-all budgets (#527) 2026-06-15 16:07:19 +00:00
BudgetTransactionServiceTest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
BulkUpdateTransactionsTest.php refactor: Simplify transaction endpoints architecture (#76) 2026-01-25 16:15:17 +01:00
CancelFreeEnableBankingConnectionsCommandTest.php Cancel Enable Banking connections for free users (#289) 2026-04-15 16:23:03 +02:00
CashflowAnalyticsTest.php fix(cashflow): bound trend window to prevent request timeout (#534) 2026-06-15 12:47:27 +02:00
CashflowPageTest.php feat(cashflow): add savings and period views (#424) 2026-05-25 16:41:00 +02:00
CatchAllBudgetTest.php feat: add catch-all budgets (#527) 2026-06-15 16:07:19 +00:00
CategoryMonthlyBreakdownTest.php feat(analysis): per-category 12-month spending drawer (#519) 2026-06-11 09:52:53 +02:00
CurrencyConversionServiceTest.php fix(currency): make rate fetching resilient to slow CDN (#502) 2026-06-08 09:10:38 +02:00
DashboardAnalyticsTest.php fix(accounts): show credit cards as positive and exclude them from net worth (#673) 2026-07-13 07:27:52 +00:00
DashboardTest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
DecryptTransactionsTest.php refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
DeleteUserCommandTest.php Support soft-deleted users with reusable emails (#316) 2026-04-22 11:41:41 +01:00
DemoAccountRestrictionsTest.php feat(demo): gate demo account access behind a config flag (#580) 2026-06-22 11:01:27 +00:00
DisconnectBankingConnectionsCommandTest.php feat(banking): add command to disconnect connections by id (#497) 2026-06-06 11:16:01 +02:00
DiscordWebhookTest.php feat: add Discord admin feed for daily stats and Stripe events (#458) 2026-05-30 18:14:46 +02:00
EncryptionTest.php refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
ExampleTest.php Install Pest 2025-11-07 12:01:58 +00:00
ExchangeRateServiceTest.php Fix PHP-LARAVEL-1V exchange rate cache race (#383) 2026-05-12 12:45:45 +02:00
GenerateStripePromotionCodesCommandTest.php feat(stripe): add promo code generator (#311) 2026-04-20 18:15:28 +01:00
IdorVulnerabilityTest.php refactor: Simplify transaction endpoints architecture (#76) 2026-01-25 16:15:17 +01:00
ImportDataTest.php fix: Apply automation rule labels on transaction creation and import (#79) 2026-01-27 11:11:29 +01:00
InertiaSharedDataTest.php feat(mcp): read-only MCP server for Pro accounts (#689) 2026-07-17 16:54:15 +02:00
IntegrationRequestTest.php feat(integration-requests): add done status and fix review command crash on orphaned author (#601) 2026-06-27 14:42:09 +00:00
LabelTest.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
LandingAuthOverrideTest.php feat(leads): cohort-based launch invitations with per-user Stripe coupons (#333) 2026-04-30 15:10:28 +01:00
LoanTest.php chore: harden Inertia boundary, CI type-check, and test isolation (#640) 2026-07-04 18:57:58 +00:00
LocalizationTest.php feat(i18n): add French translation support (#532) 2026-06-15 19:15:43 +02:00
LoggingConfigTest.php fix(logging): keep laravel.log writable across container UIDs (#451) 2026-05-29 15:10:50 +02:00
MailSenderTest.php refactor(drip): extract base mailable and job for the drip email family (#641) 2026-07-04 20:51:38 +02:00
NewTransactionsMarkerTest.php feat(transactions): make new-transaction marker cross-device (#611) 2026-06-29 19:11:37 +02:00
PersistUpsellSourceFromStripeTest.php feat: reuse the upgrade modal at more upsell points and attribute revenue (#699) 2026-07-18 12:53:20 +00:00
PlaintextTransactionsTest.php Remove plaintext-transactions feature flag & E2E references (#116) 2026-02-13 11:10:21 +01:00
PlanFeatureTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
PopoverSafeAreaTest.php fix: keep iOS popovers below the notch (#282) 2026-04-13 15:19:56 +01:00
PostStripeEventToDiscordTest.php fix(discord): show old → new plan on plan change notification (#637) 2026-07-04 15:49:09 +00:00
PurgeResidualEncryptionArtifactsJobTest.php chore: harden Inertia boundary, CI type-check, and test isolation (#640) 2026-07-04 18:57:58 +00:00
PwaTest.php Harden browser storage and PostHog recording (#402) 2026-05-14 15:57:48 +02:00
QueueConfigTest.php fix(queue): raise retry_after above the longest job timeout (PHP-LARAVEL-2D) (#645) 2026-07-05 09:31:23 +00:00
ReEvaluateTransactionRulesTest.php fix(security): scope job-status endpoints to owner + feature-area fixes (#627) 2026-07-03 14:49:32 +02:00
RealEstateAvailabilityTest.php refactor(real-estate): remove Pennant gating (#308) 2026-04-20 13:31:49 +01:00
RealEstateTest.php fix(balances): stop historical-balance generator OOM on ancient purchase/loan dates (PHP-LARAVEL-49) (#661) 2026-07-08 21:34:43 +00:00
ResendLeadVerificationEmailsCommandTest.php feat: resend verification emails to unverified leads (#287) 2026-04-15 09:13:27 +01:00
ResendSyncCommandTest.php feat: Sync new users to Resend contacts (#85) 2026-01-28 21:25:58 +01:00
ResendSyncLeadsCommandTest.php perf(resend): default sync-leads to last 24h window (#354) 2026-05-05 09:57:25 +01:00
RouteNotificationForMailTest.php fix(notifications): skip mail dispatch when recipient email is invalid (#387) 2026-05-13 09:47:50 +01:00
RuleEngineParityTest.php refactor: consolidate duplicated financial calculations (#643) 2026-07-04 22:26:44 +02:00
SavedFilterTest.php feat(analysis): project-aware transaction analysis (#513) 2026-06-09 15:32:07 +02:00
SelfServeRefundTest.php feat(subscriptions): trial/pricing A/B/C experiment (#600) 2026-06-27 18:00:15 +02:00
SendAiCohortReportCommandTest.php feat(stats): add --no-discord to the remaining report commands (#607) 2026-06-29 15:32:31 +02:00
SendAiConsentFollowUpEmailsCommandTest.php test(drip): deflake AI consent onboarding-grace boundary test (#625) 2026-07-03 07:06:20 +00:00
SendDailyStatsReportCommandTest.php feat(stats): add --no-discord to the remaining report commands (#607) 2026-06-29 15:32:31 +02:00
SendExperimentFunnelReportCommandTest.php fix(stats): correct the trial/pricing experiment funnel report (#679) 2026-07-15 09:17:32 +02:00
SendStuckCohortReportCommandTest.php feat(stats): add --no-discord to the remaining report commands (#607) 2026-06-29 15:32:31 +02:00
SendSubscriptionFunnelReportCommandTest.php feat(stats): add --no-discord to the remaining report commands (#607) 2026-06-29 15:32:31 +02:00
SentryConfigTest.php ci: remove production deploy job (#574) 2026-06-20 18:17:14 +00:00
SentryUserMiddlewareTest.php Add Sentry user context (#348) 2026-05-04 13:26:50 +01:00
SetLocaleTest.php feat(i18n): add French translation support (#532) 2026-06-15 19:15:43 +02:00
SitemapTest.php User Onboarding Flow (#23) 2025-12-12 13:06:08 +01:00
StrayHttpRequestGuardTest.php chore: harden Inertia boundary, CI type-check, and test isolation (#640) 2026-07-04 18:57:58 +00:00
StripeSubscriptionStatsCommandTest.php feat: add Discord admin feed for daily stats and Stripe events (#458) 2026-05-30 18:14:46 +02:00
SubscriptionExperimentTest.php feat(subscriptions): trial/pricing A/B/C experiment (#600) 2026-06-27 18:00:15 +02:00
SubscriptionTest.php feat: reuse the upgrade modal at more upsell points and attribute revenue (#699) 2026-07-18 12:53:20 +00:00
SuggestionPersistenceTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
SyncBankingConnectionsCommandTest.php chore: Remove account-mapping feature flag (#252) 2026-04-01 12:09:22 +02:00
SyncStripePricesCommandTest.php fix(pricing): update final release prices (#288) 2026-04-15 14:49:02 +01:00
TrackLastActiveAtTest.php feat(users): track last login and last active timestamps (#516) 2026-06-10 11:01:30 +02:00
TransactionAnalysisTest.php fix(analysis): respect category types like the cashflow screen (#612) 2026-06-29 21:04:18 +02:00
TransactionFilterTest.php fix(transactions): prevent crash when sorting by nullable column (#501) 2026-06-06 17:23:21 +02:00
TransactionSideClassificationTest.php refactor: consolidate duplicated financial calculations (#643) 2026-07-04 22:26:44 +02:00
TransactionTest.php feat(transactions): allow editing all fields of manual transactions (#683) 2026-07-16 08:59:10 +02:00
UserLeadTest.php feat: verify waitlist leads (#285) 2026-04-14 11:26:01 +01:00
WelcomeBanksOrderingTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00