Skip to content

Reapply Reconciliation - All Expenses export for card group selections with an ungrouped query - #102431

Merged
NikkiWines merged 7 commits into
mainfrom
claude-reapplyReconciliationExportForCardGroups
Oct 5, 2026
Merged

NikkiWines merged 7 commits into
mainfrom
claude-reapplyReconciliationExportForCardGroups

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Explanation of Change

This reapplies Extend Reconciliation - All Expenses export to any group-by:card selection, which was reverted in [CP Staging] Revert "Extend Reconciliation - All Expenses export to any group-by:card selection", and fixes the deploy blocker that caused the revert: Spend- Click "Reconciliation - All Expenses" exported file not covered all expenses.

Commit 1 (Reapply …) is a straight git revert of the revert. It is identical to the merged #101522: on a search grouped by card, Reconciliation - All Expenses stays in the Export menu for a card group selection, scoped to the selected cards through a cardID: filter.

Commit 2 fixes the bug from the deploy blocker investigation. Exporting one card group produced a CSV with a single line. The template export sent the query that addSelectedGroupsFilter built, and that query still carried groupBy:card and limit. ExportSearchWithTemplate has no isGroupExport flag (unlike the Current view export), so the backend ran a grouped search and exported one row per selected card.

Changes in src/hooks/useSearchBulkActions.ts:

  • New getTemplateGroupExportQuery helper. It adds the cardID: filter, then drops groupBy and limit and sorts by date, the same way buildSpecificGroupQuery builds a group's drill-down query. The query now lists the card's expenses. limit has to go too, because once groupBy is gone it would cap the exported expenses instead of the number of groups.
  • The template export now sends searchKey, as the Current view export already does. searchKey changes which expenses the backend matches (for example, Reconciliation includes Expensify Card cash back), so leaving it out would make the exported set differ from the viewed set.
  • If the selected group can't be turned into a cardID: filter, the export shows the download error instead of sending an ungrouped query with no card filter. That query would export every card in the search.
AI tests run locally
  • npm test -- tests/unit/hooks/useSearchBulkActionsExportTest.ts: 46 passed (2 new for this fix)
  • The 2 new tests fail against the reapplied source without commit 2 (44 passed, 2 failed), so they guard against this regression
  • npm test -- --silent --testPathPattern "useSearchBulkActions|Search/SearchUIUtils|SearchActions": 794 passed across 12 suites
  • npm run lint-changed: clean
  • npm run typecheck: passed
  • npm run fmt: no changes
  • npm run spell-changed: no issues
  • npm run react-compiler-compliance-check check src/hooks/useSearchBulkActions.ts: fails with the same Cannot access refs during render errors on main. This PR adds no new errors.
  • Browser verification was not run. Earlier web runs on Extend Reconciliation - All Expenses export to any group-by:card selection #101522 showed the CI test account has no imported card transactions, so a groupBy:card search has no group rows to select. The card-group export still needs a manual run on an account with real card data.

Fixed Issues

$ #99172
PROPOSAL: #99172 (comment)

Tests

