Add alternate route for map-based mileage expenses - #96169
Conversation
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
|
|
@codex please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1aa5fc97f
ℹ️ 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 please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1d602e2a1c
ℹ️ 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".
| const {unit} = mileageRate ?? {}; | ||
| const isAlternativeDirectionSelected = transaction?.comment?.selectedRouteKey === 'route1'; | ||
| const handleRouteSelection = (isAlternative: boolean) => { | ||
| setSelectedRoute(transaction?.transactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID, isAlternative ? 'route1' : 'route0', transactionState); |
There was a problem hiding this comment.
Persist alternate route edits on save
When this handler runs in the edit flow for an existing map-distance expense, it only merges selectedRouteKey locally. The save path in IOURequestStepDistance still returns early when waypoint addresses and manual quantity are unchanged, so tapping the alternate route and pressing Save sends no updateMoneyRequestDistance/selected-route data to the backend; the report can keep the old route/amount after sync even though the editor appeared to save the alternate selection. Include the selected route change in edit change detection and the update payload.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
yeah I'm aware, will fix this
|
🚧 JmillsExpensify has triggered a test Expensify/App build. You can view the workflow run here. |
This comment has been minimized.
This comment has been minimized.
|
@codex please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1eed358e66
ℹ️ 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 please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 63d2059aa7
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ebc582da2f
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 76b40d042b
ℹ️ 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".
| linkedTrackedExpenseReportAction, | ||
| linkedTrackedExpenseReportID, | ||
| customUnitRateID, | ||
| selectedRouteDistance, |
There was a problem hiding this comment.
Preserve route choice when moving tracked expenses
When the selected-route distance is only carried in the retry/default TrackExpense payload here, tracked-distance moves still lose it: the CATEGORIZE, SHARE, and SUBMIT branches below rebuild transactionParams without selectedRouteDistance, and moveTrackedExpenseToPolicy/shareTrackedExpense only spread those params into the API request. For a tracked map expense created on route1, later submitting/categorizing/sharing it to a workspace can therefore send only the waypoints and let the backend fall back to route0, changing the moved expense's distance/amount. Include this field in those action-specific transactionParams as well.
Useful? React with 👍 / 👎.
|
Let us know when this is ready for some builds. |
|
@shawnborton feel free to build and test |
|
🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here. |
|
Removed Tom since this is my assigned initiative. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
Hmm the app immediately crashes when I try to test the iOS build. Any ideas? |
JmillsExpensify
left a comment
There was a problem hiding this comment.
Still looks great!
|
Interesting. I didn't see that in the last test. |
|
Visually this looks good, but the behavior of clicking on the miles badge and having it convert to KM feels weird. I feel like when you first click the alternate route, it should just select the alt route. Maybe clicking it a second time can convert the distance? Also, we should use a pointer cursor here instead of a drag cursor when you go to hover over the alt line. I also wonder if we need a hover state for the line but we can tackle that as a follow up if we want to. CleanShot.2026-08-03.at.11.55.47.mp4 |
Ah interesting. I didn't run into this. I like this suggestion. Honestly I'd prefer to remove it for the v1, though I can see how it's useful at this stage because you haven't selected the workspace yet, so we don't really know which unit to use. |

Explanation of Change
Adds alternate route selection for map-based distance expenses. The map now renders both routes returned by the BE and lets the user tap one to pick it; the pick is stored in
comment.selectedRouteKeyand recovered for saved expenses by distance-matchingcustomUnit.routeDistanceMeters.selectedRouteDistanceis sent on create/track/update so the BE uses the chosen route.Fixed Issues
$ #90658
PROPOSAL: N/A
Tests
1. Create a map distance expense on the alternate route (check both workspace and selfDM expense)
2. Switch the route on an already-created expense
3. Manual distance override and waypoint edits
Offline tests
Map is not loaded in offline mode so user can't change route in offline mode
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
Screen.Recording.2026-08-03.at.17.12.16.mov
Screen.Recording.2026-08-03.at.17.11.29.mov
Screen.Recording.2026-08-03.at.17.09.28.mov
Android: mWeb Chrome
iOS: Native
Screen.Recording.2026-08-03.at.16.51.42.mov
Screen.Recording.2026-08-03.at.17.02.38.mov
Screen.Recording.2026-08-03.at.17.03.19.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-08-03.at.16.24.20.mov
Screen.Recording.2026-08-03.at.16.26.13.mov
Screen.Recording.2026-08-03.at.16.30.42.1.mov