Repository navigation
[$175] Workspace - Rules settings are not applied when copying workspace settings #102447
Description
Activity
- addedBugSomething is broken. Auto assigns a BugZero manager.Something is broken. Auto assigns a BugZero manager.
on Sep 28, 2026 Proposal
Please re-state the problem that we are trying to solve in this issue.
When you copy workspace settings with Rules selected, the Rules setting "Require company cards for all purchases" is not applied to the target workspaces.
What is the root cause of that problem?
This toggle is stored in
policy.requireCompanyCardsEnabled(IndividualExpenseRulesSectionRevamp.tsx:139). The Copy settings flow leaves that field out of therulespart in three places:- Optimistic copy:
PARTS_TO_POLICY_FIELDS.ruleslists 17 fields, butrequireCompanyCardsEnabledis not one of them. As a result,buildPolicyFieldPatchnever writes it to the target policies (CopyPolicySettings.ts:48-66). - Backend copy:
CopyPolicySettingssends only part names (parts: 'rules,...'), so the backend decides which fields to copy (CopyPolicySettings.ts:589-595). The App list was built to match the backend list (CopyPolicySettings: add missing rules and categories fields). That suggests the backend also skips this field. - Settings summary:
getWorkspaceRulesdoes not list "Require company cards" in the Rules row description on the Select settings step (duplicate/utils.ts:15-75). By contrast,hasConfiguredRulesdoes check this field (PolicyUtils.ts:1338).
feat: add require company cards toggle to rules revamp added the toggle to the Rules page but did not update the copy mapping.
What changes do you think we should make in order to solve the problem?
- Add
requireCompanyCardsEnabledtoPARTS_TO_POLICY_FIELDS.rulesinCopyPolicySettings.ts. The target policies then get the value optimistically, with the right pending-field handling. - In
getWorkspaceRules, add "Require company cards for all purchases" (workspace.rules.individualExpenseRules.requireCompanyCard) to the summary whenpolicy.requireCompanyCardsEnabledis true. - Update the
rulesfield list intests/actions/CopyPolicySettingsTest.ts, and add a test that checks the target policies end up withrequireCompanyCardsEnabled: true.
The backend copy must also carry this field for the
rulespart. Without that, the server response will reset the toggle after the optimistic update. An internal engineer needs to confirm and handle that part.What alternative solutions did you explore? (Optional)
The Rules page has other settings that the
rulespart also doesn't copy, such as receipt visibility (isReceiptVisibilityPublic) andrequiresTag. We could add them in the same PR if the backend copies them too.Verification notes
- Verified in App code: the field name, the missing field in
PARTS_TO_POLICY_FIELDS.rulesandgetWorkspaceRules, and that the App sends only part names. The App treats every target the same way, so nothing skips therulespart for Control targets. - Not verified: backend behavior. It is inferred from the App list being built to match the backend list. If other Rules fields (for example, the max expense amount) also failed to copy in the video, a separate backend problem exists.
Next Steps for Contributor+ team:
To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/102447#issuecomment-5878848071)to create a draft PR.
To refine:@MelvinBot <your feedback>
To reject: Explain why you are rejecting Melvin's proposal.- Optimistic copy:
- addedExternalAdded to denote the issue can be worked on by a contributorAdded to denote the issue can be worked on by a contributor
on Sep 28, 2026 Current assignee @situchan is eligible for the External assigner, not assigning anyone new.
- changed the title
[-]Workspace - Rules settings are not applied when copying workspace settings[/-][+][$175] Workspace - Rules settings are not applied when copying workspace settings[/+]on Sep 28, 2026 Job added to Upwork: https://www.upwork.com/jobs/~022104682617334572080
- ## Proposal
Please re-state the problem that we are trying to solve in this issue.
When copying workspace settings from a source workspace with Rules enabled and "Require company card for all purchases" turned ON to target workspaces, the "Require company card for all purchases" rule is not applied to the target workspaces. Additionally, on the "Select settings" step of the Copy Settings flow, "Require company cards" is omitted from the Rules row description.
What is the root cause of that problem?
There are two distinct root causes stemming from PR #100241 (feat: add require company cards toggle to rules revamp), which introduced
policy.requireCompanyCardsEnabledandPublicReceiptVisibilityToggleto the Rules page without updating the Copy Policy Settings subsystem:-
Missing from
PARTS_TO_POLICY_FIELDS.rulesinsrc/libs/actions/Policy/CopyPolicySettings.ts:
PARTS_TO_POLICY_FIELDS.rules(lines 48–66) defines the exhaustive array of OnyxPolicyfields copied whenpart === 'rules':rules: [ 'areRulesEnabled', 'maxExpenseAmount', 'maxExpenseAge', 'maxExpenseAmountNoReceipt', 'maxExpenseAmountNoItemizedReceipt', 'defaultBillable', 'defaultReimbursable', 'prohibitedExpenses', 'eReceipts', 'isAttendeeTrackingEnabled', 'preventSelfApproval', 'disabledFields', 'glCodes', 'showTagGLCodes', 'shouldShowAutoApprovalOptions', 'shouldShowAutoReimbursementLimitOption', 'customRules', ],
requireCompanyCardsEnabled(as well asisReceiptVisibilityPublic) is omitted from this list.
Consequently:- In
buildPolicyFieldPatch(lines 425–438), the iteration overPARTS_TO_POLICY_FIELDS.rulesnever reads or patchessourcePolicy.requireCompanyCardsEnabledonto the target policy. - In
buildPendingFields(lines 484–492) andbuildClearedPendingFields(lines 494–502),pendingFields.requireCompanyCardsEnabledis never registered for optimistic Red Brick Road (RBR) feedback. - The optimistic update payload for
ONYXKEYS.COLLECTION.POLICYleavesrequireCompanyCardsEnableduntouched on target workspaces.
- In
-
Missing from
getWorkspaceRulesinsrc/pages/workspace/duplicate/utils.ts:
IngetWorkspaceRules(lines 15–58), the function builds the summary list of active rules displayed onCopyPolicySettingsSelectFeaturesPage.tsxandWorkspaceDuplicateSelectFeaturesForm.tsx.
WhilePolicyUtils.hasConfiguredRulescheckspolicy.requireCompanyCardsEnabled(line 1338),getWorkspaceRulesdoes not checkpolicy?.requireCompanyCardsEnabledorpolicy?.isReceiptVisibilityPublic.
As a result, the "Select settings" UI summary never displays "Require company cards for all purchases" to the user before they initiate the copy. -
Backend sync:
InCopyPolicySettings.ts,write(WRITE_COMMANDS.COPY_POLICY_SETTINGS, ...)passes onlyparts: 'rules,overview,...'. The backend processes the copy by part name, referencing the server-side rule bundle that corresponds to the client'sPARTS_TO_POLICY_FIELDS.rules. AddingrequireCompanyCardsEnabled(andisReceiptVisibilityPublic) ensures full client-side optimistic synchronization and parity with the backend schema (as previously completed in PR CopyPolicySettings: add missing rules and categories fields #93936).
What changes do you think we should make in order to solve the problem?
-
In
src/libs/actions/Policy/CopyPolicySettings.ts:
Add'requireCompanyCardsEnabled'and'isReceiptVisibilityPublic'toPARTS_TO_POLICY_FIELDS.rules:diff --git a/src/libs/actions/Policy/CopyPolicySettings.ts b/src/libs/actions/Policy/CopyPolicySettings.ts --- a/src/libs/actions/Policy/CopyPolicySettings.ts +++ b/src/libs/actions/Policy/CopyPolicySettings.ts @@ -57,6 +57,8 @@ const PARTS_TO_POLICY_FIELDS = { 'defaultReimbursable', 'prohibitedExpenses', 'eReceipts', + 'requireCompanyCardsEnabled', + 'isReceiptVisibilityPublic', 'isAttendeeTrackingEnabled', 'preventSelfApproval', 'disabledFields',
-
In
src/pages/workspace/duplicate/utils.ts(getWorkspaceRules):
Add description items forrequireCompanyCardsEnabledandisReceiptVisibilityPublic:diff --git a/src/pages/workspace/duplicate/utils.ts b/src/pages/workspace/duplicate/utils.ts --- a/src/pages/workspace/duplicate/utils.ts +++ b/src/pages/workspace/duplicate/utils.ts @@ -43,6 +43,12 @@ function getWorkspaceRules(policy: Policy | undefined, translate: LocaleContextP if (policy?.eReceipts) { total.push(translate('workspace.rules.individualExpenseRules.eReceipts')); } + if (policy?.requireCompanyCardsEnabled) { + total.push(translate('workspace.rules.individualExpenseRules.requireCompanyCard')); + } + if (policy?.isReceiptVisibilityPublic) { + total.push(translate('workspace.rules.individualExpenseRules.publicReceiptVisibility')); + } if (policy?.isAttendeeTrackingEnabled) { total.push(translate('workspace.rules.individualExpenseRules.attendeeTracking')); }
-
Automated Unit Tests:
Intests/actions/CopyPolicySettingsTest.ts:- Add
'requireCompanyCardsEnabled'and'isReceiptVisibilityPublic'to the parameterized test cases underPARTS_TO_POLICY_FIELDS.rules(lines 142–158). - Add a dedicated unit test asserting:
it('copies requireCompanyCardsEnabled and isReceiptVisibilityPublic when copying rules', () => { const sourcePolicy = makeSourcePolicy({requireCompanyCardsEnabled: true, isReceiptVisibilityPublic: true}); const targetPolicy = makeTargetPolicy({requireCompanyCardsEnabled: false, isReceiptVisibilityPublic: false}); const {optimisticData, successData} = buildCopyPolicySettingsData(sourcePolicy, [targetPolicy], ['rules'], {}, {}); const policy = getOptimisticPolicy(optimisticData); expect(policy?.requireCompanyCardsEnabled).toBe(true); expect(policy?.isReceiptVisibilityPublic).toBe(true); expect(policy?.pendingFields?.requireCompanyCardsEnabled).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); expect(policy?.pendingFields?.isReceiptVisibilityPublic).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); const targetSuccess = successData.find((entry) => entry.key === POLICY_KEY && entry.onyxMethod === Onyx.METHOD.MERGE); const successPatch = getMergedPolicyPatch(targetSuccess); expect(successPatch?.pendingFields?.requireCompanyCardsEnabled).toBeNull(); expect(successPatch?.pendingFields?.isReceiptVisibilityPublic).toBeNull(); });
- Add
What alternative solutions did you explore? (Optional)
- Copying
requireCompanyCardsEnabledonly if cards are enabled on the target policy:
Rejected. A target workspace may enable Expensify/Company cards after the settings copy. The policy rule definition itself should be faithfully mirrored in the policy data model just like other rule toggles (preventSelfApproval,defaultBillable), while row interaction locks inIndividualExpenseRulesSectionRevamp.tsxhandle missing card preconditions gracefully.
Contributor details
Your Expensify account email: bonnhatnguyen0@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~0139ed4098bdc50182
-
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
bconnnnn commented
on Sep 29, 2026 More actionsI traced this to the Rules copy allowlist.
requireCompanyCardsEnabledis a real Rules field and the Rules page reads/writes it, but it isn’t included inPARTS_TO_POLICY_FIELDS.rulesinCopyPolicySettings.ts. So the target policy’s optimistic copy never gets that value.I’d add
requireCompanyCardsEnabledto the Rules copy contract and cover both directions in tests:- source
true-> target becomestrue - source
false-> target that wastruegets cleared
I’d also verify the backend Rules allowlist uses the same field set. The frontend optimistic state and persisted copy need to stay in sync.
If the copy-settings summary is meant to enumerate the individual Rules being copied, I’d include this setting there too.
- source
bconnnnn commented
on Sep 29, 2026 More actionsProposal
Please re-state the problem that we are trying to solve in this issue.
When copying workspace settings with Rules selected, the source workspace's Require company cards for all purchases setting is not applied to the target workspaces, which retain their prior value.
What is the root cause of that problem?
The App models this toggle as
policy.requireCompanyCardsEnabled, butPARTS_TO_POLICY_FIELDS.rulesin CopyPolicySettings.ts does not include that field. This is a confirmed App-side gap in the optimistic patch: selecting Rules will not optimistically copy or mark this property pending. The Rules copy summary in duplicate/utils.ts also does not list this toggle.The App request sends selected part names such as
rules, not individual policy fields. I cannot confirm from this repository whether the server-sideCopyPolicySettingshandler also omitsrequireCompanyCardsEnabled; that needs to be checked to explain why the persisted target value remains unchanged after the server response.What changes do you think we should make in order to solve the problem?
- Add
requireCompanyCardsEnabledto therulesfield mapping so the optimistic target-policy update includes it. - Include this setting in the Rules copy summary when enabled, so the review step accurately shows what will be copied.
- Verify that the server handler copies the same field for the
rulespart; update its mapping too if it is missing. The client-side allowlist alone cannot guarantee persistence.
What tests should be added?
- Extend CopyPolicySettingsTest.ts to cover copying the field both from true to false and false to true, including its pending state.
- Test that the Rules copy summary includes the company-card requirement when enabled.
- Verify the reported multi-workspace flow on staging, then reload both targets to confirm the server response preserves the copied value.
What alternative solutions did you explore? (Optional)
Changing only the Rules summary would improve visibility but would not copy the field. Changing only the optimistic client map could make the UI look correct temporarily while a server response restores the old value, so both sides need to be aligned.
Concrete App-side patch
Add the missing field to the Rules copy allowlist in
src/libs/actions/Policy/CopyPolicySettings.ts:rules: [ 'areRulesEnabled', 'requireCompanyCardsEnabled', // existing Rules fields... ],
If the copy-settings summary is intended to list enabled Rules, add:
if (policy?.requireCompanyCardsEnabled) { total.push(translate('workspace.rules.individualExpenseRules.requireCompanyCard')); }
Then check the server-side
CopyPolicySettingsallowlist before calling this complete: the App sends the part namerules, so this client patch alone cannot prove the persisted target value is copied. Test both true→true and true→false, and reload the target workspace to verify the server response.- Add
bconnnnn commented
on Sep 30, 2026 More actions@MelvinBot @situchan I added the App-side Rules allowlist/summary patch and called out the server-side copy check needed to confirm the persisted value. I’m available to implement and verify the fix. Could you review the updated proposal?
@situchan Whoops! This issue is 2 days overdue. Let's get this updated quick!
@situchan 6 days overdue. This is scarier than being forced to listen to Vogon poetry!
Reviewing
@MelvinBot implement this for testing
Reacted by melvin-bot🤖 Draft PR created: #103410
Please mark it as "Ready for review" or ask me to do that for you when it is ready for review.The PR adds
requireCompanyCardsEnabledto the Rules copy fields and to the Rules summary on the Select settings step, plus unit tests. The related Jest suites and typecheck pass.The backend must also copy this field for the
rulespart. If it doesn't, the server response will reset the toggle after the optimistic update. An internal engineer should confirm that before QA.
Reproducible in staging?: Yes
Reproducible in production?: Yes
Issue reported by:
lanitochka17(Applause), during deploy QA of Copy workspace settings one target at a time in BulkCopyPolicySettings. That PR did not cause this bug. It also happens in production.App Component: Workspace Settings
Action Performed:
Preconditions: The account has 3 workspaces. Workspace A has a tag, a tax rate, and Rules enabled with "Require company card for all purchases" turned on.
Expected Result:
All selected settings are applied to Workspace B and Workspace C, including Rules settings such as "Require company card for all purchases."
Actual Result:
Rules settings are not applied to Workspace B and Workspace C.
Workaround:
Unknown
Screenshots/Videos
Bug7271788_1790617752505.Recording__1215.mp4
Possibly related
Upwork Automation - Do Not Edit