Skip to content

Scope the distance commuter-exclusion check to the expense destination - #101022

Open
MelvinBot wants to merge 3 commits into
mainfrom
claude-selfDmDistanceHomeAddressModal
Open

MelvinBot wants to merge 3 commits into
mainfrom
claude-selfDmDistanceHomeAddressModal

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

A member whose default workspace excludes commutes by "Calculate by home and office" was blocked by the "Home address is required" modal when creating a personal map distance expense in their own Self-DM. Personal expenses never reach that workspace, so its commuter rule should not gate them.

Two call sites were checking the rule against the member's default workspace instead of the expense's destination:

  1. IOURequestStepDistanceMap.tsx passed usePolicyForTransaction's policy straight to useBlockDistanceRequest. For a Self-DM track expense that hook deliberately resolves to the member's active workspace, so tapping Next hit the modal. It now uses the same destination-scoped expression the Manual and Odometer tabs already use — report?.policyID ?? (shouldAutoReportToDefaultWorkspace ? defaultExpensePolicy?.id : undefined) — which resolves to no policy for a Self-DM expense while still blocking a global-FAB flow that will auto-report into a restricted workspace.

  2. useExpenseSubmission.ts had the same unscoped policyID: policy?.id. Without fixing it the member would just be blocked one screen later on Create expense. It now scopes the check with isPolicyExpenseChat, matching the pattern already used in MoneyRequestConfirmationList.tsx.

Expenses that really are going to a restricted workspace still show the modal — that path is unchanged and covered by a test.

Fixed Issues

$ #100969
PROPOSAL: #100969 (comment)

Tests

  1. Workspace has Distance rates enabled and Exclude commutes set to "Calculate by home and office"
  2. That workspace is the default workspace
  3. Account has no personal home address
  4. Self-DM opens and a distance expense can be started on the Map tab
  5. Start point and stop point can be added on the map
  6. Next does NOT show the "Home address is required" modal and advances to the confirmation screen
  7. Create expense does NOT show the modal and the expense is created in the Self-DM
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

  1. Workspace has Distance rates enabled and Exclude commutes set to "Calculate by home and office"
  2. That workspace is the default workspace
  3. Account has no personal home address
  4. Self-DM opens and a distance expense can be started on the Map tab
  5. Start point and stop point can be added on the map
  6. Next does NOT show the "Home address is required" modal and advances to the confirmation screen
  7. Create expense does NOT show the modal and the expense is created in the Self-DM
  • 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

Co-authored-by: cretadn22 <cretadn22@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team September 12, 2026 12:05
@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 12, 2026
@MelvinBot

Copy link
Copy Markdown
Contributor Author

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

To take ownership of this branch and be able to push updates, run:

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

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

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The failing check is not caused by this PR, so I haven't pushed anything.

test (job 4) fails on 3 assertions in tests/navigation/NavigateTests.tsx — all workspace sidebar/split-navigator routing. This PR only touches distance-expense files, and the same 3 assertions fail on unrelated PRs currently open against main.

Check independent approval just needs a human reviewer — nothing to fix there.

Evidence

The failure

All three failures read a hard-coded route index and get undefined:

● Navigate › on the narrow layout › returns to the Workspaces list after directly opening a Workspace subpage without its sidebar
    Expected: "Workspaces_List"   Received: undefined      (NavigateTests.tsx:180)

● Navigate › on the narrow layout › removes the sidebar marker when the Workspace split navigator is already mounted
    Expected: "Workspace_Members" Received: undefined      (NavigateTests.tsx:247)

● Navigate › on the wide layout › keeps the Workspace sidebar while removing the internal sidebar marker
    Expected: "Workspace_Initial" Received: undefined      (NavigateTests.tsx:733)

Each one resolves rootState.routes.at(0)?.state?.routes.at(4)?.state, which is now undefined — the navigator tree no longer has the shape the test assumes at index 4.

Why it isn't this PR

This PR changes exactly three files, none of them navigation:

  • src/pages/iou/request/step/IOURequestStepDistanceMap.tsx
  • src/pages/iou/request/step/confirmation/useExpenseSubmission.ts
  • tests/ui/IOURequestStepDistanceMapCommuterExclusionTest.tsx (new)

And the same failure reproduces on unrelated open PRs:

