From cd8c36caa0a48cde013436e83552295e632f0474 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 28 Dec 2023 11:23:24 +0100 Subject: [PATCH 01/16] unrevert --- src/CONST.ts | 3 + src/components/MoneyReportHeader.js | 28 +++++++- src/languages/en.ts | 5 ++ src/languages/es.ts | 5 ++ src/languages/types.ts | 3 + src/libs/OptionsListUtils.js | 2 +- src/libs/ReportUtils.ts | 48 ++++++++++++- src/libs/actions/IOU.js | 103 ++++++++++++++++++++++++++++ 8 files changed, 192 insertions(+), 5 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 34720adf8a21..9ed1b91a5903 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -595,6 +595,9 @@ const CONST = { }, }, }, + CANCEL_PAYMENT_REASONS: { + ADMIN: 'CANCEL_REASON_ADMIN', + }, ARCHIVE_REASON: { DEFAULT: 'default', ACCOUNT_CLOSED: 'accountClosed', diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index a559e876af18..535c1194229e 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -1,6 +1,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useMemo} from 'react'; +import React, {useCallback, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -24,7 +24,9 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import Button from './Button'; +import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; +import * as Expensicons from './Icon/Expensicons'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; import participantPropTypes from './participantPropTypes'; import SettlementButton from './SettlementButton'; @@ -89,6 +91,13 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt isPolicyAdmin && (isApproved || isManager) : isPolicyAdmin || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && isManager); const isDraft = ReportUtils.isDraftExpenseReport(moneyRequestReport); + const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); + + const cancelPayment = useCallback(() => { + IOU.cancelPayment(moneyRequestReport, chatReport); + setIsConfirmModalVisible(false); + }, [moneyRequestReport, chatReport]); + const shouldShowPayButton = useMemo( () => isPayer && !isDraft && !isSettled && !moneyRequestReport.isWaitingOnBankAccount && reimbursableTotal !== 0 && !ReportUtils.isArchivedRoom(chatReport), [isPayer, isDraft, isSettled, moneyRequestReport, reimbursableTotal, chatReport], @@ -109,6 +118,13 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const isMoreContentShown = shouldShowNextStep || (shouldShowAnyButton && isSmallScreenWidth); const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)]; + if (isPayer && isSettled) { + threeDotsMenuItems.push({ + icon: Expensicons.Trashcan, + text: 'Cancel payment', + onSelected: () => setIsConfirmModalVisible(true), + }); + } if (!ReportUtils.isArchivedRoom(chatReport)) { threeDotsMenuItems.push({ icon: ZoomIcon, @@ -206,6 +222,16 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt )} + 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 3cdb7cf2bf98..2fd453e4ba48 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2,6 +2,7 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST'; import CONST from '@src/CONST'; import type { AddressLineParams, + AdminCanceledRequestParams, AlreadySignedInParams, AmountEachParams, ApprovedAmountParams, @@ -96,6 +97,7 @@ type AllCountries = Record; export default { common: { cancel: 'Cancel', + dismiss: 'Dismiss', yes: 'Yes', no: 'No', ok: 'OK', @@ -549,6 +551,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', @@ -585,6 +589,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: ({amount}: AdminCanceledRequestParams) => `The ${amount} payment has been cancelled by the admin.`, 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 d11211ca9325..8c21e9ccf6f5 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1,6 +1,7 @@ import CONST from '@src/CONST'; import type { AddressLineParams, + AdminCanceledRequestParams, AlreadySignedInParams, AmountEachParams, ApprovedAmountParams, @@ -86,6 +87,7 @@ import type { export default { common: { cancel: 'Cancelar', + dismiss: 'Descartar', yes: 'Sí', no: 'No', ok: 'OK', @@ -542,6 +544,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ó', @@ -578,6 +582,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: ({amount}: AdminCanceledRequestParams) => `El pago de ${amount} ha sido cancelado por el administrador.`, 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 8e72c700a9cc..1a4b833b4122 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -131,6 +131,8 @@ type WaitingOnBankAccountParams = {submitterDisplayName: string}; type CanceledRequestParams = {amount: string; submitterDisplayName: string}; +type AdminCanceledRequestParams = {amount: string}; + type SettledAfterAddedBankAccountParams = {submitterDisplayName: string; amount: string}; type PaidElsewhereWithAmountParams = {payer: string; amount: string}; @@ -293,6 +295,7 @@ export type { PayerSettledParams, WaitingOnBankAccountParams, CanceledRequestParams, + AdminCanceledRequestParams, SettledAfterAddedBankAccountParams, PaidElsewhereWithAmountParams, PaidWithExpensifyWithAmountParams, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 6e84ef4dca27..1c2899f14c94 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -402,7 +402,7 @@ function getLastMessageTextForReport(report) { } 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 (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 81bbf1df6273..331fc03990bb 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -15,7 +15,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import {IOUMessage, OriginalMessageActionName, OriginalMessageCreated, ReimbursementDeQueuedMessage} from '@src/types/onyx/OriginalMessage'; import {NotificationPreference} from '@src/types/onyx/Report'; import {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -176,6 +176,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' @@ -1535,9 +1540,13 @@ function getReimbursementQueuedActionMessage(reportAction: OnyxEntry): string { +function getReimbursementDeQueuedActionMessage(reportAction: OnyxEntry, report: OnyxEntry): string { + const amount = CurrencyUtils.convertToDisplayString(Math.abs(report?.total ?? 0), report?.currency); + const originalMessage = reportAction?.originalMessage as ReimbursementDeQueuedMessage | undefined; + if (originalMessage?.cancellationReason === CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN) { + return Localize.translateLocal('iou.adminCanceledRequest', {amount}); + } const submitterDisplayName = getDisplayNameForParticipant(report?.ownerAccountID, true) ?? ''; - const amount = CurrencyUtils.convertToDisplayString(report?.total ?? 0, report?.currency); return Localize.translateLocal('iou.canceledRequest', {submitterDisplayName, amount}); } @@ -2787,6 +2796,38 @@ function buildOptimisticSubmittedReportAction(amount: number, currency: string, }; } +/** + * Builds an optimistic REIMBURSEMENTDEQUEUED report action with a randomly generated reportActionID. + * + */ +function buildOptimisticCancelPaymentReportAction(): OptimisticCancelPaymentReportAction { + return { + actionName: CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED, + actorAccountID: currentUserAccountID, + message: [ + { + cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN, + type: CONST.REPORT.MESSAGE.TYPE.COMMENT, + text: '', + }, + ], + originalMessage: { + cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN, + }, + 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, + }; +} + /** * Builds an optimistic report preview action with a randomly generated reportActionID. * @@ -4271,6 +4312,7 @@ export { buildOptimisticIOUReportAction, buildOptimisticReportPreview, buildOptimisticModifiedExpenseReportAction, + buildOptimisticCancelPaymentReportAction, updateReportPreview, buildOptimisticTaskReportAction, buildOptimisticAddCommentReportAction, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d43fefca20bc..add8bded5e25 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3114,6 +3114,108 @@ function submitReport(expenseReport) { ); } +/** + * @param {Object} expenseReport + * @param {Object} chatReport + */ +function cancelPayment(expenseReport, chatReport) { + const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(); + const policy = ReportUtils.getPolicy(chatReport.policyID); + const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + value: { + [optimisticReportAction.reportActionID]: { + ...optimisticReportAction, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, + value: { + ...expenseReport, + lastMessageText: lodashGet(optimisticReportAction, 'message.0.text', ''), + lastMessageHtml: lodashGet(optimisticReportAction, 'message.0.html', ''), + state: isFree ? CONST.REPORT.STATE.SUBMITTED : CONST.REPORT.STATE.OPEN, + stateNum: isFree ? CONST.REPORT.STATE_NUM.PROCESSING : CONST.REPORT.STATE.OPEN, + statusNum: isFree ? CONST.REPORT.STATUS.SUBMITTED : CONST.REPORT.STATE.OPEN, + }, + }, + ...(chatReport.reportID + ? [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + ...chatReport, + hasOutstandingIOU: true, + hasOutstandingChildRequest: true, + iouReportID: expenseReport.reportID, + }, + }, + ] + : []), + ]; + + const successData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, + value: { + [optimisticReportAction.reportActionID]: { + pendingAction: null, + }, + }, + }, + ]; + + const failureData = [ + { + 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.REIMBURSED, + }, + }, + ...(chatReport.reportID + ? [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + hasOutstandingIOU: false, + hasOutstandingChildRequest: false, + iouReportID: 0, + }, + }, + ] + : []), + ]; + + API.write( + 'CancelPayment', + { + reportID: expenseReport.reportID, + managerAccountID: expenseReport.managerID, + reportActionID: optimisticReportAction.reportActionID, + }, + {optimisticData, successData, failureData}, + ); +} + /** * @param {String} paymentType * @param {Object} chatReport @@ -3431,4 +3533,5 @@ export { detachReceipt, getIOUReportID, editMoneyRequest, + cancelPayment, }; From 05f0e3db60f3a7851eefbbd6131d27f8dc9a5f2b Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 28 Dec 2023 11:28:42 +0100 Subject: [PATCH 02/16] a couple extra files --- src/pages/home/report/ReportActionItem.js | 5 +---- src/types/onyx/OriginalMessage.ts | 6 ++++++ src/types/onyx/ReportAction.ts | 6 +++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 57627d819197..763c6610fb14 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -422,10 +422,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(lodashGet(personalDetails, [props.report.ownerAccountID, 'displayName'])); - const amount = CurrencyUtils.convertToDisplayString(props.report.total, props.report.currency); - - children = ; + children = ; } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE) { children = ; } else { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 767f724dd571..5ed696a9019b 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -43,10 +43,15 @@ type IOUMessage = { participantAccountIDs?: number[]; type: ValueOf; paymentType?: DeepValueOf; + cancellationReason?: string; /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; +type ReimbursementDeQueuedMessage = { + cancellationReason: string; +}; + type OriginalMessageIOU = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.IOU; originalMessage: IOUMessage; @@ -260,6 +265,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 a881b63fbb95..02adc8ae0b95 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -51,9 +51,13 @@ type Message = { translationKey?: string; /** ID of a task report */ - taskReportID?: string; + taskReportID?: string + + /** Reason pf payment cancellation */ + cancellationReason?: string; }; + type ImageMetadata = { /** The height of the image. */ height?: number; From 47426208214aff0536fc61aa49824a8b188fbc50 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 28 Dec 2023 11:58:40 +0100 Subject: [PATCH 03/16] only allow in expense reports --- src/components/MoneyReportHeader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 535c1194229e..ce749a027704 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -118,7 +118,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const isMoreContentShown = shouldShowNextStep || (shouldShowAnyButton && isSmallScreenWidth); const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)]; - if (isPayer && isSettled) { + if (isPayer && isSettled && ReportUtils.isExpenseReport(moneyRequestReport)) { threeDotsMenuItems.push({ icon: Expensicons.Trashcan, text: 'Cancel payment', From b7932d686605b53d460fe96ffc4527659db70adb Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 2 Jan 2024 11:26:38 +0100 Subject: [PATCH 04/16] add reportID to original message --- src/libs/ReportUtils.ts | 3 ++- src/libs/actions/IOU.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 331fc03990bb..3f735e130c2d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2800,13 +2800,14 @@ function buildOptimisticSubmittedReportAction(amount: number, currency: string, * Builds an optimistic REIMBURSEMENTDEQUEUED report action with a randomly generated reportActionID. * */ -function buildOptimisticCancelPaymentReportAction(): OptimisticCancelPaymentReportAction { +function buildOptimisticCancelPaymentReportAction(expenseReportID: 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: '', }, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index add8bded5e25..337c69764ecf 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3119,7 +3119,7 @@ function submitReport(expenseReport) { * @param {Object} chatReport */ function cancelPayment(expenseReport, chatReport) { - const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(); + const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID); const policy = ReportUtils.getPolicy(chatReport.policyID); const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; const optimisticData = [ From c7518ab4c8f6942c6b8cc88fa0214745dc3191b2 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 2 Jan 2024 11:40:42 +0100 Subject: [PATCH 05/16] copy correct message --- src/libs/ReportUtils.ts | 1 + src/pages/home/report/ContextMenu/ContextMenuActions.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3f735e130c2d..d3c4a7d0a01d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2814,6 +2814,7 @@ function buildOptimisticCancelPaymentReportAction(expenseReportID: string): Opti ], originalMessage: { cancellationReason: CONST.REPORT.CANCEL_PAYMENT_REASONS.ADMIN, + expenseReportID, }, person: [ { diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index f1a46785a59a..dec687e06688 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -291,7 +291,13 @@ export default [ } else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) { const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportAction); Clipboard.setString(modifyExpenseMessage); - } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { + }else if (ReportActionUtils.isReimbursementDeQueuedAction(reportAction)) { + const {expenseReportID} = reportAction.originalMessage; + const expenseReport = ReportUtils.getReport(expenseReportID); + console.log(reportAction); + const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReport); + Clipboard.setString(displayMessage); + }else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); } else if (ReportActionsUtils.isCreatedTaskReportAction(reportAction)) { From 7a119c9155eca1d83ab5e4b1844f1e9b7a9eb910 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 2 Jan 2024 11:42:58 +0100 Subject: [PATCH 06/16] typo --- src/pages/home/report/ContextMenu/ContextMenuActions.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index dec687e06688..c465035b2a25 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -291,10 +291,9 @@ export default [ } else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) { const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportAction); Clipboard.setString(modifyExpenseMessage); - }else if (ReportActionUtils.isReimbursementDeQueuedAction(reportAction)) { + }else if (ReportActionsUtils.isReimbursementDeQueuedAction(reportAction)) { const {expenseReportID} = reportAction.originalMessage; const expenseReport = ReportUtils.getReport(expenseReportID); - console.log(reportAction); const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReport); Clipboard.setString(displayMessage); }else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { From 8aa11a4f95ba71c69b134c8e6529f75fd7eab294 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 2 Jan 2024 12:13:55 +0100 Subject: [PATCH 07/16] Correctly copy owes message --- src/libs/ReportUtils.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d3c4a7d0a01d..c956928c5ac7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4112,17 +4112,22 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry) const formattedAmount = CurrencyUtils.convertToDisplayString(amount, currency) ?? ''; const payerName = isExpenseReport(iouReport) ? getPolicyName(iouReport) : getDisplayNameForParticipant(iouReport?.managerID, true); - switch (originalMessage.paymentType) { - case CONST.IOU.PAYMENT_TYPE.ELSEWHERE: - translationKey = 'iou.paidElsewhereWithAmount'; - break; - case CONST.IOU.PAYMENT_TYPE.EXPENSIFY: - case CONST.IOU.PAYMENT_TYPE.VBBA: - translationKey = 'iou.paidWithExpensifyWithAmount'; - break; - default: - translationKey = 'iou.payerPaidAmount'; - break; + // If the payment was cancelled, show the "Owes" message + if (!isSettled(IOUReportID)) { + translationKey = 'iou.payerOwesAmount'; + } else { + switch (originalMessage.paymentType) { + case CONST.IOU.PAYMENT_TYPE.ELSEWHERE: + translationKey = 'iou.paidElsewhereWithAmount'; + break; + case CONST.IOU.PAYMENT_TYPE.EXPENSIFY: + case CONST.IOU.PAYMENT_TYPE.VBBA: + translationKey = 'iou.paidWithExpensifyWithAmount'; + break; + default: + translationKey = 'iou.payerPaidAmount'; + break; + } } return Localize.translateLocal(translationKey, {amount: formattedAmount, payer: payerName ?? ''}); } From 274a58acde359ba362385d785ad358d6f2231d78 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 2 Jan 2024 12:49:52 +0100 Subject: [PATCH 08/16] pass chat report --- src/libs/actions/IOU.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 337c69764ecf..9188fe56920f 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3208,7 +3208,8 @@ function cancelPayment(expenseReport, chatReport) { API.write( 'CancelPayment', { - reportID: expenseReport.reportID, + iouReportID: expenseReport.reportID, + chatReportID: chatReport.reportID, managerAccountID: expenseReport.managerID, reportActionID: optimisticReportAction.reportActionID, }, From 134f074ae8d23d32fa5bc342e017a9e7fee87787 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 3 Jan 2024 13:22:45 +0100 Subject: [PATCH 09/16] lint --- src/pages/home/report/ReportActionItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e177d309adb2..fe84cc812f48 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -37,7 +37,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 focusTextInputAfterAnimation from '@libs/focusTextInputAfterAnimation'; import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage'; From 09e7e8ad24a84c361f6966bafa9d69e388b908c7 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 3 Jan 2024 13:24:25 +0100 Subject: [PATCH 10/16] typescript --- src/types/onyx/OriginalMessage.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 5ed696a9019b..e5803774632e 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -36,6 +36,7 @@ type IOUMessage = { /** The ID of the iou transaction */ IOUTransactionID?: string; IOUReportID?: string; + expenseReportID?: string; amount: number; comment?: string; currency: string; From de3678b3814a8475f4b860eafac02a7d06dae27d Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 3 Jan 2024 13:30:25 +0100 Subject: [PATCH 11/16] prettier --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.js | 42 +++++++++---------- .../report/ContextMenu/ContextMenuActions.js | 4 +- src/types/onyx/ReportAction.ts | 3 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index feb68c5169a2..30f64cd8d134 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -181,7 +181,7 @@ type OptimisticSubmittedReportAction = Pick< type OptimisticCancelPaymentReportAction = Pick< ReportAction, 'actionName' | 'actorAccountID' | 'message' | 'originalMessage' | 'person' | 'reportActionID' | 'shouldShow' | 'created' | 'pendingAction' - >; +>; type OptimisticEditedTaskReportAction = Pick< ReportAction, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 43eef2b4e46c..549719a6906d 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3191,17 +3191,17 @@ function cancelPayment(expenseReport, chatReport) { }, ...(chatReport.reportID ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - ...chatReport, - hasOutstandingIOU: true, - hasOutstandingChildRequest: true, - iouReportID: expenseReport.reportID, - }, - }, - ] + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + ...chatReport, + hasOutstandingIOU: true, + hasOutstandingChildRequest: true, + iouReportID: expenseReport.reportID, + }, + }, + ] : []), ]; @@ -3236,16 +3236,16 @@ function cancelPayment(expenseReport, chatReport) { }, ...(chatReport.reportID ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingIOU: false, - hasOutstandingChildRequest: false, - iouReportID: 0, - }, - }, - ] + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + hasOutstandingIOU: false, + hasOutstandingChildRequest: false, + iouReportID: 0, + }, + }, + ] : []), ]; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index f6fe30817d35..66502ad1d4ab 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -281,12 +281,12 @@ export default [ } else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) { const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportAction); Clipboard.setString(modifyExpenseMessage); - }else if (ReportActionsUtils.isReimbursementDeQueuedAction(reportAction)) { + } 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)) { + } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) { const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); } else if (ReportActionsUtils.isCreatedTaskReportAction(reportAction)) { diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 02adc8ae0b95..cea5a5a570b5 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -51,13 +51,12 @@ type Message = { translationKey?: string; /** ID of a task report */ - taskReportID?: string + taskReportID?: string; /** Reason pf payment cancellation */ cancellationReason?: string; }; - type ImageMetadata = { /** The height of the image. */ height?: number; From cb2daae3cc629a84c1783985a7b916547efa8466 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 3 Jan 2024 13:34:09 +0100 Subject: [PATCH 12/16] more typescript --- src/types/onyx/ReportAction.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index cea5a5a570b5..95f0a97bdbeb 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -53,8 +53,11 @@ type Message = { /** ID of a task report */ taskReportID?: string; - /** Reason pf payment cancellation */ + /** Reason of payment cancellation */ cancellationReason?: string; + + /** ID of an expense report */ + expenseReportID?: string; }; type ImageMetadata = { From cd9e8ee5295417223ffe3b92c0910095e2a6e345 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 5 Jan 2024 14:25:53 +0100 Subject: [PATCH 13/16] lint --- src/components/HoldMenuSectionList.tsx | 7 ++++--- src/components/ProcessMoneyRequestHoldMenu.tsx | 4 ++-- src/components/TextPill.tsx | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/HoldMenuSectionList.tsx b/src/components/HoldMenuSectionList.tsx index 9a9857f037f2..24c5bb663cf9 100644 --- a/src/components/HoldMenuSectionList.tsx +++ b/src/components/HoldMenuSectionList.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import {ImageSourcePropType, View} from 'react-native'; -import {SvgProps} from 'react-native-svg'; +import type {ImageSourcePropType} from 'react-native'; +import { View} from 'react-native'; +import type {SvgProps} from 'react-native-svg'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; -import {TranslationPaths} from '@src/languages/types'; +import type {TranslationPaths} from '@src/languages/types'; import Icon from './Icon'; import * as Illustrations from './Icon/Illustrations'; import Text from './Text'; diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index be72fdb98a8b..1b711633ed3b 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -4,9 +4,9 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Button from './Button'; import HoldMenuSectionList from './HoldMenuSectionList'; -import {PopoverAnchorPosition} from './Modal/types'; +import type {PopoverAnchorPosition} from './Modal/types'; import Popover from './Popover'; -import {AnchorAlignment} from './Popover/types'; +import type {AnchorAlignment} from './Popover/types'; import Text from './Text'; import TextPill from './TextPill'; diff --git a/src/components/TextPill.tsx b/src/components/TextPill.tsx index 035ae1dd42d8..6d473b189534 100644 --- a/src/components/TextPill.tsx +++ b/src/components/TextPill.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {StyleProp, TextStyle} from 'react-native'; +import type {StyleProp, TextStyle} from 'react-native'; // eslint-disable-next-line no-restricted-imports import useThemeStyles from '@hooks/useThemeStyles'; import colors from '@styles/theme/colors'; From 39ecbe26f62c980a2a92290acf696885d0fded69 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 5 Jan 2024 14:30:23 +0100 Subject: [PATCH 14/16] prettier --- src/components/HoldMenuSectionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HoldMenuSectionList.tsx b/src/components/HoldMenuSectionList.tsx index 24c5bb663cf9..cbaad537647b 100644 --- a/src/components/HoldMenuSectionList.tsx +++ b/src/components/HoldMenuSectionList.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type {ImageSourcePropType} from 'react-native'; -import { View} from 'react-native'; +import {View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; From 7425c00e075e127a058d36da78e1baaab418c793 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 11 Jan 2024 13:02:18 +0100 Subject: [PATCH 15/16] translate menu --- src/components/MoneyReportHeader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 3d6e4bde830c..ce1c9611c733 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -132,7 +132,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt if (isPayer && isSettled && ReportUtils.isExpenseReport(moneyRequestReport)) { threeDotsMenuItems.push({ icon: Expensicons.Trashcan, - text: 'Cancel payment', + text: translate('iou.cancelPayment'), onSelected: () => setIsConfirmModalVisible(true), }); } From 429f277778456dcac42cba65f33cf09c076e6b58 Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 12 Jan 2024 15:42:46 +0100 Subject: [PATCH 16/16] Display correct message --- src/pages/home/report/ReportActionItemMessage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemMessage.tsx b/src/pages/home/report/ReportActionItemMessage.tsx index 3a71ee8356b3..025b0cbb8b0a 100644 --- a/src/pages/home/report/ReportActionItemMessage.tsx +++ b/src/pages/home/report/ReportActionItemMessage.tsx @@ -57,7 +57,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); } }