Prerequisite: sign in as a workspace admin (or card admin) of a workspace that has Company cards or the Expensify Card enabled and that has imported card transactions. Without imported card transactions a groupBy:card search returns no group rows, so steps 2–7 cannot be reached.

  1. Go to Reports → Expenses.
  2. Open the Card statements suggested search from the Accounting section of the left sidebar. (Equivalently: on the Expenses search open Display → Group by → Card.) Verify the list shows one row per card.
  3. Tick the checkbox on one card group row. Verify the selection header appears with an Export button.
  4. Click Export. Verify the menu lists exactly two entries: Current view and Reconciliation - All Expenses. Verify no other template is listed — no All Data - expense level, no All Data - report level, no Basic export, and no custom workspace template.
  5. Click Reconciliation - All Expenses. Verify the export starts, and that the resulting CSV has one line per expense on the selected card, not a single line for the card (the bug from Spend- Click "Reconciliation - All Expenses" exported file not covered all expenses). Verify it covers every expense on the selected card — including expenses further down the list that were never scrolled into view — not only the rows that happened to be loaded on screen, and no expenses from other cards.
  6. With the card group still ticked, additionally tick one individual expense belonging to a different card. Export with Reconciliation - All Expenses again. Verify the CSV contains the whole selected card group plus that one extra expense.
  7. Repeat steps 3–6 using Current view instead of Reconciliation - All Expenses. Verify the same rows are covered — the two export paths must agree on scope.
  8. Regression, non-card grouping: on the Expenses search set Display → Group by → Category. Tick a category group row and click Export. Verify Current view is the only option and no templates are listed at all.
  9. Regression, no grouping: on the ungrouped Expenses search, tick one or more individual expense rows and click Export. Verify the full template list is still offered (Current view, All Data - expense level, All Data - report level, Basic export, Reconciliation - All Expenses).
  10. Failure case, not a card-enabled admin: sign in as a user who is only a workspace member, or an admin of a workspace with no card product enabled. Repeat steps 2–4. Verify Reconciliation - All Expenses is not offered for a card group selection — only Current view.
  • Verify that no errors appear in the JS console

Offline tests

Selecting a card group and choosing Reconciliation - All Expenses while offline shows the existing offline modal instead of starting an export — unchanged by this PR, which only decides whether the option is listed and which query is exported.

QA Steps

Same as the Tests section above, including the prerequisite: the QA account must be a workspace or card admin of a card-enabled workspace with imported card transactions, otherwise the Card statements search has no group rows to select and steps 2–7 cannot be run.

  • 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 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 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)
  • 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)
  • 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.
  • 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

MelvinBot and others added 2 commits September 28, 2026 14:08
… searchKey for a card group selection

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team September 28, 2026 14:17
@melvin-bot melvin-bot Bot added Melvin-Test-Android Melvin-Test-Web Triggers Melvin to run the testing steps of the PR on web labels Sep 28, 2026
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I created this PR from an upstream branch because I don't have push access to your fork.

To take ownership of the branch so you can push updates:

git remote add upstream https://github.com/Expensify/App.git
git fetch upstream claude-reapplyReconciliationExportForCardGroups
git checkout -b claude-reapplyReconciliationExportForCardGroups upstream/claude-reapplyReconciliationExportForCardGroups
git push -u origin claude-reapplyReconciliationExportForCardGroups

Then you can close this PR and open a new one from your fork.

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/hooks/useSearchBulkActions.ts 79.13% <91.89%> (+2.42%) ⬆️
... and 5 files with indirect coverage changes

@MelvinBot

MelvinBot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Android test steps verification results: ⚠️ partial

This PR's Tests section is still empty, so I ran the steps from Extend Reconciliation - All Expenses export to any group-by:card selection. Steps 1 and 9 pass; the card-group steps (2–7, 10) are blocked because the fresh test account has no card transactions (linking company cards requires an emailed magic code CI can't read), and step 8 is blocked by a Group by sheet that froze on Android after tapping Apply.
Group by sheet still frozen after Metro restart