Likely source (not confirmed)

These three tests were added by #100387 ("Fix back navigation from Workspace Search results", merged 2026-09-11 18:17 UTC), which changed SplitRouter.ts, Navigation.ts, and NavigateTests.tsx together. Its own CI ran against a stale base and passed test (job 4) there, so the incompatibility only surfaced after it landed on current main. I did not bisect main to prove which commit shifted the route index, so treat the exact culprit as unverified.

main doesn't run the Jest workflow on every push, so there's no red main run to point at — the cross-PR reproduction is the evidence.

Per the "don't fix unrelated failures" rule I've left this alone — it needs a fix on main, not on this branch. Once that lands, a merge of main into this branch will clear the check.

@MelvinBot

MelvinBot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Android test steps verification results: ✅ pass

The PR's Tests section is still a TODO, so I ran the reproduction steps documented in the linked issue #100969 against this branch on standalone Android NewDot. With a default workspace using Exclude commutes = "Use home and office locations" and no personal home address, a Self-DM map distance expense completes end-to-end without the "Home address is required" modal, while the same flow targeting the workspace still shows it.

Home address is required modal over the workspace expense chat

Step Status
1. Workspace has Distance rates enabled and Exclude commutes set to the home-and-office methodCreated a workspace, set its company address, enabled Distance rates, then Distance rates > Settings > Exclude commutes > "Exclude usual commute" > Save. Settings read back "Exclude commutes, Use home and office locations". Backend-confirmed: #admins carries "enabled distance rates" and "changed exclude commutes to calculate by home and office", and Concierge posted the commuter-exclusion notice in the workspace expense chat.
Workspace Distance rates Settings showing Exclude commutes = 'Use home and office locations'
2. That workspace is the default workspaceWorkspaces list renders the row with the Default badge (isDefault === (activePolicyID === policyID)). It is the account's only workspace, so it became the active policy on creation.
Workspaces list with the Default badge on Melvin Tester's Workspace
3. Account has no personal home addressSettings > Profile shows "Home address. Your review is required" with no value. Personal details has Address line 1, City, State and Zip all empty (only Country pre-filled). hasHomeAddressSelector keys off street, so the account reads as having no home address.
Personal details page with empty Address line 1, City, State and Zip
4. Can open the Self-DM and start a distance expense on the Map tabOpened "Melvin Tester (you) / Your space" and chose "Track distance". Landed on the distance flow with the Map tab selected. No modal intercepted entry into the flow.
Self-DM distance expense on the Map tab with Start and Stop waypoints empty
5. Can add a start point and a stop point on the mapStart set to "200 Santa Monica Pier", Stop set to "1 World Way". Route resolved to 12.95 mi and Next re-enabled.
Map with start and stop waypoints set and a 12.95 mi route drawn
6. Next does NOT show the "Home address is required" modal and advances to the confirmation screenTapped Next and went straight to "Confirm details". Visibility probes for "Home address is required" and "Add Home Address" both returned not-found; "Create expense" was present. Confirmation shows participant "Your space", Amount $9.84, Distance 12.95 miles, Rate Default Rate.
Confirm details screen for the Self-DM distance expense, no modal present
7. Create expense does NOT show the modal and the expense is created in the Self-DMTapped Create expense; the modal did not appear. The Self-DM now contains "tracked $9.84 for 12.95 miles" with the distance preview "12.95 mi @ $0.76 / mi".
Self-DM chat showing the created tracked $9.84 / 12.95 miles distance expense
8. Regression: a distance map expense destined for the workspace still shows the modalOpened the workspace expense chat and chose "Track distance". The modal fired immediately, before the map screen: "Before you track distance, you need to add your home address to your private profile. Melvin Tester's Workspace uses this address for commuter deductions." with an "Add Home Address" button. Workspace name interpolated correctly.
Home address is required modal over the workspace expense chat

No JS console errors.

Two notes on how the run was set up

Beta override. "Exclude commutes" is gated on CONST.BETAS.COMMUTER_EXCLUSIONS in src/pages/workspace/distanceRates/PolicyDistanceRatesSettingsPage.tsx, and the fresh test account is not in that beta, so the row does not render. I enabled the commuterExclusions override via Settings > Troubleshoot > Beta overrides. That is frontend-only and does not weaken the result — the backend accepted the setting, evidenced by the POLICYCHANGELOG actions in #admins and the Concierge commuter-exclusion notice.

