diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7ad51f723369..885491bf59c8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -620,6 +620,9 @@ const ONYXKEYS = { /** Is unreported transactions loading */ IS_LOADING_UNREPORTED_TRANSACTIONS: 'isLoadingUnreportedTransactions', + /** Is outstanding reports loading */ + IS_LOADING_OUTSTANDING_REPORTS: 'isLoadingOutstandingReports', + /** List of transaction IDs used when navigating to prev/next transaction when viewing it in RHP */ TRANSACTION_THREAD_NAVIGATION_TRANSACTION_IDS: 'transactionThreadNavigationTransactionIDs', @@ -1364,6 +1367,7 @@ type OnyxValuesMapping = { [ONYXKEYS.IS_COMING_FROM_GLOBAL_REIMBURSEMENTS_FLOW]: boolean | undefined; [ONYXKEYS.HAS_MORE_UNREPORTED_TRANSACTIONS_RESULTS]: boolean | undefined; [ONYXKEYS.IS_LOADING_UNREPORTED_TRANSACTIONS]: boolean | undefined; + [ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS]: boolean | undefined; [ONYXKEYS.NVP_LAST_ECASH_IOS_LOGIN]: string; [ONYXKEYS.NVP_LAST_ECASH_ANDROID_LOGIN]: string; [ONYXKEYS.NVP_LAST_IPHONE_LOGIN]: string; diff --git a/src/libs/API/parameters/GetOutstandingReportsParams.ts b/src/libs/API/parameters/GetOutstandingReportsParams.ts new file mode 100644 index 000000000000..a3a6aecc5022 --- /dev/null +++ b/src/libs/API/parameters/GetOutstandingReportsParams.ts @@ -0,0 +1,6 @@ +type GetOutstandingReportsParams = { + policyID: string; + accountID: number; +}; + +export default GetOutstandingReportsParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index bece054a0237..27e4b5ad7a8b 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -76,6 +76,7 @@ export type {default as ResolveActionableMentionWhisperParams} from './ResolveAc export type {default as ChangePolicyUberBillingAccountPageParams} from './ChangePolicyUberBillingAccountPageParams'; export type {default as ResolveActionableReportMentionWhisperParams} from './ResolveActionableReportMentionWhisperParams'; export type {default as RevealExpensifyCardDetailsParams} from './RevealExpensifyCardDetailsParams'; +export type {default as GetOutstandingReportsParams} from './GetOutstandingReportsParams'; export type {default as SearchForReportsParams} from './SearchForReportsParams'; export type {default as SearchForRoomsToMentionParams} from './SearchForRoomsToMentionParams'; export type {default as SendPerformanceTimingParams} from './SendPerformanceTimingParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 3eb92f0892e4..e66fabf5d7ed 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -1110,6 +1110,7 @@ const READ_COMMANDS = { SEARCH: 'Search', GET_OLDER_ACTIONS: 'GetOlderActions', GET_NEWER_ACTIONS: 'GetNewerActions', + GET_OUTSTANDING_REPORTS: 'GetOutstandingReports', EXPAND_URL_PREVIEW: 'ExpandURLPreview', GET_REPORT_PRIVATE_NOTE: 'GetReportPrivateNote', OPEN_ROOM_MEMBERS_PAGE: 'OpenRoomMembersPage', @@ -1190,6 +1191,7 @@ type ReadCommandParameters = { [READ_COMMANDS.OPEN_PLAID_BANK_ACCOUNT_SELECTOR]: Parameters.OpenPlaidBankAccountSelectorParams; [READ_COMMANDS.GET_OLDER_ACTIONS]: Parameters.GetOlderActionsParams; [READ_COMMANDS.GET_NEWER_ACTIONS]: Parameters.GetNewerActionsParams; + [READ_COMMANDS.GET_OUTSTANDING_REPORTS]: Parameters.GetOutstandingReportsParams; [READ_COMMANDS.GET_CORPAY_BANK_ACCOUNT_FIELDS]: Parameters.GetCorpayBankAccountFieldsParams; [READ_COMMANDS.EXPAND_URL_PREVIEW]: Parameters.ExpandURLPreviewParams; [READ_COMMANDS.GET_REPORT_PRIVATE_NOTE]: Parameters.GetReportPrivateNoteParams; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 5a45120de261..3fec3c8849af 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -23,6 +23,7 @@ import type { FlagCommentParams, GetNewerActionsParams, GetOlderActionsParams, + GetOutstandingReportsParams, GetReportPrivateNoteParams, InviteToGroupChatParams, InviteToRoomParams, @@ -4534,6 +4535,55 @@ function searchInServer(searchInput: string, policyID?: string) { searchForReports(isOffline, searchInput, policyID); } +/** + * Fetch all outstanding reports for a workspace to ensure they are available in Onyx + */ +function fetchOutstandingReportsForWorkspace(policyID: string, accountID: number) { + // We are not getting isOffline from components as useEffect change will re-trigger the search on network change + const isOffline = NetworkStore.isOffline(); + + // We do not try to make this request while offline because it sets a loading indicator optimistically + if (isOffline) { + Onyx.set(ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS, false); + return; + } + + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS, + value: true, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS, + value: false, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS, + value: false, + }, + ]; + + const parameters: GetOutstandingReportsParams = { + policyID, + accountID, + }; + + API.read(READ_COMMANDS.GET_OUTSTANDING_REPORTS, parameters, { + optimisticData, + successData, + failureData, + }); +} + function updateLastVisitTime(reportID: string) { if (!isValidReportIDFromPath(reportID)) { return; @@ -6360,6 +6410,7 @@ export { saveReportActionDraft, saveReportDraftComment, searchInServer, + fetchOutstandingReportsForWorkspace, setDeleteTransactionNavigateBackUrl, setGroupDraft, setIsComposerFullSize, diff --git a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx index 579d85eb012a..b56496a3478e 100644 --- a/src/pages/iou/request/step/IOURequestEditReportCommon.tsx +++ b/src/pages/iou/request/step/IOURequestEditReportCommon.tsx @@ -1,5 +1,5 @@ import {createPoliciesSelector} from '@selectors/Policy'; -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import ConfirmModal from '@components/ConfirmModal'; // eslint-disable-next-line no-restricted-imports @@ -17,6 +17,7 @@ import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses'; import useReportTransactions from '@hooks/useReportTransactions'; +import {fetchOutstandingReportsForWorkspace} from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import { @@ -104,6 +105,8 @@ function IOURequestEditReportCommon({ const [allPoliciesID] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: policiesSelector, canBeMissing: false}); + const [isLoadingOutstandingReports] = useOnyx(ONYXKEYS.IS_LOADING_OUTSTANDING_REPORTS); + const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const isSelectedReportUnreported = useMemo(() => !!(isUnreported ?? selectedReportID === CONST.REPORT.UNREPORTED_REPORT_ID), [isUnreported, selectedReportID]); const isOwner = useMemo( @@ -126,6 +129,18 @@ function IOURequestEditReportCommon({ const shouldShowRemoveFromReport = !!(selectedReportID && selectedReportID !== CONST.REPORT.UNREPORTED_REPORT_ID && selectedReport) && isEditing && isOwner && !isReportIOU && !isCardTransaction; + // Fetch all outstanding reports for the workspace when the component mounts + useEffect(() => { + const policyID = selectedPolicyID ?? selectedReport?.policyID; + + if (!policyID || !resolvedReportOwnerAccountID) { + return; + } + + // Fetch all outstanding reports for this policy and owner + fetchOutstandingReportsForWorkspace(policyID, resolvedReportOwnerAccountID); + }, [selectedPolicyID, selectedReport?.policyID, resolvedReportOwnerAccountID]); + const expenseReports = useMemo(() => { // Early return if no reports are available to prevent useless loop if (!outstandingReportsByPolicyID || isEmptyObject(outstandingReportsByPolicyID)) { @@ -317,6 +332,7 @@ function IOURequestEditReportCommon({ = CONST.STANDARD_LIST_ITEM_LIMIT} textInputOptions={{ value: searchValue,