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; };