Skip to content

[Submit] Update "Submit to someone" to two options to allow for Submit workspace creation - #94319

Merged
iwiznia merged 25 commits into
Expensify:mainfrom
abzokhattab:abzokhattab-submit-to-employer-92704
Jul 13, 2026
Merged

iwiznia merged 25 commits into
Expensify:mainfrom
abzokhattab:abzokhattab-submit-to-employer-92704

Conversation

@abzokhattab

@abzokhattab abzokhattab commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

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 the SUBMIT_2026 beta):

  • Submit to a friend — keeps the existing recipient picker (individuals + workspaces).
  • Submit to my employer — routes the expense to a submit-enabled workspace based on how many the user can access:
    • 0 workspaces → creates a new Submit (submit2026) workspace and adds the expense to its draft report.
    • 1 workspace → skips the picker and submits straight to that workspace's draft report.
    • >1 workspaces → shows the destination picker limited to workspaces only.

When the SUBMIT_2026 beta 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) and submitToFriend/submitToEmployer translations (en + es).
  • ChatActionableButtons.tsx: beta-gated two-option split.
  • createDraftTransactionAndNavigateToParticipantSelector (ReportUtils.ts): new submitDestination param + employer routing logic.
  • createDraftWorkspace / createDraftWorkspaceAndNavigateToConfirmationScreen: accept a policy type and apply Submit-plan defaults (Categories, Tags, Workflows, Distance, advanced approval, manual submission) when creating a submit2026 workspace.
  • New isWorkspacesOnly param threaded through the MONEY_REQUEST_STEP_PARTICIPANTS route so the employer picker shows workspaces only.

Fixed Issues

$ #92704
PROPOSAL:

Tests

  1. Enable the SUBMIT_2026 beta for your account.
  2. In the self-DM, create a tracked expense (Manual/Scan) so the "What do you want to do?" whisper appears.
  3. Verify the whisper now shows Submit to a friend and Submit to my employer (instead of "Submit it to someone").
  4. Submit to a friend → verify the existing recipient picker opens (individuals can be selected) and the report submits as before.
  5. Submit to my employer with no Collect/Control/Submit workspace → verify a new Submit workspace is created and the expense lands in its draft report.
  6. Submit to my employer with exactly one workspace → verify no destination picker is shown and the expense is submitted to that workspace's draft report.
  7. Submit to my employer with multiple workspaces → verify the destination picker opens limited to workspaces only (no individual recipients).
  • Verify that no errors appear in the JS console

Offline tests

  1. Go offline.
  2. Submit to my employer with no workspace → verify the Submit workspace and draft report are created optimistically with pending (brick-road) indicators.
  3. Go back online → verify the workspace/report sync without duplicates.

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

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.
@melvin-bot

melvin-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

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 --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@codecov

codecov Bot commented Jun 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 Δ
src/CONST/index.ts 94.81% <ø> (ø)
src/ROUTES.ts 20.85% <100.00%> (+0.07%) ⬆️
...onfirmationList/hooks/useConfirmationValidation.ts 83.33% <100.00%> (ø)
...firmationListFooter/hooks/useFooterDerivedFlags.ts 100.00% <100.00%> (ø)
...es/iou/request/step/IOURequestStepConfirmation.tsx 64.34% <ø> (ø)
src/libs/actions/Policy/Policy.ts 70.00% <90.00%> (+0.10%) ⬆️
src/pages/inbox/ReportFetchHandler.tsx 87.64% <66.66%> (-0.38%) ⬇️
...ox/report/actionContents/ChatActionableButtons.tsx 80.68% <87.50%> (+0.43%) ⬆️
.../request/step/confirmation/useExpenseSubmission.ts 69.88% <75.00%> (+0.11%) ⬆️
src/pages/iou/request/ParticipantSearchResults.tsx 0.65% <0.00%> (ø)
... and 3 more
... and 11 files with indirect coverage changes

@abzokhattab

abzokhattab commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Working on this issue. I face a backend authorization error with the ConvertTrackedExpenseToRequest request:

Screen.Recording.2026-06-23.at.16.21.05.mov

Looks like the submit workspace conversation is not supported yet in this endpoint

Screenshot 2026-06-23 at 16 21 51
Request Data
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="amount"

400
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="currency"

EUR
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="comment"


------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="created"

2026-06-23
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="merchant"

d
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="payerAccountID"

0
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="payerEmail"


------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="chatReportID"

4940718405236022
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="transactionID"

799359454979335196
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="actionableWhisperReportActionID"

4992010111822589471
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="createdChatReportActionID"

