Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 60 additions & 118 deletions src/components/MoneyReportHeaderOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,45 @@ import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaymentAnimations from '@hooks/usePaymentAnimations';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsActions';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {exportReportToCSV} from '@libs/actions/Report';
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';
import {getIOUActionForTransactionID, getOriginalMessage, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils';
import {getOriginalMessage, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils';
import {
canBeExported,
canDeleteCardTransactionByLiabilityType,
canDeleteTransaction,
getArchiveReason,
getBankAccountRoute,
getMoneyRequestSpendBreakdown,
getNonHeldAndFullAmount,
getTransactionsWithReceipts,
hasActionsWithErrors,
hasHeldExpenses as hasHeldExpensesReportUtils,
hasMissingSmartscanFields,
hasNoticeTypeViolations,
hasOnlyHeldExpenses as hasOnlyHeldExpensesReportUtils,
hasReportViolations,
hasUpdatedTotal,
hasViolations,
hasWarningTypeViolations,
isAllowedToApproveExpenseReport,
isAllowedToSubmitDraftExpenseReport,
isArchivedReportWithID,
isInvoiceReport,
isProcessingReport,
isReportApproved,
isReportOwner,
isSettled,
isWaitingForSubmissionFromCurrentUser as isWaitingForSubmissionFromCurrentUserReportUtils,
navigateBackOnDeleteTransaction,
reportTransactionsSelector,
} from '@libs/ReportUtils';
import {
allHavePendingRTERViolation,
checkIfShouldShowMarkAsCashButton,
getTransaction,
hasDuplicateTransactions,
isDuplicate as isDuplicateTransactionUtils,
isExpensifyCardTransaction,
Expand All @@ -64,7 +68,6 @@ import {
payInvoice,
payMoneyRequest,
submitReport,
unholdRequest,
} from '@userActions/IOU';
import {markAsCash as markAsCashAction} from '@userActions/Transaction';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -123,12 +126,10 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();
const route = useRoute();
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID || CONST.DEFAULT_NUMBER_ID}`);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
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 [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID}`, {canBeMissing: true});
const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID}`, {canBeMissing: true});
const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true});
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false});
const requestParentReportAction = useMemo(() => {
if (!reportActions || !transactionThreadReport?.parentReportActionID) {
return null;
Expand All @@ -138,9 +139,12 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {
selector: (_transactions) => reportTransactionsSelector(_transactions, moneyRequestReport?.reportID),
initialValue: [],
canBeMissing: true,
});
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(requestParentReportAction) && getOriginalMessage(requestParentReportAction)?.IOUTransactionID}`);
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true});
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${isMoneyRequestAction(requestParentReportAction) && getOriginalMessage(requestParentReportAction)?.IOUTransactionID}`, {
canBeMissing: true,
});
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true, canBeMissing: true});
const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);

const {isPaidAnimationRunning, isApprovedAnimationRunning, stopAnimation, startAnimation, startApprovedAnimation} = usePaymentAnimations();
Expand All @@ -149,7 +153,6 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
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);
Expand All @@ -170,7 +173,7 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
return !!transactions && transactions.length > 0 && transactions.every((t) => isExpensifyCardTransaction(t) && isPending(t));
}, [transactions]);
const transactionIDs = useMemo(() => transactions?.map((t) => t.transactionID) ?? [], [transactions]);
const [allViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const [allViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true});
const violations = useMemo(
() => Object.fromEntries(Object.entries(allViolations ?? {}).filter(([key]) => transactionIDs.includes(key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')))),
[allViolations, transactionIDs],
Expand All @@ -182,7 +185,7 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID);
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportWithID(moneyRequestReport?.reportID);
const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason});
const [archiveReason] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${moneyRequestReport?.reportID}`, {selector: getArchiveReason, canBeMissing: true});

