diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 4eb3be871a8d..c9e73a125fee 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -13,6 +13,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
+import * as TransactionActions from '@userActions/Transaction';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
@@ -125,9 +126,10 @@ function MoneyReportHeader({
const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0 && !allHavePendingRTERViolation;
const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport);
+ const shouldShowMarkAsCashButton = isDraft && allHavePendingRTERViolation;
const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE;
const shouldShowNextStep = !ReportUtils.isClosedExpenseReportWithNoExpenses(moneyRequestReport) && isFromPaidPolicy && !!nextStep?.message?.length && !allHavePendingRTERViolation;
- const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep;
+ const shouldShowAnyButton = shouldShowSettlementButton || shouldShowApproveButton || shouldShowSubmitButton || shouldShowNextStep || allHavePendingRTERViolation;
const bankAccountRoute = ReportUtils.getBankAccountRoute(chatReport);
const formattedAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, moneyRequestReport.currency);
const [nonHeldAmount, fullAmount] = ReportUtils.getNonHeldAndFullAmount(moneyRequestReport, policy);
@@ -169,6 +171,16 @@ function MoneyReportHeader({
setIsDeleteRequestModalVisible(false);
}, [moneyRequestReport?.reportID, requestParentReportAction, setIsDeleteRequestModalVisible]);
+ const markAsCash = useCallback(() => {
+ if (!requestParentReportAction) {
+ return;
+ }
+ const iouTransactionID = requestParentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? requestParentReportAction.originalMessage?.IOUTransactionID ?? '' : '';
+ const reportID = transactionThreadReport?.reportID ?? '';
+
+ TransactionActions.markAsCash(iouTransactionID, reportID);
+ }, [requestParentReportAction, transactionThreadReport?.reportID]);
+
// The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on
const isWaitingForSubmissionFromCurrentUser = useMemo(
() => chatReport?.isOwnPolicyExpenseChat && !policy?.harvesting?.enabled,
@@ -249,20 +261,44 @@ function MoneyReportHeader({
/>
)}
+ {shouldShowMarkAsCashButton && !shouldUseNarrowLayout && (
+
+
+
+ )}
{allHavePendingRTERViolation && (
-
- }
- description={translate('iou.pendingMatchWithCreditCardDescription')}
- shouldShowBorderBottom
- />
+
+ {shouldShowMarkAsCashButton && shouldUseNarrowLayout && (
+
+
+
+ )}
+
+ }
+ description={translate('iou.pendingMatchWithCreditCardDescription')}
+ shouldShowBorderBottom
+ />
+
)}
{shouldShowSettlementButton && shouldUseNarrowLayout && (
diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 7c122f120e8a..13e8eb7be432 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -14,12 +14,14 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
+import * as TransactionActions from '@userActions/Transaction';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Policy, Report, ReportAction, ReportActions, Session, Transaction, TransactionViolations} from '@src/types/onyx';
import type {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage';
import type IconAsset from '@src/types/utils/IconAsset';
+import Button from './Button';
import ConfirmModal from './ConfirmModal';
import HeaderWithBackButton from './HeaderWithBackButton';
import Icon from './Icon';
@@ -86,6 +88,7 @@ function MoneyRequestHeader({
const moneyRequestReport = parentReport;
const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID);
const isApproved = ReportUtils.isReportApproved(moneyRequestReport);
+ const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport);
const isOnHold = TransactionUtils.isOnHold(transaction);
const {windowWidth} = useWindowDimensions();
@@ -93,6 +96,8 @@ function MoneyRequestHeader({
const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && parentReportAction.actorAccountID === session?.accountID;
const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID;
+ const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID ?? '']);
+ const shouldShowMarkAsCashButton = isDraft && hasAllPendingRTERViolations;
const deleteTransaction = useCallback(() => {
if (parentReportAction) {
@@ -107,6 +112,10 @@ function MoneyRequestHeader({
setIsDeleteModalVisible(false);
}, [parentReport?.reportID, parentReportAction, setIsDeleteModalVisible]);
+ const markAsCash = useCallback(() => {
+ TransactionActions.markAsCash(transaction?.transactionID ?? '', report.reportID);
+ }, [report.reportID, transaction?.transactionID]);
+
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
const isDeletedParentAction = ReportActionsUtils.isDeletedAction(parentReportAction);
@@ -143,12 +152,16 @@ function MoneyRequestHeader({
if (TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction)) {
return {title: getStatusIcon(Expensicons.CreditCardHourglass), description: translate('iou.transactionPendingDescription'), shouldShowBorderBottom: true};
}
+ if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) {
+ return {
+ title: getStatusIcon(Expensicons.Hourglass),
+ description: translate('iou.pendingMatchWithCreditCardDescription'),
+ shouldShowBorderBottom: true,
+ };
+ }
if (isScanning) {
return {title: getStatusIcon(Expensicons.ReceiptScan), description: translate('iou.receiptScanInProgressDescription'), shouldShowBorderBottom: true};
}
- if (TransactionUtils.hasPendingRTERViolation(TransactionUtils.getTransactionViolations(transaction?.transactionID ?? '', transactionViolations))) {
- return {title: getStatusIcon(Expensicons.Hourglass), description: translate('iou.pendingMatchWithCreditCardDescription'), shouldShowBorderBottom: true};
- }
};
const statusBarProps = getStatusBarProps();
@@ -231,7 +244,28 @@ function MoneyRequestHeader({
policy={policy}
shouldShowBackButton={shouldUseNarrowLayout}
onBackButtonPress={onBackButtonPress}
- />
+ >
+ {shouldShowMarkAsCashButton && !shouldUseNarrowLayout && (
+
+ )}
+
+ {shouldShowMarkAsCashButton && shouldUseNarrowLayout && (
+
+
+
+ )}
{statusBarProps && (
`${count === 1 ? 'Problema encontrado' : 'Problemas encontrados'}`,
fieldPending: 'Pendiente...',
diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts
index 8274a4af20d6..afb8e00d5f07 100644
--- a/src/libs/actions/Transaction.ts
+++ b/src/libs/actions/Transaction.ts
@@ -11,7 +11,7 @@ import {buildOptimisticDismissedViolationReportAction} from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import type {RecentWaypoint, ReportActions, Transaction, TransactionViolation} from '@src/types/onyx';
+import type {RecentWaypoint, ReportActions, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx';
import type {OnyxData} from '@src/types/onyx/Request';
import type {WaypointCollection} from '@src/types/onyx/Transaction';
@@ -33,6 +33,12 @@ Onyx.connect({
},
});
+let allTransactionViolations: TransactionViolations = [];
+Onyx.connect({
+ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
+ callback: (val) => (allTransactionViolations = val ?? []),
+});
+
function createInitialWaypoints(transactionID: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {
comment: {
@@ -265,7 +271,7 @@ function clearError(transactionID: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null, errorFields: {route: null}});
}
-function markAsCash(transactionID: string, transactionThreadReportID: string, existingViolations: TransactionViolation[]) {
+function markAsCash(transactionID: string, transactionThreadReportID: string) {
const optimisticReportAction = buildOptimisticDismissedViolationReportAction({
reason: 'manual',
violationName: CONST.VIOLATIONS.RTER,
@@ -279,7 +285,7 @@ function markAsCash(transactionID: string, transactionThreadReportID: string, ex
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`,
- value: existingViolations.filter((violation: TransactionViolation) => violation.name !== CONST.VIOLATIONS.RTER),
+ value: allTransactionViolations.filter((violation: TransactionViolation) => violation.name !== CONST.VIOLATIONS.RTER),
},
// Optimistically adding the system message indicating we dismissed the violation
{
@@ -293,7 +299,7 @@ function markAsCash(transactionID: string, transactionThreadReportID: string, ex
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`,
- value: existingViolations,
+ value: allTransactionViolations,
},
{
onyxMethod: Onyx.METHOD.MERGE,
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 8620d8d4866e..f1a5bbe10231 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -344,6 +344,11 @@ function ReportScreen({
/>
);
+ const transactionThreadReportID = useMemo(
+ () => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], false, isOffline),
+ [report.reportID, reportActions, isOffline],
+ );
+
if (isSingleTransactionView) {
headerView = (
ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], false, isOffline),
- [report.reportID, reportActions, isOffline],
- );
-
useEffect(() => {
if (!transactionThreadReportID || !route.params.reportActionID) {
return;