Repository navigation
Split held expenses onto a new Draft report when submitting offline - #96922
Conversation
Mirror the backend SubmitReport held-transaction split (Auth Expensify#21958) in the App's optimistic data so the offline/manual submit path matches: held expenses move to a new Draft report while only unheld expenses stay on the submitted report. Reuses getReportFromHoldRequestsOnyxData with the ApproveMoneyRequest param convention. The submitted report action now reflects the unheld total. Fixes Expensify#91080
…ne-hold-split # Conflicts: # src/libs/actions/IOU/ReportWorkflow.ts # tests/actions/IOUTest/ReportWorkflowTest.ts
…ne-hold-split # Conflicts: # tests/actions/IOUTest/ReportWorkflowTest.ts
|
@codex review. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c55470c5d
ℹ️ 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".
Auth splits held transactions on every submit path regardless of approval mode, but the App's optimistic guard skipped Submit and Close policies, leaving held expenses stuck on the report after it closes offline.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e0d2ac279
ℹ️ 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".
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
joekaufmanexpensify
left a comment
There was a problem hiding this comment.
Good for product
|
@c3024 Tag me when it's ready for review |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-08-14.at.11.18.14.mp4Android: mWeb ChromeScreen.Recording.2026-08-14.at.11.13.51.mp4iOS: HybridAppScreen.Recording.2026-08-14.at.11.29.42.mp4iOS: mWeb SafariScreen.Recording.2026-08-14.at.11.25.01.mp4MacOS: Chrome / SafariScreen.Recording.2026-08-14.at.11.06.55.mp4 |
Screen.Recording.2026-08-03.at.13.33.44.mov@c3024 When a report contains only one held expense and is submitted offline, it is optimistically marked as submitted. Once the app comes back online, the report is moved back to |
huult flagged an offline single-held-expense report getting optimistically marked as submitted, then flipping back to Draft once the app reconnects. isTransactionSubmittable now returns false for held transactions, which flows into the primary Submit button and canSubmitReport. isSubmitAction (secondary menu) gets the same guard, placed after the retracted-report bypass so resubmitting a retracted report still works. submitReport() also no-ops when every transaction on the report is held, as a defensive backstop.
Screen.Recording.2026-08-03.at.16.41.47.mov@c3024 Could you take a look at this video? It looks like tapping the Submit button doesn't do anything when the report contains only one held expense. I think we should show the "Remove Hold to Submit" modal in this case. |
…reports huult reported that hiding Submit for all-held reports left the secondary More menu, report-preview button, and search entry points inconsistent. Each Submit entry point now shows a remove-the-hold-to-submit modal when every expense on the report is held, matching the existing pending-card-transactions pattern, while partial-held reports still submit and split as before. The report-header primary button keeps its existing, separate all-held gate unchanged.
The backend splits held transactions on every submit path, DEW included, so the optimistic split no longer excludes DEW policies. Report state predictions stay suppressed for DEW as before.
Extract shouldSplitHeldExpenses and use it for both the submitted action amount and the split block, so the action shows the unheld total only when the held expenses are actually split optimistically.
The client makes no optimistic workflow predictions for DEW policies, so the hold split gate keeps the isDEWPolicy exclusion to stay consistent with that convention, even though the backend splits on every submit path.
|
Implemented all the review suggestions! |
|
@c3024 could you sync with main? |
# Conflicts: # tests/unit/TransactionUtilsTest.ts
|
Done! |
|
🚧 NikkiWines has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/NikkiWines in version: 9.4.57-0 🚀
|
|
Help site changes are required — the behavior this PR mirrors (submitting a report with held expenses) wasn't documented on the help site. Draft PR: #99026 What I documentedUpdated
Verified against the live web UI
Verification screenshotsDraft report with two expenses, Submit button
After submit: unheld expense on Outstanding report, held expense split onto a new Draft report
Unable to submit report: all expenses on hold error
@MelvinBot, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
Deploy Blocker #99028 was identified to be related to this PR. |
|
Deploy Blocker #99029 was identified to be related to this PR. |
|
🚀 Deployed to production by https://github.com/Beamanator in version: 9.4.57-3 🚀
Bundle Size Analysis (Sentry): |




Explanation of Change
The backend splits held expenses onto a new Draft report on submit, but the App's optimistic data didn't mirror that, so offline submission left held expenses on the submitted report until reconnect.
This PR mirrors the backend split in
submitReport(), reusinggetReportFromHoldRequestsOnyxData(same pattern asapproveMoneyRequest):optimisticHoldReportID,optimisticHoldActionID,optimisticHoldReportExpenseActionIDs) let the backend reconcile without duplicates after sync.Backend/API dependencies (merged & deployed):
Fixed Issues
$ #91080
PROPOSAL:
Tests
You can't submit this report because all expenses are on hold. Remove the hold to submit."
Screen.Recording.2026-07-31.at.1.08.03.PM.mov
Offline tests
QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-07-31.at.1.08.03.PM.mov