Skip to content

Add vendor enrichment and transaction selection#22

Merged
andrewzolotukhin merged 20 commits into
mainfrom
feat/merchant-information
Jun 4, 2026
Merged

Add vendor enrichment and transaction selection#22
andrewzolotukhin merged 20 commits into
mainfrom
feat/merchant-information

Conversation

@andrewzolotukhin
Copy link
Copy Markdown
Contributor

@andrewzolotukhin andrewzolotukhin commented Jun 3, 2026

Original request

Add vendor information so users can optionally attach a vendor to an expense, select a vendor first and get a category suggestion from prior usage, enrich vendors with third-party metadata, document the required env/secrets, and use the user's country for better vendor matching.

Follow-up work also moved vendor management out of the main daily nav, replaced the old brand/merchant dashboard block with a dedicated Vendors analytics page, preserved Dashboard/Vendors period state, globally renamed Brands/Merchants to Vendors, added vendor context to weekly/monthly LLM report summaries, improved the Vendor edit/Capture picker UX, and made vendor analytics totals match Dashboard by including transactions without a vendor.

What changed

  • Renamed the app-facing merchant/brand feature to Vendors across routes, contracts, handlers, schemas, UI, tests, and docs.
  • Changed main navigation order to Dashboard, Vendors, Add, Transactions, Reports.
  • Replaced the old main-menu Brands/Merchants management page with a Vendors analytics page at /vendors.
  • Moved vendor management to Settings at /settings/vendors and vendor details/history to /settings/vendors/[vendorId].
  • Preserved period and date when switching between Dashboard and Vendors.
  • Reused the dashboard analytics surface for category and vendor grouping instead of keeping a separate collapsible vendor block on Dashboard.
  • Updated Vendors analytics to show Income and Expenses vendor groups, including No vendor rows for each transaction type when present, so totals reconcile with Dashboard.
  • Added transaction links for No vendor groups using vendorId=none, backed by contract/API query parsing with @cleverbrush/schema union types.
  • Replaced remaining user-facing purchase wording with transaction on Vendors surfaces.
  • Removed the end-user Enriched badge/status column from the Manage Vendors table.
  • Added an Add transaction button to /vendors, matching Dashboard.
  • Kept vendor creation in Add transaction Brandfetch-backed: typing a vendor name shows candidate vendors with logos/domains, and selecting one creates/upgrades the saved vendor.
  • Updated the Capture vendor picker so suggestions are hidden until the Vendor textbox is focused, local saved vendors appear first, Brandfetch is queried only when there is no exact saved-vendor match, selecting a vendor hides the search field until Clear is used, and successful transaction save resets the vendor field.
  • Simplified Vendor detail/edit for end users: one Display name, Profile fields, transaction history, and a Refresh details action instead of raw enrichment/provider diagnostics.
  • Added Brandfetch candidate details lookup so the edit dialog can show alternatives and let the user accept/keep suggested fields before saving.
  • Kept vendor category suggestions based on prior transactions with that vendor.
  • Added linked vendor summaries to weekly/monthly report analytics and prompt payloads, including vendor totals, shares, domains/descriptions, top categories, and vendor fields on notable transactions.
  • Hard-renamed enrichment env wiring to VENDOR_ENRICHMENT_* and kept Brandfetch provider terms only where they refer to Brandfetch itself.
  • Modified the existing feature migrations instead of creating new migrations, per review request.

Reasoning

Vendor analytics now behaves like the existing Dashboard analytics instead of adding another always-visible panel to Dashboard. Vendor management is in Settings because users rarely edit vendors after creation, while the main Vendors nav item is reserved for period-based spending analysis.

The Vendor edit flow keeps user-entered Display name as the canonical app label and treats Brandfetch data as optional suggestions. This avoids confusing users with duplicate technical name fields while still allowing them to improve logo/domain/description/color metadata.

vendorId=none is modeled directly in the transaction query contract with a schema union instead of a parallel boolean flag, so the UI URL and API filter describe the same concept clearly.

Environment And Third-Party Setup

GitHub repository secret:

BRANDFETCH_API_KEY

GitHub repository variable and local/production Compose variable:

BRANDFETCH_CLIENT_ID=xpenser

Optional GitHub repository variables and local/production Compose variables:

VENDOR_ENRICHMENT_ENABLED=0
VENDOR_ENRICHMENT_TIMEOUT_MS=2000

Third-party account/key to create for this feature:

