fix: Update preventSelfApproval on approvalMode change#84320
fix: Update preventSelfApproval on approvalMode change#84320NikkiWines merged 6 commits intoExpensify:mainfrom
Conversation
src/libs/actions/Policy/Policy.ts
Outdated
| pendingFields: { | ||
| approvalMode: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, | ||
| preventSelfApproval: | ||
| approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL && policy?.preventSelfApproval |
There was a problem hiding this comment.
❌ CONSISTENCY-3 (docs)
The compound condition approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL && policy?.preventSelfApproval is duplicated in the optimistic data (line 925) and the success data (line 963), and a partial form also appears on line 929. Extracting this to a named boolean improves readability and ensures the logic stays consistent if the condition ever changes.
Suggested fix -- add a local variable before the optimisticData declaration:
const shouldResetPreventSelfApproval = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL && !!policy?.preventSelfApproval;Then use it in each Onyx update:
// optimistic pendingFields
preventSelfApproval: shouldResetPreventSelfApproval
? CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE
: policy?.pendingFields?.preventSelfApproval,
// optimistic value
preventSelfApproval: shouldResetPreventSelfApproval ? false : policy?.preventSelfApproval,
// success pendingFields
preventSelfApproval: shouldResetPreventSelfApproval ? null : policy?.pendingFields?.preventSelfApproval,Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
@Eskalifer1 please also test failure case with Simulate failing network requests toggled on
There was a problem hiding this comment.
Done! Here is the recording:
2026-03-05.23.03.23.mp4
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e6855bd998
ℹ️ 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".
src/libs/actions/Policy/Policy.ts
Outdated
| preventSelfApproval: policy?.preventSelfApproval, | ||
| pendingFields: {approvalMode: null, preventSelfApproval: policy?.pendingFields?.preventSelfApproval}, |
There was a problem hiding this comment.
Avoid rolling back preventSelfApproval with stale snapshot
This rollback now always writes preventSelfApproval and its pending flag from the policy snapshot captured when setWorkspaceApprovalMode() started, even when the approval-mode change did not modify that field. If a user enables approvals and then changes "prevent self-approvals" before the first request resolves, a failure of the approval-mode request will overwrite the newer toggle state/pending status with stale values from before that later action. Previously this failure path only touched approvalMode, so the newer change was preserved.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not sure about that, since we want to rollback value that we have before if request fails
There was a problem hiding this comment.
Same here. rollback to null only when shouldResetPreventSelfApproval is true
There was a problem hiding this comment.
Sorry, I don't quite understand what needs to be done here. Could you please send me a suggestion change? It's not very clear what value null needs to be rolled back to. Did you perhaps mean this code:
- preventSelfApproval: policy?.preventSelfApproval,
+...(shouldResetPreventSelfApproval && {preventSelfApproval: policy?.preventSelfApproval}),
-pendingFields: {approvalMode: null, preventSelfApproval: policy?.pendingFields?.preventSelfApproval},
+pendingFields: {approvalMode: null, ...(shouldResetPreventSelfApproval && {preventSelfApproval: policy?.pendingFields?.preventSelfApproval})},There was a problem hiding this comment.
I have updated PR with a code i have send above, let me know if that's wrong!
JmillsExpensify
left a comment
There was a problem hiding this comment.
Testing steps look good for product.
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / Safariweb.mov |
src/libs/actions/Policy/Policy.ts
Outdated
| preventSelfApproval: policy?.preventSelfApproval, | ||
| pendingFields: {approvalMode: null, preventSelfApproval: policy?.pendingFields?.preventSelfApproval}, |
There was a problem hiding this comment.
Same here. rollback to null only when shouldResetPreventSelfApproval is true
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
src/libs/actions/Policy/Policy.ts
Outdated
| approver: policy?.approver, | ||
| approvalMode: policy?.approvalMode, | ||
| pendingFields: {approvalMode: null}, | ||
| pendingFields: {approvalMode: null, ...(shouldResetPreventSelfApproval && {preventSelfApproval: policy?.pendingFields?.preventSelfApproval})}, |
There was a problem hiding this comment.
| pendingFields: {approvalMode: null, ...(shouldResetPreventSelfApproval && {preventSelfApproval: policy?.pendingFields?.preventSelfApproval})}, | |
| pendingFields: {approvalMode: null, ...(shouldResetPreventSelfApproval && {preventSelfApproval: null})}, |
There was a problem hiding this comment.
I have a question about this because I don't think it should work this way, and I also added a test case for this scenario, and if this change is applied, the test will fail. In my opinion, it should work like this:
If preventSelfApproval has a pendingFields value (e.g., Update), and the user disables Approvals, we update pendingFields.preventSelfApproval. but if the request fails, then in failure I think we want to return the value of pendingFields.preventSelfApproval that was in the field before the user tried to disable Approvals.
In my opinion, we need to set not null, but the value that was there before.
Of course, this is a super edge case and most likely will never happen, but I would leave this code that returns the previous value. What do you think about it?
There was a problem hiding this comment.
ok that makes sense.
Failing requests 2 times in a row. Super edge case.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 438672f9c9
ℹ️ 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 Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
NikkiWines
left a comment
There was a problem hiding this comment.
looks good, one small request
| const nextStepFailureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.NEXT_STEP>> = []; | ||
| const shouldUpdateNextSteps = additionalData?.reportNextSteps != null && additionalData?.transactionViolations != null && additionalData?.betas != null; | ||
|
|
||
| const shouldResetPreventSelfApproval = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL && !!policy?.preventSelfApproval; |
There was a problem hiding this comment.
I think this variable name is a little unclear, can we either adjust it or add a comment expalin what "reset" means in this case?
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 @NikkiWines 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! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/NikkiWines in version: 9.3.36-0 🚀
|
|
🚀 Deployed to production by https://github.com/luacmartins in version: 9.3.36-10 🚀
|
Explanation of Change
In this PR, the logic for disabling
prevent self approvalwas added when the user disablesApprovals.Fixed Issues
$#82197
PROPOSAL:#82197 (comment)
Tests
Precondition: Have created workspace
prevent self approvalis also turned offOffline tests
Same as tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, 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.ScrollViewcomponent to make it scrollable when more elements are added to the page.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
82197-android-native.mov
Android: mWeb Chrome
82197-android-web.mov
iOS: Native
82197-ios-native.mp4
iOS: mWeb Safari
82197-ios-web.mov
MacOS: Chrome / Safari
82197-web.mov