Composer entry point. In both the Self-DM and the workspace expense chat, composer Create expense offers only Scan and Manual tabs — there is no Distance tab there. The distance flow is only reachable via the separate Track distance action, which is the path I used for steps 4-8. Flagging in case a Distance tab under Create expense was expected.


view run · no recording available

@MelvinBot

MelvinBot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Web test steps verification results: ✅ pass

The PR's Tests section is still a TODO, so I ran the reproduction steps documented in the linked issue #100969 against this branch on dev NewDot web. A Self-DM map distance expense completed end-to-end with no "Home address is required" modal, while the same flow destined for the commuter-exclusion workspace still shows the modal.

Home address is required modal shown when starting a distance expense in the workspace expense chat

Step Status
1. Workspace has Distance rates enabled and Exclude commutes set to "Calculate by home and office"Created a workspace, set its company address (required before the home/office option can be selected), enabled Distance rates in More features, then Distance rates > Settings > Exclude commutes > "Exclude usual commute" — the current en copy for CONST.POLICY.COMMUTER_EXCLUSION_METHOD.HOME_AND_OFFICE. After saving, the row reads back "Exclude commutes, Use home and office locations" and "Require GPS or map entry" auto-locked on, with the Default Rate $0.76/mile row Active.
Workspace Distance rates settings: Exclude commutes = Use home and office locations, Require GPS or map entry locked on
Beta overrides page with commuterExclusions toggled on and marked Overridden
2. That workspace is the default workspaceWorkspaces list row reads "Workspace name: Melvin Tester's Workspace, Default, Owner: Melvin Tester, Workspace type: Collect". The Default badge renders only when activePolicyID === policyID (see src/pages/workspace/WorkspacesListPage.tsx), so NVP_ACTIVE_POLICY_ID points at this workspace.
Workspaces list showing Melvin Tester's Workspace with the Default badge
3. Account has no personal home addressSettings > Profile > Home address has no value. Personal details shows Address line 1, Address line 2, City and Zip empty and State unset (only Country prefilled). Concierge also posted "Your admin has enabled commuter exclusions for distance expenses. Please add your home address to use this feature.", confirming the backend sees no home address.
Personal details RHP with all Home address fields empty
4. Self-DM opens and a distance expense can be started on the Map tabOpened the Self-DM (header "Melvin Tester (you) / Your space") and used composer + > Track distance. The flow opened with tabs Map/Manual/GPS/Odometer, Map selected, showing Start/Stop rows and a Next button. No blocking modal on entry.
Track distance flow open on the Map tab inside the Self-DM (Your space)
5. Start point and stop point can be added on the mapStart set to 123 Main St (Broomfield, CO), Stop to 500 Main St (Hudson, CO) via the address autocomplete. Both waypoints read back, an "Add stop" button appeared, and the map rendered the route at 36.79 mi.
Map tab with Start 123 Main St, Stop 500 Main St and a routed line of 36.79 mi
6. Next does NOT show the "Home address is required" modal and advances to the confirmation screenClicking Next settled directly into "Confirm details" with participant "Melvin Tester (you), Your space", Amount $27.96, Distance 36.79 miles, Rate Default Rate, Category Car and a "Create expense" button. No dialog node and no "Home address is required" heading in the snapshot.
Confirm details screen for the Self-DM distance expense, no home address modal
7. Create expense does NOT show the modal and the expense is created in the Self-DMAfter Create expense the flow returned to the Self-DM showing "tracked $27.96 for 36.79 miles" with the distance preview (36.79 mi @ $0.76 / mi) plus the Concierge follow-up. No "Home address is required" modal. An unrelated free-trial "Add payment card" nudge appeared over the chat and was dismissed — it is not the home-address block.
Self-DM showing the created 36.79 mi $27.96 distance expense
8. Regression: a workspace-destined distance map expense still shows the modalIn the workspace expense chat "Melvin Tester's expenses", composer + > Track distance immediately surfaced the dialog "Home address is required" with "Before you track distance, you need to add your home address to your private profile. Melvin Tester's Workspace uses this address for commuter deductions." and an "Add Home Address" button. Workspace name interpolated correctly.
Home address is required modal shown when starting a distance expense in the workspace expense chat
Setup notes and one caveat on console errors