Brandfetch account with Transaction API access
Brandfetch server API key for BRANDFETCH_API_KEY
Brandfetch developer client ID for BRANDFETCH_CLIENT_ID

OpenAI is intentionally not listed as a new account/key.

PR Environment Notes

  • Updated /opt/pr-env/pr-env.sh on root@10.200.1.2 and verified it reads/exports VENDOR_ENRICHMENT_ENABLED and VENDOR_ENRICHMENT_TIMEOUT_MS.
  • Refreshed PR Add vendor enrichment and transaction selection #22 from a fresh DB volume before the corrected deploy by stopping only the pr22 compose project, removing pr22_postgres_data, and removing /var/lib/pr-envs/pr-22/db.initialized.
  • GitHub deploy recreated pr22_postgres_data and /var/lib/pr-envs/pr-22/db.initialized, confirming the production logical restore path ran.

Screenshots / Preview Evidence

Preview URL: https://xpenser-pr-022.cleverbrush.com

  • /dashboard shows the requested nav order: Dashboard, Vendors, Add, Transactions, Reports.
  • Switching Dashboard to month and then opening Vendors preserved the selected period and date query parameters.
  • /vendors?period=year&date=2026-06-04 renders vendor analytics with Income and Expenses sections. The preview data shows No vendor income and expense groups plus Walmart, and the group totals match the visible Dashboard totals for the same period.
  • /vendors includes the Dashboard-style Add transaction button.
  • /settings/preferences includes a Vendors section with Manage vendors.
  • /settings/vendors lists Walmart with suggested category and transaction count; the previous Status/Enriched column is no longer present.
  • /settings/vendors/1 renders a friendly Vendor profile with Edit, Refresh details, website, and transaction history, without duplicate name fields or raw enrichment diagnostics.
  • The Vendor edit dialog exposes Display name, Website, Logo URL, Primary color, Description, and Brandfetch search. Searching Walmart showed multiple Brandfetch candidates, including regional variants, and selecting one displayed a review panel with per-field Use/Keep controls before save.
  • /capture shows no vendor suggestion list on initial load. Focusing the Vendor textbox and typing Wal opens an autocomplete with saved Walmart first and Brandfetch logo/domain candidates after it. Typing the exact saved name Walmart leaves only the local Walmart suggestion after debounce.
  • Agent-browser screenshots were captured locally during QA: /tmp/pr22-vendors-groups.png, /tmp/pr22-settings-vendors-no-status.png, and /tmp/pr22-capture-autocomplete.png.

SigNoz Findings

Last hour after preview QA, scoped to xpenser-web-pr-22 and xpenser-api-pr-22:

ERROR/FATAL logs: none
Error traces: none
Metrics: http.server.duration.count telemetry present for xpenser-api-pr-22 during the QA window

Validation

Local:

npm test -- apps/api/src/application/transactions.test.ts packages/contracts/src/schemas.test.ts apps/web/lib/transaction-query.test.ts apps/web/components/vendor-analytics-panel.test.tsx apps/web/components/vendor-directory.test.tsx apps/web/components/vendor-picker.test.tsx
npm run lint
npm run typecheck
npm test
npm run build

npm run build passed when rerun with sandbox escalation because Turbopack needs to bind a local worker port in this environment.

GitHub checks for 2722865:

Lint and test: success
Deploy PR environment: success
Playwright e2e: success

Preview QA:

/vendors year view: verified Add transaction button, Income/Expenses vendor groups, No vendor links, and transaction wording
/settings/vendors: verified no Status/Enriched column
/capture: verified suggestions hidden until focus, autocomplete dropdown, Brandfetch candidates for non-exact query, and local-only exact match

@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 00:56 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 01:01 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 01:05 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 01:44 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 01:55 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 02:22 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 03:37 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 04:01 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 04:13 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 04:28 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 04:33 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 04:44 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 06:07 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 06:21 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 06:26 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 07:13 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin force-pushed the feat/merchant-information branch from e3f4451 to 7cd40ce Compare June 3, 2026 07:19
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 07:20 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 07:27 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin changed the title Add merchant enrichment and transaction selection Add vendor enrichment and transaction selection Jun 3, 2026
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 22:47 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 3, 2026 23:43 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-22 June 4, 2026 00:30 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin merged commit 7d58a7d into main Jun 4, 2026
4 checks passed
@andrewzolotukhin andrewzolotukhin deleted the feat/merchant-information branch June 4, 2026 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant