Skip to content

Commit 0a78c91

Browse files
committed
fixed offline regressions
1 parent 61a979b commit 0a78c91

4 files changed

Lines changed: 59 additions & 4 deletions

File tree

src/components/ReportActionItem/MoneyReportView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ function MoneyReportView({
108108
const isTaxEnabled = isPolicyTaxEnabled(policy);
109109
// Exclude transactions pending deletion so a report being reduced to a single expense (e.g. deleting one of two) is treated as single immediately,
110110
// instead of waiting for the optimistic delete to be removed from Onyx (https://github.com/Expensify/App/issues/91058).
111-
const nonPendingDeleteTransactions = transactions.filter((transaction) => !isTransactionPendingDelete(transaction));
112-
const isSingleNonReimbursableExpense = isSingleTransactionReport(report, nonPendingDeleteTransactions) && nonPendingDeleteTransactions.at(0)?.reimbursable === false;
111+
// While offline the deleted expense is still rendered, so keep counting it to stay consistent with the visible transaction list.
112+
const visibleTransactions = transactions.filter((transaction) => isOffline || !isTransactionPendingDelete(transaction));
113+
const isSingleNonReimbursableExpense = isSingleTransactionReport(report, visibleTransactions) && visibleTransactions.at(0)?.reimbursable === false;
113114
// The reimbursable/non-reimbursable rows duplicate the Total for a single non-reimbursable expense, so suppress only those rows.
114115
// Billable and tax rows convey distinct information and must still show.
115116
const shouldShowReimbursabilityBreakdown = !isSingleNonReimbursableExpense && !!nonReimbursableSpend;

src/components/ReportActionItem/MoneyRequestView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ function MoneyRequestView({
255255
const parentReportTransactions = useReportTransactions(moneyRequestReport?.reportID);
256256
// Exclude transactions pending deletion so the report is recognized as single-expense immediately after deleting one of its expenses,
257257
// instead of waiting for the optimistic delete to be removed from Onyx (https://github.com/Expensify/App/issues/91058).
258-
const nonPendingDeleteParentReportTransactions = parentReportTransactions.filter((t) => !isTransactionPendingDelete(t));
258+
// While offline the deleted expense is still rendered, so keep counting it to stay consistent with the visible transaction list.
259+
const visibleParentReportTransactions = parentReportTransactions.filter((t) => isOffline || !isTransactionPendingDelete(t));
259260
const isApproved = isReportApproved({report: moneyRequestReport});
260261
const isInvoice = isInvoiceReport(moneyRequestReport);
261262
const isTrackExpense = !mergeTransactionID && isTrackExpenseReportNew(transactionThreadReport, moneyRequestReport, parentReportAction);
@@ -574,7 +575,7 @@ function MoneyRequestView({
574575
amountDescription += ` ${CONST.DOT_SEPARATOR} ${translate('common.converted')} ${convertToDisplayString(transactionConvertedAmount, moneyRequestReport?.currency)}`;
575576
}
576577
const isCurrentTransactionReimbursable = updatedTransaction?.reimbursable ?? !!transactionReimbursable;
577-
if (!isCurrentTransactionReimbursable && isSingleTransactionReport(moneyRequestReport, nonPendingDeleteParentReportTransactions)) {
578+
if (!isCurrentTransactionReimbursable && isSingleTransactionReport(moneyRequestReport, visibleParentReportTransactions)) {
578579
amountDescription += ` ${CONST.DOT_SEPARATOR} ${Str.UCFirst(translate('iou.nonReimbursable'))}`;
579580
}
580581

tests/ui/MoneyReportViewTest.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,23 @@ describe('MoneyReportView reimbursable/non-reimbursable breakdown rows', () => {
191191
expect(screen.queryByText('cardTransactions.companySpend')).not.toBeOnTheScreen();
192192
});
193193
});
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+
});
194213
});

tests/ui/MoneyRequestViewTest.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,4 +366,38 @@ describe('MoneyRequestView edit fields', () => {
366366
expect(screen.getByTestId(/^menu-item-iou\.amount.*iou\.nonReimbursable/i)).toBeOnTheScreen();
367367
});
368368
});
369+
370+
it('should NOT append "Non-reimbursable" while offline because the pending-deleted expense is still rendered', async () => {
371+
const threadReport = {
372+
...LHNTestUtils.getFakeReport(),
373+
parentReportID: expenseReportID,
374+
parentReportActionID,
375+
};
376+
377+
await setupTestData();
378+
await act(async () => {
379+
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {reimbursable: false});
380+
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}_sibling`, {
381+
transactionID: `${transactionID}_sibling`,
382+
reportID: expenseReportID,
383+
amount: 2500,
384+
currency: CONST.CURRENCY.USD,
385+
created: '2025-06-02',
386+
merchant: 'Sibling',
387+
comment: {},
388+
reimbursable: false,
389+
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
390+
});
391+
await Onyx.merge(ONYXKEYS.NETWORK, {shouldForceOffline: true});
392+
});
393+
await waitForBatchedUpdatesWithAct();
394+
395+
renderMoneyRequestView(threadReport);
396+
await waitForBatchedUpdatesWithAct();
397+
398+
await waitFor(() => {
399+
expect(screen.getByTestId(/^menu-item-iou\.amount/)).toBeOnTheScreen();
400+
expect(screen.queryByTestId(/^menu-item-iou\.amount.*iou\.nonReimbursable/i)).not.toBeOnTheScreen();
401+
});
402+
});
369403
});

0 commit comments

Comments
 (0)