6679303503805099452
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="moneyRequestReportID"

2760836178316881
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="moneyRequestCreatedReportActionID"

3045256285652373956
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="moneyRequestPreviewReportActionID"

2877136598889150918
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="transactionThreadReportID"

8794132548138119
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="modifiedExpenseReportActionID"

5261090384934621505
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="reportPreviewReportActionID"

3103902565218404955
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="isDistance"

false
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="apiRequestType"

write
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="pusherSocketID"

894519.3098259
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="shouldRetry"

true
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="canCancel"

true
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="authToken"

3853B6608C47F4928E17EF0A789650A97C94E8EA757CD06C8F93AA4D40550232B18F94F881AFF93E4D7849552BDD75BB66071CCEF87E6D3446ED148FCB6A029C0670AB149CDF6A5868B9337CEA97BF72A42987F56016AA14D03E6EF8D9D640E7D9477A09CB7DE9C8ECB091BE70573BF327921B97E6D831192172D375A19B52A78C713AFA1405F021EB9CF4BEA68BD0C094D0764D4D6BE6A2138595733486125A6AE1CB9E692BDF9833695AF155D283DB00A3FDFB484DE54A02668D23B2B310914925675C92DE02B1309F43379485226B13DB30600BD15167E97C2E5B4A787F6380B6E65259A95257B0B06ACCDF183A07D17D2D7A9A26BB4EDE9535B4501DA3330F8465688BD986A36E361375A9D99C007A17AFAF7F4E3C0122F5E9FA6BA98D69A558EDE7958CEF86970966999C21D564D7FE3FA5657CFF65BDD0AD0579E9E75B65B06E2576EC43F0C348E0267B7672C0531A762E40B0BCDD38CE537D04C3BA692653B6770A9B33B33C76D2EAA43748233D56942854F03A2E7D330AEEF65EE255EBC76A03ADED95FC1351E963DC9C38B4
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="referer"

ecash
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="platform"

web
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="api_setCookie"

false
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="email"

abzokhattab+testsubmit59@gmail.com
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="isFromDevEnv"

true
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="appversion"

9.4.17-0
------WebKitFormBoundarySoB0YBYwbCnNaKGn
Content-Disposition: form-data; name="clientUpdateID"

14156832843
------WebKitFormBoundarySoB0YBYwbCnNaKGn--

cc @iwiznia @carlosmiceli

@iwiznia

iwiznia commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

I think it's because you are passing both payer params as empty/0: payerAccountID: '0' payerEmail: ''

@abzokhattab

abzokhattab commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @iwiznia! I looked into this a bit more and I think the empty payerAccountID/payerEmail are actually a symptom, not the real cause. For "Submit to my employer" the destination is a workspace, so there isn't really an individual payer to set here.

The actual problem is that we end up calling ConvertTrackedExpenseToRequest (the person-to-person command) when we shouldn't. Submitting to a workspace should go through AddTrackedExpenseToPolicy instead, which just takes a policyID and doesn't need a payer at all.

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 requestMoneyConvertTrackedExpenseToRequest, and that command has no way to also create the workspace, so the draft never becomes real, the submit points at a policy that doesn't exist, and it falls back to the person path with an empty payer → 404.

Categorize/Share don't have this issue because they use a single command (CategorizeTrackedExpense) that creates the real workspace from the draft and converts the expense in one go. We don't have an equivalent for submit.

So I think it comes down to which way we want to go:

  1. Two-step, frontend only: create the workspace for real first (CreateWorkspace), then AddTrackedExpenseToPolicy with the new policyID. No BE changes, but it's two separate writes so it's not atomic .
  2. One combined command like Categorize has — either a new submit command or extending AddTrackedExpenseToPolicy to accept the created-workspace params. Cleaner/atomic, but needs backend work.

Do you have a preference? The 1+ existing workspace cases should already route to a real policy chat and hit AddTrackedExpenseToPolicy fine, I'm verifying those separately.

@iwiznia

iwiznia commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Not sure why I missed this comment, sorry!
CategorizeTrackedExpense and AddTrackedExpenseToPolicy are aliases, so they support the same parameters.
So I think the only thing that we need to change is to allow to pass a policy type so that we create a submit workspace instead of a team one.

@iwiznia

iwiznia commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

This PR https://github.com/Expensify/Web-Expensify/pull/54066 adds type as param, so you can pass submit2026 as the type to get a submit2026 workspace created

@iwiznia

iwiznia commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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.
@abzokhattab

abzokhattab commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

