From ce1969a8da2df4c5849cdb8c20758815b877154f Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 13:24:41 +0000 Subject: [PATCH 01/10] Keep existing violations when adding SmartScan warnings --- src/libs/Violations/ViolationsUtils.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 85060e157196..182a44b39e0f 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -260,11 +260,7 @@ const ViolationsUtils = { const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED; const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) { - return { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${updatedTransaction.transactionID}`, - value: [{name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}], - }; + newTransactionViolations.push({name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}); } if (!shouldShowSmartScanFailedError && hasSmartScanFailedError) { newTransactionViolations = reject(newTransactionViolations, {name: CONST.VIOLATIONS.SMARTSCAN_FAILED}); From 8f39c031d443bd21b7bae98b313e787ebe5071b0 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 13:25:57 +0000 Subject: [PATCH 02/10] Update violations test --- tests/unit/ViolationUtilsTest.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index 3ae011a20d00..84e9acdbb834 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -365,7 +365,7 @@ describe('getViolationsOnyxData', () => { expect(result.value).toEqual(expect.arrayContaining([missingCategoryViolation, ...transactionViolations])); }); - it('should only return smartscanFailed violation for smart scan failed transactions', () => { + it('should keep other violations while adding smartscanFailed for smart scan failed transactions', () => { const partialTransaction = { ...transaction, amount: 0, @@ -375,7 +375,9 @@ describe('getViolationsOnyxData', () => { receipt: {state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED}, }; const result = ViolationsUtils.getViolationsOnyxData(partialTransaction, transactionViolations, policy, policyTags, policyCategories, false, false); - expect(result.value).toEqual([{name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}]); + expect(result.value).toEqual( + expect.arrayContaining([{name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}, missingCategoryViolation]), + ); }); }); From 4790b2e16be17318dea73656a159b07b421a3076 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 14:40:31 +0000 Subject: [PATCH 03/10] Fix smartscan warning flicker after manual entry --- src/libs/Violations/ViolationsUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 182a44b39e0f..3198e1ed18f9 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -257,7 +257,8 @@ const ViolationsUtils = { } } - const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED; + const shouldShowSmartScanFailedError = + isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && TransactionUtils.hasMissingSmartscanFields(updatedTransaction); const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) { newTransactionViolations.push({name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}); From dd412b4511a3aebab256cc5acf3cd1f79961c361 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 14:41:01 +0000 Subject: [PATCH 04/10] Add test to cover change --- tests/unit/ViolationUtilsTest.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index 84e9acdbb834..e1ec51428144 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -379,6 +379,18 @@ describe('getViolationsOnyxData', () => { expect.arrayContaining([{name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}, missingCategoryViolation]), ); }); + + it('should not add smartscanFailed when scan failed but required fields are filled', () => { + const transactionWithEnteredDetails = { + ...transaction, + amount: 10000, + merchant: 'Coffee Shop', + iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN, + receipt: {state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED}, + }; + const result = ViolationsUtils.getViolationsOnyxData(transactionWithEnteredDetails, transactionViolations, policy, policyTags, policyCategories, false, false); + expect(result.value).not.toContainEqual(expect.objectContaining({name: CONST.VIOLATIONS.SMARTSCAN_FAILED})); + }); }); describe('policy does not require Categories', () => { From 2cf29d31a5e2218cad3f77ce4f82f191488e2523 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 19:37:07 +0000 Subject: [PATCH 05/10] Fix additional SMARTSCAN_FAILED flicker on reimbursable toggle --- src/libs/Violations/ViolationsUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 3198e1ed18f9..faceb24c3c8a 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -257,8 +257,9 @@ const ViolationsUtils = { } } + // Only show SmartScan failed when scan failed AND the user hasn't filled required fields yet const shouldShowSmartScanFailedError = - isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && TransactionUtils.hasMissingSmartscanFields(updatedTransaction); + isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && TransactionUtils.hasMissingSmartscanFields(updatedTransaction, iouReport); const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) { newTransactionViolations.push({name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}); From 32fcfc0d8f8541b4976463f37f0de882db37a38f Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 19:37:15 +0000 Subject: [PATCH 06/10] Update ViolationUtilsTest.ts --- tests/unit/ViolationUtilsTest.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts index e1ec51428144..4fa2b34fafa8 100644 --- a/tests/unit/ViolationUtilsTest.ts +++ b/tests/unit/ViolationUtilsTest.ts @@ -391,6 +391,28 @@ describe('getViolationsOnyxData', () => { const result = ViolationsUtils.getViolationsOnyxData(transactionWithEnteredDetails, transactionViolations, policy, policyTags, policyCategories, false, false); expect(result.value).not.toContainEqual(expect.objectContaining({name: CONST.VIOLATIONS.SMARTSCAN_FAILED})); }); + + it('should not add smartscanFailed when scan failed but modified fields are filled (amount and merchant)', () => { + const transactionWithModifiedDetails = { + ...transaction, + amount: 0, + modifiedAmount: 12345, + merchant: '', + modifiedMerchant: 'Manual Merchant', + iouRequestType: CONST.IOU.REQUEST_TYPE.SCAN, + receipt: {state: CONST.IOU.RECEIPT_STATE.SCAN_FAILED}, + }; + const result = ViolationsUtils.getViolationsOnyxData( + transactionWithModifiedDetails as unknown as Transaction, + transactionViolations, + policy, + policyTags, + policyCategories, + false, + false, + ); + expect(result.value).not.toContainEqual(expect.objectContaining({name: CONST.VIOLATIONS.SMARTSCAN_FAILED})); + }); }); describe('policy does not require Categories', () => { From 5cf0c0a7b610085c538a81d1b182f7584c081844 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Tue, 9 Dec 2025 19:59:55 +0000 Subject: [PATCH 07/10] Fix typecheck error --- src/libs/Violations/ViolationsUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index faceb24c3c8a..0f4e5f9ad985 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -259,7 +259,9 @@ const ViolationsUtils = { // Only show SmartScan failed when scan failed AND the user hasn't filled required fields yet const shouldShowSmartScanFailedError = - isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && TransactionUtils.hasMissingSmartscanFields(updatedTransaction, iouReport); + isScanRequest && + updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && + TransactionUtils.hasMissingSmartscanFields(updatedTransaction, iouReport ?? undefined); const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) { newTransactionViolations.push({name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}); From 17cdbf7d57f786de9446181e47c83beeaa72664c Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Thu, 11 Dec 2025 14:39:11 +0000 Subject: [PATCH 08/10] Guard smartscan failure warning when user starts fixing result --- src/libs/Violations/ViolationsUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 0f4e5f9ad985..5c8eec5d0256 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -258,10 +258,15 @@ const ViolationsUtils = { } // Only show SmartScan failed when scan failed AND the user hasn't filled required fields yet + const hasUserStartedFixingSmartscan = + !TransactionUtils.isAmountMissing(updatedTransaction) || + !TransactionUtils.isMerchantMissing(updatedTransaction) || + !TransactionUtils.isCreatedMissing(updatedTransaction); const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && - TransactionUtils.hasMissingSmartscanFields(updatedTransaction, iouReport ?? undefined); + TransactionUtils.hasMissingSmartscanFields(updatedTransaction, iouReport ?? undefined) && + !hasUserStartedFixingSmartscan; const hasSmartScanFailedError = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); if (shouldShowSmartScanFailedError && !hasSmartScanFailedError) { newTransactionViolations.push({name: CONST.VIOLATIONS.SMARTSCAN_FAILED, type: CONST.VIOLATION_TYPES.WARNING, showInReview: true}); From 56a05ee4aa9ec967b4dab05b362c46b0ba968650 Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Thu, 11 Dec 2025 17:42:45 +0000 Subject: [PATCH 09/10] Tweak hasUserStartedFixingSmartscan --- src/libs/Violations/ViolationsUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 5c8eec5d0256..9e313c3a245a 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -260,8 +260,7 @@ const ViolationsUtils = { // Only show SmartScan failed when scan failed AND the user hasn't filled required fields yet const hasUserStartedFixingSmartscan = !TransactionUtils.isAmountMissing(updatedTransaction) || - !TransactionUtils.isMerchantMissing(updatedTransaction) || - !TransactionUtils.isCreatedMissing(updatedTransaction); + !TransactionUtils.isMerchantMissing(updatedTransaction); const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED && From 7a9a960fe2e487e0f4075e4fe6255b674b0954cd Mon Sep 17 00:00:00 2001 From: Issa Nimaga Date: Thu, 11 Dec 2025 20:10:58 +0000 Subject: [PATCH 10/10] prettier --- src/libs/Violations/ViolationsUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 9e313c3a245a..d24e21957f26 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -258,9 +258,7 @@ const ViolationsUtils = { } // Only show SmartScan failed when scan failed AND the user hasn't filled required fields yet - const hasUserStartedFixingSmartscan = - !TransactionUtils.isAmountMissing(updatedTransaction) || - !TransactionUtils.isMerchantMissing(updatedTransaction); + const hasUserStartedFixingSmartscan = !TransactionUtils.isAmountMissing(updatedTransaction) || !TransactionUtils.isMerchantMissing(updatedTransaction); const shouldShowSmartScanFailedError = isScanRequest && updatedTransaction.receipt?.state === CONST.IOU.RECEIPT_STATE.SCAN_FAILED &&