Skip to content

[Payment due @ahmedGaber93] Enable group exports#88577

Merged
JS00001 merged 49 commits into
Expensify:mainfrom
getusha:enable-group-exports
Jun 1, 2026
Merged

[Payment due @ahmedGaber93] Enable group exports#88577
JS00001 merged 49 commits into
Expensify:mainfrom
getusha:enable-group-exports

Conversation

@getusha

@getusha getusha commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Fixed Issues

$ https://github.com/Expensify/Expensify/issues/596490
PROPOSAL:

Tests

  • Verify that no errors appear in the JS console
  1. Navigate to Spend 1> Card Accruals
  2. Select all records without expanding any groups
  3. Click Export > Basic Export
  4. Verify that:
  • Each group appears as its own row with a header
  • Transactions are listed under their respective group
  1. Repeat steps 2–4 for all groupBy types
  2. Confirm the export format matches the expected structure from step 4
  3. Expand some groups while leaving others collapsed
  4. Select all records and click Export > Basic Export
  5. Verify the export still follows the grouped format defined in step 4
  6. Expand all groups and select all records
  7. Verify that each transaction is exported as an individual row (no grouping)

Offline tests

N/a

QA Steps

same as Tests

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
...h/SearchList/ListItem/TransactionGroupListItem.tsx 72.91% <0.00%> (-0.19%) ⬇️
src/components/Search/SearchList/index.tsx 56.81% <75.00%> (+2.27%) ⬆️
src/pages/Search/SearchPage.tsx 70.58% <0.00%> (-3.26%) ⬇️
src/components/Search/SearchBulkActionsButton.tsx 1.61% <0.00%> (-0.12%) ⬇️
src/libs/SearchUIUtils.ts 63.59% <0.00%> (-0.52%) ⬇️
src/components/Search/index.tsx 0.58% <0.00%> (-0.02%) ⬇️
src/hooks/useSearchBulkActions.ts 46.31% <13.72%> (-0.78%) ⬇️
... and 225 files with indirect coverage changes

@getusha

getusha commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot what exactly is causing the ESLint check to fail that's related to this PR?

@MelvinBot

Copy link
Copy Markdown
Contributor

The full-repo ESLint check fails because your changes in src/hooks/useSearchBulkActions.ts introduce new react-hooks warnings:

  1. useSearchBulkActions.ts:486react-hooks/exhaustive-deps: Your new code at ~line 463-475 uses Object.values(selectedTransactions) (the includesEmptyReports variable) inside a useCallback, but selectedTransactions is not in the dependency array.

  2. useSearchBulkActions.ts:893react-hooks/refs: The headerButtonsOptions useMemo (line 893–1458) now accesses a ref value during render via the new includesGroupExport variable (line 915), which also uses selectedTransactions. This triggers the "Cannot access refs during render" warning.

The "Changed files ESLint check" passed because it only lints the diff hunks, but the full-repo ESLint check has a max-warnings threshold that these new warnings push over.

Fix: Add selectedTransactions to the dependency array of the useCallback at line 486, and refactor the ref access out of the useMemo at line 893 (e.g., move the ref read into an effect or event handler).

@getusha

getusha commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot can you write comprehensive testing steps for grouped exports and also ensure that existing export functionalities continue to work correctly?

@MelvinBot

This comment has been minimized.

@getusha

getusha commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot can you hide this comment as "Low Quality"?

@getusha getusha marked this pull request as ready for review April 23, 2026 12:18
@getusha getusha requested review from a team as code owners April 23, 2026 12:18
@melvin-bot melvin-bot Bot requested review from ahmedGaber93 and flaviadefaria and removed request for a team April 23, 2026 12:18
@melvin-bot

melvin-bot Bot commented Apr 23, 2026

Copy link
Copy Markdown

@ahmedGaber93 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@getusha getusha requested a review from JS00001 April 23, 2026 12:19
@JS00001 JS00001 requested a review from trjExpensify April 23, 2026 12:21

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a4bd07816

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/hooks/useSearchBulkActions.ts Outdated
Comment thread src/hooks/useSearchBulkActions.ts Outdated
@MelvinBot

Copy link
Copy Markdown
Contributor

All groupBy values can produce empty groups (groups with transactions.length === 0).

The possible groupBy values are defined in src/CONST/index.ts:7587-7598: from, card, withdrawal-id, category, merchant, tag, month, week, year, quarter.

Additionally, EXPENSE_REPORT (not a groupBy value, but a search data type) can also produce empty reports.

Why any groupBy can be empty

Groups are created upfront from backend metadata before their individual transactions are fetched. A group can have transactions.length === 0 when:

  1. The group exists in the backend response but its transaction snapshot hasn't been loaded yet
  2. The group's transactions were all filtered out or deleted
  3. For expense reports specifically: a report passes status filters but has no matching transactions in the search results

The group creation functions (e.g., getMemberSections, getCardSections, getCategorySections in src/libs/SearchUIUtils.ts) all initialize groups with transactions: [], and transactions are populated separately in src/components/Search/index.tsx:593-639.

@getusha

getusha commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

