@@ -3415,6 +3415,7 @@ describe('actions/IOU/ReportWorkflow', () => {
34153415 amount : 100 ,
34163416 status : CONST . TRANSACTION . STATUS . POSTED ,
34173417 bank : '' ,
3418+ reimbursable : true ,
34183419 } ;
34193420
34203421 await Onyx . set ( `${ ONYXKEYS . COLLECTION . POLICY } ${ policyID } ` , fakePolicy ) ;
@@ -3754,6 +3755,184 @@ describe('actions/IOU/ReportWorkflow', () => {
37543755 expect ( result ) . toBe ( CONST . REPORT . ACTION_BADGE . PAY ) ;
37553756 } ) ;
37563757
3758+ it ( 'should skip PAY badge for non-reimbursable-only report via pay-elsewhere path' , async ( ) => {
3759+ const iouReportID = '1400' ;
3760+ const chatReportID = '1401' ;
3761+ const policyID = '1402' ;
3762+
3763+ const fakePolicy : Policy = {
3764+ ...createRandomPolicy ( Number ( policyID ) ) ,
3765+ id : policyID ,
3766+ type : CONST . POLICY . TYPE . TEAM ,
3767+ approvalMode : CONST . POLICY . APPROVAL_MODE . BASIC ,
3768+ role : CONST . POLICY . ROLE . ADMIN ,
3769+ reimbursementChoice : CONST . POLICY . REIMBURSEMENT_CHOICES . REIMBURSEMENT_MANUAL ,
3770+ } ;
3771+
3772+ const fakeChatReport : Report = {
3773+ ...createRandomReport ( Number ( chatReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3774+ reportID : chatReportID ,
3775+ policyID,
3776+ } ;
3777+
3778+ // Non-reimbursable-only report: total and nonReimbursableTotal are equal.
3779+ // canBePaidElsewhere returns true (admin payer + isOnlyNonReimbursablePayElsewhere).
3780+ // But since all transactions are non-reimbursable, PAY badge should be skipped.
3781+ // Use a different managerID so canApproveIOU also returns false (current user is not the manager).
3782+ const fakeIouReport : Report = {
3783+ ...createRandomReport ( Number ( iouReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3784+ reportID : iouReportID ,
3785+ type : CONST . REPORT . TYPE . EXPENSE ,
3786+ policyID,
3787+ stateNum : CONST . REPORT . STATE_NUM . APPROVED ,
3788+ statusNum : CONST . REPORT . STATUS_NUM . APPROVED ,
3789+ managerID : 999999 ,
3790+ total : - 5000 ,
3791+ nonReimbursableTotal : - 5000 ,
3792+ isWaitingOnBankAccount : false ,
3793+ } ;
3794+
3795+ const fakeTransaction : Transaction = {
3796+ ...createRandomTransaction ( 0 ) ,
3797+ reportID : iouReportID ,
3798+ amount : 5000 ,
3799+ status : CONST . TRANSACTION . STATUS . POSTED ,
3800+ bank : '' ,
3801+ reimbursable : false ,
3802+ } ;
3803+
3804+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . POLICY } ${ policyID } ` , fakePolicy ) ;
3805+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ chatReportID } ` , fakeChatReport ) ;
3806+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ iouReportID } ` , fakeIouReport ) ;
3807+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ fakeTransaction . transactionID } ` , fakeTransaction ) ;
3808+ await waitForBatchedUpdates ( ) ;
3809+
3810+ const result = getBadgeFromIOUReport ( fakeIouReport , fakeChatReport , fakePolicy , { } , undefined , RORY_EMAIL , RORY_ACCOUNT_ID ) ;
3811+ expect ( result ) . toBeUndefined ( ) ;
3812+ } ) ;
3813+
3814+ it ( 'should return PAY badge for mixed reimbursable and non-reimbursable report' , async ( ) => {
3815+ const iouReportID = '1500' ;
3816+ const chatReportID = '1501' ;
3817+ const policyID = '1502' ;
3818+
3819+ const fakePolicy : Policy = {
3820+ ...createRandomPolicy ( Number ( policyID ) ) ,
3821+ id : policyID ,
3822+ type : CONST . POLICY . TYPE . TEAM ,
3823+ approvalMode : CONST . POLICY . APPROVAL_MODE . BASIC ,
3824+ role : CONST . POLICY . ROLE . ADMIN ,
3825+ reimbursementChoice : CONST . POLICY . REIMBURSEMENT_CHOICES . REIMBURSEMENT_MANUAL ,
3826+ } ;
3827+
3828+ const fakeChatReport : Report = {
3829+ ...createRandomReport ( Number ( chatReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3830+ reportID : chatReportID ,
3831+ policyID,
3832+ } ;
3833+
3834+ // Mixed report: has both reimbursable and non-reimbursable spend
3835+ // PAY badge should still be shown because not all transactions are non-reimbursable
3836+ const fakeIouReport : Report = {
3837+ ...createRandomReport ( Number ( iouReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3838+ reportID : iouReportID ,
3839+ type : CONST . REPORT . TYPE . EXPENSE ,
3840+ policyID,
3841+ stateNum : CONST . REPORT . STATE_NUM . APPROVED ,
3842+ statusNum : CONST . REPORT . STATUS_NUM . APPROVED ,
3843+ managerID : RORY_ACCOUNT_ID ,
3844+ total : - 8000 ,
3845+ nonReimbursableTotal : - 3000 ,
3846+ isWaitingOnBankAccount : false ,
3847+ } ;
3848+
3849+ const fakeReimbursableTransaction : Transaction = {
3850+ ...createRandomTransaction ( 0 ) ,
3851+ reportID : iouReportID ,
3852+ amount : 5000 ,
3853+ status : CONST . TRANSACTION . STATUS . POSTED ,
3854+ bank : '' ,
3855+ reimbursable : true ,
3856+ } ;
3857+
3858+ const fakeNonReimbursableTransaction : Transaction = {
3859+ ...createRandomTransaction ( 1 ) ,
3860+ reportID : iouReportID ,
3861+ amount : 3000 ,
3862+ status : CONST . TRANSACTION . STATUS . POSTED ,
3863+ bank : '' ,
3864+ reimbursable : false ,
3865+ } ;
3866+
3867+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . POLICY } ${ policyID } ` , fakePolicy ) ;
3868+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ chatReportID } ` , fakeChatReport ) ;
3869+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ iouReportID } ` , fakeIouReport ) ;
3870+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ fakeReimbursableTransaction . transactionID } ` , fakeReimbursableTransaction ) ;
3871+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ fakeNonReimbursableTransaction . transactionID } ` , fakeNonReimbursableTransaction ) ;
3872+ await waitForBatchedUpdates ( ) ;
3873+
3874+ const result = getBadgeFromIOUReport ( fakeIouReport , fakeChatReport , fakePolicy , { } , undefined , RORY_EMAIL , RORY_ACCOUNT_ID ) ;
3875+ expect ( result ) . toBe ( CONST . REPORT . ACTION_BADGE . PAY ) ;
3876+ } ) ;
3877+
3878+ it ( 'should return PAY badge for negative reimbursable expense via pay-elsewhere path' , async ( ) => {
3879+ const iouReportID = '1600' ;
3880+ const chatReportID = '1601' ;
3881+ const policyID = '1602' ;
3882+
3883+ const fakePolicy : Policy = {
3884+ ...createRandomPolicy ( Number ( policyID ) ) ,
3885+ id : policyID ,
3886+ type : CONST . POLICY . TYPE . TEAM ,
3887+ approvalMode : CONST . POLICY . APPROVAL_MODE . BASIC ,
3888+ role : CONST . POLICY . ROLE . ADMIN ,
3889+ reimbursementChoice : CONST . POLICY . REIMBURSEMENT_CHOICES . REIMBURSEMENT_MANUAL ,
3890+ } ;
3891+
3892+ const fakeChatReport : Report = {
3893+ ...createRandomReport ( Number ( chatReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3894+ reportID : chatReportID ,
3895+ policyID,
3896+ } ;
3897+
3898+ // Negative reimbursable expense (credit): total is positive on expense reports,
3899+ // which flips to negative reimbursableSpend in getMoneyRequestSpendBreakdown.
3900+ // canBePaidNow returns false (negative reimbursableSpend), but canBePaidElsewhere
3901+ // returns true via canShowMarkedAsPaidForNegativeAmount.
3902+ // Since the transaction IS reimbursable, hasOnlyNonReimbursableTransactions returns
3903+ // false, so PAY badge should be shown.
3904+ const fakeIouReport : Report = {
3905+ ...createRandomReport ( Number ( iouReportID ) , CONST . REPORT . CHAT_TYPE . POLICY_EXPENSE_CHAT ) ,
3906+ reportID : iouReportID ,
3907+ type : CONST . REPORT . TYPE . EXPENSE ,
3908+ policyID,
3909+ stateNum : CONST . REPORT . STATE_NUM . APPROVED ,
3910+ statusNum : CONST . REPORT . STATUS_NUM . APPROVED ,
3911+ managerID : 999999 ,
3912+ total : 5000 ,
3913+ nonReimbursableTotal : 0 ,
3914+ isWaitingOnBankAccount : false ,
3915+ } ;
3916+
3917+ const fakeTransaction : Transaction = {
3918+ ...createRandomTransaction ( 0 ) ,
3919+ reportID : iouReportID ,
3920+ amount : 5000 ,
3921+ status : CONST . TRANSACTION . STATUS . POSTED ,
3922+ bank : '' ,
3923+ reimbursable : true ,
3924+ } ;
3925+
3926+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . POLICY } ${ policyID } ` , fakePolicy ) ;
3927+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ chatReportID } ` , fakeChatReport ) ;
3928+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . REPORT } ${ iouReportID } ` , fakeIouReport ) ;
3929+ await Onyx . set ( `${ ONYXKEYS . COLLECTION . TRANSACTION } ${ fakeTransaction . transactionID } ` , fakeTransaction ) ;
3930+ await waitForBatchedUpdates ( ) ;
3931+
3932+ const result = getBadgeFromIOUReport ( fakeIouReport , fakeChatReport , fakePolicy , { } , undefined , RORY_EMAIL , RORY_ACCOUNT_ID ) ;
3933+ expect ( result ) . toBe ( CONST . REPORT . ACTION_BADGE . PAY ) ;
3934+ } ) ;
3935+
37573936 it ( 'should return undefined badge for settled report' , async ( ) => {
37583937 const iouReportID = '1300' ;
37593938 const chatReportID = '1301' ;
0 commit comments