diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index f4a258b9d6f81..9dc78f3644249 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -610,6 +610,7 @@ function MoneyReportHeader({ paymentMethod, activePolicy, betas, + isSelfTourViewed, }); } else { startAnimation(); @@ -623,6 +624,7 @@ function MoneyReportHeader({ activePolicy, policy, betas, + isSelfTourViewed, }); if (currentSearchQueryJSON && !isOffline) { search({ @@ -657,6 +659,7 @@ function MoneyReportHeader({ shouldCalculateTotals, currentSearchResults?.search?.isLoading, betas, + isSelfTourViewed, ], ); diff --git a/src/components/ProcessMoneyReportHoldMenu.tsx b/src/components/ProcessMoneyReportHoldMenu.tsx index c0f213484fde8..6eee5de90d7da 100644 --- a/src/components/ProcessMoneyReportHoldMenu.tsx +++ b/src/components/ProcessMoneyReportHoldMenu.tsx @@ -1,3 +1,4 @@ +import {hasSeenTourSelector} from '@selectors/Onboarding'; import React, {useMemo} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -76,6 +77,7 @@ function ProcessMoneyReportHoldMenu({ const policy = usePolicy(moneyRequestReport?.policyID); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); + const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [moneyRequestReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID}`); const {isBetaEnabled} = usePermissions(); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); @@ -121,6 +123,7 @@ function ProcessMoneyReportHoldMenu({ activePolicy, policy, betas, + isSelfTourViewed, }); } onClose(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 17e402d64dbdd..2b722a47286c1 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -1,4 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; +import {hasSeenTourSelector} from '@selectors/Onboarding'; import {FlashList} from '@shopify/flash-list'; import type {FlashListRef, ListRenderItemInfo} from '@shopify/flash-list'; import React, {useCallback, useDeferredValue, useEffect, useMemo, useRef, useState} from 'react'; @@ -185,6 +186,7 @@ function MoneyRequestReportPreviewContent({ const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const [betas] = useOnyx(ONYXKEYS.BETAS); + const [isSelfTourViewed = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const isDEWBetaEnabled = isBetaEnabled(CONST.BETAS.NEW_DOT_DEW); const hasViolations = hasViolationsReportUtils(iouReport?.reportID, transactionViolations, currentUserAccountID, currentUserEmail); @@ -275,6 +277,7 @@ function MoneyRequestReportPreviewContent({ paymentMethod, activePolicy, betas, + isSelfTourViewed, }); } else { payMoneyRequest({ @@ -287,6 +290,7 @@ function MoneyRequestReportPreviewContent({ activePolicy, policy, betas, + isSelfTourViewed, }); } } @@ -305,6 +309,7 @@ function MoneyRequestReportPreviewContent({ activePolicy, policy, betas, + isSelfTourViewed, ], ); diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index a0bfbacf8126b..75fbc3e87326f 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -333,6 +333,7 @@ type PayInvoiceArgs = { paymentMethod?: PaymentMethod; activePolicy?: OnyxTypes.Policy; betas: OnyxEntry; + isSelfTourViewed: boolean; }; type RejectMoneyRequestData = { @@ -898,6 +899,7 @@ type PayMoneyRequestFunctionParams = { activePolicy?: OnyxEntry; policy?: OnyxEntry; betas: OnyxEntry; + isSelfTourViewed: boolean; }; let allTransactions: NonNullable> = {}; @@ -10029,6 +10031,7 @@ function getPayMoneyRequestParams({ activePolicy, iouReportCurrentNextStepDeprecated, betas, + isSelfTourViewed, }: { initialChatReport: OnyxTypes.Report; iouReport: OnyxEntry; @@ -10047,6 +10050,7 @@ function getPayMoneyRequestParams({ introSelected?: OnyxEntry; iouReportCurrentNextStepDeprecated: OnyxEntry; betas: OnyxEntry; + isSelfTourViewed: boolean; }): PayMoneyRequestData { const isInvoiceReport = isInvoiceReportReportUtils(iouReport); let payerPolicyID = activePolicy?.id; @@ -10084,6 +10088,7 @@ function getPayMoneyRequestParams({ introSelected, activePolicyID: activePolicy?.id, companySize: introSelected?.companySize as OnboardingCompanySize, + isSelfTourViewed, }); const {adminsChatReportID, adminsCreatedReportActionID, expenseChatReportID, expenseCreatedReportActionID, customUnitRateID, customUnitID, ownerEmail, policyName} = params; @@ -11970,7 +11975,20 @@ function completePaymentOnboarding( } function payMoneyRequest(params: PayMoneyRequestFunctionParams) { - const {paymentType, chatReport, iouReport, introSelected, iouReportCurrentNextStepDeprecated, currentUserAccountID, paymentPolicyID, full = true, activePolicy, policy, betas} = params; + const { + paymentType, + chatReport, + iouReport, + introSelected, + iouReportCurrentNextStepDeprecated, + currentUserAccountID, + paymentPolicyID, + full = true, + activePolicy, + policy, + betas, + isSelfTourViewed, + } = params; if (chatReport.policyID && shouldRestrictUserBillableActions(chatReport.policyID)) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(chatReport.policyID)); return; @@ -11992,6 +12010,7 @@ function payMoneyRequest(params: PayMoneyRequestFunctionParams) { iouReportCurrentNextStepDeprecated, currentUserAccountIDParam: currentUserAccountID, betas, + isSelfTourViewed, }); // For now, we need to call the PayMoneyRequestWithWallet API since PayMoneyRequest was not updated to work with @@ -12018,6 +12037,7 @@ function payInvoice({ activePolicy, invoiceReportCurrentNextStepDeprecated, betas, + isSelfTourViewed, }: PayInvoiceArgs) { const recipient = {accountID: invoiceReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID}; const { @@ -12049,6 +12069,7 @@ function payInvoice({ currentUserEmailParam, introSelected, betas, + isSelfTourViewed, }); const paymentSelected = paymentMethodType === CONST.IOU.PAYMENT_TYPE.VBBA ? CONST.IOU.PAYMENT_SELECTED.BBA : CONST.IOU.PAYMENT_SELECTED.PBA; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index f173cef2edc29..736a0dd3ac8cc 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -6230,6 +6230,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); } return waitForBatchedUpdates(); @@ -6433,6 +6434,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID: CARLOS_ACCOUNT_ID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); } return waitForBatchedUpdates(); @@ -6590,6 +6592,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID: CARLOS_ACCOUNT_ID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); } return waitForBatchedUpdates(); @@ -6639,6 +6642,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID: CARLOS_ACCOUNT_ID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); await waitForBatchedUpdates(); @@ -6750,6 +6754,7 @@ describe('actions/IOU', () => { currentUserAccountID: CARLOS_ACCOUNT_ID, full: false, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); return waitForBatchedUpdates(); }) @@ -6842,12 +6847,103 @@ describe('actions/IOU', () => { full: false, policy, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); await waitForBatchedUpdates(); const newExpenseReport = await getOnyxValue(`${ONYXKEYS.COLLECTION.REPORT}${newExpenseReportID}`); expect(newExpenseReport?.stateNum).toBe(CONST.REPORT.STATE_NUM.OPEN); expect(newExpenseReport?.statusNum).toBe(CONST.REPORT.STATUS_NUM.OPEN); }); + + it('should accept isSelfTourViewed as true and apply optimistic data correctly', async () => { + const chatReport = { + ...createRandomReport(0, undefined), + lastReadTime: DateUtils.getDBTime(), + lastVisibleActionCreated: DateUtils.getDBTime(), + }; + const iouReport = { + ...createRandomReport(1, undefined), + chatType: undefined, + type: CONST.REPORT.TYPE.IOU, + total: 10, + }; + mockFetch?.pause?.(); + + jest.advanceTimersByTime(10); + + payMoneyRequest({ + paymentType: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + chatReport, + iouReport, + introSelected: undefined, + iouReportCurrentNextStepDeprecated: undefined, + currentUserAccountID: CARLOS_ACCOUNT_ID, + betas: [CONST.BETAS.ALL], + isSelfTourViewed: true, + }); + + await waitForBatchedUpdates(); + + // The IOU report should be settled with optimistic data regardless of isSelfTourViewed + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + callback: (report) => { + Onyx.disconnect(connection); + expect(report?.hasOutstandingChildRequest).toBe(false); + expect(report?.statusNum).toBe(CONST.REPORT.STATUS_NUM.REIMBURSED); + resolve(); + }, + }); + }); + + mockFetch?.resume?.(); + }); + + it('should accept isSelfTourViewed as false and apply optimistic data correctly', async () => { + const chatReport = { + ...createRandomReport(0, undefined), + lastReadTime: DateUtils.getDBTime(), + lastVisibleActionCreated: DateUtils.getDBTime(), + }; + const iouReport = { + ...createRandomReport(1, undefined), + chatType: undefined, + type: CONST.REPORT.TYPE.IOU, + total: 10, + }; + mockFetch?.pause?.(); + + jest.advanceTimersByTime(10); + + payMoneyRequest({ + paymentType: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + chatReport, + iouReport, + introSelected: undefined, + iouReportCurrentNextStepDeprecated: undefined, + currentUserAccountID: CARLOS_ACCOUNT_ID, + betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, + }); + + await waitForBatchedUpdates(); + + // The IOU report should be settled with optimistic data regardless of isSelfTourViewed + await new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + callback: (report) => { + Onyx.disconnect(connection); + expect(report?.hasOutstandingChildRequest).toBe(false); + expect(report?.statusNum).toBe(CONST.REPORT.STATUS_NUM.REIMBURSED); + resolve(); + }, + }); + }); + + mockFetch?.resume?.(); + }); }); describe('a expense chat with a cancelled payment', () => { @@ -6943,6 +7039,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID: CARLOS_ACCOUNT_ID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); } return waitForBatchedUpdates(); @@ -11311,6 +11408,7 @@ describe('actions/IOU', () => { iouReportCurrentNextStepDeprecated: undefined, currentUserAccountID: CARLOS_ACCOUNT_ID, betas: [CONST.BETAS.ALL], + isSelfTourViewed: false, }); } await waitForBatchedUpdates();