@@ -9,6 +9,7 @@ import type {ChangeTransactionsReportParams, DismissViolationParams, GetRoutePar
99import { READ_COMMANDS , WRITE_COMMANDS } from '@libs/API/types' ;
1010import * as CollectionUtils from '@libs/CollectionUtils' ;
1111import DateUtils from '@libs/DateUtils' ;
12+ import { buildNextStep } from '@libs/NextStepUtils' ;
1213import * as NumberUtils from '@libs/NumberUtils' ;
1314import { rand64 } from '@libs/NumberUtils' ;
1415import { hasDependentTags , isPaidGroupPolicy } from '@libs/PolicyUtils' ;
@@ -21,12 +22,24 @@ import {
2122 buildOptimisticUnreportedTransactionAction ,
2223 buildTransactionThread ,
2324 findSelfDMReportID ,
25+ getReportTransactions ,
2426} from '@libs/ReportUtils' ;
2527import { getAmount , waypointHasValidAddress } from '@libs/TransactionUtils' ;
2628import ViolationsUtils from '@libs/Violations/ViolationsUtils' ;
2729import CONST from '@src/CONST' ;
2830import ONYXKEYS from '@src/ONYXKEYS' ;
29- import type { PersonalDetails , Policy , RecentWaypoint , Report , ReportAction , ReviewDuplicates , Transaction , TransactionViolation , TransactionViolations } from '@src/types/onyx' ;
31+ import type {
32+ PersonalDetails ,
33+ Policy ,
34+ RecentWaypoint ,
35+ Report ,
36+ ReportAction ,
37+ ReportNextStep ,
38+ ReviewDuplicates ,
39+ Transaction ,
40+ TransactionViolation ,
41+ TransactionViolations ,
42+ } from '@src/types/onyx' ;
3043import type { OriginalMessageIOU , OriginalMessageModifiedExpense } from '@src/types/onyx/OriginalMessage' ;
3144import type { OnyxData } from '@src/types/onyx/Request' ;
3245import type { WaypointCollection } from '@src/types/onyx/Transaction' ;
@@ -100,6 +113,11 @@ Onyx.connect({
100113 callback : ( val ) => ( allTransactionViolations = val ?? [ ] ) ,
101114} ) ;
102115
116+ // Helper to safely check for a string 'name' property
117+ function isViolationWithName ( violation : unknown ) : violation is { name : string } {
118+ return ! ! ( violation && typeof violation === 'object' && typeof ( violation as { name ?: unknown } ) . name === 'string' ) ;
119+ }
120+
103121function saveWaypoint ( transactionID : string , index : string , waypoint : RecentWaypoint | null , isDraft = false ) {
104122 Onyx . merge ( `${ isDraft ? ONYXKEYS . COLLECTION . TRANSACTION_DRAFT : ONYXKEYS . COLLECTION . TRANSACTION } ${ transactionID } ` , {
105123 comment : {
@@ -595,7 +613,7 @@ function setTransactionReport(transactionID: string, transaction: Partial<Transa
595613 Onyx . merge ( `${ isDraft ? ONYXKEYS . COLLECTION . TRANSACTION_DRAFT : ONYXKEYS . COLLECTION . TRANSACTION } ${ transactionID } ` , transaction ) ;
596614}
597615
598- function changeTransactionsReport ( transactionIDs : string [ ] , reportID : string , policy ?: OnyxEntry < Policy > ) {
616+ function changeTransactionsReport ( transactionIDs : string [ ] , reportID : string , policy ?: OnyxEntry < Policy > , reportNextStep ?: OnyxEntry < ReportNextStep > ) {
599617 const newReport = allReports ?. [ `${ ONYXKEYS . COLLECTION . REPORT } ${ reportID } ` ] ;
600618
601619 const transactions = transactionIDs . map ( ( id ) => allTransactions ?. [ id ] ) . filter ( ( t ) : t is NonNullable < typeof t > => t !== undefined ) ;
@@ -694,6 +712,11 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, po
694712 }
695713
696714 let transactionsMoved = false ;
715+ let shouldFixViolations = false ;
716+
717+ const policyTagList = getPolicyTagsData ( policy ?. id ) ;
718+ const policyCategories = getPolicyCategoriesData ( policy ?. id ) ;
719+ const policyHasDependentTags = hasDependentTags ( policy , policyTagList ) ;
697720
698721 transactions . forEach ( ( transaction ) => {
699722 const isUnreportedExpense = ! transaction . reportID || transaction . reportID === CONST . REPORT . UNREPORTED_REPORT_ID ;
@@ -769,22 +792,25 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, po
769792
770793 // 2. Calculate transaction violations if moving transaction to a workspace
771794 if ( isPaidGroupPolicy ( policy ) && policy ?. id ) {
772- const policyTagList = getPolicyTagsData ( policy . id ) ;
773- const violationData = ViolationsUtils . getViolationsOnyxData (
774- transaction ,
775- allTransactionViolations ,
776- policy ,
777- policyTagList ,
778- getPolicyCategoriesData ( policy . id ) ,
779- hasDependentTags ( policy , policyTagList ) ,
780- false ,
781- ) ;
795+ const violationData = ViolationsUtils . getViolationsOnyxData ( transaction , allTransactionViolations , policy , policyTagList , policyCategories , policyHasDependentTags , false ) ;
782796 optimisticData . push ( violationData ) ;
783797 failureData . push ( {
784798 onyxMethod : Onyx . METHOD . MERGE ,
785799 key : `${ ONYXKEYS . COLLECTION . TRANSACTION_VIOLATIONS } ${ transaction . transactionID } ` ,
786800 value : allTransactionViolation ?. [ transaction . transactionID ] ,
787801 } ) ;
802+ const transactionHasViolations = Array . isArray ( violationData . value ) && violationData . value . length > 0 ;
803+ const hasOtherViolationsBesideDuplicates =
804+ Array . isArray ( violationData . value ) &&
805+ ! violationData . value . every ( ( violation ) => {
806+ if ( ! isViolationWithName ( violation ) ) {
807+ return false ;
808+ }
809+ return violation . name === CONST . VIOLATIONS . DUPLICATED_TRANSACTION ;
810+ } ) ;
811+ if ( transactionHasViolations && hasOtherViolationsBesideDuplicates ) {
812+ shouldFixViolations = true ;
813+ }
788814 }
789815
790816 // 3. Keep track of the new report totals
@@ -1029,6 +1055,39 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, po
10291055 } ) ;
10301056 } ) ;
10311057
1058+ const reportTransactions = getReportTransactions ( reportID ) ;
1059+ reportTransactions . forEach ( ( transaction ) => {
1060+ if ( ! isPaidGroupPolicy ( policy ) || ! policy ?. id ) {
1061+ return ;
1062+ }
1063+ const violationData = ViolationsUtils . getViolationsOnyxData ( transaction , allTransactionViolations , policy , policyTagList , policyCategories , policyHasDependentTags , false ) ;
1064+ const hasOtherViolationsBesideDuplicates =
1065+ Array . isArray ( violationData . value ) &&
1066+ ! violationData . value . every ( ( violation ) => {
1067+ if ( ! isViolationWithName ( violation ) ) {
1068+ return false ;
1069+ }
1070+ return violation . name === CONST . VIOLATIONS . DUPLICATED_TRANSACTION ;
1071+ } ) ;
1072+ if ( Array . isArray ( violationData . value ) && violationData . value . length > 0 && hasOtherViolationsBesideDuplicates ) {
1073+ shouldFixViolations = true ;
1074+ }
1075+ } ) ;
1076+
1077+ // 9. Update next step for report
1078+ const nextStepReport = { ...newReport , total : updatedReportTotals [ reportID ] ?? newReport ?. total , reportID : newReport ?. reportID ?? reportID } ;
1079+ const optimisticNextStep = buildNextStep ( nextStepReport , nextStepReport . statusNum ?? CONST . REPORT . STATUS_NUM . OPEN , shouldFixViolations ) ;
1080+ optimisticData . push ( {
1081+ onyxMethod : Onyx . METHOD . MERGE ,
1082+ key : `${ ONYXKEYS . COLLECTION . NEXT_STEP } ${ reportID } ` ,
1083+ value : optimisticNextStep ,
1084+ } ) ;
1085+ failureData . push ( {
1086+ onyxMethod : Onyx . METHOD . MERGE ,
1087+ key : `${ ONYXKEYS . COLLECTION . NEXT_STEP } ${ reportID } ` ,
1088+ value : reportNextStep ,
1089+ } ) ;
1090+
10321091 const parameters : ChangeTransactionsReportParams = {
10331092 transactionList : transactionIDs . join ( ',' ) ,
10341093 reportID,
0 commit comments