[Submit] Update "Submit to someone" to two options to allow for Submit workspace creation - #94319
Conversation
Add a beta-gated (SUBMIT_2026) split of the track-expense "Submit it to someone" whisper into "Submit to a friend" and "Submit to my employer". The employer destination routes the expense into a submit-enabled workspace, auto-selecting a single workspace, creating a new Submit (submit2026) workspace when none exists, or showing a workspaces-only destination picker when multiple exist.
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
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.
|
|
I think it's because you are passing both payer params as empty/0: |
|
Thanks @iwiznia! I looked into this a bit more and I think the empty The actual problem is that we end up calling It only blows up in the case where the user doesn't have a workspace yet. In that flow we create a draft workspace, but a draft is optimistic-only and never actually gets created on the backend by itself. The submit goes through Categorize/Share don't have this issue because they use a single command ( So I think it comes down to which way we want to go:
Do you have a preference? The 1+ existing workspace cases should already route to a real policy chat and hit |
|
Not sure why I missed this comment, sorry! |
|
This PR https://github.com/Expensify/Web-Expensify/pull/54066 adds |
|
PR is on staging, so you should be able to resume this |
…to-employer-92704 # Conflicts: # Mobile-Expensify # android/app/build.gradle # config/eslint/eslint.seatbelt.tsv # docs/articles/new-expensify/concierge-ai/Support-Agent.md # docs/articles/new-expensify/concierge-ai/What-Concierge-Can-Do.md # docs/articles/new-expensify/connections/netsuite/Configure-Netsuite.md # ios/NewExpensify/Info.plist # ios/NotificationServiceExtension/Info.plist # ios/ShareViewController/Info.plist # package-lock.json # package.json # patches/@shopify/flash-list/details.md # src/CONST/index.ts # src/SCREENS.ts # src/components/AvatarWithDisplayName.tsx # src/components/FlashList/InvertedFlashList/index.tsx # src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx # src/components/MoneyRequestConfirmationFields/Provider.tsx # src/components/MoneyRequestConfirmationFields/context.ts # src/components/MoneyRequestConfirmationList.tsx # src/components/MoneyRequestConfirmationList/sections/DescriptionField.tsx # src/components/MoneyRequestConfirmationListFooter.tsx # src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx # src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx # src/components/Search/SearchRouter/SearchRouterContext.tsx # src/components/Search/index.tsx # src/components/SubStepForms/AddressStep.tsx # src/components/Table/TableHeader.tsx # src/components/Table/TableRow.tsx # src/components/Table/middlewares/selection.ts # src/components/Tables/WorkspaceListTable/WorkspaceTableRow.tsx # src/hooks/useExportActions.ts # src/hooks/useLifecycleActions.tsx # src/hooks/useProactiveAppReview.ts # src/hooks/useSearchBulkActions.ts # src/hooks/useSelectionModeReportActions.ts # src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx # src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts # src/libs/Navigation/types.ts # src/libs/OptionsListUtils/index.ts # src/libs/PolicyUtils.ts # src/libs/ReportUtils.ts # src/libs/SearchParser/autocompleteParser.js # src/libs/SearchParser/searchParser.js # src/libs/SidebarUtils.ts # src/libs/actions/App.ts # src/libs/actions/Task.ts # src/pages/DynamicReportDetailsPage.tsx # src/pages/inbox/HeaderView.tsx # src/pages/inbox/report/ReportActionsList.tsx # src/pages/inbox/report/ReportActionsView.tsx # src/pages/iou/request/ParticipantSearchResults.tsx # src/pages/tasks/NewTaskPage.tsx # src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx # src/pages/workspace/tags/WorkspaceTagsPage.tsx # src/types/form/SearchAdvancedFiltersForm.ts # tests/ui/MoneyRequestReportPreview.test.tsx # tests/unit/DateUtilsTest.ts # tests/unit/PolicyUtilsTest.ts # tests/unit/ReportUtilsTest.ts # tests/unit/SearchAutocompleteParserTest.ts # tests/unit/hooks/useSelectionModeReportActions.test.ts
When a user has no submit-enabled workspace, "Submit to my employer" now creates a Submit (submit2026) workspace and submits the tracked expense in a single AddTrackedExpenseToPolicy request (a backend alias of CategorizeTrackedExpense that accepts a policy `type`), instead of falling back to ConvertTrackedExpenseToRequest which can't create a workspace and 404s. - Add SUBMIT case to the trackExpense action -> submitTrackedExpenseToPolicy - Thread submit2026 policy type into buildPolicyData via getTrackExpenseInformation - Route the draft-workspace SUBMIT path through trackExpense in useExpenseSubmission - Give the optimistic draft workspace a localized default name
Guard the new "Submit to my employer" routing so it can only ever create a Submit (submit2026) workspace and never diverts other (team/corporate) draft flows: - useExpenseSubmission: only route a draft SUBMIT through trackExpense when the draft policy type is submit2026. - getTrackExpenseInformation: only request a submit2026 workspace type when the draft policy is actually submit2026 (otherwise keep the default team type). No behavior change for categorize/share or existing-workspace submits.
The employer destination flow previously keyed off the generic group-policy count (shouldShowPolicy counts team/corporate/submit alike) and the standard participant picker (lists all owned workspace chats). That let the flow route into a Collect/Control workspace, violating "Submit workspace types only". - ReportUtils: count only submit2026 policies for the 0/1/many decision so a user with no Submit workspace always gets a new one, and a single-Submit user is routed straight to it (never into a team/corporate workspace). - Thread a new isSubmitWorkspacesOnly route param through the participant picker and filter the workspace list to submit2026 policies for the multi-workspace case.
|
Summary In the "Submit to my employer" zero-workspace flow, the frontend sends a clean manual tracked expense to The optimistic/offline result is correct (manual expense, full amount) — only the server response is wrong — so this is server-side. Steps to reproduce
What we send (request) — note: no What we get back (response, transaction trimmed) — "transactions_8986178610094677878": {
"amount": -500,
"cardName": "Cash Expense",
"iouRequestType": "distance-manual",
"comment": {
"comment": "",
"customUnit": {
"customUnitID": "6a43c406dc9e3",
"customUnitRateID": "6a43c406dc9ee",
"distanceUnit": "mi",
"name": "Distance",
"quantity": 0,
"routeDistanceMeters": 0
},
"type": "customUnit"
},
"modifiedAmount": 0,
"modifiedMerchant": "0.00 mi @ €0.725 / mi",
"merchant": "Expense"
}in offline the report is converted successfully however in online it shows distance: here is the Screen.Recording.2026-06-30.at.14.53.46.mov |
- Forward policyName so the backend-created Submit workspace matches the optimistic name - Gate workspace distance customUnit on isDistanceRequest so manual/scan expenses aren't distance-ified - Skip the category upgrade path for Submit (submit2026) workspaces (categories ship enabled) - Use the user's display name for the default workspace name - Pass policyDraft to usePolicyForTransaction so the draft Submit policy resolves correctly
|
Sent PR to fix that here https://github.com/Expensify/Web-Expensify/pull/54091/ |
|
@iwiznia let me know when the BE PR gets merged ✌️ |
|
It's already in production |
…to-employer-92704 # Conflicts: # index.js # modules/ExpensifyNitroUtils/nitrogen/generated/android/ExpensifyNitroUtilsOnLoad.cpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/c++/JContact.hpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/c++/JContactFields.hpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/c++/JHybridAppStartTimeModuleSpec.hpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/c++/JHybridContactsModuleSpec.hpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/c++/JStringHolder.hpp # modules/ExpensifyNitroUtils/nitrogen/generated/android/kotlin/com/margelo/nitro/utils/Contact.kt # modules/ExpensifyNitroUtils/nitrogen/generated/android/kotlin/com/margelo/nitro/utils/StringHolder.kt # modules/ExpensifyNitroUtils/nitrogen/generated/ios/ExpensifyNitroUtils+autolinking.rb # server/stubs/react-native-nitro-fetch.ts # src/components/MoneyRequestConfirmationListFooter/hooks/useFooterDerivedFlags.ts # src/libs/API/parameters/SubmitReportParams.ts # src/libs/ReportUtils.ts # src/pages/inbox/report/actionContents/ChatActionableButtons.tsx # tests/unit/SessionUtilsTest.ts
|
@iwiznia Another issue: Per the Submit design doc ( Looks like the create-workspace path in
Screen.Recording.2026-07-05.at.14.20.24.mov |
|
Hmmmm seems it's because you are passing |
|
Good point ... changed that param and it worked :D Screen.Recording.2026-07-07.at.16.06.24.mov |
…sets ADVANCED approval
…-employer-92704 # Conflicts: # src/libs/ReportUtils.ts # src/libs/actions/IOU/TrackExpense.ts
…-employer-92704 # Conflicts: # src/components/MoneyRequestConfirmationListFooter/hooks/useFooterDerivedFlags.ts # src/libs/ReportUtils.ts
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 iwiznia has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/iwiznia in version: 9.4.34-0 🚀
|
Help site review — changes required ✅This PR splits the track-expense Submit it to someone whisper into Submit to a friend and Submit to my employer on the Submit plan (behind the
I checked the rest of Draft help site PR: #96048 It adds the two new options to @abzokhattab, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
Deploy Blocker #96085 was identified to be related to this PR. |
|
This PR failing because of the issue #96119 |
|
Deploy Blocker #96200 was identified to be related to this PR. |
|
Deploy Blocker #96214 was identified to be related to this PR. |
|
Deploy Blocker #96212 was identified to be related to this PR. |
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.4.34-14 🚀
|
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.4.34-14 🚀
Bundle Size Analysis (Sentry): |
…yname-92704 [Submit] Fix regressions from the "Submit to my employer" flow (#94319)

Explanation of Change
On the Submit (
submit2026) plan, the track-expense actionable whisper option "Submit it to someone" only let users pick an individual recipient, hiding the employer-reimbursement workflow. This PR splits that single option into two explicit destinations (behind theSUBMIT_2026beta):submit2026) workspace and adds the expense to its draft report.When the
SUBMIT_2026beta is off, the behavior is unchanged (the single "Submit it to someone" option remains), so this is safe to merge ahead of launch.Key changes:
CONST.IOU.SUBMIT_DESTINATION(friend/employer) andsubmitToFriend/submitToEmployertranslations (en + es).ChatActionableButtons.tsx: beta-gated two-option split.createDraftTransactionAndNavigateToParticipantSelector(ReportUtils.ts): newsubmitDestinationparam + employer routing logic.createDraftWorkspace/createDraftWorkspaceAndNavigateToConfirmationScreen: accept a policytypeand apply Submit-plan defaults (Categories, Tags, Workflows, Distance, advanced approval, manual submission) when creating asubmit2026workspace.isWorkspacesOnlyparam threaded through theMONEY_REQUEST_STEP_PARTICIPANTSroute so the employer picker shows workspaces only.Fixed Issues
$ #92704
PROPOSAL:
Tests
SUBMIT_2026beta for your account.Offline tests
QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)Avatar, 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
Screen.Recording.2026-07-11.at.15.58.04.mov
Screen.Recording.2026-07-11.at.16.07.02.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-07-09.at.15.42.14.mov
Screen.Recording.2026-07-09.at.16.09.27.mov