From 9d22bfd11c36def3460cb9900c33fe4e2dafd9d1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 13 Jan 2025 10:05:36 +0100 Subject: [PATCH 1/6] Hide pay button if submit button is visible --- src/components/MoneyReportHeader.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 36310e4eb588..e575af6677a2 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -154,7 +154,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && ReportUtils.canBeExported(moneyRequestReport); const shouldShowSettlementButton = - (shouldShowPayButton || shouldShowApproveButton) && !hasAllPendingRTERViolations && !shouldShowExportIntegrationButton && !shouldShowBrokenConnectionViolation; + !shouldShowSubmitButton && + (shouldShowPayButton || shouldShowApproveButton) && + !hasAllPendingRTERViolations && + !shouldShowExportIntegrationButton && + !shouldShowBrokenConnectionViolation; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; From 2897b2b255ad56e207fdb6b8b45c4beef427cb34 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 13 Jan 2025 10:21:45 +0100 Subject: [PATCH 2/6] Fix lint --- .../BrokenConnectionDescription.tsx | 6 ++-- src/components/MoneyReportHeader.tsx | 36 +++++++++---------- src/libs/actions/Transaction.ts | 6 +++- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index c54bd0058f99..218efcb5b8d6 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -14,7 +14,7 @@ import TextLink from './TextLink'; type BrokenConnectionDescriptionProps = { /** Transaction id of the corresponding report */ - transactionID: string; + transactionID?: string; /** Current report */ report: OnyxEntry; @@ -26,7 +26,7 @@ type BrokenConnectionDescriptionProps = { function BrokenConnectionDescription({transactionID, policy, report}: BrokenConnectionDescriptionProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`); + const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID ?? CONST.DEFAULT_NUMBER_ID}`); const brokenConnection530Error = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION_530); const brokenConnectionError = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION); @@ -46,7 +46,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn {`${translate('violations.adminBrokenConnectionError')}`} Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy?.id ?? '-1'))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_COMPANY_CARDS.getRoute(policy?.id))} >{`${translate('workspace.common.companyCards')}`} . diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index e575af6677a2..8b175d5ae785 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -67,8 +67,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout(); const route = useRoute(); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID ?? '-1'}`); - const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID ?? '-1'}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID ?? CONST.DEFAULT_NUMBER_ID}`); + const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID ?? CONST.DEFAULT_NUMBER_ID}`); const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`); const [session] = useOnyx(ONYXKEYS.SESSION); const requestParentReportAction = useMemo(() => { @@ -83,7 +83,9 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const transaction = transactions?.[ `${ONYXKEYS.COLLECTION.TRANSACTION}${ - ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? -1 : -1 + ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID + : CONST.DEFAULT_NUMBER_ID }` ] ?? undefined; @@ -115,12 +117,12 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const hasScanningReceipt = ReportUtils.getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => TransactionUtils.isReceiptBeingScanned(t)); const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => TransactionUtils.isExpensifyCardTransaction(t) && TransactionUtils.isPending(t)); const transactionIDs = allTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID ?? '-1']); - const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID ?? '-1', moneyRequestReport, policy); - const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport?.reportID ?? ''); + const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation(transaction?.transactionID ? [transaction?.transactionID] : []); + const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID, moneyRequestReport, policy); + const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport?.reportID); const isPayAtEndExpense = TransactionUtils.isPayAtEndExpense(transaction); const isArchivedReport = ReportUtils.isArchivedRoomWithID(moneyRequestReport?.reportID); - const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID ?? '-1'}`, {selector: ReportUtils.getArchiveReason}); + const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID ?? CONST.DEFAULT_NUMBER_ID}`, {selector: ReportUtils.getArchiveReason}); const getCanIOUBePaid = useCallback( (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere), @@ -154,11 +156,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && ReportUtils.canBeExported(moneyRequestReport); const shouldShowSettlementButton = - !shouldShowSubmitButton && - (shouldShowPayButton || shouldShowApproveButton) && - !hasAllPendingRTERViolations && - !shouldShowExportIntegrationButton && - !shouldShowBrokenConnectionViolation; + (shouldShowPayButton || shouldShowApproveButton) && !hasAllPendingRTERViolations && !shouldShowExportIntegrationButton && !shouldShowBrokenConnectionViolation; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; @@ -219,10 +217,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const deleteTransaction = useCallback(() => { if (requestParentReportAction) { const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? '-1' - : '-1'; + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID + : undefined; if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID ?? '-1', iouTransactionID, requestParentReportAction, true); + navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, true); } else { navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); } @@ -236,9 +234,9 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea return; } const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? '-1' - : '-1'; - const reportID = transactionThreadReport?.reportID ?? '-1'; + ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID + : undefined; + const reportID = transactionThreadReport?.reportID; TransactionActions.markAsCash(iouTransactionID, reportID); }, [requestParentReportAction, transactionThreadReport?.reportID]); @@ -269,7 +267,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea icon: getStatusIcon(Expensicons.Hourglass), description: ( diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index c8a007458242..976424aab6a7 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -461,7 +461,11 @@ function clearError(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } -function markAsCash(transactionID: string, transactionThreadReportID: string) { +function markAsCash(transactionID: string | undefined, transactionThreadReportID: string | undefined) { + if (!transactionID || !transactionThreadReportID) { + return; + } + const optimisticReportAction = buildOptimisticDismissedViolationReportAction({ reason: 'manual', violationName: CONST.VIOLATIONS.RTER, From 8aa2bb719224fe0ac0178ea0ca592c0d21716c10 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 13 Jan 2025 10:25:35 +0100 Subject: [PATCH 3/6] Bring back the original change --- src/ROUTES.ts | 2 +- src/components/BrokenConnectionDescription.tsx | 2 +- src/components/MoneyReportHeader.tsx | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7f8b75f353e1..2533df759873 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1479,7 +1479,7 @@ const ROUTES = { TRANSACTION_DUPLICATE_REVIEW_PAGE: { route: 'r/:threadReportID/duplicates/review', - getRoute: (threadReportID: string, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review` as const, backTo), + getRoute: (threadReportID: string | undefined, backTo?: string) => getUrlWithBackToParam(`r/${threadReportID}/duplicates/review` as const, backTo), }, TRANSACTION_DUPLICATE_REVIEW_MERCHANT_PAGE: { route: 'r/:threadReportID/duplicates/review/merchant', diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index 218efcb5b8d6..6e7d9ed293df 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -40,7 +40,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn return translate('violations.brokenConnection530Error'); } - if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID ?? '')) { + if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID)) { return ( <> {`${translate('violations.adminBrokenConnectionError')}`} diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 8b175d5ae785..a761c607c875 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -156,7 +156,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && ReportUtils.canBeExported(moneyRequestReport); const shouldShowSettlementButton = - (shouldShowPayButton || shouldShowApproveButton) && !hasAllPendingRTERViolations && !shouldShowExportIntegrationButton && !shouldShowBrokenConnectionViolation; + !shouldShowSubmitButton && + (shouldShowPayButton || shouldShowApproveButton) && + !hasAllPendingRTERViolations && + !shouldShowExportIntegrationButton && + !shouldShowBrokenConnectionViolation; const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; @@ -356,7 +360,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea text={translate('iou.reviewDuplicates')} style={styles.p0} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID, Navigation.getReportRHPActiveRoute())); }} /> From d3735e1e881d03fb24a8a8e33e5ff599fa773d83 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 13 Jan 2025 10:32:47 +0100 Subject: [PATCH 4/6] Fix last batch of lint errors --- src/components/MoneyReportHeader.tsx | 10 ++++------ src/libs/TransactionUtils/index.ts | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index a761c607c875..d40ddd8b794b 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -55,8 +55,7 @@ type MoneyReportHeaderProps = { reportActions: OnyxTypes.ReportAction[]; /** The reportID of the transaction thread report associated with this current report, if any */ - // eslint-disable-next-line react/no-unused-prop-types - transactionThreadReportID?: string | null; + transactionThreadReportID?: string | undefined; /** Method to trigger when pressing close button of the header */ onBackButtonPress: () => void; @@ -98,7 +97,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport); const isOnHold = TransactionUtils.isOnHold(transaction); const isDeletedParentAction = !!requestParentReportAction && ReportActionsUtils.isDeletedAction(requestParentReportAction); - const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID ?? ''); + const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID); // Only the requestor can delete the request, admins can only edit it. const isActionOwner = @@ -133,8 +132,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); const shouldShowMarkAsCashButton = - hasAllPendingRTERViolations || - (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(moneyRequestReport?.reportID ?? ''))); + hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(moneyRequestReport?.reportID))); const shouldShowPayButton = canIOUBePaid || onlyShowPayElsewhere; @@ -428,7 +426,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea text={translate('iou.reviewDuplicates')} style={[styles.flex1, styles.pr0]} onPress={() => { - Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID ?? '', Navigation.getReportRHPActiveRoute())); + Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(transactionThreadReportID, Navigation.getReportRHPActiveRoute())); }} /> )} diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 528481dae237..7549c0b83ddf 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -891,7 +891,7 @@ function getRecentTransactions(transactions: Record, size = 2): * @param transactionID - the transaction to check * @param checkDismissed - whether to check if the violation has already been dismissed as well */ -function isDuplicate(transactionID: string, checkDismissed = false): boolean { +function isDuplicate(transactionID: string | undefined, checkDismissed = false): boolean { const hasDuplicatedViolation = !!allTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]?.some( (violation: TransactionViolation) => violation.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION, ); From 989b45b4a16984be4c04df8b7a87e3883eaa9982 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 13 Jan 2025 11:27:21 +0100 Subject: [PATCH 5/6] Fix lint v3 --- src/components/MoneyReportHeader.tsx | 4 ++++ src/libs/actions/Transaction.ts | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index d40ddd8b794b..87d56a43fcf1 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -240,6 +240,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea : undefined; const reportID = transactionThreadReport?.reportID; + if (!iouTransactionID || !reportID) { + return; + } + TransactionActions.markAsCash(iouTransactionID, reportID); }, [requestParentReportAction, transactionThreadReport?.reportID]); diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 976424aab6a7..c8a007458242 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -461,11 +461,7 @@ function clearError(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null, waypoints: null, routes: null}}); } -function markAsCash(transactionID: string | undefined, transactionThreadReportID: string | undefined) { - if (!transactionID || !transactionThreadReportID) { - return; - } - +function markAsCash(transactionID: string, transactionThreadReportID: string) { const optimisticReportAction = buildOptimisticDismissedViolationReportAction({ reason: 'manual', violationName: CONST.VIOLATIONS.RTER, From fdd1be5022862299f46b863fbc617d3c42fe0102 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 15 Jan 2025 11:11:44 +0100 Subject: [PATCH 6/6] Fix lint --- .../BrokenConnectionDescription.tsx | 10 +- src/components/MoneyReportHeader.tsx | 150 +++++++++++------- 2 files changed, 96 insertions(+), 64 deletions(-) diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx index 3e4ad62cb1e6..078cbed25631 100644 --- a/src/components/BrokenConnectionDescription.tsx +++ b/src/components/BrokenConnectionDescription.tsx @@ -3,8 +3,8 @@ import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {isInstantSubmitEnabled, isPolicyAdmin as isPolicyAdminPolicyUtils} from '@libs/PolicyUtils'; +import {isCurrentUserSubmitter, isProcessingReport, isReportApproved, isReportManuallyReimbursed} from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -30,7 +30,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn const brokenConnection530Error = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION_530); const brokenConnectionError = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION); - const isPolicyAdmin = PolicyUtils.isPolicyAdmin(policy); + const isPolicyAdmin = isPolicyAdminPolicyUtils(policy); if (!brokenConnection530Error && !brokenConnectionError) { return ''; @@ -40,7 +40,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn return translate('violations.brokenConnection530Error'); } - if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID)) { + if (isPolicyAdmin && !isCurrentUserSubmitter(report?.reportID)) { return ( <> {`${translate('violations.adminBrokenConnectionError')}`} @@ -53,7 +53,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn ); } - if (ReportUtils.isReportApproved(report) || ReportUtils.isReportManuallyReimbursed(report) || (ReportUtils.isProcessingReport(report) && !PolicyUtils.isInstantSubmitEnabled(policy))) { + if (isReportApproved(report) || isReportManuallyReimbursed(report) || (isProcessingReport(report) && !isInstantSubmitEnabled(policy))) { return translate('violations.memberBrokenConnectionError'); } diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index dc1cace479f7..2e72424efc75 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -9,15 +9,54 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {getCurrentUserAccountID} from '@libs/actions/Report'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getConnectedIntegration, isPolicyAdmin} from '@libs/PolicyUtils'; +import {getOriginalMessage, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; +import { + canBeExported, + canDeleteTransaction, + getArchiveReason, + getBankAccountRoute, + getMoneyRequestSpendBreakdown, + getNonHeldAndFullAmount, + getTransactionsWithReceipts, + hasHeldExpenses as hasHeldExpensesReportUtils, + hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, + hasUpdatedTotal, + isAllowedToApproveExpenseReport, + isAllowedToSubmitDraftExpenseReport, + isArchivedReport as isArchivedReportPolicyUtils, + isClosedExpenseReportWithNoExpenses, + isCurrentUserSubmitter, + isInvoiceReport, + isOpenExpenseReport, + navigateBackOnDeleteTransaction, +} from '@libs/ReportUtils'; +import { + allHavePendingRTERViolation, + getAllReportTransactions, + isDuplicate as isDuplicateTransactionUtils, + isExpensifyCardTransaction, + isOnHold as isOnHoldTransactionUtils, + isPayAtEndExpense as isPayAtEndExpenseTransactionUtils, + isPending, + isReceiptBeingScanned, + shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionsUtils, +} from '@libs/TransactionUtils'; import variables from '@styles/variables'; -import * as IOU from '@userActions/IOU'; -import * as TransactionActions from '@userActions/Transaction'; +import { + approveMoneyRequest, + canApproveIOU, + canIOUBePaid as canIOUBePaidIOUUtils, + deleteMoneyRequest, + deleteTrackExpense, + dismissHoldUseExplanation, + payInvoice, + payMoneyRequest, + submitReport, +} from '@userActions/IOU'; +import {markAsCash as markAsCashFn} from '@userActions/Transaction'; import CONST from '@src/CONST'; import useDelegateUserDetails from '@src/hooks/useDelegateUserDetails'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -81,11 +120,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true}); const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult); const transaction = - transactions?.[ - `${ONYXKEYS.COLLECTION.TRANSACTION}${ - ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) && ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID - }` - ] ?? undefined; + transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(requestParentReportAction) && getOriginalMessage(requestParentReportAction)?.IOUTransactionID}`] ?? + undefined; const styles = useThemeStyles(); const theme = useTheme(); @@ -93,37 +129,37 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [shouldShowHoldMenu, setShouldShowHoldMenu] = useState(false); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport); - const isOnHold = TransactionUtils.isOnHold(transaction); - const isDeletedParentAction = !!requestParentReportAction && ReportActionsUtils.isDeletedAction(requestParentReportAction); - const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID); + const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport); + const isOnHold = isOnHoldTransactionUtils(transaction); + const isDeletedParentAction = !!requestParentReportAction && isDeletedAction(requestParentReportAction); + const isDuplicate = isDuplicateTransactionUtils(transaction?.transactionID); // Only the requestor can delete the request, admins can only edit it. const isActionOwner = typeof requestParentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && requestParentReportAction.actorAccountID === session?.accountID; - const canDeleteRequest = isActionOwner && ReportUtils.canDeleteTransaction(moneyRequestReport) && !isDeletedParentAction; + const canDeleteRequest = isActionOwner && canDeleteTransaction(moneyRequestReport) && !isDeletedParentAction; const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); const [paymentType, setPaymentType] = useState(); const [requestType, setRequestType] = useState(); - const allTransactions = useMemo(() => TransactionUtils.getAllReportTransactions(moneyRequestReport?.reportID, transactions), [moneyRequestReport?.reportID, transactions]); - const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy); + const allTransactions = useMemo(() => getAllReportTransactions(moneyRequestReport?.reportID, transactions), [moneyRequestReport?.reportID, transactions]); + const canAllowSettlement = hasUpdatedTotal(moneyRequestReport, policy); const policyType = policy?.type; - const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport); - const connectedIntegration = PolicyUtils.getConnectedIntegration(policy); + const isDraft = isOpenExpenseReport(moneyRequestReport); + const connectedIntegration = getConnectedIntegration(policy); const navigateBackToAfterDelete = useRef(); - const hasHeldExpenses = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); - const hasScanningReceipt = ReportUtils.getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => TransactionUtils.isReceiptBeingScanned(t)); - const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => TransactionUtils.isExpensifyCardTransaction(t) && TransactionUtils.isPending(t)); + const hasHeldExpenses = hasHeldExpensesReportUtils(moneyRequestReport?.reportID); + const hasScanningReceipt = getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => isReceiptBeingScanned(t)); + const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => isExpensifyCardTransaction(t) && isPending(t)); const transactionIDs = allTransactions.map((t) => t.transactionID); - const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID]); - const shouldShowBrokenConnectionViolation = TransactionUtils.shouldShowBrokenConnectionViolation(transaction?.transactionID, moneyRequestReport, policy); - const hasOnlyHeldExpenses = ReportUtils.hasOnlyHeldExpenses(moneyRequestReport?.reportID); - const isPayAtEndExpense = TransactionUtils.isPayAtEndExpense(transaction); - const isArchivedReport = ReportUtils.isArchivedReport(moneyRequestReport); - const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: ReportUtils.getArchiveReason}); + const hasAllPendingRTERViolations = allHavePendingRTERViolation([transaction?.transactionID]); + const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionsUtils(transaction?.transactionID, moneyRequestReport, policy); + const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID); + const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction); + const isArchivedReport = isArchivedReportPolicyUtils(moneyRequestReport); + const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason}); const getCanIOUBePaid = useCallback( - (onlyShowPayElsewhere = false) => IOU.canIOUBePaid(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere), + (onlyShowPayElsewhere = false) => canIOUBePaidIOUUtils(moneyRequestReport, chatReport, policy, transaction ? [transaction] : undefined, onlyShowPayElsewhere), [moneyRequestReport, chatReport, policy, transaction], ); const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]); @@ -131,13 +167,13 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const onlyShowPayElsewhere = useMemo(() => !canIOUBePaid && getCanIOUBePaid(true), [canIOUBePaid, getCanIOUBePaid]); const shouldShowMarkAsCashButton = - hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!PolicyUtils.isPolicyAdmin(policy) || ReportUtils.isCurrentUserSubmitter(moneyRequestReport?.reportID))); + hasAllPendingRTERViolations || (shouldShowBrokenConnectionViolation && (!isPolicyAdmin(policy) || isCurrentUserSubmitter(moneyRequestReport?.reportID))); const shouldShowPayButton = canIOUBePaid || onlyShowPayElsewhere; - const shouldShowApproveButton = useMemo(() => IOU.canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions, [moneyRequestReport, policy, hasOnlyPendingTransactions]); + const shouldShowApproveButton = useMemo(() => canApproveIOU(moneyRequestReport, policy) && !hasOnlyPendingTransactions, [moneyRequestReport, policy, hasOnlyPendingTransactions]); - const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport); + const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(moneyRequestReport); const currentUserAccountID = getCurrentUserAccountID(); const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; @@ -151,7 +187,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea !shouldShowBrokenConnectionViolation && (moneyRequestReport?.ownerAccountID === currentUserAccountID || isAdmin || moneyRequestReport?.managerID === currentUserAccountID); - const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && ReportUtils.canBeExported(moneyRequestReport); + const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && canBeExported(moneyRequestReport); const shouldShowSettlementButton = !shouldShowSubmitButton && @@ -160,11 +196,11 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea !shouldShowExportIntegrationButton && !shouldShowBrokenConnectionViolation; - const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport); + const shouldDisableSubmitButton = shouldShowSubmitButton && !isAllowedToSubmitDraftExpenseReport(moneyRequestReport); const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; const shouldShowStatusBar = hasAllPendingRTERViolations || shouldShowBrokenConnectionViolation || hasOnlyHeldExpenses || hasScanningReceipt || isPayAtEndExpense || hasOnlyPendingTransactions; - const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length && !shouldShowStatusBar; + const shouldShowNextStep = !isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length && !shouldShowStatusBar; const shouldShowAnyButton = isDuplicate || shouldShowSettlementButton || @@ -173,10 +209,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea shouldShowNextStep || shouldShowMarkAsCashButton || shouldShowExportIntegrationButton; - const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport); - const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); - const {nonHeldAmount, fullAmount, hasValidNonHeldAmount} = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, shouldShowPayButton); - const isAnyTransactionOnHold = ReportUtils.hasHeldExpenses(moneyRequestReport?.reportID); + const bankAccountRoute = getBankAccountRoute(chatReport); + const formattedAmount = convertToDisplayString(reimbursableSpend, moneyRequestReport?.currency); + const {nonHeldAmount, fullAmount, hasValidNonHeldAmount} = getNonHeldAndFullAmount(moneyRequestReport, shouldShowPayButton); + const isAnyTransactionOnHold = hasHeldExpensesReportUtils(moneyRequestReport?.reportID); const displayedAmount = isAnyTransactionOnHold && canAllowSettlement && hasValidNonHeldAmount ? nonHeldAmount : formattedAmount; const isMoreContentShown = shouldShowNextStep || shouldShowStatusBar || (shouldShowAnyButton && shouldUseNarrowLayout); const {isDelegateAccessRestricted} = useDelegateUserDetails(); @@ -196,10 +232,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea setIsNoDelegateAccessMenuVisible(true); } else if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); - } else if (ReportUtils.isInvoiceReport(moneyRequestReport)) { - IOU.payInvoice(type, chatReport, moneyRequestReport, payAsBusiness); + } else if (isInvoiceReport(moneyRequestReport)) { + payInvoice(type, chatReport, moneyRequestReport, payAsBusiness); } else { - IOU.payMoneyRequest(type, chatReport, moneyRequestReport, true); + payMoneyRequest(type, chatReport, moneyRequestReport, true); } }, [chatReport, isAnyTransactionOnHold, isDelegateAccessRestricted, moneyRequestReport], @@ -212,19 +248,17 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea } else if (isAnyTransactionOnHold) { setIsHoldMenuVisible(true); } else { - IOU.approveMoneyRequest(moneyRequestReport, true); + approveMoneyRequest(moneyRequestReport, true); } }; const deleteTransaction = useCallback(() => { if (requestParentReportAction) { - const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID - : undefined; - if (ReportActionsUtils.isTrackExpenseAction(requestParentReportAction)) { - navigateBackToAfterDelete.current = IOU.deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, true); + const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined; + if (isTrackExpenseAction(requestParentReportAction)) { + navigateBackToAfterDelete.current = deleteTrackExpense(moneyRequestReport?.reportID, iouTransactionID, requestParentReportAction, true); } else { - navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); + navigateBackToAfterDelete.current = deleteMoneyRequest(iouTransactionID, requestParentReportAction, true); } } @@ -235,15 +269,13 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea if (!requestParentReportAction) { return; } - const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction) - ? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID - : undefined; + const iouTransactionID = isMoneyRequestAction(requestParentReportAction) ? getOriginalMessage(requestParentReportAction)?.IOUTransactionID : undefined; const reportID = transactionThreadReport?.reportID; if (!iouTransactionID || !reportID) { return; } - TransactionActions.markAsCash(iouTransactionID, reportID); + markAsCashFn(iouTransactionID, reportID); }, [requestParentReportAction, transactionThreadReport?.reportID]); const getStatusIcon: (src: IconAsset) => React.ReactNode = (src) => ( @@ -329,7 +361,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea }, [isSmallScreenWidth, shouldShowHoldMenu]); const handleHoldRequestClose = () => { - IOU.dismissHoldUseExplanation(); + dismissHoldUseExplanation(); }; useEffect(() => { @@ -404,7 +436,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea success={isWaitingForSubmissionFromCurrentUser} text={translate('common.submit')} style={[styles.mnw120, styles.pv2, styles.pr0]} - onPress={() => IOU.submitReport(moneyRequestReport)} + onPress={() => submitReport(moneyRequestReport)} isDisabled={shouldDisableSubmitButton} /> @@ -466,7 +498,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea success={isWaitingForSubmissionFromCurrentUser} text={translate('common.submit')} style={[styles.flex1, styles.pr0]} - onPress={() => IOU.submitReport(moneyRequestReport)} + onPress={() => submitReport(moneyRequestReport)} isDisabled={shouldDisableSubmitButton} /> )} @@ -511,7 +543,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea isVisible={isDeleteRequestModalVisible} onConfirm={deleteTransaction} onCancel={() => setIsDeleteRequestModalVisible(false)} - onModalHide={() => ReportUtils.navigateBackOnDeleteTransaction(navigateBackToAfterDelete.current)} + onModalHide={() => navigateBackOnDeleteTransaction(navigateBackToAfterDelete.current)} prompt={translate('iou.deleteConfirmation', {count: 1})} confirmText={translate('common.delete')} cancelText={translate('common.cancel')}