Step Status
1. Reports → Expenses search loadsSpend → Expenses opened with the Expenses tab selected and Search, Filters, Save and Display controls shown.
Expenses search loaded
✅
2. Card statements / Group by Card shows one row per cardBlocked: the account has no card transactions. Workspace → Company cards → Add cards opened a "Validate your account" magic-code screen, which CI can't complete.
Validate your account screen blocking company card linking
⚠️
3. Select a card group row → selection header with ExportBlocked: no card group rows exist (see step 2).
⚠️
4. Export menu lists only Current view and Reconciliation - All ExpensesBlocked: no card group rows exist.
⚠️
5. Reconciliation - All Expenses export covers every expense on the cardBlocked: no card group rows exist.
⚠️
6. Card group + expense from another card exports via ReconciliationBlocked: no card data.
⚠️
7. Repeat steps 3–6 with Current viewBlocked: no card group rows exist.
⚠️
8. Regression: Group by Category → Export offers only Current viewBlocked: after creating a $42.00 expense and choosing Display → Group by → Category → Apply, the bottom sheet stopped responding to every tap. Metro logged nothing further (one earlier DISMISS_MODAL was not handled by any navigator warning). A Metro restart did not recover it. This PR doesn't change the Group by sheet, so the freeze is likely unrelated, but I didn't confirm that.
Group by Category selected, Apply tappedGroup by sheet still frozen after Metro restart
⚠️
9. Regression: ungrouped selection offers the full template listWith one expense selected, Export listed Current view, All Data - expense level, All Data - report level, Basic export and Reconciliation - All Expenses.
Full export template list for ungrouped expense selection
✅
10. Non-card user doesn't see Reconciliation for a card groupBlocked: no second account available, and the card-group path is unreachable (step 2).
⚠️

view run · view recording

@MelvinBot

MelvinBot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Web test steps verification results: ⚠️ partial

This PR's Tests section is still empty, so I ran the steps from Extend Reconciliation - All Expenses export to any group-by:card selection. Steps 1, 8 and 9 pass; the card-group steps (2–7, 10) are blocked because the test account has no card transactions (adding company cards needs a live bank login Melvin doesn't have).
Export menu for a category group showing only Current view

Step Status
1. Reports → Expenses search loadsSpend → Expenses showed the search page with Filters, Display, Save and Create.
Expenses search page loaded
✅
2. Card statements / Group by Card shows one row per cardBlocked: Display → Group by → Card applied, but the account has no card feed, so there are no card group rows. Company cards only offers a live bank connection.
Group by Card applied, no group rowsCompany cards page offering only live bank connection
⚠️
3. Select a card group row → selection header with ExportBlocked: no card group rows exist (see step 2).
⚠️
4. Export menu lists only Current view and Reconciliation - All ExpensesBlocked: no card group rows exist.
⚠️
5. Reconciliation - All Expenses export covers every expense on the cardBlocked: no card group rows exist.
⚠️
6. Card group + expense from another card exports via ReconciliationBlocked: no card data.
⚠️
7. Repeat steps 3–6 with Current viewBlocked: no card group rows exist.
⚠️
8. Regression: Group by Category → Export offers only Current viewWith the Advertising category group ($25.00) selected, the Export menu had one item: Current view.
Expenses grouped by Category showing the Advertising group rowExport menu for a category group showing only Current view
✅
9. Regression: ungrouped selection offers the full template listWith one expense selected, Export listed Current view, All Data - expense level, All Data - report level, Basic export and Reconciliation - All Expenses.
Ungrouped Expenses list with the manual expenseExport menu on an ungrouped selection showing all 5 templates
✅
10. Non-card user doesn't see Reconciliation for a card groupBlocked: no card group rows exist, and no second member account was available.
⚠️

view run · view recording

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Fix conflicts

…liationExportForCardGroups

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>

# Conflicts:
#	tests/unit/hooks/useSearchBulkActionsExportTest.ts
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I merged main into this branch and fixed the conflicts. The PR can merge again.

The only conflict was in tests/unit/hooks/useSearchBulkActionsExportTest.ts. I kept both sides in each hunk:

  • The getSelectedGroupFilterEntry import from this PR, plus the SearchUIUtilsModule type import from main.
  • The getColumnsToShow requireActual from main inside the @libs/SearchUIUtils mock.
  • The cardGroupedExpenseQueryJSON fixture from this PR, plus the ungroupedExpenseQueryJSON and groupedWithdrawalQueryJSON fixtures from main.

Checks: the test file passes (52/52), lint is clean on the changed files, and npm run typecheck passes.


view run

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Complete the author checklist and add test steps in the PR description
You can take from the original PR

@MelvinBot