AddTrackedExpenseToPolicy / CategorizeTrackedExpense converts a manual tracked expense into a 0-mile distance expense when creating a submit2026 workspace

Summary

In the "Submit to my employer" zero-workspace flow, the frontend sends a clean manual tracked expense to AddTrackedExpenseToPolicy (which creates a new submit2026 workspace and moves the expense into it). The backend response returns the transaction converted to iouRequestType: "distance-manual" with the newly-created workspace's distance customUnit applied, which zeroes the amount (0.00 mi @ €0.725/mi).

The optimistic/offline result is correct (manual expense, full amount) — only the server response is wrong — so this is server-side.

Steps to reproduce

  1. As a user with no submit-enabled workspace, track a manual cash expense in the self-DM (e.g. €5.00).
  2. Use the track-expense whisper → "Submit to my employer".
  3. Confirm. This fires AddTrackedExpenseToPolicy with isDraftPolicy=true and type=submit2026.

What we send (request) — note: no distance, waypoints, customUnitID, or customUnitRateID:

policyID: E0C23A7E07CC93C4
isDraftPolicy: true
transactionID: 8986178610094677878
amount: 500
currency: EUR
comment: (empty)
merchant: fqw
created: 2026-06-30
taxAmount: 0
billable: false
reimbursable: true
attendees: [{"email":"abzokhattab+testsubmit69@gmail.com",...}]
type: submit2026

What we get back (response, transaction trimmed)iouRequestType flipped to distance-manual, a customUnit added, modifiedAmount: 0:

"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 requestID: a13d51953a2662e5-LAX

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
@iwiznia

iwiznia commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Sent PR to fix that here https://github.com/Expensify/Web-Expensify/pull/54091/

@abzokhattab

Copy link
Copy Markdown
Contributor Author

@iwiznia let me know when the BE PR gets merged ✌️

@iwiznia

iwiznia commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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
@abzokhattab

Copy link
Copy Markdown
Contributor Author

@iwiznia Another issue: AddTrackedExpenseToPolicy creates the submit2026 workspace with approvalMode: OPTIONAL instead of ADVANCED.

Per the Submit design doc (generateFree2026), new Submit workspaces should be APPROVAL_MODE_ADVANCED. Because it's OPTIONAL, the report CTA shows "Mark as done" instead of "Submit" (offline it's correct since the client optimistically sets ADVANCED, then it flips on the server response).

Looks like the create-workspace path in AddTrackedExpenseToPolicy/CategorizeTrackedExpense isn't routing submit2026 through generateFree2026.

  • requestID: a16652f78ec077fb-SJC
  • policyID: B427319C2EC07B30
  • Expected: approvalMode: ADVANCED · Actual: approvalMode: OPTIONAL
Screen.Recording.2026-07-05.at.14.20.24.mov

@iwiznia

iwiznia commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Hmmmm seems it's because you are passing engagementChoice: 'newDotTrackWorkspace'... I think that's not correct, is it?

@abzokhattab

Copy link
Copy Markdown
Contributor Author

Good point ... changed that param and it worked :D

Screen.Recording.2026-07-07.at.16.06.24.mov

…-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
@iwiznia
iwiznia merged commit 07951fc into Expensify:main Jul 13, 2026
36 of 37 checks passed
@OSBotify

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.

@github-actions

Copy link
Copy Markdown
Contributor

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

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/iwiznia in version: 9.4.34-0 🚀

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

@MelvinBot

Copy link
Copy Markdown
Contributor

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 SUBMIT_2026 beta). One help-site article documents that whisper:

  • Track-Expenses.md → "What you can do with tracked expenses" lists Submit it to someone as an option.

I checked the rest of docs/articles (Track-Personal-Expenses, Create-an-Expense, Create-and-Submit-Reports, Expense-Assistant, etc.) — none of them describe the whisper options, so no other article needs updating.

Draft help site PR: #96048

It adds the two new options to Track-Expenses.md while leaving the default Submit it to someone wording intact (since that's unchanged when the beta is off). Labels are copied verbatim from src/languages/en.ts.

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

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #96085 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

This PR failing because of the issue #96119
This issue is reproducible in: Android app

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #96200 was identified to be related to this PR.

@lanitochka17

Copy link
Copy Markdown

Deploy Blocker #96214 was identified to be related to this PR.

@jponikarchuk

Copy link
Copy Markdown

Deploy Blocker #96212 was identified to be related to this PR.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/roryabraham in version: 9.4.34-14 🚀

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

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/roryabraham in version: 9.4.34-14 🚀

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants