From 4b6c1f7d5ed9756764bf03eda075f6fc6b26073a Mon Sep 17 00:00:00 2001 From: "Hans (via MelvinBot)" Date: Wed, 15 Apr 2026 04:24:46 +0000 Subject: [PATCH 1/4] Fall back to policy.requiresTag for tag required label When per-tag-list `required` field is undefined (e.g. after backend sync overwrites tag data), the confirm details page now falls back to `policy.requiresTag` instead of defaulting to false. This aligns tag behavior with how categories already use `policy.requiresCategory`. Co-authored-by: Hans --- src/libs/TagsOptionsListUtils.ts | 2 +- tests/unit/TagsOptionsListUtilsTest.ts | 68 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/libs/TagsOptionsListUtils.ts b/src/libs/TagsOptionsListUtils.ts index b829fdf2161e..af18576ddc32 100644 --- a/src/libs/TagsOptionsListUtils.ts +++ b/src/libs/TagsOptionsListUtils.ts @@ -213,7 +213,7 @@ function getTagVisibility({ const policyTagLists = getTagLists(policyTags); return policyTagLists.map(({tags, required}, index) => { - const isTagRequired = required ?? false; + const isTagRequired = required ?? !!policy?.requiresTag; let shouldShow = false; if (shouldShowTags) { diff --git a/tests/unit/TagsOptionsListUtilsTest.ts b/tests/unit/TagsOptionsListUtilsTest.ts index d8c01a5fb99f..36701436642b 100644 --- a/tests/unit/TagsOptionsListUtilsTest.ts +++ b/tests/unit/TagsOptionsListUtilsTest.ts @@ -843,6 +843,74 @@ describe('TagsOptionsListUtils', () => { {isTagRequired: false, shouldShow: true}, ]); }); + + it('should fall back to policy.requiresTag when tag list required is undefined', () => { + const policyWithRequiresTag = {...mockPolicy, requiresTag: true}; + const policyTagsWithoutRequired: PolicyTagLists = { + tagList1: { + name: 'Department', + tags: { + tag1: {name: 'Engineering', enabled: true}, + tag2: {name: 'Sales', enabled: true}, + }, + orderWeight: 0, + }, + }; + + const result = getTagVisibility({ + shouldShowTags: true, + policy: policyWithRequiresTag, + policyTags: policyTagsWithoutRequired, + transaction: mockTransaction, + }); + + expect(result).toEqual([{isTagRequired: true, shouldShow: true}]); + }); + + it('should not mark tags as required when policy.requiresTag is false and tag list required is undefined', () => { + const policyWithoutRequiresTag = {...mockPolicy, requiresTag: false}; + const policyTagsWithoutRequired: PolicyTagLists = { + tagList1: { + name: 'Department', + tags: { + tag1: {name: 'Engineering', enabled: true}, + }, + orderWeight: 0, + }, + }; + + const result = getTagVisibility({ + shouldShowTags: true, + policy: policyWithoutRequiresTag, + policyTags: policyTagsWithoutRequired, + transaction: mockTransaction, + }); + + expect(result).toEqual([{isTagRequired: false, shouldShow: true}]); + }); + + it('should respect explicit tag list required=false even when policy.requiresTag is true', () => { + const policyWithRequiresTag = {...mockPolicy, requiresTag: true}; + const policyTagsExplicitFalse: PolicyTagLists = { + tagList1: { + name: 'Department', + required: false, + tags: { + tag1: {name: 'Engineering', enabled: true}, + }, + orderWeight: 0, + }, + }; + + const result = getTagVisibility({ + shouldShowTags: true, + policy: policyWithRequiresTag, + policyTags: policyTagsExplicitFalse, + transaction: mockTransaction, + }); + + expect(result).toEqual([{isTagRequired: false, shouldShow: true}]); + }); }); describe('getEnabledTags', () => { From 7bfd3f2ec35cdfebe61a162561873a2dfcd3e8ac Mon Sep 17 00:00:00 2001 From: "Hans (via MelvinBot)" Date: Wed, 15 Apr 2026 04:32:45 +0000 Subject: [PATCH 2/4] Fix TypeScript error: use type assertion for test objects with intentionally omitted required field Co-authored-by: Hans --- tests/unit/TagsOptionsListUtilsTest.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/unit/TagsOptionsListUtilsTest.ts b/tests/unit/TagsOptionsListUtilsTest.ts index 36701436642b..149519af92f4 100644 --- a/tests/unit/TagsOptionsListUtilsTest.ts +++ b/tests/unit/TagsOptionsListUtilsTest.ts @@ -846,7 +846,8 @@ describe('TagsOptionsListUtils', () => { it('should fall back to policy.requiresTag when tag list required is undefined', () => { const policyWithRequiresTag = {...mockPolicy, requiresTag: true}; - const policyTagsWithoutRequired: PolicyTagLists = { + // Intentionally omitting 'required' to simulate backend sync stripping the field + const policyTagsWithoutRequired = { tagList1: { name: 'Department', tags: { @@ -855,7 +856,7 @@ describe('TagsOptionsListUtils', () => { }, orderWeight: 0, }, - }; + } as PolicyTagLists; const result = getTagVisibility({ shouldShowTags: true, @@ -869,7 +870,8 @@ describe('TagsOptionsListUtils', () => { it('should not mark tags as required when policy.requiresTag is false and tag list required is undefined', () => { const policyWithoutRequiresTag = {...mockPolicy, requiresTag: false}; - const policyTagsWithoutRequired: PolicyTagLists = { + // Intentionally omitting 'required' to simulate backend sync stripping the field + const policyTagsWithoutRequired = { tagList1: { name: 'Department', tags: { @@ -877,7 +879,7 @@ describe('TagsOptionsListUtils', () => { }, orderWeight: 0, }, - }; + } as PolicyTagLists; const result = getTagVisibility({ shouldShowTags: true, From 7d157774bd17c936f3c84dc177c95df256f75478 Mon Sep 17 00:00:00 2001 From: "Hans (via MelvinBot)" Date: Wed, 15 Apr 2026 04:38:47 +0000 Subject: [PATCH 3/4] Fix TypeScript error: cast through unknown for intentionally incomplete test objects Co-authored-by: Hans --- tests/unit/TagsOptionsListUtilsTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/TagsOptionsListUtilsTest.ts b/tests/unit/TagsOptionsListUtilsTest.ts index 149519af92f4..a7097e7ef659 100644 --- a/tests/unit/TagsOptionsListUtilsTest.ts +++ b/tests/unit/TagsOptionsListUtilsTest.ts @@ -856,7 +856,7 @@ describe('TagsOptionsListUtils', () => { }, orderWeight: 0, }, - } as PolicyTagLists; + } as unknown as PolicyTagLists; const result = getTagVisibility({ shouldShowTags: true, @@ -879,7 +879,7 @@ describe('TagsOptionsListUtils', () => { }, orderWeight: 0, }, - } as PolicyTagLists; + } as unknown as PolicyTagLists; const result = getTagVisibility({ shouldShowTags: true, From 61bd1ec1ad5f3b8079210a4cb5835ff9737f35f5 Mon Sep 17 00:00:00 2001 From: "Hans (via MelvinBot)" Date: Wed, 15 Apr 2026 07:33:14 +0000 Subject: [PATCH 4/4] Use || instead of ?? for tag required fallback When backend syncs reset a tag list's required field to false (not just undefined), the ?? operator prevents the policy-level requiresTag from being consulted. Using || ensures policy.requiresTag is checked whenever the per-list required is falsy, matching the intended behavior that the global "Members must tag all expenses" toggle always takes effect. Co-authored-by: Hans --- src/libs/TagsOptionsListUtils.ts | 2 +- tests/unit/TagsOptionsListUtilsTest.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/TagsOptionsListUtils.ts b/src/libs/TagsOptionsListUtils.ts index af18576ddc32..777474c69824 100644 --- a/src/libs/TagsOptionsListUtils.ts +++ b/src/libs/TagsOptionsListUtils.ts @@ -213,7 +213,7 @@ function getTagVisibility({ const policyTagLists = getTagLists(policyTags); return policyTagLists.map(({tags, required}, index) => { - const isTagRequired = required ?? !!policy?.requiresTag; + const isTagRequired = required || !!policy?.requiresTag; let shouldShow = false; if (shouldShowTags) { diff --git a/tests/unit/TagsOptionsListUtilsTest.ts b/tests/unit/TagsOptionsListUtilsTest.ts index a7097e7ef659..c89044fadcf2 100644 --- a/tests/unit/TagsOptionsListUtilsTest.ts +++ b/tests/unit/TagsOptionsListUtilsTest.ts @@ -891,7 +891,7 @@ describe('TagsOptionsListUtils', () => { expect(result).toEqual([{isTagRequired: false, shouldShow: true}]); }); - it('should respect explicit tag list required=false even when policy.requiresTag is true', () => { + it('should mark tags as required when policy.requiresTag is true even if tag list required is false', () => { const policyWithRequiresTag = {...mockPolicy, requiresTag: true}; const policyTagsExplicitFalse: PolicyTagLists = { tagList1: { @@ -911,7 +911,7 @@ describe('TagsOptionsListUtils', () => { transaction: mockTransaction, }); - expect(result).toEqual([{isTagRequired: false, shouldShow: true}]); + expect(result).toEqual([{isTagRequired: true, shouldShow: true}]); }); });