Pushed the fix @ahmedGaber93. let me know if we're missing anything

@ahmedGaber93

Copy link
Copy Markdown
Contributor

@getusha The export scenarios worked as expected in the latest test.

The remaining issue only affects small screens; everything works correctly on larger screens.

Bug: When expanding a group for the first time, it becomes unselected after the expenses are fetched from the backend.

I think we can move forward once this bug is fixed.

Screen.Recording.2026-05-31.at.8.37.25.PM.mov

@getusha

getusha commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@ahmedGaber93 done!

@ahmedGaber93

Copy link
Copy Markdown
Contributor

We have a production issue with grouping by merchant. The merchant grouping logic uses a "includes" check instead of strict equality, which leads to inconsistent expense counts and row counts.

Screen.Recording.2026-06-01.at.2.07.56.PM.mov



This affects the consistency between the selected groups and the exported output (merchant only). However, I believe the export behavior here is correct. The root cause appears to be the merchant grouping logic, which uses a "includes" check instead of strict equality

Screen.Recording.2026-06-01.at.2.04.05.PM.mov



May be we can handle this with the follow-ups or report it on slack.

@getusha @JS00001

@melvin-bot melvin-bot Bot changed the title Enable group exports [Payment due @ahmedGaber93] Enable group exports Jun 1, 2026
@melvin-bot

melvin-bot Bot commented Jun 1, 2026

Copy link
Copy Markdown

🎯 @ahmedGaber93, thanks for reviewing and testing this PR! 🎉

A payment issue will be created for your review once this PR is deployed to production.
E/E issue linked to the PR - https://www.github.com/Expensify/Expensify/issues/596490.

If payment is not needed (e.g., regression PR review fix etc), react with 👎 to this comment to prevent the payment issue from being created.

@ahmedGaber93

Copy link
Copy Markdown
Contributor

Follow-ups:

@JS00001 JS00001 merged commit 75069ed into Expensify:main Jun 1, 2026
45 of 46 checks passed
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🚧 @JS00001 has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

OSBotify commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@getusha

getusha commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot can you create issues for the follow up issues listed here #88577 (comment) and assign me

@MelvinBot

Copy link
Copy Markdown
Contributor

Created the 4 follow-up issues from this comment and assigned them to getusha.

I created them in Expensify/Expensify (alongside the parent issue Expensify/Expensify#596490 and the internal C+ workflow). Two of these were flagged as likely needing a BE change, which also fits the internal repo. If you'd rather track any of these in Expensify/App, let me know and I'll move them.

Follow-up Issue
Hide Card ID from the export output https://github.com/Expensify/Expensify/issues/643661
First row of expanded group + collapsed group exports both full groups (needs BE) https://github.com/Expensify/Expensify/issues/643662
Groups exported with incorrect sort order (needs BE) https://github.com/Expensify/Expensify/issues/643663
Incorrect merchant grouping (includes vs strict equality) https://github.com/Expensify/Expensify/issues/643664

@OSBotify

OSBotify commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/JS00001 in version: 9.3.91-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

@MelvinBot

Copy link
Copy Markdown
Contributor

Help site review: changes required ✅

This PR enables group exports — when expenses on the Spend page are grouped (via Display > Group by) and the groups are selected, choosing Basic export now produces an export that preserves the group structure (each group is its own section with a header row, with its expenses listed below).

This user-facing behavior wasn't documented anywhere in the help site. The closest article, Export Expenses and Reports, covered CSV/PDF export but said nothing about exporting grouped results.

Draft help site PR: #92318

What it adds to Search-and-Download-Expenses.md:

  • A new Export grouped expenses section with the step-by-step flow.
  • An FAQ entry: "Can I keep my groups when I export?"
  • Updated keywords for retrieval.
How the UI labels were verified

I verified the flow against the live app and used exact labels per docs/HELPSITE_NAMING_CONVENTIONS.md:

  • Grouping is set via the Display button → Group by (options: From, Card, Category, Merchant, Tag, Month, Week, Year, Quarter, Withdrawal ID) → Apply.
  • The selection button reads N selected (e.g. 1 selected).
  • In grouped mode the selection button offers only Basic export (no submenu), matching the single-export-option behavior added in this PR.

Note: the existing article still uses some older labels (e.g. "Selected", "Basic Export", "All Data – Expense Level Export"). I scoped this PR to the grouped-export addition only and did not rewrite those, to keep the change focused — flagging in case you want a follow-up.


@getusha, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR Ready for review

@izarutskaya

Copy link
Copy Markdown

Hi @getusha @JS00001 I used these fake_transactions-2026-01-16.10_56_55.629.csv in wallet and have this result. Is it pass, could you please check and let me know? Thank you in advance

Recording.3799.mp4

@OSBotify

OSBotify commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/JS00001 in version: 9.3.94-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

@OSBotify

OSBotify commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/luacmartins in version: 9.3.94-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@melvin-bot

melvin-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown

🤖 Payment issue created: #92451

@ahmedGaber93

Copy link
Copy Markdown
Contributor

@izarutskaya Yes, it passed. The result shown in your video is expected and matches the test steps' expected outcome.

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.

10 participants