You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Wrong distance at confirm page when create from FAB for ws set different unit with default ws #100973
Switching the recipient to another workspace converts the odometer expense's stored distance between units, but the odometer readings it was derived from are never converted — so the two screens disagree.
When you pick a participant on a different workspace, the confirmation step swaps in that workspace's mileage rate via src/pages/iou/request/step/IOURequestStepConfirmation.tsx:479. setCustomUnitRateID then rewrites both comment.customUnit.distanceUnitandcomment.customUnit.quantity, converting the quantity into the new unit at src/libs/actions/IOU/MoneyRequest.ts:783-790. That conversion is correct for map/manual distance, but wrong for odometer: there the quantity is just end reading − start reading, a plain number that belongs to whatever the vehicle's odometer counts, not a measurement to re-scale.
The odometer screen still derives its total live from the untouched readings in comment.odometerStart / comment.odometerEnd (IOURequestStepDistanceOdometer.tsx:272-280), while the confirmation page reads the converted quantity (useDistanceRequestState.ts:97). With readings 1000 → 1100 saved on a km workspace and then moved to a mi workspace: confirmation shows ~62.14 mi, the Distance page shows 100 mi. The amount and merchant are seeded from the same converted quantity, so the expense total is wrong too — not just the label.
What changes do you think we should make in order to solve the problem?
Add the same odometer exemption to setCustomUnitRateID: keep updating distanceUnit to the destination workspace's unit, but skip the quantity conversion when the transaction is an odometer expense, so quantity stays equal to end reading − start reading. That makes the confirmation row, the Distance page, the merchant and the amount all agree, and matches the behaviour already implemented for the rate picker.
Unit test to add alongside tests/unit/useParticipantSubmissionTest.ts: calling setCustomUnitRateID with a km→mi rate change leaves quantity untouched for an odometer transaction, and still converts it for a map/manual distance transaction.
What alternative solutions did you explore? (Optional)
Converting the odometer readings themselves along with the quantity. Rejected — the readings are what the tester physically entered from the vehicle, and rewriting them would make the odometer screen contradict the receipt images. It also contradicts the existing rate-picker behaviour.
Notes and confidence
Confidence: medium. The code path is traced end to end, but I could not drive NewDot in a browser in this run, so the numeric mismatch is not reproduced live.
Both screens resolve the display unit the same way, so after the switch they agree on the unit and differ only in the number — which matches the report.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/100973) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
Please mark it as "Ready for review" when it is ready for review.
What changed:setCustomUnitRateID now skips the quantity conversion for odometer expenses, matching the guard setMoneyRequestDistanceRate already had.
Switching the recipient to a workspace with a different distance unit was re-scaling comment.customUnit.quantity into the new unit, but for odometer expenses that quantity is just end reading − start reading — a count off the vehicle, not a measurement. The readings in comment.odometerStart / comment.odometerEnd were left alone, so the Distance page kept deriving 100 live from them while the confirmation page showed the converted ~62.14. The amount and merchant are seeded from the same quantity, so the total was wrong too, not just the label.
distanceUnit still updates to the destination workspace's unit, so both screens now agree on the unit and the number.
Unit tests added — a new setCustomUnitRateID block in tests/actions/IOU/MoneyRequestSettersTest.ts: a km→mi rate change still converts the quantity for a normal distance expense, and leaves it untouched for an odometer one.
AI tests run locally — npm run typecheck, npm run lint-changed, and npm run spell-changed all passed. npm test across tests/actions/IOU plus the distance and odometer suites passed: 34 suites, 1040 tests.
⚠️Not verified in a browser. The automated web session for this run rendered a blank page, so I could not drive the two-workspace repro end to end. The fix is covered by unit tests only — please run the repro steps from the issue before marking the PR ready for review.
@jayeshmangwani, could you update the App PR so that we send a new odometerUnit param with CreateDistanceRequest, TrackExpense, and UpdateMoneyRequestDistance.
We could have the backend use the client's distance directly, but the backend treats the odometer readings as the source of truth, so that they always match the photos.
Please re-state the problem that we are trying to solve in this issue.
When creating an odometer distance expense from the global FAB (Create expense > Distance > Odometer) and selecting a workspace whose distance unit differs from the default workspace's unit (e.g. km vs mi), the confirmation page converts the stored distance quantity into the new unit while leaving odometerStart and odometerEnd untouched, causing the confirmation page distance to disagree with endReading - startReading.
What is the root cause of that problem?
In src/pages/iou/request/step/IOURequestStepConfirmation/index.tsx (line 479), switching the recipient to a workspace with a different mileage rate invokes setCustomUnitRateID.
In src/libs/actions/IOU/MoneyRequest.ts (lines 783-790), setCustomUnitRateID converts comment.customUnit.quantity from the previous distanceUnit to the new workspace's distanceUnit (convertDistanceUnit). While converting physical distance is valid for Map/Manual distance requests, for Odometer distance requests (isOdometerDistanceRequest(transaction)), quantity is strictly the numeric delta odometerEnd - odometerStart entered by the user (src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx:272-280), so scaling quantity by 1.609 or 0.621 makes quantity no longer equal odometerEnd - odometerStart.
What changes do you think we should make in order to solve the problem?
In src/libs/actions/IOU/MoneyRequest.ts (setCustomUnitRateID, lines 783-790), skip the convertDistanceUnit rescaling of comment.customUnit.quantity when isOdometerDistanceRequest(transaction) is true (preserving quantity = odometerEnd - odometerStart while updating distanceUnit and customUnitRateID to the selected workspace's rate).
What alternative solutions did you explore? (Optional)
None — odometer readings are raw numeric counter values entered by the user, so odometerEnd - odometerStart must remain invariant when switching workspaces.
Activity
applause-bot commentedon Sep 11, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/42beaf7e733e10f52520f9febcd39e39d6622d36bed64176e225f0db1f93eb43
MelvinBot commentedon Sep 11, 2026
Proposal
What is the root cause of that problem?
Switching the recipient to another workspace converts the odometer expense's stored distance between units, but the odometer readings it was derived from are never converted — so the two screens disagree.
When you pick a participant on a different workspace, the confirmation step swaps in that workspace's mileage rate via
src/pages/iou/request/step/IOURequestStepConfirmation.tsx:479.setCustomUnitRateIDthen rewrites bothcomment.customUnit.distanceUnitandcomment.customUnit.quantity, converting the quantity into the new unit atsrc/libs/actions/IOU/MoneyRequest.ts:783-790. That conversion is correct for map/manual distance, but wrong for odometer: there the quantity is justend reading − start reading, a plain number that belongs to whatever the vehicle's odometer counts, not a measurement to re-scale.The odometer screen still derives its total live from the untouched readings in
comment.odometerStart/comment.odometerEnd(IOURequestStepDistanceOdometer.tsx:272-280), while the confirmation page reads the converted quantity (useDistanceRequestState.ts:97). With readings 1000 → 1100 saved on a km workspace and then moved to a mi workspace: confirmation shows ~62.14 mi, the Distance page shows 100 mi. The amount and merchant are seeded from the same converted quantity, so the expense total is wrong too — not just the label.The same code already knows odometer must be exempt:
setMoneyRequestDistanceRateguards the identical conversion with!isOdometerDistanceRequest(transaction).setCustomUnitRateIDis missing that guard.What changes do you think we should make in order to solve the problem?
Add the same odometer exemption to
setCustomUnitRateID: keep updatingdistanceUnitto the destination workspace's unit, but skip the quantity conversion when the transaction is an odometer expense, soquantitystays equal toend reading − start reading. That makes the confirmation row, the Distance page, the merchant and the amount all agree, and matches the behaviour already implemented for the rate picker.Unit test to add alongside
tests/unit/useParticipantSubmissionTest.ts: callingsetCustomUnitRateIDwith a km→mi rate change leavesquantityuntouched for an odometer transaction, and still converts it for a map/manual distance transaction.What alternative solutions did you explore? (Optional)
Converting the odometer readings themselves along with the quantity. Rejected — the readings are what the tester physically entered from the vehicle, and rewriting them would make the odometer screen contradict the receipt images. It also contradicts the existing rate-picker behaviour.
Notes and confidence
Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/100973)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
melvin-bot commentedon Sep 11, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @jayeshmangwani (
External)melvin-bot commentedon Sep 11, 2026
Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.
jayeshmangwani commentedon Sep 14, 2026
@MelvinBot implement this
MelvinBot commentedon Sep 14, 2026
🤖 Draft PR created: #101050
Please mark it as "Ready for review" when it is ready for review.
What changed:
setCustomUnitRateIDnow skips the quantity conversion for odometer expenses, matching the guardsetMoneyRequestDistanceRatealready had.Switching the recipient to a workspace with a different distance unit was re-scaling
comment.customUnit.quantityinto the new unit, but for odometer expenses that quantity is justend reading − start reading— a count off the vehicle, not a measurement. The readings incomment.odometerStart/comment.odometerEndwere left alone, so the Distance page kept deriving 100 live from them while the confirmation page showed the converted ~62.14. The amount and merchant are seeded from the same quantity, so the total was wrong too, not just the label.distanceUnitstill updates to the destination workspace's unit, so both screens now agree on the unit and the number.Code and testing details
The fix —
src/libs/actions/IOU/MoneyRequest.ts:778, adding&& !isOdometerDistanceRequest(transaction)toshouldUpdateQuantity. The same exemption already existed one function over atMoneyRequest.ts:939.Why the two screens disagreed — the odometer screen computes its total live from the readings at
IOURequestStepDistanceOdometer.tsx:270-280, while the confirmation page reads the stored quantity viauseDistanceRequestState.ts:97.Unit tests added — a new
setCustomUnitRateIDblock intests/actions/IOU/MoneyRequestSettersTest.ts: a km→mi rate change still converts the quantity for a normal distance expense, and leaves it untouched for an odometer one.AI tests run locally —
npm run typecheck,npm run lint-changed, andnpm run spell-changedall passed.npm testacrosstests/actions/IOUplus the distance and odometer suites passed: 34 suites, 1040 tests.melvin-bot commentedon Sep 17, 2026
@jayeshmangwani Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Sep 21, 2026
@jayeshmangwani 6 days overdue. This is scarier than being forced to listen to Vogon poetry!
jayeshmangwani commentedon Sep 23, 2026
Update: We’re discussing the expected behavior in the draft PR #101050 and trying to reach a consensus.
15 remaining items
Julesssss commentedon Oct 16, 2026
Clearing up the blocking discussion here.
melvin-bot commentedon Oct 19, 2026
@jayeshmangwani Whoops! This issue is 2 days overdue. Let's get this updated quick!
jayeshmangwani commentedon Oct 20, 2026
Not overdue. There’s a slight inconsistency between FE and BE, and we’re checking #101050 whether a BE fix is also needed to fix the issue.
Julesssss commentedon Oct 21, 2026
As @jayeshmangwani mentioned we need a backend fix to accompany the App fix. Looking for contributor.
Julesssss commentedon Oct 21, 2026
Backend issue here (internal).
@jayeshmangwani, could you update the App PR so that we send a new
odometerUnitparam withCreateDistanceRequest,TrackExpense, andUpdateMoneyRequestDistance.We could have the backend use the client's distance directly, but the backend treats the odometer readings as the source of truth, so that they always match the photos.
Kabeer099 commentedon Oct 29, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When creating an odometer distance expense from the global FAB (
Create expense > Distance > Odometer) and selecting a workspace whose distance unit differs from the default workspace's unit (e.g.kmvsmi), the confirmation page converts the stored distancequantityinto the new unit while leavingodometerStartandodometerEnduntouched, causing the confirmation page distance to disagree withendReading - startReading.What is the root cause of that problem?
src/pages/iou/request/step/IOURequestStepConfirmation/index.tsx(line 479), switching the recipient to a workspace with a different mileage rate invokessetCustomUnitRateID.src/libs/actions/IOU/MoneyRequest.ts(lines 783-790),setCustomUnitRateIDconvertscomment.customUnit.quantityfrom the previousdistanceUnitto the new workspace'sdistanceUnit(convertDistanceUnit). While converting physical distance is valid for Map/Manual distance requests, for Odometer distance requests (isOdometerDistanceRequest(transaction)),quantityis strictly the numeric deltaodometerEnd - odometerStartentered by the user (src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx:272-280), so scalingquantityby1.609or0.621makesquantityno longer equalodometerEnd - odometerStart.What changes do you think we should make in order to solve the problem?
src/libs/actions/IOU/MoneyRequest.ts(setCustomUnitRateID,lines 783-790), skip theconvertDistanceUnitrescaling ofcomment.customUnit.quantitywhenisOdometerDistanceRequest(transaction)istrue(preservingquantity = odometerEnd - odometerStartwhile updatingdistanceUnitandcustomUnitRateIDto the selected workspace's rate).What alternative solutions did you explore? (Optional)
None — odometer readings are raw numeric counter values entered by the user, so
odometerEnd - odometerStartmust remain invariant when switching workspaces.