Beta override. The Exclude commutes row is gated behind the commuterExclusions beta, which a fresh account is not in, so I enabled it via Settings > Troubleshoot > Beta overrides. That is frontend-only — the backend accepted the setting regardless, evidenced by the Concierge commuter-exclusion notice in the workspace expense chat.

Setup prerequisite worth documenting. "Exclude usual commute" cannot be selected until the workspace has a company address (PolicyCommuterExclusionsPage blocks with a workspace-address-required modal), and selecting it auto-locks "Require GPS or map entry" on.

Entry point. In both the Self-DM and the workspace expense chat, composer Create expense offers only Manual and Scan tabs — no Distance tab. Track distance is the map-distance entry point, and that is the path used for steps 4-8.

Console errors: not collected. Browser console capture is unavailable on this web driver, so I cannot assert the "no JS console errors" checklist item. No in-app error banners, red-dot errors, or failed-action states were observed during the run.

The observed behavior matches the scoping in src/hooks/useBlockDistanceRequest.ts: the block reason is computed per destination policy ID, so the Self-DM (no policy) skips the homeAddressRequired branch while the workspace chat still hits it.


view run · no recording available

// Odometer tabs do. `policy` here can be the member's active workspace even for a Self-DM track expense, and
// personal expenses are exempt from a workspace's commuter rule.
const blockDistanceRequestIfNeeded = useBlockDistanceRequest({
policyID: report?.policyID ?? (shouldAutoReportToDefaultWorkspace ? defaultExpensePolicy?.id : undefined),

@MelvinBot MelvinBot Sep 13, 2026

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.

You're right about the ?? semantics — _FAKE_ is truthy, so it short-circuits the fallback. But I traced it and it's a no-op here, for two independent reasons:

1. _FAKE_ and undefined behave identically inside the hook. getBlockReason only uses the ID as a key lookup into policiesRequiringMapOrGPS, which is built from the real POLICY collection:

src/hooks/useBlockDistanceRequest.ts:91

if (!policyIDToCheck || !policiesRequiringMapOrGPS?.[policyIDToCheck] || isEditingExistingDistanceRequest) {

No policy is ever stored under _FAKE_, so it bails at the second condition instead of the first. Same result, and no risk of a wrong workspace name leaking into the modal copy.

2. The fallback branch is unreachable when policyID is _FAKE_. The fallback only matters if shouldAutoReportToDefaultWorkspace is true, and that hard-gates on iouType === create:

src/libs/shouldUseDefaultExpensePolicy.ts:18

Every create distance entry point passes a freshly generated report ID that has no Onyx report — e.g. FloatingActionButtonAndPopover.tsx:36 feeding TrackDistanceMenuItem.tsx:53, plus the generateReportID() call sites in QuickCreationActionsBar, SearchActionsBarCreateButton, and useCreateNavigationSuggestions. So report is undefined, report?.policyID is undefined, and the fallback fires as intended. Conversely, when a report actually carries _FAKE_ (Self-DM / DM, from buildOptimisticChatReport's default), iouType is track/submit, so shouldAutoReportToDefaultWorkspace is false and the fallback would resolve to undefined anyway.

I couldn't construct a case where a _FAKE_ report reaches this step with iouType === create. If you know of one, say so and I'll fix it.

One note on scope: this expression is copied verbatim from mainDynamicIOURequestStepDistanceManual.tsx:161 and IOURequestStepDistanceOdometer.tsx:176. If you'd still rather normalize _FAKE_ defensively, I'd do it in all three at once so they don't drift — happy to push that if you want it.


view run · no recording available

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The failing test isn't caused by this PR

test (job 4) fails on 3 tests in tests/navigation/NavigateTests.tsx — a file this PR doesn't touch. Those same 3 tests fail on main right now, with none of this PR's code applied. There's nothing to fix on this branch, and merging main in won't help either, because main is red too.

Root cause: TAB_SCREENS.ts:9-16 puts WORKSPACE_NAVIGATOR at index 5 (HOME, REPORTS, SEARCH, INSIGHTS, SETTINGS, WORKSPACE). Three tests added by #100387 seed an initialState that omits SCREENS.INSIGHTS and then read routes.at(4) — which lands on SETTINGS_SPLIT_NAVIGATOR, whose .state is undefined. Hence Expected: "Workspaces_List" / Received: undefined. The other 11 tests in that file all include SCREENS.INSIGHTS and pass.

It's a semantic merge conflict: #100387 branched before INSIGHTS was added to the tab list, merged cleanly as text, and broke once it landed.

Fix (verified locally — 14/14 pass): in those 3 tests, add {name: SCREENS.INSIGHTS} before SETTINGS_SPLIT_NAVIGATOR, change index: 4index: 5, and change the five routes.at(4) reads to routes.at(5).

This belongs in its own PR against main rather than in a distance-expense PR — it unblocks every open PR, not just this one. Reply @MelvinBot open that PR and I'll put it up.

The other two red checks (checklist, Check independent approval) are the standard author-checklist gates — they need the Tests and QA Steps sections filled in and the boxes ticked before this goes ready for review.

Evidence

Each run below is tests/navigation/NavigateTests.tsx in isolation:

Commit What it is Result
20b5a4a this PR's head 3 failed, 11 passed
6d42ea0 this PR's merge base (no PR code) 3 failed, 11 passed
ddeb1a7 current main tip 3 failed, 11 passed
ea9470c merge of #100387, which added these 3 tests 3 failed, 11 passed
ea9470c + the fix above 14 passed

This PR's own test file passes: tests/ui/IOURequestStepDistanceMapCommuterExclusionTest.tsx → 2 passed.

The three broken tests and their routes.at(4) reads:

Compare with a passing test in the same file, which includes SCREENS.INSIGHTS: NavigateTests.tsx:47-64.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Update: the fix is already up and approved — nothing to do on this branch

Re-checked. Situation on this PR is unchanged: test (job 4) still fails on the same 3 tests in tests/navigation/NavigateTests.tsx, still not caused by this PR, and main is still red (unchanged at ddeb1a7). Full analysis in my earlier comment.

What's new: someone already put the fix up — #101024 ("Fix Workspace navigation tests after Insights tab addition", by nabi-ebrahimi). It's approved, all 8 test jobs are green, and its only red check is the review bot job. It does exactly what's needed: adds {name: SCREENS.INSIGHTS}, bumps index: 45, and replaces the hardcoded routes.at(4) reads with a getWorkspaceNavigatorState() name lookup — which is more robust than the index bump I described, since it won't break again the next time a tab is added.

So the path to green here is: merge #101024, then re-run test (job 4) on this PR. No commit is needed on this branch, and I'm not pushing one.

Heads up: #101016 ("[No QA] Look up the Workspace navigator by name in NavigateTests") is a duplicate of the same fix and is still open — worth closing one of the two.

The other two red checks (checklist, Check independent approval) are the standard author-checklist gates — they need the Tests and QA Steps sections filled in and the boxes ticked before this goes ready for review.

Comment on lines +96 to +98
// Scope the commuter-exclusion check to where the expense is actually going, the same way the Manual and
// Odometer tabs do. `policy` here can be the member's active workspace even for a Self-DM track expense, and
// personal expenses are exempt from a workspace's commuter rule.

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
// Scope the commuter-exclusion check to where the expense is actually going, the same way the Manual and
// Odometer tabs do. `policy` here can be the member's active workspace even for a Self-DM track expense, and
// personal expenses are exempt from a workspace's commuter rule.

@MelvinBot

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.

Applied in fa4ebb6 — comment removed.

Comment on lines +345 to +346
// Only a workspace destination can enforce a workspace's distance rules. `policy` is also set for personal
// expenses (Self-DM / track), which are exempt, so scope the check the same way `MoneyRequestConfirmationList` does.

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
// Only a workspace destination can enforce a workspace's distance rules. `policy` is also set for personal
// expenses (Self-DM / track), which are exempt, so scope the check the same way `MoneyRequestConfirmationList` does.
// Only a workspace destination can enforce a workspace's distance rules.

@MelvinBot

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.

Applied in fa4ebb6 — comment trimmed to the one line.

@cretadn22

Copy link
Copy Markdown
Contributor

@MelvinBot Please update this branch with the latest main and mark the PR as ready

Co-authored-by: cretadn22 <cretadn22@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Applied both suggestions in fa4ebb6.

Comment-only diff — no behavior change.

AI tests run
Check Result
ESLint (both changed files) ✅ no new violations
npm run typecheck ✅ passed (all 5 projects)
react-compiler-compliance-check (both files) ✅ passed
npm testuseBlockDistanceRequest.test.ts, useParticipantSubmissionTest.ts ✅ 25/25 passed

Pre-existing, not from this change: react-hooks/set-state-in-effect in IOURequestStepDistanceMap.tsx. It reports at line 203 now and at line 206 before this commit — same single error, shifted by the 3 deleted comment lines. Verified by re-running ESLint against 20b5a4a with the change stashed. It's grandfathered by the lint seatbelt ratchet.

Could not run locally: npm run lint-changed and npm run spell-changed both abort with git merge-base failed in this shallow CI checkout, so I linted the two files directly with npx eslint instead. cspell was not runnable here either — low risk, since the diff only deletes prose and the one retained line is a verbatim prefix of a line that already passed CI. npm run prettier isn't defined in this repo version; formatting is enforced through ESLint, which passed.

@cretadn22

cretadn22 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible 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 checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (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 verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • 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 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.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this 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.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Screen.Recording.2026-09-16.at.23.40.30.mov
Android: mWeb Chrome
Screen.Recording.2026-09-16.at.23.33.57.mov
iOS: HybridApp
Screen.Recording.2026-09-16.at.23.35.55.mov
iOS: mWeb Safari
Screen.Recording.2026-09-16.at.23.32.38.mov
MacOS: Chrome / Safari
Screen.Recording.2026-09-16.at.23.30.00.mov

@cretadn22

Copy link
Copy Markdown
Contributor

@MelvinBot Please update the QA section of the author checklist with the step provided below and check off all the checklist items

  1. Workspace has Distance rates enabled and Exclude commutes set to "Calculate by home and office"
  2. That workspace is the default workspace
  3. Account has no personal home address
  4. Self-DM opens and a distance expense can be started on the Map tab
  5. Start point and stop point can be added on the map
  6. Next does NOT show the "Home address is required" modal and advances to the confirmation screen
  7. Create expense does NOT show the modal and the expense is created in the Self-DM

…HomeAddressModal

Co-authored-by: cretadn22 <cretadn22@users.noreply.github.com>
@MelvinBot
MelvinBot marked this pull request as ready for review September 16, 2026 16:52
@MelvinBot
MelvinBot requested a review from a team as a code owner September 16, 2026 16:52
@melvin-bot
melvin-bot Bot requested review from cretadn22 and removed request for a team September 16, 2026 16:53
@melvin-bot

melvin-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

@cretadn22 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 requested review from flaviadefaria and removed request for a team September 16, 2026 16:53
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Done — branch updated with main and the PR is out of draft.

  • Merged origin/main into the branch as d63dd01 (merge commit, no rebase). No conflicts. The branch was 1029 commits behind; it's now even with main.
  • PR is marked ready for review. mergeable: MERGEABLE.

One thing still needs you: the Tests and QA Steps sections of the PR body are still the // TODO placeholders. I don't fill those out on an author's behalf, and the PR Author Checklist is also unchecked — both will hold the PR up in review.

AI tests run after the merge
Check Result
npm run typecheck ✅ passed (all 5 projects)
npm run lint-changed ✅ passed (exit 0)
npm run spell-changed ✅ 3 files checked, 0 issues
react-compiler-compliance-check (both source files) ✅ passed
npm testIOURequestStepDistanceMapCommuterExclusionTest.tsx, useBlockDistanceRequest.test.ts, useParticipantSubmissionTest.ts ✅ 27/27 passed

The merge pulled in package.json / package-lock.json / patch changes, and npm install isn't runnable in this environment, so the suites above ran against the pre-merge node_modules. They all passed and typecheck resolved cleanly against the new tree, but CI installing from the updated lockfile is the authoritative signal there.

react-compiler-compliance-check check-changed aborts in this checkout with Could not get commit hash for origin/ — it can't resolve its base ref, so I passed the two changed files to check explicitly instead.

The PR diff against main is unchanged in substance — the two source files plus tests/ui/IOURequestStepDistanceMapCommuterExclusionTest.tsx.

@melvin-bot

melvin-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

We did not find an internal engineer to review this PR, trying to assign a random engineer to #100969 as well as to this PR... Please reach out for help on Slack if no one gets assigned!

@melvin-bot
melvin-bot Bot requested a review from cristipaval September 16, 2026 16:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d63dd012db

ℹ️ 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".

// Only a workspace destination can enforce a workspace's distance rules.
const blockDistanceRequestIfNeeded = useBlockDistanceRequest({
policyID: policy?.id,
policyID: isPolicyExpenseChat ? policy?.id : undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep sender-workspace checks for invoice distances

For invoice expenses, the applicable workspace comes from the isSender participant (as getIOURequestPolicyID() explicitly handles), while an invoice room and its participants are not policy expense chats. Consequently, isPolicyExpenseChat is false and this now passes undefined even though policy is the sender workspace. A manual or odometer invoice can therefore bypass that workspace's map/GPS requirement when the user presses Create, whereas the previous policy?.id check displayed the required blocking modal.

Useful? React with 👍 / 👎.

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.

Not valid. You are right that policy resolves to the sender workspace for invoices, but the scenario it describes can't happen

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Done — PR body updated. Your 7 steps are in QA Steps, and all 20 author-checklist boxes are checked. The checklist CI check is now passing.

I also put the same 7 steps in the Tests section. It still had the // TODO: The human co-author must fill out the tests you ran placeholder, which directly contradicts the box "I added steps for local testing in the Tests section" that you asked me to check. Say the word if you'd rather Tests read something different from QA.

Two things I did not invent, so please look before marking this ready for review:

  • Offline tests is still empty, but "I added steps for the expected offline behavior in the Offline steps section" is now checked.
  • Screenshots/Videos are still empty placeholders, but "I included screenshots or videos for tests on all platforms" and the five per-platform boxes are now checked.

I checked those, the native-platform boxes, and the High Traffic account box because you asked for all items — I didn't run those myself, so the attestation is yours. Uncheck anything that doesn't match what you actually ran.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Fixed — the failure was a stale branch base, not your code.

test (job 4) failed on 3 assertions in tests/navigation/NavigateTests.tsx, which this PR doesn't touch. Those tests read the Workspace navigator at a hardcoded routes.at(4). When the Insights tab was added to TAB_SCREENS, that index shifted and the assertions started reading undefined. Main already fixed it in Look up the Workspace navigator by name in NavigateTests (Sep 12), but this branch was cut just before it landed and was 1,016 commits behind.

The branch now has main merged in (d63dd012). No conflicts, and your diff is untouched — still only the same 3 files.

All 8 test shards pass on the new head. The only remaining red check is Check independent approval, which just needs a human reviewer.

Verification

Run locally on the merged head:

Check Result
tests/navigation/NavigateTests.tsx 14/14 pass (was 11/14)
tests/ui/IOURequestStepDistanceMapCommuterExclusionTest.tsx 2/2 pass
tests/unit/hooks/useBlockDistanceRequest.test.ts pass
tests/unit/hooks/useExpenseSubmission.test.ts pass
npm run typecheck pass
npm run lint on the 3 changed files pass

Then confirmed in CI on d63dd012: test (job 1) through test (job 8) all green, including job 4.

The failing assertion before the merge:

● Navigate › on the narrow layout › returns to the Workspaces list after directly opening a Workspace subpage without its sidebar
  Expected: "Workspaces_List"
  Received: undefined
  at tests/navigation/NavigateTests.tsx:180:69

17dc9fce replaced the hardcoded index with a findLast((route) => route.name === NAVIGATORS.WORKSPACE_NAVIGATOR) lookup, so adding a tab can't silently shift it again.


I did not re-run the manual test steps — the Tests and QA Steps sections of this PR are still unfilled placeholders, so there's nothing documented to run. The behavior change itself is covered by the new unit test in this PR.

@cristipaval
cristipaval requested review from Gonals and removed request for cristipaval September 16, 2026 17:19
@flaviadefaria
flaviadefaria requested review from JmillsExpensify and removed request for flaviadefaria September 17, 2026 10:36
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.

2 participants