From bee234c07b69bd5ab8cf53116755901c3e7ac280 Mon Sep 17 00:00:00 2001 From: "Abdelrahman Khattab (via MelvinBot)" Date: Tue, 14 Apr 2026 13:39:41 +0000 Subject: [PATCH] Allow submitters to see admin-dismissed duplicate violations on open reports When an admin dismisses duplicate transaction violations via "Keep all", the dismissal is recorded under the admin's email. The submitter's client previously had no reciprocal check to see that dismissal, causing the duplicate review UI to persist. Add a check so that when the submitter views their own open expense report, any existing dismissal (by admin or approver) is recognized. Co-authored-by: Abdelrahman Khattab --- src/libs/TransactionUtils/index.ts | 10 +++++++-- tests/unit/TransactionUtilsTest.ts | 33 +++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 3d4d7f74045a..af9bc666b634 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1911,14 +1911,15 @@ function isViolationDismissed( return dismissedByEmails.length > 0; } - // If the admin is looking at an open report, we check for both, submitter and admin. + // If someone is looking at an open report, we check for cross-role dismissals. if (!iouReport) { return false; } const isSubmitter = iouReport.ownerAccountID === currentUserAccountID; - const shouldViewAsSubmitter = !isSubmitter && isOpenExpenseReport(iouReport); + // If the admin is looking at an open report, check if the submitter dismissed it. + const shouldViewAsSubmitter = !isSubmitter && isOpenExpenseReport(iouReport); if (shouldViewAsSubmitter && iouReport.ownerAccountID) { const reportOwnerEmail = getLoginsByAccountIDs([iouReport.ownerAccountID]).at(0); if (reportOwnerEmail && dismissedByEmails.includes(reportOwnerEmail)) { @@ -1926,6 +1927,11 @@ function isViolationDismissed( } } + // If the submitter is looking at an open report, check if an admin/approver dismissed it. + if (isSubmitter && isOpenExpenseReport(iouReport) && dismissedByEmails.length > 0) { + return true; + } + return false; } diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts index b2e480163dd9..24e17f19375f 100644 --- a/tests/unit/TransactionUtilsTest.ts +++ b/tests/unit/TransactionUtilsTest.ts @@ -927,14 +927,14 @@ describe('TransactionUtils', () => { expect(result).toBe(false); }); - it('should return false when submitter views their own open report (not condition 2)', () => { + it('should return true when submitter views their own open report and admin dismissed violation', () => { // Given an OPEN report owned by current user const iouReport: Report = { ...openReport, ownerAccountID: CURRENT_USER_ID, }; - // And a transaction where someone else dismissed a violation + // And a transaction where someone else (admin) dismissed a violation const transaction = generateTransaction({ reportID: iouReport.reportID, comment: { @@ -950,7 +950,34 @@ describe('TransactionUtils', () => { // When current user (the submitter) checks if violation is dismissed const result = TransactionUtils.isViolationDismissed(transaction, violation, CURRENT_USER_EMAIL, CURRENT_USER_ID, iouReport, undefined); - // Then it should return false (condition 2 doesn't apply to submitters) + // Then it should return true because admin dismissed it on behalf of the submitter + expect(result).toBe(true); + }); + + it('should return false when submitter views their own processing report and admin dismissed violation', () => { + // Given a PROCESSING report owned by current user + const iouReport: Report = { + ...processingReport, + ownerAccountID: CURRENT_USER_ID, + }; + + // And a transaction where someone else (admin) dismissed a violation + const transaction = generateTransaction({ + reportID: iouReport.reportID, + comment: { + dismissedViolations: { + [CONST.VIOLATIONS.DUPLICATED_TRANSACTION]: { + [OTHER_USER_EMAIL]: DateUtils.getDBTime(), + }, + }, + }, + }); + const violation = {type: CONST.VIOLATION_TYPES.VIOLATION, name: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}; + + // When current user (the submitter) checks if violation is dismissed + const result = TransactionUtils.isViolationDismissed(transaction, violation, CURRENT_USER_EMAIL, CURRENT_USER_ID, iouReport, undefined); + + // Then it should return false because on processing reports, submitter must dismiss separately expect(result).toBe(false); }); });