@@ -1784,6 +1784,87 @@ describe('Transaction', () => {
17841784 expect ( Object . keys ( reportActions ?? { } ) . length ) . toBe ( 0 ) ;
17851785 } ) ;
17861786
1787+ it ( 'should preserve an existing hold when optimistic dismissal is built from a stale transaction snapshot' , async ( ) => {
1788+ const transactionID = 'dismissTxnHold' ;
1789+ const threadReportID = 'threadDismissHold' ;
1790+ const transactionKey = `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ transactionID } ` as const ;
1791+ const testerEmail = 'tester@example.com' ;
1792+ const mockViolations : TransactionViolation [ ] = [ { name : CONST . VIOLATIONS . DUPLICATED_TRANSACTION , type : 'warning' } ] ;
1793+
1794+ mockFetch . pause ( ) ;
1795+
1796+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . TRANSACTION_VIOLATIONS } ${ transactionID } ` , mockViolations ) ;
1797+
1798+ const transactionInOnyx = generateTransaction ( {
1799+ transactionID,
1800+ reportID : FAKE_OLD_REPORT_ID ,
1801+ comment : {
1802+ hold : 'holdReportActionID' ,
1803+ dismissedViolations : {
1804+ [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] : {
1805+ owner : 123 ,
1806+ } ,
1807+ } ,
1808+ } ,
1809+ } ) ;
1810+ await Onyx . merge ( transactionKey , transactionInOnyx ) ;
1811+
1812+ const staleTransaction = {
1813+ ...transactionInOnyx ,
1814+ comment : {
1815+ dismissedViolations : {
1816+ [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] : {
1817+ owner : 123 ,
1818+ } ,
1819+ } ,
1820+ hold : null ,
1821+ } ,
1822+ } as Transaction ;
1823+
1824+ const iouAction = {
1825+ reportActionID : rand64 ( ) ,
1826+ actionName : CONST . REPORT . ACTIONS . TYPE . IOU ,
1827+ childReportID : threadReportID ,
1828+ actorAccountID : CURRENT_USER_ID ,
1829+ created : DateUtils . getDBTime ( ) ,
1830+ originalMessage : {
1831+ IOUReportID : FAKE_OLD_REPORT_ID ,
1832+ IOUTransactionID : transactionID ,
1833+ amount : transactionInOnyx . amount ,
1834+ currency : transactionInOnyx . currency ,
1835+ type : CONST . IOU . REPORT_ACTION_TYPE . CREATE ,
1836+ } ,
1837+ } ;
1838+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ FAKE_OLD_REPORT_ID } ` , { [ iouAction . reportActionID ] : iouAction } ) ;
1839+ await Onyx . merge ( `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ threadReportID } ` , { } ) ;
1840+
1841+ mockFetch . fail ( ) ;
1842+
1843+ dismissDuplicateTransactionViolation ( {
1844+ transactionIDs : [ transactionID ] ,
1845+ dismissedPersonalDetails : { login : testerEmail , accountID : CURRENT_USER_ID } ,
1846+ expenseReport : newReport ,
1847+ policy : undefined ,
1848+ isASAPSubmitBetaEnabled : false ,
1849+ allTransactions : { [ transactionKey ] : staleTransaction } ,
1850+ } ) ;
1851+ await waitForBatchedUpdates ( ) ;
1852+
1853+ const optimisticTransaction = await getOnyxValue ( transactionKey ) ;
1854+ const duplicateDismissals = optimisticTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . DUPLICATED_TRANSACTION ] ;
1855+ expect ( optimisticTransaction ?. comment ?. hold ) . toBe ( 'holdReportActionID' ) ;
1856+ expect ( optimisticTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] ) . toEqual ( { owner : 123 } ) ;
1857+ expect ( duplicateDismissals ?. [ testerEmail ] ) . toEqual ( expect . any ( Number ) ) ;
1858+
1859+ await mockFetch . resume ( ) ;
1860+ await waitForBatchedUpdates ( ) ;
1861+
1862+ const revertedTransaction = await getOnyxValue ( transactionKey ) ;
1863+ expect ( revertedTransaction ?. comment ?. hold ) . toBe ( 'holdReportActionID' ) ;
1864+ expect ( revertedTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . SMARTSCAN_FAILED ] ) . toEqual ( { owner : 123 } ) ;
1865+ expect ( revertedTransaction ?. comment ?. dismissedViolations ?. [ CONST . VIOLATIONS . DUPLICATED_TRANSACTION ] ) . toBeUndefined ( ) ;
1866+ } ) ;
1867+
17871868 it ( 'should not modify Onyx data when tag list does not exist at given index (empty violations array)' , async ( ) => {
17881869 const transactionID = 'dismissTxn3' ;
17891870 const threadReportID = 'threadDismiss3' ;
0 commit comments