From 49013de0651ba528f1d1aa67f6d822ce138752cd Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 11 Aug 2026 21:56:53 +0100 Subject: [PATCH 1/3] fix: allow editing existing distance requests --- src/hooks/useCommuterExclusionGuard.ts | 24 ++++++++++++++++--- .../step/IOURequestStepDistanceManual.tsx | 1 + .../step/IOURequestStepDistanceOdometer.tsx | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/hooks/useCommuterExclusionGuard.ts b/src/hooks/useCommuterExclusionGuard.ts index e1ba09a158d1..6458f7eed865 100644 --- a/src/hooks/useCommuterExclusionGuard.ts +++ b/src/hooks/useCommuterExclusionGuard.ts @@ -22,6 +22,9 @@ type UseCommuterExclusionGuardParams = { /** Whether the current flow is for an odometer distance request */ isOdometerDistanceRequest?: boolean; + + /** Whether an existing distance request is being edited */ + isEditingExistingDistanceRequest?: boolean; }; type PoliciesWithCommuterExclusions = Record; @@ -43,7 +46,12 @@ const policiesWithCommuterExclusionsSelector = (policies: OnyxCollection * is supported (because exclusions are computed from the mapped route) and returns * true so callers can early return. */ -function useCommuterExclusionGuard({policyID, isManualDistanceRequest = false, isOdometerDistanceRequest = false}: UseCommuterExclusionGuardParams) { +function useCommuterExclusionGuard({ + policyID, + isManualDistanceRequest = false, + isOdometerDistanceRequest = false, + isEditingExistingDistanceRequest = false, +}: UseCommuterExclusionGuardParams) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {showConfirmModal} = useConfirmModal(); @@ -53,7 +61,7 @@ function useCommuterExclusionGuard({policyID, isManualDistanceRequest = false, i return useCallback( (...policyIDsToCheck: [string?]) => { const policyIDToCheck = policyIDsToCheck.length > 0 ? policyIDsToCheck[0] : policyID; - if (!isManualDistanceRequest && !isOdometerDistanceRequest) { + if (isEditingExistingDistanceRequest || (!isManualDistanceRequest && !isOdometerDistanceRequest)) { return false; } @@ -76,7 +84,17 @@ function useCommuterExclusionGuard({policyID, isManualDistanceRequest = false, i return true; }, - [policyID, isManualDistanceRequest, isOdometerDistanceRequest, policiesWithCommuterExclusions, showConfirmModal, translate, styles, illustrations.HouseWithMap], + [ + policyID, + isManualDistanceRequest, + isOdometerDistanceRequest, + isEditingExistingDistanceRequest, + policiesWithCommuterExclusions, + showConfirmModal, + translate, + styles, + illustrations.HouseWithMap, + ], ); } diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 3011d6aac64b..c5881f46cf46 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -151,6 +151,7 @@ function IOURequestStepDistanceManual({ const blockManualOrOdometerDistanceRequestIfNeeded = useCommuterExclusionGuard({ policyID: report?.policyID ?? (shouldAutoReportToDefaultWorkspace ? defaultExpensePolicy?.id : undefined), isManualDistanceRequest: true, + isEditingExistingDistanceRequest: isEditing, }); // to make sure the correct distance amount and unit will be shown we use distance unit diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index e87b784de7a4..9b3eb213990f 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -163,6 +163,7 @@ function IOURequestStepDistanceOdometer({ const blockManualOrOdometerDistanceRequestIfNeeded = useCommuterExclusionGuard({ policyID: report?.policyID ?? (shouldAutoReportToDefaultWorkspace ? defaultExpensePolicy?.id : undefined), isOdometerDistanceRequest: true, + isEditingExistingDistanceRequest: isEditing, }); const mileageRate = DistanceRequestUtils.getRate({ From 3f4fc42c0ea418fe7e0f43cb05a66a1eaf67a784 Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Tue, 11 Aug 2026 21:57:09 +0100 Subject: [PATCH 2/3] test: cover historical distance edits --- tests/unit/TransactionUtilsTest.ts | 52 +++++++++++++++++++ .../hooks/useCommuterExclusionGuard.test.ts | 24 +++++++++ 2 files changed, 76 insertions(+) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 6c83267d4448..c289fa7ca460 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -729,6 +729,58 @@ describe('TransactionUtils', () => { expect(updatedTransaction.modifiedMerchant).not.toContain('20'); }); + it('does not apply a newly enabled commuter exclusion when a historical manual distance is changed', () => { + const fakePolicy: Policy = { + ...createRandomPolicy(0), + commuterExclusions: { + method: CONST.POLICY.COMMUTER_EXCLUSION_METHOD.FIXED_DISTANCE, + fixedDistance: 3, + fixedDistanceUnit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + }, + customUnits: { + distance: { + name: CONST.CUSTOM_UNITS.NAME_DISTANCE, + customUnitID: 'distance', + rates: { + default: { + customUnitRateID: '1', + currency: CONST.CURRENCY.USD, + rate: 1, + }, + }, + attributes: { + unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + }, + }, + }, + }; + const transaction = generateTransaction({ + iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE_MANUAL, + comment: { + customUnit: { + distanceUnit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, + quantity: 10, + }, + }, + currency: CONST.CURRENCY.USD, + }); + + const updatedTransaction = TransactionUtils.getUpdatedTransaction({ + transaction, + isFromExpenseReport: false, + policy: fakePolicy, + transactionChanges: {distance: 20}, + personalPolicyOutputCurrency: undefined, + getCurrencyDecimals, + getCurrencySymbol, + }); + + expect(updatedTransaction.comment?.customUnit?.quantity).toBe(20); + expect(updatedTransaction.comment?.customUnit?.commuterExclusion).toBeUndefined(); + expect(updatedTransaction.comment?.customUnit?.reimbursableDistance).toBeUndefined(); + expect(updatedTransaction.modifiedAmount).toBe(20); + }); + it('converts commuter exclusion data when the distance rate unit is changed', () => { // Given a policy with a 3 mile fixed distance commuter exclusion and a kilometer rate const fakePolicy: Policy = { diff --git a/tests/unit/hooks/useCommuterExclusionGuard.test.ts b/tests/unit/hooks/useCommuterExclusionGuard.test.ts index af4dbd5958f5..633a2385d34b 100644 --- a/tests/unit/hooks/useCommuterExclusionGuard.test.ts +++ b/tests/unit/hooks/useCommuterExclusionGuard.test.ts @@ -137,4 +137,28 @@ describe('useCommuterExclusionGuard', () => { expect(result.current('policy_forced')).toBe(false); expect(mockShowConfirmModal).not.toHaveBeenCalled(); }); + + it('does not block editing an existing manual distance request', async () => { + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}policy_forced`, { + id: 'policy_forced', + name: 'Forced workspace', + commuterExclusions: { + method: 'fixedDistance', + fixedDistance: 1, + fixedDistanceUnit: 'mi', + }, + }); + await waitForBatchedUpdates(); + + const {result} = renderHook(() => + useCommuterExclusionGuard({ + policyID: 'policy_forced', + isManualDistanceRequest: true, + isEditingExistingDistanceRequest: true, + }), + ); + + expect(result.current()).toBe(false); + expect(mockShowConfirmModal).not.toHaveBeenCalled(); + }); }); From a0c92984653d5336e20f63aec27f8240ae5f77cb Mon Sep 17 00:00:00 2001 From: Fedi Rajhi Date: Wed, 2 Sep 2026 14:17:37 +0100 Subject: [PATCH 3/3] Fix TransactionUtils test helper references --- tests/unit/TransactionUtilsTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index 7d688eb38979..f2a8b422f351 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -848,8 +848,8 @@ describe('TransactionUtils', () => { policy: fakePolicy, transactionChanges: {distance: 20}, personalPolicyOutputCurrency: undefined, - getCurrencyDecimals, - getCurrencySymbol, + getCurrencyDecimals: getCurrencyDecimalsLocal, + getCurrencySymbol: getCurrencySymbolLocal, }); expect(updatedTransaction.comment?.customUnit?.quantity).toBe(20);