Copy link
Copy Markdown
Contributor Author

I updated the PR description with the Tests, Offline tests, QA Steps, and PR Author Checklist from Extend Reconciliation - All Expenses export to any group-by:card selection.

  • Tests: I copied all 10 steps. Step 5 now also checks that the CSV has one line per expense, not one line per card, which is the bug from Spend- Click "Reconciliation - All Expenses" exported file not covered all expenses.
  • Checklist: I checked the same items as the original PR. The items that need a person are still unchecked: offline testing, the High Traffic account, screenshots, testing on all platforms, and console errors.

You still need to run the Tests on an account with imported card transactions, because the CI test account has no card groups to select.


view run

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Open the PR

@MelvinBot

Copy link
Copy Markdown
Contributor Author

I couldn't mark this PR as ready for review. My environment blocked the gh pr ready command in this run, so the PR is still a draft.

To open it, click Ready for review at the bottom of the PR page. The branch is up to date with main and has no conflicts.


view run

@NikkiWines
NikkiWines marked this pull request as ready for review September 29, 2026 10:47
@NikkiWines
NikkiWines requested a review from a team as a code owner September 29, 2026 10:47
@melvin-bot
melvin-bot Bot requested review from ZhenjaHorbach and flaviadefaria and removed request for a team September 29, 2026 10:47
@melvin-bot

melvin-bot Bot commented Sep 29, 2026

Copy link
Copy Markdown

@ZhenjaHorbach 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]

@melvin-bot
melvin-bot Bot removed the request for review from a team September 29, 2026 10:47

