diff --git a/src/CONST.ts b/src/CONST.ts
index f7de601a36c7..1889fe0acfd6 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -651,6 +651,9 @@ const CONST = {
},
THREAD_DISABLED: ['CREATED'],
},
+ CANCEL_PAYMENT_REASONS: {
+ ADMIN: 'CANCEL_REASON_ADMIN',
+ },
ACTIONABLE_MENTION_WHISPER_RESOLUTION: {
INVITE: 'invited',
NOTHING: 'nothing',
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 0e08aed214a6..3f551da788f5 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -1,4 +1,4 @@
-import React, {useMemo} from 'react';
+import React, {useCallback, useMemo, useState} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
@@ -17,7 +17,9 @@ import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import Button from './Button';
+import ConfirmModal from './ConfirmModal';
import HeaderWithBackButton from './HeaderWithBackButton';
+import * as Expensicons from './Icon/Expensicons';
import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar';
import {usePersonalDetails} from './OnyxProvider';
import SettlementButton from './SettlementButton';
@@ -62,6 +64,16 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
isPolicyAdmin && (isApproved || isManager)
: isPolicyAdmin || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && isManager);
const isDraft = ReportUtils.isDraftExpenseReport(moneyRequestReport);
+ const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false);
+
+ const cancelPayment = useCallback(() => {
+ if (!chatReport) {
+ return;
+ }
+ IOU.cancelPayment(moneyRequestReport, chatReport);
+ setIsConfirmModalVisible(false);
+ }, [moneyRequestReport, chatReport]);
+
const isOnInstantSubmitPolicy = PolicyUtils.isInstantSubmitEnabled(policy);
const isOnSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
const shouldShowPayButton = useMemo(
@@ -93,6 +105,13 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
);
const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)];
+ if (isPayer && isSettled && ReportUtils.isExpenseReport(moneyRequestReport)) {
+ threeDotsMenuItems.push({
+ icon: Expensicons.Trashcan,
+ text: translate('iou.cancelPayment'),
+ onSelected: () => setIsConfirmModalVisible(true),
+ });
+ }
return (
@@ -178,6 +197,16 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money
)}
+ setIsConfirmModalVisible(false)}
+ prompt={translate('iou.cancelPaymentConfirmation')}
+ confirmText={translate('iou.cancelPayment')}
+ cancelText={translate('common.dismiss')}
+ danger
+ />
);
}
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 0098eb517b15..0baaee4c07a8 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -4,6 +4,7 @@ import CONST from '@src/CONST';
import type {Country} from '@src/CONST';
import type {
AddressLineParams,
+ AdminCanceledRequestParams,
AlreadySignedInParams,
AmountEachParams,
ApprovedAmountParams,
@@ -114,6 +115,7 @@ type AllCountries = Record;
export default {
common: {
cancel: 'Cancel',
+ dismiss: 'Dismiss',
yes: 'Yes',
no: 'No',
ok: 'OK',
@@ -583,6 +585,8 @@ export default {
requestMoney: 'Request money',
sendMoney: 'Send money',
pay: 'Pay',
+ cancelPayment: 'Cancel payment',
+ cancelPaymentConfirmation: 'Are you sure that you want to cancel this payment?',
viewDetails: 'View details',
pending: 'Pending',
canceled: 'Canceled',
@@ -621,6 +625,7 @@ export default {
payerSettled: ({amount}: PayerSettledParams) => `paid ${amount}`,
approvedAmount: ({amount}: ApprovedAmountParams) => `approved ${amount}`,
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `started settling up, payment is held until ${submitterDisplayName} adds a bank account`,
+ adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager} cancelled the ${amount} payment.`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
`Canceled the ${amount} payment, because ${submitterDisplayName} did not enable their Expensify Wallet within 30 days`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
diff --git a/src/languages/es.ts b/src/languages/es.ts
index a61df46d5eaa..ae566f3033f0 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -2,6 +2,7 @@ import Str from 'expensify-common/lib/str';
import CONST from '@src/CONST';
import type {
AddressLineParams,
+ AdminCanceledRequestParams,
AlreadySignedInParams,
AmountEachParams,
ApprovedAmountParams,
@@ -104,6 +105,7 @@ import type {
export default {
common: {
cancel: 'Cancelar',
+ dismiss: 'Descartar',
yes: 'Sí',
no: 'No',
ok: 'OK',
@@ -576,6 +578,8 @@ export default {
requestMoney: 'Pedir dinero',
sendMoney: 'Enviar dinero',
pay: 'Pagar',
+ cancelPayment: 'Cancelar el pago',
+ cancelPaymentConfirmation: '¿Estás seguro de que quieres cancelar este pago?',
viewDetails: 'Ver detalles',
pending: 'Pendiente',
canceled: 'Canceló',
@@ -614,6 +618,7 @@ export default {
payerSettled: ({amount}: PayerSettledParams) => `pagó ${amount}`,
approvedAmount: ({amount}: ApprovedAmountParams) => `aprobó ${amount}`,
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `inicio el pago, pero no se procesará hasta que ${submitterDisplayName} añada una cuenta bancaria`,
+ adminCanceledRequest: ({manager, amount}: AdminCanceledRequestParams) => `${manager} canceló el pago de ${amount}.`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
`Canceló el pago ${amount}, porque ${submitterDisplayName} no habilitó su billetera Expensify en un plazo de 30 días.`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
diff --git a/src/languages/types.ts b/src/languages/types.ts
index fb5026684c67..f7e580819fdf 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -135,6 +135,8 @@ type WaitingOnBankAccountParams = {submitterDisplayName: string};
type CanceledRequestParams = {amount: string; submitterDisplayName: string};
+type AdminCanceledRequestParams = {manager: string; amount: string};
+
type SettledAfterAddedBankAccountParams = {submitterDisplayName: string; amount: string};
type PaidElsewhereWithAmountParams = {payer?: string; amount: string};
@@ -292,6 +294,7 @@ type ElectronicFundsParams = {percentage: string; amount: string};
type LogSizeParams = {size: number};
export type {
+ AdminCanceledRequestParams,
ApprovedAmountParams,
AddressLineParams,
AlreadySignedInParams,
diff --git a/src/libs/API/parameters/CancelPaymentParams.ts b/src/libs/API/parameters/CancelPaymentParams.ts
new file mode 100644
index 000000000000..7546b4beeba0
--- /dev/null
+++ b/src/libs/API/parameters/CancelPaymentParams.ts
@@ -0,0 +1,8 @@
+type CancelPaymentParams = {
+ iouReportID: string;
+ chatReportID: string;
+ managerAccountID: number;
+ reportActionID: string;
+};
+
+export default CancelPaymentParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index e93a93213eeb..ada3b84e6cf4 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -139,5 +139,6 @@ export type {default as ReplaceReceiptParams} from './ReplaceReceiptParams';
export type {default as SubmitReportParams} from './SubmitReportParams';
export type {default as DetachReceiptParams} from './DetachReceiptParams';
export type {default as PayMoneyRequestParams} from './PayMoneyRequestParams';
+export type {default as CancelPaymentParams} from './CancelPaymentParams';
export type {default as AcceptACHContractForBankAccount} from './AcceptACHContractForBankAccount';
export type {default as UpdateWorkspaceDescriptionParams} from './UpdateWorkspaceDescriptionParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 8cf0b2a806ef..250ee76d58e1 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -141,6 +141,7 @@ const WRITE_COMMANDS = {
DETACH_RECEIPT: 'DetachReceipt',
PAY_MONEY_REQUEST_WITH_WALLET: 'PayMoneyRequestWithWallet',
PAY_MONEY_REQUEST: 'PayMoneyRequest',
+ CANCEL_PAYMENT: 'CancelPayment',
ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT: 'AcceptACHContractForBankAccount',
UPDATE_WORKSPACE_DESCRIPTION: 'UpdateWorkspaceDescription',
} as const;
@@ -281,6 +282,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.DETACH_RECEIPT]: Parameters.DetachReceiptParams;
[WRITE_COMMANDS.PAY_MONEY_REQUEST_WITH_WALLET]: Parameters.PayMoneyRequestParams;
[WRITE_COMMANDS.PAY_MONEY_REQUEST]: Parameters.PayMoneyRequestParams;
+ [WRITE_COMMANDS.CANCEL_PAYMENT]: Parameters.CancelPaymentParams;
[WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT]: Parameters.AcceptACHContractForBankAccount;
[WRITE_COMMANDS.UPDATE_WORKSPACE_DESCRIPTION]: Parameters.UpdateWorkspaceDescriptionParams;
};
diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts
index bbd7678a9679..e2af10649bad 100644
--- a/src/libs/OptionsListUtils.ts
+++ b/src/libs/OptionsListUtils.ts
@@ -561,7 +561,7 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails
} else if (ReportActionUtils.isReimbursementQueuedAction(lastReportAction)) {
lastMessageTextFromReport = ReportUtils.getReimbursementQueuedActionMessage(lastReportAction, report);
} else if (ReportActionUtils.isReimbursementDeQueuedAction(lastReportAction)) {
- lastMessageTextFromReport = ReportUtils.getReimbursementDeQueuedActionMessage(report);
+ lastMessageTextFromReport = ReportUtils.getReimbursementDeQueuedActionMessage(lastReportAction, report);
} else if (ReportActionUtils.isDeletedParentAction(lastReportAction) && ReportUtils.isChatReport(report)) {
lastMessageTextFromReport = ReportUtils.getDeletedParentActionMessageForChatReport(lastReportAction);
} else if (ReportActionUtils.isPendingRemove(lastReportAction) && ReportActionUtils.isThreadParentMessage(lastReportAction, report?.reportID ?? '')) {
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index 3658fb53a722..0364d879c8a9 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -393,6 +393,12 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry, key:
return false;
}
+ // Ignore markedAsReimbursed action here since we're already display message that explains the request was paid
+ // elsewhere in the IOU reportAction
+ if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.MARKEDREIMBURSED) {
+ return false;
+ }
+
if (isWhisperActionTargetedToOthers(reportAction)) {
return false;
}
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 8dda1a176237..ebde1b1bf8ab 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -31,7 +31,16 @@ import type {
} from '@src/types/onyx';
import type {Participant} from '@src/types/onyx/IOU';
import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon';
-import type {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated, OriginalMessageRenamed, PaymentMethodType} from '@src/types/onyx/OriginalMessage';
+import type {
+ ChangeLog,
+ IOUMessage,
+ OriginalMessageActionName,
+ OriginalMessageCreated,
+ OriginalMessageReimbursementDequeued,
+ OriginalMessageRenamed,
+ PaymentMethodType,
+ ReimbursementDeQueuedMessage,
+} from '@src/types/onyx/OriginalMessage';
import type {Status} from '@src/types/onyx/PersonalDetails';
import type {NotificationPreference} from '@src/types/onyx/Report';
import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction';
@@ -186,6 +195,11 @@ type OptimisticSubmittedReportAction = Pick<
'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'isAttachment' | 'originalMessage' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction'
>;
+type OptimisticCancelPaymentReportAction = Pick<
+ ReportAction,
+ 'actionName' | 'actorAccountID' | 'message' | 'originalMessage' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction'
+>;
+
type OptimisticEditedTaskReportAction = Pick<
ReportAction,
'reportActionID' | 'actionName' | 'pendingAction' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'shouldShow' | 'message' | 'person'
@@ -1714,11 +1728,55 @@ function getReimbursementQueuedActionMessage(reportAction: OnyxEntry): string {
+function getReimbursementDeQueuedActionMessage(reportAction: OnyxEntry, report: OnyxEntry | EmptyObject): string {
+ const originalMessage = reportAction?.originalMessage as ReimbursementDeQueuedMessage | undefined;
+ const amount = originalMessage?.amount;
+ const currency = originalMessage?.currency;
+ const formattedAmount = CurrencyUtils.convertToDisplayString(amount, currency);
+ if (originalMessage?.cancellationReason === CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN) {
+ const payerOrApproverName = isExpenseReport(report) ? getPolicyName(report, false) : getDisplayNameForParticipant(report?.managerID) ?? '';
+ return Localize.translateLocal('iou.adminCanceledRequest', {manager: payerOrApproverName, amount: formattedAmount});
+ }
const submitterDisplayName = getDisplayNameForParticipant(report?.ownerAccountID, true) ?? '';
- const amount = CurrencyUtils.convertToDisplayString(report?.total ?? 0, report?.currency);
+ return Localize.translateLocal('iou.canceledRequest', {submitterDisplayName, amount: formattedAmount});
+}
- return Localize.translateLocal('iou.canceledRequest', {submitterDisplayName, amount});
+/**
+ * Builds an optimistic REIMBURSEMENTDEQUEUED report action with a randomly generated reportActionID.
+ *
+ */
+function buildOptimisticCancelPaymentReportAction(expenseReportID: string, amount: number, currency: string): OptimisticCancelPaymentReportAction {
+ return {
+ actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED,
+ actorAccountID: currentUserAccountID,
+ message: [
+ {
+ cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN,
+ expenseReportID,
+ type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
+ text: '',
+ amount,
+ currency,
+ },
+ ],
+ originalMessage: {
+ cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN,
+ expenseReportID,
+ amount,
+ currency,
+ },
+ person: [
+ {
+ style: 'strong',
+ text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
+ type: 'TEXT',
+ },
+ ],
+ reportActionID: NumberUtils.rand64(),
+ shouldShow: true,
+ created: DateUtils.getDBTime(),
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ };
}
/**
@@ -1977,10 +2035,6 @@ function getMoneyRequestReportName(report: OnyxEntry, policy: OnyxEntry<
return `${payerPaidAmountMessage} • ${Localize.translateLocal('iou.pending')}`;
}
- if (report?.isCancelledIOU) {
- return `${payerPaidAmountMessage} • ${Localize.translateLocal('iou.canceled')}`;
- }
-
if (hasNonReimbursableTransactions(report?.reportID)) {
return Localize.translateLocal('iou.payerSpentAmount', {payer: payerOrApproverName, amount: formattedAmount});
}
@@ -4559,7 +4613,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry)
// differentiate between these two scenarios, we check if the `originalMessage` contains the `IOUDetails`
// property. If it does, it indicates that this is a 'Send money' action.
const {amount, currency} = originalMessage.IOUDetails ?? originalMessage;
- const formattedAmount = CurrencyUtils.convertToDisplayString(amount, currency) ?? '';
+ const formattedAmount = CurrencyUtils.convertToDisplayString(Math.abs(amount), currency) ?? '';
const payerName = isExpenseReport(iouReport) ? getPolicyName(iouReport) : getDisplayNameForParticipant(iouReport?.managerID, true);
switch (originalMessage.paymentType) {
@@ -4945,6 +4999,7 @@ export {
buildOptimisticIOUReportAction,
buildOptimisticReportPreview,
buildOptimisticModifiedExpenseReportAction,
+ buildOptimisticCancelPaymentReportAction,
updateReportPreview,
buildOptimisticTaskReportAction,
buildOptimisticAddCommentReportAction,
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 7fca6614f1a1..ea06cfddfa11 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -3621,6 +3621,95 @@ function submitReport(expenseReport: OnyxTypes.Report) {
API.write(WRITE_COMMANDS.SUBMIT_REPORT, parameters, {optimisticData, successData, failureData});
}
+function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxTypes.Report) {
+ const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID, -(expenseReport.total ?? 0), expenseReport.currency ?? '');
+ const policy = ReportUtils.getPolicy(chatReport.policyID);
+ const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE;
+ const approvalMode = policy.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC;
+ let stateNum: ValueOf = CONST.REPORT.STATE_NUM.SUBMITTED;
+ let statusNum: ValueOf = CONST.REPORT.STATUS_NUM.SUBMITTED;
+ if (!isFree) {
+ stateNum = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.APPROVED;
+ statusNum = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.APPROVED;
+ }
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticReportAction.reportActionID]: {
+ ...(optimisticReportAction as OnyxTypes.ReportAction),
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ },
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ ...expenseReport,
+ lastMessageText: optimisticReportAction.message?.[0].text,
+ lastMessageHtml: optimisticReportAction.message?.[0].html,
+ stateNum,
+ statusNum,
+ },
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticReportAction.reportActionID]: {
+ pendingAction: null,
+ },
+ },
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [expenseReport.reportActionID ?? '']: {
+ errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'),
+ },
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED,
+ },
+ },
+ ];
+
+ if (chatReport?.reportID) {
+ failureData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`,
+ value: {
+ hasOutstandingChildRequest: true,
+ iouReportID: expenseReport.reportID,
+ },
+ });
+ }
+
+ API.write(
+ WRITE_COMMANDS.CANCEL_PAYMENT,
+ {
+ iouReportID: expenseReport.reportID,
+ chatReportID: chatReport.reportID,
+ managerAccountID: expenseReport.managerID ?? 0,
+ reportActionID: optimisticReportAction.reportActionID,
+ },
+ {optimisticData, successData, failureData},
+ );
+}
+
function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report) {
const recipient = {accountID: iouReport.ownerAccountID};
const {params, optimisticData, successData, failureData} = getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentType);
@@ -3901,6 +3990,7 @@ export {
detachReceipt,
getIOUReportID,
editMoneyRequest,
+ cancelPayment,
navigateToStartStepIfScanFileCannotBeRead,
savePreferredPaymentMethod,
};
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
index 64a7d1813255..1e634794eec2 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
@@ -353,6 +353,11 @@ const ContextMenuActions: ContextMenuAction[] = [
} else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) {
const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportID, reportAction);
Clipboard.setString(modifyExpenseMessage);
+ } else if (ReportActionsUtils.isReimbursementDeQueuedAction(reportAction)) {
+ const {expenseReportID} = reportAction.originalMessage;
+ const expenseReport = ReportUtils.getReport(expenseReportID);
+ const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReport);
+ Clipboard.setString(displayMessage);
} else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) {
const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction);
Clipboard.setString(displayMessage);
diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js
index 859f9c183d80..2d768a851c4e 100644
--- a/src/pages/home/report/ReportActionItem.js
+++ b/src/pages/home/report/ReportActionItem.js
@@ -38,7 +38,6 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import ControlSelection from '@libs/ControlSelection';
-import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as ErrorUtils from '@libs/ErrorUtils';
import focusTextInputAfterAnimation from '@libs/focusTextInputAfterAnimation';
@@ -446,10 +445,7 @@ function ReportActionItem(props) {
);
} else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED) {
- const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(personalDetails, props.report.ownerAccountID));
- const amount = CurrencyUtils.convertToDisplayString(props.report.total, props.report.currency);
-
- children = ;
+ children = ;
} else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE) {
children = ;
} else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.MARKEDREIMBURSED) {
diff --git a/src/pages/home/report/ReportActionItemMessage.tsx b/src/pages/home/report/ReportActionItemMessage.tsx
index 0ffb8b9f6964..ac3238002906 100644
--- a/src/pages/home/report/ReportActionItemMessage.tsx
+++ b/src/pages/home/report/ReportActionItemMessage.tsx
@@ -58,7 +58,7 @@ function ReportActionItemMessage({action, displayAsGroup, reportID, style, isHid
const originalMessage = action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? action.originalMessage : null;
const iouReportID = originalMessage?.IOUReportID;
if (iouReportID) {
- iouMessage = ReportUtils.getReportPreviewMessage(ReportUtils.getReport(iouReportID), action);
+ iouMessage = ReportUtils.getIOUReportActionDisplayMessage(action);
}
}
diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts
index 7cfaa968e2ad..6a498215c17b 100644
--- a/src/types/onyx/OriginalMessage.ts
+++ b/src/types/onyx/OriginalMessage.ts
@@ -39,17 +39,26 @@ type IOUMessage = {
/** The ID of the iou transaction */
IOUTransactionID?: string;
IOUReportID?: string;
+ expenseReportID?: string;
amount: number;
comment?: string;
currency: string;
lastModified?: string;
participantAccountIDs?: number[];
type: ValueOf;
+ cancellationReason?: string;
paymentType?: PaymentMethodType;
/** Only exists when we are sending money */
IOUDetails?: IOUDetails;
};
+type ReimbursementDeQueuedMessage = {
+ cancellationReason: string;
+ expenseReportID?: string;
+ amount: number;
+ currency: string;
+};
+
type OriginalMessageIOU = {
actionName: typeof CONST.REPORT.ACTIONS.TYPE.IOU;
originalMessage: IOUMessage;
@@ -139,6 +148,11 @@ type OriginalMessageCreated = {
originalMessage?: unknown;
};
+type OriginalMessageMarkedReimbursed = {
+ actionName: typeof CONST.REPORT.ACTIONS.TYPE.MARKEDREIMBURSED;
+ originalMessage?: unknown;
+};
+
type OriginalMessageRenamed = {
actionName: typeof CONST.REPORT.ACTIONS.TYPE.RENAMED;
originalMessage: {
@@ -269,7 +283,8 @@ type OriginalMessage =
| OriginalMessageModifiedExpense
| OriginalMessageReimbursementQueued
| OriginalMessageReimbursementDequeued
- | OriginalMessageMoved;
+ | OriginalMessageMoved
+ | OriginalMessageMarkedReimbursed;
export default OriginalMessage;
export type {
@@ -278,6 +293,7 @@ export type {
Reaction,
ActionName,
IOUMessage,
+ ReimbursementDeQueuedMessage,
Closed,
OriginalMessageActionName,
ChangeLog,
diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts
index 3ec98fd43178..8f732a253cb5 100644
--- a/src/types/onyx/ReportAction.ts
+++ b/src/types/onyx/ReportAction.ts
@@ -54,6 +54,18 @@ type Message = {
/** ID of a task report */
taskReportID?: string;
+ /** Reason of payment cancellation */
+ cancellationReason?: string;
+
+ /** ID of an expense report */
+ expenseReportID?: string;
+
+ /** Amount of an expense */
+ amount?: number;
+
+ /** Currency of an expense */
+ currency?: string;
+
/** resolution for actionable mention whisper */
resolution?: ValueOf | null;
};