From ce6a980995ed91e9f0eb8add14800f057e485445 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 24 Apr 2025 12:45:12 +0200 Subject: [PATCH 1/7] Fix amount value in header & report preview --- src/components/MoneyReportHeader.tsx | 42 +++++++++++++++---- .../ReportActionItem/ReportPreview.tsx | 29 ++++++------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index fdff6c8e7697..0522b1e20fd4 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -10,7 +10,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsActions'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {convertToDisplayString} from '@libs/CurrencyUtils'; +import {getIOUReportPreviewButtonType, getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; import Navigation from '@libs/Navigation/Navigation'; import {buildOptimisticNextStepForPreventSelfApprovalsEnabled} from '@libs/NextStepUtils'; import {getConnectedIntegration} from '@libs/PolicyUtils'; @@ -20,12 +20,17 @@ import { canDeleteTransaction, getArchiveReason, getBankAccountRoute, - getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getTransactionsWithReceipts, + hasActionsWithErrors, hasHeldExpenses as hasHeldExpensesReportUtils, + hasMissingSmartscanFields, + hasNoticeTypeViolations, hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils, + hasReportViolations, hasUpdatedTotal, + hasViolations, + hasWarningTypeViolations, isAllowedToApproveExpenseReport, isAllowedToSubmitDraftExpenseReport, isArchivedReportWithID, @@ -33,6 +38,7 @@ import { isProcessingReport, isReportApproved, isReportOwner, + isSettled, isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils, navigateBackOnDeleteTransaction, reportTransactionsSelector, @@ -149,7 +155,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const [isDeleteRequestModalVisible, setIsDeleteRequestModalVisible] = useState(false); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport); const isOnHold = isOnHoldTransactionUtils(transaction); const isDeletedParentAction = !!requestParentReportAction && isDeletedAction(requestParentReportAction); const isDuplicate = isDuplicateTransactionUtils(transaction?.transactionID) && (!isReportApproved({report: moneyRequestReport}) || isApprovedAnimationRunning); @@ -219,7 +224,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const filteredTransactions = transactions?.filter((t) => t) ?? []; const shouldShowSubmitButton = canSubmitReport(moneyRequestReport, policy, filteredTransactions, violations); - const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && canBeExported(moneyRequestReport); + const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && !!connectedIntegration && isAdmin && canBeExported(moneyRequestReport); const shouldShowSettlementButton = !shouldShowSelectedTransactionsButton && @@ -254,10 +259,8 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea shouldShowMarkAsCashButton || shouldShowExportIntegrationButton; 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(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); @@ -417,6 +420,29 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const shouldShowBackButton = shouldDisplayBackButton || shouldUseNarrowLayout; + const iouReportID = moneyRequestReport?.reportID; + + const iouSettled = isSettled(iouReportID) || requestParentReportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; + + const shouldShowRBR = + ((hasMissingSmartscanFields(iouReportID) && !iouSettled) || + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + hasViolations(iouReportID, violations, true) || + hasNoticeTypeViolations(iouReportID, violations, true) || + hasWarningTypeViolations(iouReportID, violations, true) || + (isReportOwner(moneyRequestReport) && hasReportViolations(iouReportID)) || + hasActionsWithErrors(iouReportID)) && + !iouSettled; + + const buttonType = getIOUReportPreviewButtonType({ + shouldShowPayButton, + shouldShowApproveButton, + shouldShowSubmitButton, + shouldShowSettlementButton, + shouldShowRBR, + shouldShowExportIntegrationButton, + }); + return ( @@ -552,7 +578,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea addBankAccountRoute={bankAccountRoute} shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} - formattedAmount={!hasOnlyHeldExpenses ? displayedAmount : ''} + formattedAmount={getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, buttonType)} shouldDisableApproveButton={shouldDisableApproveButton} isDisabled={isOffline && !canAllowSettlement} isLoading={!isOffline && !canAllowSettlement} diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 7fd486c3d736..bddafd95553c 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -30,6 +30,7 @@ import useTransactionViolations from '@hooks/useTransactionViolations'; import ControlSelection from '@libs/ControlSelection'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; +import {getIOUReportPreviewButtonType, getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; import Navigation from '@libs/Navigation/Navigation'; import Parser from '@libs/Parser'; import Performance from '@libs/Performance'; @@ -205,7 +206,7 @@ function ReportPreview({ const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(iouReport?.reportID); const managerID = iouReport?.managerID ?? action.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; - const {totalDisplaySpend, reimbursableSpend} = getMoneyRequestSpendBreakdown(iouReport); + const {totalDisplaySpend} = getMoneyRequestSpendBreakdown(iouReport); const [reports] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}`); const iouSettled = isSettled(iouReportID, isOnSearch ? reports : undefined) || action?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; const previewMessageOpacity = useSharedValue(1); @@ -301,19 +302,6 @@ function ReportPreview({ } }; - const getSettlementAmount = () => { - if (hasOnlyHeldExpenses) { - return ''; - } - - // We shouldn't display the nonHeldAmount as the default option if it's not valid since we cannot pay partially in this case - if (hasHeldExpensesReportUtils(iouReport?.reportID) && canAllowSettlement && hasValidNonHeldAmount) { - return nonHeldAmount; - } - - return convertToDisplayString(reimbursableSpend, iouReport?.currency); - }; - const getDisplayAmount = (): string => { if (totalDisplaySpend) { return convertToDisplayString(totalDisplaySpend, iouReport?.currency); @@ -475,7 +463,7 @@ function ReportPreview({ */ const connectedIntegration = getConnectedIntegration(policy); - const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && canBeExported(iouReport); + const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && !!connectedIntegration && isAdmin && canBeExported(iouReport); useEffect(() => { if (!isPaidAnimationRunning || isApprovedAnimationRunning) { @@ -516,6 +504,15 @@ function ReportPreview({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(iouReportID)); }, [iouReportID]); + const buttonType = getIOUReportPreviewButtonType({ + shouldShowPayButton, + shouldShowApproveButton, + shouldShowSubmitButton, + shouldShowSettlementButton, + shouldShowRBR, + shouldShowExportIntegrationButton, + }); + return ( Date: Thu, 24 Apr 2025 15:14:22 +0200 Subject: [PATCH 2/7] Fix eslint errors --- src/components/ReportActionItem/ReportPreview.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index bddafd95553c..1ea0abeedb8c 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -153,17 +153,19 @@ function ReportPreview({ shouldDisplayContextMenu = true, }: ReportPreviewProps) { const policy = usePolicy(policyID); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: true}); const [iouReport, transactions, violations] = useReportWithTransactionsAndViolations(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; - const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); + const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true}); const [invoiceReceiverPolicy] = useOnyx( - `${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : CONST.DEFAULT_NUMBER_ID}`, + `${ONYXKEYS.COLLECTION.POLICY}${chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : undefined}`, + {canBeMissing: true}, ); const [invoiceReceiverPersonalDetail] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { selector: (personalDetails) => personalDetails?.[chatReport?.invoiceReceiver && 'accountID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.accountID : CONST.DEFAULT_NUMBER_ID], + canBeMissing: true, }); const theme = useTheme(); const styles = useThemeStyles(); @@ -207,7 +209,7 @@ function ReportPreview({ const managerID = iouReport?.managerID ?? action.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; const {totalDisplaySpend} = getMoneyRequestSpendBreakdown(iouReport); - const [reports] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}`); + const [reports] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}`, {canBeMissing: true}); const iouSettled = isSettled(iouReportID, isOnSearch ? reports : undefined) || action?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; const previewMessageOpacity = useSharedValue(1); const previewMessageStyle = useAnimatedStyle(() => ({ @@ -413,7 +415,7 @@ function ReportPreview({ const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchantOrDescription || numberOfRequests > 1) && !isDisplayAmountZero(getDisplayAmount()); const isPayAtEndExpense = isPayAtEndExpenseReport(iouReportID, transactions); - const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`, {selector: getArchiveReason}); + const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReportID}`, {selector: getArchiveReason, canBeMissing: true}); const getPendingMessageProps: () => PendingMessageProps = () => { if (isPayAtEndExpense) { From 961d15f7d2fc1314b68dd9ac84a76b89f34a94e5 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 24 Apr 2025 17:27:58 +0200 Subject: [PATCH 3/7] Fix iouSettled name --- src/components/MoneyReportHeader.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 0522b1e20fd4..443b6a628b6d 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -422,17 +422,17 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const iouReportID = moneyRequestReport?.reportID; - const iouSettled = isSettled(iouReportID) || requestParentReportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; + const isIOUSettled = isSettled(iouReportID) || requestParentReportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; const shouldShowRBR = - ((hasMissingSmartscanFields(iouReportID) && !iouSettled) || + ((hasMissingSmartscanFields(iouReportID) && !isIOUSettled) || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing hasViolations(iouReportID, violations, true) || hasNoticeTypeViolations(iouReportID, violations, true) || hasWarningTypeViolations(iouReportID, violations, true) || (isReportOwner(moneyRequestReport) && hasReportViolations(iouReportID)) || hasActionsWithErrors(iouReportID)) && - !iouSettled; + !isIOUSettled; const buttonType = getIOUReportPreviewButtonType({ shouldShowPayButton, From ed21c74cec696d64c8720e4da2985cd9a747db4f Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 24 Apr 2025 19:05:04 +0200 Subject: [PATCH 4/7] Fix Eslint errors --- src/components/MoneyReportHeader.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index cc1b34180065..b1f473b6ce55 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -12,7 +12,6 @@ import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsAction import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {exportReportToCSV, exportToIntegration, markAsManuallyExported} from '@libs/actions/Report'; -import {convertToDisplayString} from '@libs/CurrencyUtils'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; import Navigation from '@libs/Navigation/Navigation'; import {buildOptimisticNextStepForPreventSelfApprovalsEnabled} from '@libs/NextStepUtils'; @@ -25,7 +24,6 @@ import { getArchiveReason, getBankAccountRoute, getIntegrationIcon, - getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getTransactionsWithReceipts, hasHeldExpenses as hasHeldExpensesReportUtils, @@ -158,7 +156,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const theme = useTheme(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); - const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport); const isOnHold = isOnHoldTransactionUtils(transaction); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); From 5ad07c7a58300dd01bea5cf91b3d9c485fba5643 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 24 Apr 2025 19:34:03 +0200 Subject: [PATCH 5/7] Revert changing new components --- src/components/MoneyReportHeader.tsx | 10 ++++-- .../ReportActionItem/ReportPreview.tsx | 32 ++++++++++++------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index b1f473b6ce55..726e0ecbb8aa 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -12,7 +12,7 @@ import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsAction import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {exportReportToCSV, exportToIntegration, markAsManuallyExported} from '@libs/actions/Report'; -import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; +import {convertToDisplayString} from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {buildOptimisticNextStepForPreventSelfApprovalsEnabled} from '@libs/NextStepUtils'; import {getConnectedIntegration} from '@libs/PolicyUtils'; @@ -24,6 +24,7 @@ import { getArchiveReason, getBankAccountRoute, getIntegrationIcon, + getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getTransactionsWithReceipts, hasHeldExpenses as hasHeldExpensesReportUtils, @@ -156,6 +157,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const theme = useTheme(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); + const {reimbursableSpend} = getMoneyRequestSpendBreakdown(moneyRequestReport); const isOnHold = isOnHoldTransactionUtils(transaction); const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false); @@ -224,8 +226,10 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea const shouldShowNextStep = isFromPaidPolicy && !!optimisticNextStep?.message?.length && !shouldShowStatusBar; 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 {isDelegateAccessRestricted} = useDelegateUserDetails(); const [isNoDelegateAccessMenuVisible, setIsNoDelegateAccessMenuVisible] = useState(false); const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true}); @@ -376,7 +380,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea