Skip to content

[$250] Downgrading to Collect doesn’t clear Control features #61612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
4 of 16 tasks
m-natarajan opened this issue May 7, 2025 · 22 comments
Open
4 of 16 tasks

[$250] Downgrading to Collect doesn’t clear Control features #61612

m-natarajan opened this issue May 7, 2025 · 22 comments
Assignees
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor Help Wanted Apply this label when an issue is open to proposals by contributors Reviewing Has a PR in review

Comments

@m-natarajan
Copy link

m-natarajan commented May 7, 2025

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Version Number: 9.1.41-0
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Expensify/Expensify Issue URL:
Issue reported by: @JmillsExpensify
Slack conversation (hyperlinked to channel name): #Expensify Bugs

Action Performed:

  1. Create a Collect workspace
  2. Navigate to Workflows in the workspace editor
  3. Click Add approval workflow
  4. Get the Control update modal, confirm
  5. Add custom approval workflows
  6. Navigate to Overview in the workspace editor
  7. Click Plan and choose Collect
  8. Confirm the downgrade

Expected Result:

Added approval flow removed or not displayed

Actual Result:

Existing custom workflows still appear in the workspace editor, even though they should have been removed and no longer apply.

Workaround:

Unknown

Platforms:

Select the officially supported platforms where the issue was reproduced:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop
Platforms Tested: On which of our officially supported platforms was this issue tested:
  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Add any screenshot/video evidence
Recording.1198.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021920211342191991857
  • Upwork Job ID: 1920211342191991857
  • Last Price Increase: 2025-05-21
Issue OwnerCurrent Issue Owner: @carlosmiceli
@m-natarajan m-natarajan added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels May 7, 2025
Copy link

melvin-bot bot commented May 7, 2025

Current assignee @JmillsExpensify is eligible for the Bug assigner, not assigning anyone new.

@JmillsExpensify JmillsExpensify added the External Added to denote the issue can be worked on by a contributor label May 7, 2025
@melvin-bot melvin-bot bot changed the title Downgrading to Collect doesn’t clear Control features [$250] Downgrading to Collect doesn’t clear Control features May 7, 2025
Copy link

melvin-bot bot commented May 7, 2025

Job added to Upwork: https://www.upwork.com/jobs/~021920211342191991857

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label May 7, 2025
Copy link

melvin-bot bot commented May 7, 2025

Triggered auto assignment to Contributor-plus team member for initial proposal review - @Pujan92 (External)

@JmillsExpensify
Copy link

For the solutions, let's make sure that we are clearing related workspace features whenever an admin downgrades from Control to Collect. That minimally includes:

  • Workflows > Any approval workflow aside from the default/everyone workflow
  • Rules on categories and members
  • Report fields
  • Per diem

Based on testing I believe the main loophole is approval workflows, though I think we need to confirm as part of this issue.

@JoshIri360
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

When a workspace is downgraded from a "Control" plan to a "Collect" plan, the user interface does not immediately and fully reflect this change. Features exclusive to the Control plan (like custom approval workflows, advanced rules, report fields, and per diem) may still appear in the UI or appear in an inconsistent state. This is because the frontend data (in Onyx) is not comprehensively updated optimistically during the downgrade action.

What is the root cause of that problem?

The frontend root cause is that the downgradeToTeam action in src/libs/actions/Policy/Policy.ts does not clear all Control-plan-specific attributes from the policy object in its optimistic Onyx update. It primarily changes the policy type and isPendingDowngrade status, leaving other Control-specific data intact on the client-side until a full policy refresh occurs from the backend.

What changes do you think we should make in order to solve the problem?

We will modify the downgradeToTeam function in src/libs/actions/Policy/Policy.ts. The optimisticData payload sent to Onyx.METHOD.MERGE will be expanded to include a more comprehensive set of default "Collect" plan values for the policy object. This ensures that the UI immediately reflects the cleared state of Control-specific features.

Specifically, the optimisticData will be updated to:

  1. Reset approvalMode to the Collect plan's default (e.g., CONST.POLICY.APPROVAL_MODE.OPTIONAL) and clear any policy-level approver.
  2. Reset the rules object on the policy (e.g., policy.rules.approvalRules = [], policy.rules.expenseRules = []) to an empty or default state expected for Collect plans.
  3. Clear fieldList (e.g., policy.fieldList = {}) to optimistically remove custom report fields from the UI.
  4. Clear customUnits (e.g., policy.customUnits = {}) and any other per diem related configurations from the policy object.

The failureData will also be correspondingly updated to ensure that if the backend API call fails, these additional optimistically set fields are reverted to their state prior to the downgrade attempt, using the currentPolicy values.

--- a/src/libs/actions/Policy/Policy.ts
+++ b/src/libs/actions/Policy/Policy.ts
@@ -3718,22 +3718,51 @@
 
 function downgradeToTeam(policyID: string) {
-    const policy = getPolicy(policyID);
+    const currentPolicy = getPolicy(policyID); // Get current policy state from Onyx
+
+    // Define the state for a downgraded 'Collect' (formerly 'Team') plan
+    const downgradedPolicyValues: Partial<Policy> = {
+        type: CONST.POLICY.TYPE.TEAM, // This is the core change for plan type
+        isPendingDowngrade: true,
+
+        // 1. Reset Workflow features
+        approvalMode: CONST.POLICY.APPROVAL_MODE.OPTIONAL,
+        approver: '',
+
+        // 2. Reset Rules on categories and members
+        rules: {
+            approvalRules: [],
+            expenseRules: [],
+        },
+
+        // 3. Reset Report Fields
+        fieldList: {},
+
+        // 4. Reset Per Diem features
+        customUnits: {},
+        // perDiem: { // Example, actual structure might vary
+        //     enabled: false,
+        //     rates: {},
+        // },
+    };
+
     const optimisticData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
-            key: `policy_${policyID}`,
-            value: {
-                isPendingDowngrade: true,
-                type: CONST.POLICY.TYPE.TEAM,
-            },
+            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
+            value: downgradedPolicyValues,
         },
     ];
 
     const successData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
-            key: `policy_${policyID}`,
+            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
             value: {
                 isPendingDowngrade: false,
             },
@@ -3744,11 +3773,19 @@
     const failureData: OnyxUpdate[] = [
         {
             onyxMethod: Onyx.METHOD.MERGE,
-            key: `policy_${policyID}`,
+            key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
             value: {
                 isPendingDowngrade: false,
-                type: policy?.type,
+                // Revert to the original policy type
+                type: currentPolicy?.type,
+                // Revert all other optimistically changed fields
+                approvalMode: currentPolicy?.approvalMode,
+                approver: currentPolicy?.approver,
+                rules: currentPolicy?.rules,
+                fieldList: currentPolicy?.fieldList,
+                customUnits: currentPolicy?.customUnits,
+                // perDiem: currentPolicy?.perDiem, // Example
             },
         },
     ];

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

None.

What alternative solutions did you explore? (Optional)

Relying solely on backend changes for data clearing without comprehensive optimistic frontend updates.

@nkdengineer
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

Existing custom workflows still appear in the workspace editor, even though they should have been removed and no longer apply.

What is the root cause of that problem?

After we downgrade to the collect, the backend doesn't update employee data to clear the custom workflow and we also don't update this in optimistic data

What changes do you think we should make in order to solve the problem?

We should update:

  1. Update approval mode to BASIC, clear forwardTo and update submitTo for all members to default approver.
  2. Disable per diem, rules, report field features
const employeeList = policy?.employeeList;

const updateEmployeeList: PolicyEmployeeList = {};
Object.keys(employeeList ?? {}).forEach((employee) => {
    updateEmployeeList[employee] = {
        ...(employeeList?.[employee] ?? {}),
        submitsTo: policy?.approver ?? policy?.owner,
        forwardsTo: '',
        overLimitForwardsTo: '',
    }
})
const optimisticData: OnyxUpdate[] = [
    {
        onyxMethod: Onyx.METHOD.MERGE,
        key: `policy_${policyID}`,
        value: {
            isPendingDowngrade: true,
            type: CONST.POLICY.TYPE.TEAM,
            areRulesEnabled: false,
            areReportFieldsEnabled: false,
            arePerDiemRatesEnabled: false,
            approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC,
            employeeList: updateEmployeeList,
        },
    },
];

const failureData: OnyxUpdate[] = [
    {
        onyxMethod: Onyx.METHOD.MERGE,
        key: `policy_${policyID}`,
        value: {
            ...policy,
            isPendingDowngrade: false,
            type: policy?.type,
        },
    },
];

function downgradeToTeam(policyID: string) {

I checked and the backend has done point 2 and updated the approval mode, we only update update employeeList data.

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

None

What alternative solutions did you explore? (Optional)

NA

Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.

@bradyrose
Copy link

bradyrose commented May 8, 2025

Withdrawing this proposal to comply with Expensify’s one-proposal-at-a-time policy.
Hope to circle back if it’s still open later. Thanks!

Copy link

melvin-bot bot commented May 13, 2025

@Pujan92 Huh... This is 4 days overdue. Who can take care of this?

@melvin-bot melvin-bot bot added the Overdue label May 13, 2025
@JmillsExpensify
Copy link

@Pujan92 mind prioritizing proposal review when you get a chance?

@JmillsExpensify JmillsExpensify added Weekly KSv2 and removed Daily KSv2 labels May 13, 2025
@melvin-bot melvin-bot bot removed the Overdue label May 13, 2025
@JmillsExpensify JmillsExpensify added Daily KSv2 and removed Weekly KSv2 labels May 14, 2025
@Pujan92
Copy link
Contributor

Pujan92 commented May 14, 2025

@JmillsExpensify I believe this is something that needs to be worked on on the backend when the user make a call for a Downgrade Plan. Just adding a stamp to confirm from internal engineer.

🎀👀🎀 C+ reviewed

Copy link

melvin-bot bot commented May 14, 2025

Triggered auto assignment to @carlosmiceli, see https://stackoverflow.com/c/expensify/questions/7972 for more details.

Copy link

melvin-bot bot commented May 14, 2025

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@carlosmiceli
Copy link
Contributor

Yes, agreed that this seems like a BE issue. @JmillsExpensify quick question, do you happen to know who worked on the advanced approval flow for ND?

@melvin-bot melvin-bot bot added the Overdue label May 19, 2025
Copy link

melvin-bot bot commented May 19, 2025

@carlosmiceli Whoops! This issue is 2 days overdue. Let's get this updated quick!

@MonilBhavsar
Copy link
Contributor

Following, as #62065 is going to be fixed by this issue

@carlosmiceli
Copy link
Contributor

Will do my best to get started on this tomorrow.

@melvin-bot melvin-bot bot removed the Overdue label May 20, 2025
@carlosmiceli
Copy link
Contributor

@JmillsExpensify from the Expected Result in the description I'm a bit confused with the sentence "Existing custom workflows still appear in the workspace editor, even though they should have been removed and no longer apply."

Can I get some clarification of:

  1. Is there any other feature that we've seen failing to toggle off when downgrading?
  2. What exactly should happen with workflows when downgrading?

Is it this being toggled off:

Image

Or this (and Workflows remain open in the LHN):

Image

Or this should still be visible and the workflows removed?:

Image

Copy link

melvin-bot bot commented May 21, 2025

@JmillsExpensify @carlosmiceli @Pujan92 this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!

Copy link

melvin-bot bot commented May 21, 2025

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@melvin-bot melvin-bot bot added the Overdue label May 23, 2025
@carlosmiceli
Copy link
Contributor

Asking for clarification on Slack: https://expensify.slack.com/archives/C03U7DCU4/p1747999493664739

@melvin-bot melvin-bot bot removed the Overdue label May 23, 2025
@JmillsExpensify
Copy link

JmillsExpensify commented May 23, 2025

Is this being toggled off

No

Or this (and Workflows remain open in the LHN):

No

Or this should still be visible and the workflows removed?:

Any workflow aside from the default workflow should be removed.

Image

@carlosmiceli
Copy link
Contributor

PR is up for review.

@carlosmiceli carlosmiceli added the Reviewing Has a PR in review label May 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor Help Wanted Apply this label when an issue is open to proposals by contributors Reviewing Has a PR in review
Projects
Status: No status
Development

No branches or pull requests

8 participants