11/* eslint-disable @typescript-eslint/naming-convention */
22import { act , renderHook } from '@testing-library/react-native' ;
3- import type { OnyxCollection , OnyxEntry } from 'react-native-onyx' ;
3+ import type { OnyxCollection , OnyxEntry , OnyxMultiSetInput } from 'react-native-onyx' ;
44import Onyx from 'react-native-onyx' ;
55import useReportIsArchived from '@hooks/useReportIsArchived' ;
6+ import { generateTransactionID } from '@libs/actions/Transaction' ;
67import DateUtils from '@libs/DateUtils' ;
78import { getLastActorDisplayName } from '@libs/OptionsListUtils' ;
89// eslint-disable-next-line no-restricted-syntax
910import type * as PolicyUtils from '@libs/PolicyUtils' ;
1011import { getOriginalMessage , getReportActionMessageText } from '@libs/ReportActionsUtils' ;
11- import { formatReportLastMessageText , getAllReportErrors , getReportPreviewMessage } from '@libs/ReportUtils' ;
12+ import { formatReportLastMessageText , generateReportID , getAllReportErrors , getReasonAndReportActionThatRequiresAttention , getReportPreviewMessage } from '@libs/ReportUtils' ;
1213import SidebarUtils from '@libs/SidebarUtils' ;
1314import initOnyxDerivedValues from '@userActions/OnyxDerived' ;
1415import CONST from '@src/CONST' ;
@@ -23,6 +24,7 @@ import createRandomPolicy from '../utils/collections/policies';
2324import createRandomReportAction from '../utils/collections/reportActions' ;
2425import { createRandomReport } from '../utils/collections/reports' ;
2526import { createSidebarReportsCollection , createSidebarTestData } from '../utils/collections/sidebarReports' ;
27+ import createRandomTransaction from '../utils/collections/transaction' ;
2628import * as LHNTestUtils from '../utils/LHNTestUtils' ;
2729import { localeCompare } from '../utils/TestHelper' ;
2830import waitForBatchedUpdates from '../utils/waitForBatchedUpdates' ;
@@ -573,6 +575,107 @@ describe('SidebarUtils', () => {
573575 expect ( result ) . toBe ( true ) ;
574576 } ) ;
575577
578+ it ( 'returns true when submitter has held expenses even if outstanding tasks trigger GBR' , async ( ) => {
579+ const policyID = generateReportID ( ) ;
580+ const expenseChatID = generateReportID ( ) ;
581+ const expenseReportID = generateReportID ( ) ;
582+ const holdReportActionID = generateReportID ( ) ;
583+
584+ const policyExpenseChat : Report = {
585+ reportID : expenseChatID ,
586+ chatType : CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ,
587+ type : CONST . REPORT . TYPE . CHAT ,
588+ ownerAccountID : 12345 ,
589+ policyID,
590+ hasOutstandingChildRequest : true ,
591+ stateNum : CONST . REPORT . STATE_NUM . OPEN ,
592+ statusNum : CONST . REPORT . STATUS_NUM . OPEN ,
593+ } ;
594+
595+ const expenseReport : Report = {
596+ reportID : expenseReportID ,
597+ chatReportID : expenseChatID ,
598+ type : CONST . REPORT . TYPE . EXPENSE ,
599+ ownerAccountID : 12345 ,
600+ managerID : 12345 ,
601+ policyID,
602+ stateNum : CONST . REPORT . STATE_NUM . OPEN ,
603+ statusNum : CONST . REPORT . STATUS_NUM . OPEN ,
604+ } ;
605+
606+ const baseTransaction = createRandomTransaction ( 700 ) ;
607+ const transactionID = generateTransactionID ( ) ;
608+ const transaction : Transaction = {
609+ ...baseTransaction ,
610+ transactionID,
611+ reportID : expenseReport . reportID ,
612+ amount : 12345 ,
613+ currency : CONST . CURRENCY . USD ,
614+ status : CONST . TRANSACTION . STATUS . POSTED ,
615+ comment : {
616+ ...( baseTransaction . comment ?? { } ) ,
617+ hold : holdReportActionID ,
618+ } ,
619+ } ;
620+
621+ const transactionKey = `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ transactionID } ` as const ;
622+ const transactionViolationsKey = `${ ONYXKEYS . COLLECTION . TRANSACTION_VIOLATIONS } ${ transactionID } ` as const ;
623+ const transactionViolations : OnyxCollection < TransactionViolation [ ] > = {
624+ [ transactionViolationsKey ] : [
625+ {
626+ name : CONST . VIOLATIONS . HOLD ,
627+ type : CONST . VIOLATION_TYPES . VIOLATION ,
628+ showInReview : true ,
629+ } ,
630+ ] ,
631+ } ;
632+
633+ await act ( async ( ) => {
634+ await Onyx . multiSet ( {
635+ [ ONYXKEYS . SESSION ] : {
636+ accountID : 12345 ,
637+ } ,
638+ [ `${ ONYXKEYS . COLLECTION . REPORT } ${ policyExpenseChat . reportID } ` ] : policyExpenseChat ,
639+ [ `${ ONYXKEYS . COLLECTION . REPORT } ${ expenseReport . reportID } ` ] : expenseReport ,
640+ [ transactionKey ] : transaction ,
641+ [ transactionViolationsKey ] : transactionViolations [ transactionViolationsKey ] ,
642+ } as unknown as OnyxMultiSetInput ) ;
643+ } ) ;
644+
645+ await waitForBatchedUpdatesWithAct ( ) ;
646+
647+ const requiresAttention = getReasonAndReportActionThatRequiresAttention ( policyExpenseChat ) ;
648+ expect ( requiresAttention ?. reason ) . toBe ( CONST . REQUIRES_ATTENTION_REASONS . HAS_CHILD_REPORT_AWAITING_ACTION ) ;
649+
650+ const { reason} =
651+ SidebarUtils . getReasonAndReportActionThatHasRedBrickRoad (
652+ policyExpenseChat ,
653+ policyExpenseChat ,
654+ { } as OnyxEntry < ReportActions > ,
655+ true ,
656+ { } ,
657+ { [ transactionKey ] : transaction } ,
658+ transactionViolations ,
659+ false ,
660+ ) ?? { } ;
661+
662+ expect ( reason ) . toBe ( CONST . RBR_REASONS . HAS_TRANSACTION_THREAD_VIOLATIONS ) ;
663+
664+ const { result : isReportArchived } = renderHook ( ( ) => useReportIsArchived ( policyExpenseChat . reportID ) ) ;
665+ const hasRedBrickRoad = SidebarUtils . shouldShowRedBrickRoad (
666+ policyExpenseChat ,
667+ policyExpenseChat ,
668+ { } as OnyxEntry < ReportActions > ,
669+ true ,
670+ { } ,
671+ { [ transactionKey ] : transaction } ,
672+ transactionViolations as OnyxCollection < TransactionViolations > ,
673+ isReportArchived . current ,
674+ ) ;
675+
676+ expect ( hasRedBrickRoad ) . toBe ( true ) ;
677+ } ) ;
678+
576679 it ( 'returns true when report has errors' , ( ) => {
577680 const MOCK_REPORT : Report = {
578681 reportID : '1' ,
0 commit comments