const getCanIOUBePaid = useCallback(
(onlyShowPayElsewhere = false, shouldCheckApprovedState = true) =>
Expand All @@ -194,101 +197,9 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh

const {selectedTransactionsID, setSelectedTransactionsID} = useMoneyRequestReportContext();

const selectedTransactionsOptions = useMemo(() => {
if (!selectedTransactionsID.length) {
return [];
}
const options = [];
const selectedTransactions = selectedTransactionsID.map((transactionID) => getTransaction(transactionID)).filter((t) => !!t);

const anyTransactionOnHold = selectedTransactions.some(isOnHoldTransactionUtils);
const allTransactionOnHold = selectedTransactions.every(isOnHoldTransactionUtils);
const isReportReimbursed = moneyRequestReport?.stateNum === CONST.REPORT.STATE_NUM.APPROVED && moneyRequestReport?.statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED;

if (!anyTransactionOnHold && selectedTransactions.length === 1 && !isReportReimbursed) {
options.push({
text: translate('iou.hold'),
icon: Expensicons.Stopwatch,
value: CONST.REPORT.SECONDARY_ACTIONS.HOLD,
onSelected: () => {
if (!moneyRequestReport?.reportID) {
return;
}
Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS.getRoute({reportID: moneyRequestReport.reportID}));
},
});
}

if (allTransactionOnHold && selectedTransactions.length === 1) {
options.push({
text: translate('iou.unhold'),
icon: Expensicons.Stopwatch,
value: 'UNHOLD',
onSelected: () => {
selectedTransactionsID.forEach((transactionID) => {
const action = getIOUActionForTransactionID(reportActions, transactionID);
if (!action?.childReportID) {
return;
}
unholdRequest(transactionID, action?.childReportID);
});
// it's needed in order to recalculate options
setSelectedTransactionsID([...selectedTransactionsID]);
},
});
}

options.push({
value: CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD,
text: translate('common.download'),
icon: Expensicons.Download,
onSelected: () => {
if (!moneyRequestReport) {
return;
}
exportReportToCSV({reportID: moneyRequestReport.reportID, transactionIDList: selectedTransactionsID}, () => {
setIsDownloadErrorModalVisible(true);
});
},
});

const canAllSelectedTransactionsBeRemoved = selectedTransactionsID.every((transactionID) => {
const canRemoveTransaction = canDeleteCardTransactionByLiabilityType(transactionID);
const action = getIOUActionForTransactionID(reportActions, transactionID);
const isActionDeleted = isDeletedAction(action);
const isIOUActionOwner = typeof action?.actorAccountID === 'number' && typeof session?.accountID === 'number' && action.actorAccountID === session?.accountID;

return canRemoveTransaction && isIOUActionOwner && !isActionDeleted;
});

const canRemoveReportTransaction = canDeleteTransaction(moneyRequestReport);

if (canRemoveReportTransaction && canAllSelectedTransactionsBeRemoved) {
options.push({
text: translate('common.delete'),
icon: Expensicons.Trashcan,
value: CONST.REPORT.SECONDARY_ACTIONS.DELETE,
onSelected: () => {
const iouActions = reportActions.filter((action) => isMoneyRequestAction(action));

const transactionsWithActions = selectedTransactions.map((t) => ({
transactionID: t?.transactionID,
action: iouActions.find((action) => {
const IOUTransactionID = (getOriginalMessage(action) as OnyxTypes.OriginalMessageIOU)?.IOUTransactionID;

return t?.transactionID === IOUTransactionID;
}),
}));

transactionsWithActions.forEach(({transactionID, action}) => action && deleteMoneyRequest(transactionID, action));
setSelectedTransactionsID([]);
},
});
}
return options;
}, [moneyRequestReport, reportActions, selectedTransactionsID, session?.accountID, setSelectedTransactionsID, translate]);
const selectedTransactionsOptions = useSelectedTransactionsActions({report: moneyRequestReport, reportActions, session, onExportFailed: () => setIsDownloadErrorModalVisible(true)});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!


const shouldShowSelectedTransactionsButton = !!selectedTransactionsOptions.length;
const shouldShowSelectedTransactionsButton = !!selectedTransactionsOptions.length && !transactionThreadReportID;

const canIOUBePaid = useMemo(() => getCanIOUBePaid(), [getCanIOUBePaid]);
const canIOUBePaidAndApproved = useMemo(() => getCanIOUBePaid(false, false), [getCanIOUBePaid]);
Expand All @@ -311,7 +222,7 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
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 &&
Expand Down Expand Up @@ -346,14 +257,12 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
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);
const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA, {canBeMissing: true});

const isReportInRHP = route.name === SCREENS.SEARCH.REPORT_RHP;
const shouldDisplaySearchRouter = !isReportInRHP || isSmallScreenWidth;
Expand Down Expand Up @@ -497,8 +406,41 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
setIsDeleteRequestModalVisible(false);
}, [canDeleteRequest]);

useEffect(() => {
if (!transactionThreadReportID) {
return;
}
setSelectedTransactionsID([]);
// We don't need to run the effect on change of setSelectedTransactionsID since it can cause the infinite loop.
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [transactionThreadReportID]);

const shouldShowBackButton = shouldDisplayBackButton || shouldUseNarrowLayout;

const iouReportID = moneyRequestReport?.reportID;

const isIOUSettled = isSettled(iouReportID) || requestParentReportAction?.childStatusNum === CONST.REPORT.STATUS_NUM.REIMBURSED;

const shouldShowRBR =
((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)) &&
!isIOUSettled;

const buttonType = getIOUReportPreviewButtonType({
shouldShowPayButton,
shouldShowApproveButton,
shouldShowSubmitButton,
shouldShowSettlementButton,
shouldShowRBR,
shouldShowExportIntegrationButton,
});

return (
<View style={[styles.pt0, styles.borderBottom]}>
<HeaderWithBackButton
Expand Down Expand Up @@ -555,7 +497,7 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
shouldShowApproveButton={shouldShowApproveButton}
shouldDisableApproveButton={shouldDisableApproveButton}
style={[styles.pv2]}
formattedAmount={!hasOnlyHeldExpenses ? displayedAmount : ''}
formattedAmount={getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, buttonType)}
isDisabled={isOffline && !canAllowSettlement}
isLoading={!isOffline && !canAllowSettlement}
/>
Expand Down Expand Up @@ -634,7 +576,7 @@ function MoneyReportHeaderOld({policy, report: moneyRequestReport, transactionTh
addBankAccountRoute={bankAccountRoute}
shouldHidePaymentOptions={!shouldShowPayButton}
shouldShowApproveButton={shouldShowApproveButton}
formattedAmount={!hasOnlyHeldExpenses ? displayedAmount : ''}
formattedAmount={getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, buttonType)}
shouldDisableApproveButton={shouldDisableApproveButton}
isDisabled={isOffline && !canAllowSettlement}
isLoading={!isOffline && !canAllowSettlement}
Expand Down
Loading