From 4b6c1f7d5ed9756764bf03eda075f6fc6b26073a Mon Sep 17 00:00:00 2001
From: "Hans (via MelvinBot)" <infra+melvinbot@expensify.com>
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 <hungvu193@users.noreply.github.com>
---
 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)" <infra+melvinbot@expensify.com>
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 <hungvu193@users.noreply.github.com>
---
 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)" <infra+melvinbot@expensify.com>
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 <hungvu193@users.noreply.github.com>
---
 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)" <infra+melvinbot@expensify.com>
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 <hungvu193@users.noreply.github.com>
---
 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}]);
         });
     });
 
