@@ -47,7 +47,7 @@ const buildExpenseReport = (overrides: Partial<OnyxTypes.Report> = {}): OnyxType
4747 ...overrides ,
4848} ) ;
4949
50- const buildTransaction = ( id : string , amount : number , reimbursable : boolean | undefined ) : OnyxTypes . Transaction =>
50+ const buildTransaction = ( id : string , amount : number , reimbursable : boolean | undefined , billable = false , taxAmount = 0 ) : OnyxTypes . Transaction =>
5151 ( {
5252 transactionID : id ,
5353 reportID,
@@ -57,6 +57,8 @@ const buildTransaction = (id: string, amount: number, reimbursable: boolean | un
5757 merchant : 'Merchant' ,
5858 comment : { } ,
5959 reimbursable,
60+ billable,
61+ taxAmount,
6062 } ) as OnyxTypes . Transaction ;
6163
6264const seedReportAndTransactions = async ( transactions : OnyxTypes . Transaction [ ] , reportOverrides : Partial < OnyxTypes . Report > = { } ) => {
@@ -76,12 +78,12 @@ const seedReportAndTransactions = async (transactions: OnyxTypes.Transaction[],
7678 await waitForBatchedUpdatesWithAct ( ) ;
7779} ;
7880
79- const renderMoneyReportView = ( report : OnyxTypes . Report ) =>
81+ const renderMoneyReportView = ( report : OnyxTypes . Report , policy : OnyxTypes . Policy | undefined = undefined ) =>
8082 render (
8183 < ComposeProviders components = { [ OnyxListItemProvider ] } >
8284 < MoneyReportView
8385 report = { report }
84- policy = { undefined }
86+ policy = { policy }
8587 shouldHideThreadDividerLine = { false }
8688 shouldShowAnimatedBackground = { false }
8789 />
@@ -125,6 +127,42 @@ describe('MoneyReportView reimbursable/non-reimbursable breakdown rows', () => {
125127 } ) ;
126128 } ) ;
127129
130+ it ( 'shows the billable row but still hides the redundant rows for a single non-reimbursable billable expense' , async ( ) => {
131+ const transactions = [ buildTransaction ( 't1' , 5000 , false , true ) ] ;
132+ await seedReportAndTransactions ( transactions , { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ;
133+
134+ renderMoneyReportView ( buildExpenseReport ( { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ) ;
135+ await waitForBatchedUpdatesWithAct ( ) ;
136+
137+ await waitFor ( ( ) => {
138+ expect ( screen . getByText ( 'common.billable' ) ) . toBeOnTheScreen ( ) ;
139+ expect ( screen . queryByText ( 'cardTransactions.outOfPocket' ) ) . not . toBeOnTheScreen ( ) ;
140+ expect ( screen . queryByText ( 'cardTransactions.companySpend' ) ) . not . toBeOnTheScreen ( ) ;
141+ } ) ;
142+ } ) ;
143+
144+ it ( 'shows the tax row but still hides the redundant rows for a single non-reimbursable taxed expense' , async ( ) => {
145+ const policy = {
146+ id : policyID ,
147+ type : CONST . POLICY . TYPE . TEAM ,
148+ role : CONST . POLICY . ROLE . ADMIN ,
149+ name : 'Policy' ,
150+ outputCurrency : CONST . CURRENCY . USD ,
151+ tax : { trackingEnabled : true } ,
152+ } as OnyxTypes . Policy ;
153+ const transactions = [ buildTransaction ( 't1' , 5000 , false , false , 500 ) ] ;
154+ await seedReportAndTransactions ( transactions , { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ;
155+
156+ renderMoneyReportView ( buildExpenseReport ( { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) , policy ) ;
157+ await waitForBatchedUpdatesWithAct ( ) ;
158+
159+ await waitFor ( ( ) => {
160+ expect ( screen . getByText ( 'common.tax' ) ) . toBeOnTheScreen ( ) ;
161+ expect ( screen . queryByText ( 'cardTransactions.outOfPocket' ) ) . not . toBeOnTheScreen ( ) ;
162+ expect ( screen . queryByText ( 'cardTransactions.companySpend' ) ) . not . toBeOnTheScreen ( ) ;
163+ } ) ;
164+ } ) ;
165+
128166 it ( 'still shows both breakdown rows when reimbursable expenses + credits net to zero alongside non-reimbursable spend' , async ( ) => {
129167 const transactions = [ buildTransaction ( 't1' , 5000 , true ) , buildTransaction ( 't2' , - 5000 , true ) , buildTransaction ( 't3' , 3000 , false ) ] ;
130168 await seedReportAndTransactions ( transactions , { nonReimbursableTotal : - 3000 , unheldNonReimbursableTotal : - 3000 , unheldTotal : - 3000 , total : - 3000 } ) ;
@@ -137,4 +175,39 @@ describe('MoneyReportView reimbursable/non-reimbursable breakdown rows', () => {
137175 expect ( screen . getByText ( 'cardTransactions.companySpend' ) ) . toBeOnTheScreen ( ) ;
138176 } ) ;
139177 } ) ;
178+
179+ it ( 'treats the report as a single non-reimbursable expense (hides redundant rows) while the deleted expense is still pending removal' , async ( ) => {
180+ const transactions = [
181+ buildTransaction ( 't1' , 5000 , false ) ,
182+ { ...buildTransaction ( 't2' , 3000 , false ) , pendingAction : CONST . RED_BRICK_ROAD_PENDING_ACTION . DELETE } as OnyxTypes . Transaction ,
183+ ] ;
184+ await seedReportAndTransactions ( transactions , { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ;
185+
186+ renderMoneyReportView ( buildExpenseReport ( { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ) ;
187+ await waitForBatchedUpdatesWithAct ( ) ;
188+
189+ await waitFor ( ( ) => {
190+ expect ( screen . queryByText ( 'cardTransactions.outOfPocket' ) ) . not . toBeOnTheScreen ( ) ;
191+ expect ( screen . queryByText ( 'cardTransactions.companySpend' ) ) . not . toBeOnTheScreen ( ) ;
192+ } ) ;
193+ } ) ;
194+
195+ it ( 'keeps the breakdown rows while offline because the pending-deleted expense is still rendered' , async ( ) => {
196+ const transactions = [
197+ buildTransaction ( 't1' , 5000 , false ) ,
198+ { ...buildTransaction ( 't2' , 3000 , false ) , pendingAction : CONST . RED_BRICK_ROAD_PENDING_ACTION . DELETE } as OnyxTypes . Transaction ,
199+ ] ;
200+ await seedReportAndTransactions ( transactions , { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ;
201+ await act ( async ( ) => {
202+ await Onyx . merge ( ONYXKEYS . NETWORK , { shouldForceOffline : true } ) ;
203+ } ) ;
204+
205+ renderMoneyReportView ( buildExpenseReport ( { nonReimbursableTotal : - 5000 , unheldNonReimbursableTotal : - 5000 } ) ) ;
206+ await waitForBatchedUpdatesWithAct ( ) ;
207+
208+ await waitFor ( ( ) => {
209+ expect ( screen . getByText ( 'cardTransactions.outOfPocket' ) ) . toBeOnTheScreen ( ) ;
210+ expect ( screen . getByText ( 'cardTransactions.companySpend' ) ) . toBeOnTheScreen ( ) ;
211+ } ) ;
212+ } ) ;
140213} ) ;
0 commit comments