fix: preserve lightning bolt icon after government rate currency change - #100607
Conversation
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
Reviewer Checklist
Screenshots/VideosiOS: HybridAppCleanShot.2026-09-10.at.13.20.21.1.mp4 |
|
@nabi-ebrahimi Please update the QA Steps section |
|
@neil-marcellini, please feel free to review the code when you have a chance. I found an edge case that I’ll look into and will keep you updated. Thanks! |
|
@suneox I was able to reproduce a rounding edge case on Web:
It looks like both the original government rate and the manually edited rate round to the same converted value, so the edited rate is classified as unmodified again. Could you confirm whether this is expected/acceptable behavior for this PR? |
| * A date omitted on both sides counts as a match; a date omitted on only one side does not. | ||
| */ | ||
| function isGovernmentRateUnmodified(rate: Rate): boolean { | ||
| function getGovernmentRateAmountForUnit(governmentRateAmount: number, sourceRateID: string | undefined, currentUnit: Unit | undefined): number { |
There was a problem hiding this comment.
The comment above this function is supposed to be for isGovernmentRateUnmodified. Please move it.
| const convertedAmount = | ||
| snapshotUnit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS | ||
| ? governmentRateAmount * CONST.CUSTOM_UNITS.MILES_TO_KILOMETERS | ||
| : governmentRateAmount / CONST.CUSTOM_UNITS.MILES_TO_KILOMETERS; |
There was a problem hiding this comment.
NAB: The conversion looked backwards to me at first because I usually think of converting distances, but since we're converting rates it's correct. It might be helpful to add a comment about this.
// If a rate is expressed in cents / km, to convert to cents / mi we multiply by a conversion factor which cancels out the kilometers: cents / km * km / mi = cents / mi. Do the opposite for a rate in cents / mi.
Yes this is expected and acceptable. The only solution is to always store rates in meters and never convert them, but that's a really big change that I don't think is worth doing. |
|
@neil-marcellini, I’ve addressed your feedback. Could you please take another look when you have a chance? Thanks! |
@suneox I checked this off for you. I don't think it applies here and it was preventing us from merging. |
|
🚧 neil-marcellini has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/neil-marcellini in version: 9.4.76-0 🚀
|
|
Yes — help site changes were required. Draft PR: #100906 The change itself is a bug fix, so it doesn't add a documented feature. But reviewing it against
What the new sections coverHow to tell which distance rates Expensify created automatically — the lightning bolt icon and its tooltip This rate is auto-generated. The icon is removed only when an admin edits the rate amount, Start date, or End date. A unit conversion Expensify performs on its own is not an edit, which is precisely the distinction What happens to distance rates when you change the workspace default currency — a new government rate is added for the new country; the workspace Unit switches to the unit that country publishes in (Miles for the United States and Great Britain, Kilometers for Canada and Australia, per Verification. Every label was checked against the live web UI on a Control workspace, not inferred from code: page heading Distance rates, button Add rate, settings rows Unit, Default category, Auto-update government rates, Require GPS or map entry, Track tax, unit options Kilometers and Miles, and the toggle description "Automatically create new rates when the United States publishes new guidance."
One thing I couldn't do: assign you to #100906. GitHub rejects the assignment because your permission on @nabi-ebrahimi, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR view run · no recording available |
|
🚀 Deployed to production by https://github.com/mountiny in version: 9.4.76-1 🚀
Bundle Size Analysis (Sentry): |















Explanation of Change
When a workspace changes its default currency, existing auto-generated government rates may be displayed using a different distance unit from the one in the original government rate snapshot.
Previously, the app compared the displayed converted rate directly against the original government snapshot. Because the values could differ after the system converted the rate to the workspace's current distance unit, the rate could incorrectly appear to have been manually edited and lose its auto-generated lightning bolt icon.
This change converts the government reference rate to the workspace's current distance unit before performing the comparison. This ensures that rates changed only by the system continue to be recognized as auto-generated, while rates manually edited by an admin still lose the lightning bolt icon as expected.
Fixed Issues
$ #98962
PROPOSAL: #98962 (comment)
Tests
Create a Control workspace and set its default currency to CAD.
Enable Distance rates and turn on Auto-update government rates.
Verify that the Canadian government rate is automatically created with the lightning bolt icon.
Change the workspace default currency from CAD to USD.
Return to Distance rates.
Verify that:
Offline tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
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-09-08.at.9.50.00.PM.mp4
Android: mWeb Chrome
Screen.Recording.2026-09-08.at.9.57.06.PM.mp4
iOS: Native
Screen.Recording.2026-09-08.at.10.20.51.PM.mp4
iOS: mWeb Safari
Screen.Recording.2026-09-08.at.10.09.36.PM.mov
MacOS: Chrome / Safari
Screen.Recording.2026-09-08.at.9.34.59.PM.mov