// When the export menu is built for a search grouped by card
const {result} = renderHook(() => useSearchBulkActions({queryJSON: cardGroupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ CONSISTENCY-16 (docs)

The comment uses the banned AI-generated phrase "carve-out". This phrasing makes the codebase read like it was written by a chatbot; CONSISTENCY-16 asks for plain, direct language and explicitly covers comments.

Replace "reach the carve-out" with a plain substitute, e.g.:

// Then the template is offered just the same, because both shapes a card group selection can take have to reach the special-case rule, or the option would come and go with how far the list happened to be scrolled

Reviewed at: 148911b | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a9f4bba. I replaced "carve-out" with "card group exception".


view run


// When the export menu is built for a search grouped by category instead of by card
const {result} = renderHook(() => useSearchBulkActions({queryJSON: groupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ CONSISTENCY-16 (docs)

The comment uses the banned AI-generated phrase "carve-out". Prefer plain, direct language in comments.

Rewrite "the carve-out is limited to card groups" without the jargon, e.g.:

// Then no template is offered at all, because the exception is limited to card groups and every other grouping keeps the existing rule that a group cannot be scoped

Reviewed at: 148911b | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a9f4bba. I replaced "carve-out" with "exception".


view run


// When the export menu is built for a search grouped by card
const {result} = renderHook(() => useSearchBulkActions({queryJSON: cardGroupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ CONSISTENCY-16 (docs)

The comment uses the banned AI-generated phrase "carve-out". Prefer plain, direct language in comments.

Rewrite "the carve-out narrows the templates" without the jargon, e.g.:

// Then only Current view is offered, because the exception only narrows the templates the user was already entitled to and must never hand out one they were not

Reviewed at: 148911b | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a9f4bba. I replaced "carve-out" with "card group exception only".


view run

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Fix comments

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>

@NikkiWines NikkiWines left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

looks good overall, just one thing

);
} else {
const isGroupExport = !!queryJSON?.groupBy && selectedTransactionsKeys.some((key) => key.startsWith(CONST.SEARCH.GROUP_PREFIX));
const {isGroupExport, transactionIDList} = getGroupExportScope(queryJSON, selectedTransactions);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The logic above (from 1019 - 1033) needs adjusting. When all matching items are selected, the Reconciliation template export skips the new group-query handling. It uses the original query, which still has groupBy:card, and does not include searchKey.

That could recreate the problem we had in the last PR: the template export may contain one row per card instead of all expenses, or include different results from the Reconciliation search.

Let's flatten the all-results query too and include searchKey.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, let's add a regression test for the all matching items selected path once this is updated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@MelvinBot
Fix this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I fixed this and pushed b6ba75e.

When all matching items are selected, the template export now does two things:

  • It drops groupBy and limit and sorts by date, so the export lists every matching expense instead of one row per card. This uses a new shared helper, getUngroupedTemplateExportQuery, which the group-selection path also uses now. An ungrouped query is sent unchanged.
  • It sends searchKey, as Current view already does (useSearchBulkActions.ts:1031-1048).

I added a regression test for the select-all path (useSearchBulkActionsExportTest.ts:1483). It fails without the fix.

Checks run
  • useSearchBulkActionsExportTest.ts: 53 passed
  • Related search suites: 801 passed across 12 suites
  • npm run typecheck: passed
  • ESLint on both changed files: clean
  • npm run fmt: no changes
  • npm run spell-changed: no issues
  • react-compiler-compliance-check: fails with the same errors as before this change. This change adds no new errors.

@NikkiWines

Copy link
Copy Markdown
Contributor

@ZhenjaHorbach can you update your reviewer checklist please 🙇

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

⚠️ MelvinBot CI Fix Loop Detected

MelvinBot has made 5 consecutive commits trying to fix CI failures. Automatic CI fix dispatches are now paused for this PR.

Next steps: A human should review the recent commits and resolve the conflicting CI errors manually.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

This comment has been minimized.

@trjExpensify
trjExpensify requested review from trjExpensify and removed request for flaviadefaria October 2, 2026 13:23
trjExpensify
trjExpensify previously approved these changes Oct 2, 2026

@trjExpensify trjExpensify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Works nicely!

2026-10-02_14-22-51.mp4

@NikkiWines NikkiWines left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm, one style and one comment request

Comment thread src/hooks/useSearchBulkActions.ts Outdated
Comment on lines +352 to +354
* ExportSearchWithTemplate has no isGroupExport flag, so a grouped query exports one row per group instead of the group's expenses.
* Drop groupBy and limit the way buildSpecificGroupQuery does, so the export covers every expense the query matches.
* `limit` has to go too, because once groupBy is gone it would cap the exported expenses instead of the number of groups.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we make this comment less AI-y?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I rewrote the comment in plain language and pushed 69015b1 (useSearchBulkActions.ts:349-353).

Comment on lines +1405 to +1418
it('scopes the Reconciliation export to the selected card groups instead of a transaction ID list', async () => {
// Given a user who qualifies for the Reconciliation template
mockTemplatesIncludingReconciliation();
// Given a ticked card group
selectCardGroup();
// Given one expense ticked on its own alongside it, because that expense belongs to another card and so is not covered by the group's filter
mockSelectedTransactions.tx2 = makeSelectedTransaction();
const searchResults = makeSearchResults([]);
// Given the card group row as it arrives in the search snapshot, which the group's filter entry is derived from
Object.assign(searchResults.data, {[CARD_GROUP_KEY]: {cardID: 1234}});
mockCurrentSearchResults = searchResults;
jest.mocked(getSelectedGroupFilterEntry).mockReturnValue({key: CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, value: 1234});

const {result} = renderHook(() => useSearchBulkActions({queryJSON: cardGroupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
it('scopes the Reconciliation export to the selected card groups instead of a transaction ID list', async () => {
// Given a user who qualifies for the Reconciliation template
mockTemplatesIncludingReconciliation();
// Given a ticked card group
selectCardGroup();
// Given one expense ticked on its own alongside it, because that expense belongs to another card and so is not covered by the group's filter
mockSelectedTransactions.tx2 = makeSelectedTransaction();
const searchResults = makeSearchResults([]);
// Given the card group row as it arrives in the search snapshot, which the group's filter entry is derived from
Object.assign(searchResults.data, {[CARD_GROUP_KEY]: {cardID: 1234}});
mockCurrentSearchResults = searchResults;
jest.mocked(getSelectedGroupFilterEntry).mockReturnValue({key: CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, value: 1234});
const {result} = renderHook(() => useSearchBulkActions({queryJSON: cardGroupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});
it('scopes the Reconciliation export to the selected card groups instead of a transaction ID list', async () => {
// Given a user who qualifies for the Reconciliation template
mockTemplatesIncludingReconciliation();
// Given a ticked card group
selectCardGroup();
// Given one expense ticked on its own alongside it, because that expense belongs to another card and so is not covered by the group's filter
mockSelectedTransactions.tx2 = makeSelectedTransaction();
const searchResults = makeSearchResults([]);
// Given the card group row as it arrives in the search snapshot, which the group's filter entry is derived from
Object.assign(searchResults.data, {[CARD_GROUP_KEY]: {cardID: 1234}});
mockCurrentSearchResults = searchResults;
jest.mocked(getSelectedGroupFilterEntry).mockReturnValue({key: CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID, value: 1234});
const {result} = renderHook(() => useSearchBulkActions({queryJSON: cardGroupedExpenseQueryJSON}), {wrapper: OnyxListItemProvider});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same for other comments, our style guide says newlines above comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I applied your suggestion and pushed 69015b1. I also added a blank line above the other comments in the card group tests that this PR adds (useSearchBulkActionsExportTest.ts:1299).

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Fix last comments

…test comments

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I fixed both of NikkiWines's comments and pushed 69015b1:

  • Source comment: I rewrote the getUngroupedTemplateExportQuery comment in plain language (reply).
  • Test style: I applied the suggestion and added a blank line above the other comments in the card group tests (reply).

Only comments and blank lines changed. The export test file passes (53/53), and lint and spell check are clean.

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@MelvinBot
Fix conflicts

…liationExportForCardGroups

Co-authored-by: Yauheni Horbach <ZhenjaHorbach@users.noreply.github.com>

# Conflicts:
#	tests/unit/hooks/useSearchBulkActionsExportTest.ts
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Merged main into this branch and resolved the conflicts in tests/unit/hooks/useSearchBulkActionsExportTest.ts. Both sides added separate test setup, so I kept both:

  • The import now includes both queueBulkMarkAsExported (from main) and queueExportSearchWithTemplate (from this PR).
  • beforeEach now resets both mockCurrentSearchKey and mockIsOffline.

The test file passes (58/58). ESLint and npm run typecheck also pass.

@NikkiWines NikkiWines left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@NikkiWines
NikkiWines merged commit fed4110 into main Oct 5, 2026
42 of 43 checks passed
@NikkiWines
NikkiWines deleted the claude-reapplyReconciliationExportForCardGroups branch October 5, 2026 18:59
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

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

@OSBotify

OSBotify commented Oct 5, 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.

@OSBotify OSBotify mentioned this pull request Oct 5, 2026
93 tasks done
@OSBotify

OSBotify commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/NikkiWines in version: 9.5.3-0 🚀

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

@MelvinBot

Copy link
Copy Markdown
Contributor Author

No help site update needed. This PR adds Reconciliation - All Expenses as an export option for card groups. It doesn't make any article sentence wrong. Current view still works for grouped exports, as Search-and-Download-Expenses.md:60-80 describes. This matches the decision on the earlier docs PR for the same change, Docs updates for E/A#101522 Reconciliation export for card group selections, which was closed as not needed.


view run

@jponikarchuk

Copy link
Copy Markdown

This PR failing because of the issue #103163
This issue is reproducible in: All platforms

@OSBotify

OSBotify commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/puneetlath in version: 9.5.3-4 🚀

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

Bundle Size Analysis (Sentry):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Melvin-Test-Android Melvin-Test-Web Triggers Melvin to run the testing steps of the PR on web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants