From 3d9f94f86e233d3cf10ce9863c15c517d64f7c95 Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 19 Mar 2026 15:31:18 +0700 Subject: [PATCH 1/2] fix: Offline deleted rules reappear after reconnecting until cache is cleared --- src/libs/actions/Policy/Category.ts | 3 +++ src/libs/actions/Policy/Policy.ts | 9 +++++++-- .../duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts index a0fe3aeda0dce..121c0699cdb2f 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -104,6 +104,9 @@ function appendSetupCategoriesOnboardingData( function buildOptimisticPolicyWithExistingCategories(policyID: string, categories: PolicyCategories) { const categoriesValues = Object.values(categories); const optimisticCategoryMap = categoriesValues.reduce>>((acc, category) => { + if (category.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + return acc; + } acc[category.name] = { ...category, errors: null, diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index b462506f03a3c..55d23737b0d91 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -3087,6 +3087,11 @@ function buildDuplicatePolicyData(policy: Policy, options: DuplicatePolicyDataOp const optimisticCategoriesData = policyCategories && isCategoriesOptionSelected ? buildOptimisticPolicyWithExistingCategories(targetPolicyID, policyCategories) : defaultOptimisticCategoriesData; + const visibleCodingRules = Object.fromEntries(Object.entries(policy?.rules?.codingRules ?? {}).filter(([, rule]) => rule.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)); + const visibleEmployeeList = Object.fromEntries( + Object.entries(policy?.employeeList ?? {}).filter(([, employee]) => employee.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE), + ); + // WARNING: The data below should be kept in sync with the API so we create the policy with the correct configuration. // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const optimisticData: Array< @@ -3119,14 +3124,14 @@ function buildDuplicatePolicyData(policy: Policy, options: DuplicatePolicyDataOp travelSettings: undefined, workspaceAccountID: undefined, tax: isTaxesOptionSelected ? policy?.tax : undefined, - employeeList: isMemberOptionSelected ? policy.employeeList : {[policy.owner]: policy?.employeeList?.[policy.owner]}, + employeeList: isMemberOptionSelected ? visibleEmployeeList : {[policy.owner]: policy?.employeeList?.[policy.owner]}, id: targetPolicyID, name: policyName, fieldList: isReportsOptionSelected ? policy?.fieldList : undefined, connections: isConnectionsOptionSelected ? policy?.connections : undefined, customUnits: getCustomUnitsForDuplication(policy, isDistanceRatesOptionSelected, isPerDiemOptionSelected, {distanceCustomUnitID, perDiemCustomUnitID}), taxRates: isTaxesOptionSelected ? policy?.taxRates : undefined, - rules: isCodingRulesOptionSelected ? {codingRules: policy?.rules?.codingRules} : undefined, + rules: isCodingRulesOptionSelected ? {codingRules: visibleCodingRules} : undefined, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, pendingFields: { autoReporting: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, diff --git a/src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx b/src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx index afbc54410f8c2..554f51bb2bcbb 100644 --- a/src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx +++ b/src/pages/workspace/duplicate/WorkspaceDuplicateSelectFeaturesForm.tsx @@ -43,8 +43,8 @@ function WorkspaceDuplicateSelectFeaturesForm({policyID}: WorkspaceDuplicateForm const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`); const taxesLength = Object.keys(policy?.taxRates?.taxes ?? {}).length ?? 0; const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`); - const categoriesCount = Object.keys(policyCategories ?? {}).length; - const codingRulesCount = Object.keys(policy?.rules?.codingRules ?? {}).length; + const categoriesCount = Object.values(policyCategories ?? {}).filter((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).length; + const codingRulesCount = Object.values(policy?.rules?.codingRules ?? {}).filter((rule) => rule.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE).length; const [selectedItems, setSelectedItems] = useState([]); const reportFields = Object.keys(getReportFieldsByPolicyID(policyID)).length ?? 0; const customUnits = getPerDiemCustomUnit(policy); From 80c8e0d973abeebacd88c9e7b6f1b7672ca0ffdc Mon Sep 17 00:00:00 2001 From: nkdengineer Date: Thu, 19 Mar 2026 15:48:55 +0700 Subject: [PATCH 2/2] fix test --- tests/actions/PolicyTest.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 42b508d18425d..7e2be8fed0792 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -266,7 +266,10 @@ describe('actions/Policy', () => { it('duplicate workspace', async () => { (fetch as MockFetch)?.pause?.(); await Onyx.set(ONYXKEYS.SESSION, {email: ESH_EMAIL, accountID: ESH_ACCOUNT_ID}); - const fakePolicy = createRandomPolicy(10, CONST.POLICY.TYPE.PERSONAL); + const fakePolicy = { + ...createRandomPolicy(10, CONST.POLICY.TYPE.PERSONAL), + employeeList: {}, + }; await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); await Onyx.set(`${ONYXKEYS.NVP_ACTIVE_POLICY_ID}`, fakePolicy.id); await Onyx.set(`${ONYXKEYS.NVP_INTRO_SELECTED}`, {choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM});