From 5d3ff13af72c61f68571211354ab0754b9f85def Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:19:43 +0100 Subject: [PATCH 01/75] add new tranlsations --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 9955d6957ea8..973d7b4803fd 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -439,6 +439,7 @@ const translations = { showMore: 'Show more', merchant: 'Merchant', category: 'Category', + report: 'Report', billable: 'Billable', nonBillable: 'Non-billable', tag: 'Tag', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6f8d22970367..b47bc8523e96 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -432,6 +432,7 @@ const translations = { showMore: 'Mostrar más', merchant: 'Comerciante', category: 'Categoría', + report: 'Informe', billable: 'Facturable', nonBillable: 'No facturable', tag: 'Etiqueta', From 54a1e4bbc3ed344bc40c8e863f76eb2f8b2e4f81 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:19:52 +0100 Subject: [PATCH 02/75] add new action type --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 745236dc52ec..34c420bcc254 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1210,6 +1210,7 @@ const CONST = { MERGED_WITH_CASH_TRANSACTION: 'MERGEDWITHCASHTRANSACTION', MODIFIED_EXPENSE: 'MODIFIEDEXPENSE', MOVED: 'MOVED', + MOVED_TRANSACTION: 'MOVEDTRANSACTION', OUTDATED_BANK_ACCOUNT: 'OUTDATEDBANKACCOUNT', // OldDot Action REIMBURSED: 'REIMBURSED', REIMBURSEMENT_ACH_BOUNCE: 'REIMBURSEMENTACHBOUNCE', // OldDot Action From 4680cab9c3cb712046981b725cea9132b4ff1d6d Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:20:33 +0100 Subject: [PATCH 03/75] add new screen and route --- src/ROUTES.ts | 5 +++++ src/SCREENS.ts | 1 + .../Navigation/AppNavigator/ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/libs/Navigation/types.ts | 7 +++++++ .../iou/request/step/withFullTransactionOrNotFound.tsx | 1 + 6 files changed, 16 insertions(+) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index e722caeed905..480b290dac1a 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -604,6 +604,11 @@ const ROUTES = { getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = 0, backTo = '') => getUrlWithBackToParam(`${action as string}/${iouType as string}/subrate/${transactionID}/${reportID}/edit/${pageIndex}`, backTo), }, + MONEY_REQUEST_STEP_REPORT: { + route: ':action/:iouType/report/:transactionID/:reportID/edit', + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => + getUrlWithBackToParam(`${action as string}/${iouType as string}/report/${transactionID}/${reportID}/edit`, backTo), + }, SETTINGS_TAGS_ROOT: { route: 'settings/:policyID/tags', getRoute: (policyID: string | undefined, backTo = '') => { diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 526bcbaec712..a6655d550706 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -249,6 +249,7 @@ const SCREENS = { STEP_DESTINATION_EDIT: 'Money_Request_Destination_Edit', STEP_TIME_EDIT: 'Money_Request_Time_Edit', STEP_SUBRATE_EDIT: 'Money_Request_SubRate_Edit', + STEP_REPORT: 'Money_Request_Report', }, TRANSACTION_DUPLICATE: { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 5bb4659b2d09..78f1cbefbe8a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -96,6 +96,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepWaypoint').default, [SCREENS.MONEY_REQUEST.STEP_SPLIT_PAYER]: () => require('../../../../pages/iou/request/step/IOURequestStepSplitPayer').default, [SCREENS.MONEY_REQUEST.STEP_SEND_FROM]: () => require('../../../../pages/iou/request/step/IOURequestStepSendFrom').default, + [SCREENS.MONEY_REQUEST.STEP_REPORT]: () => require('../../../../pages/iou/request/step/IOURequestStepReport').default, [SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO]: () => require('../../../../pages/iou/request/step/IOURequestStepCompanyInfo').default, [SCREENS.MONEY_REQUEST.HOLD]: () => require('../../../../pages/iou/HoldReasonPage').default, [SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f56b71e6519e..a9135338dbf5 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1271,6 +1271,7 @@ const config: LinkingOptions['config'] = { [SCREENS.SETTINGS_CATEGORIES.SETTINGS_CATEGORIES_ROOT]: ROUTES.SETTINGS_CATEGORIES_ROOT.route, [SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_ROOT]: ROUTES.SETTINGS_TAGS_ROOT.route, [SCREENS.MONEY_REQUEST.STEP_SEND_FROM]: ROUTES.MONEY_REQUEST_STEP_SEND_FROM.route, + [SCREENS.MONEY_REQUEST.STEP_REPORT]: ROUTES.MONEY_REQUEST_STEP_REPORT.route, [SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO]: ROUTES.MONEY_REQUEST_STEP_COMPANY_INFO.route, [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: ROUTES.MONEY_REQUEST_STEP_CATEGORY.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 9d2a12f8d70c..c91150ffefe8 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1149,6 +1149,13 @@ type MoneyRequestNavigatorParamList = { reportID: string; backTo: Routes; }; + [SCREENS.MONEY_REQUEST.STEP_REPORT]: { + action: IOUAction; + iouType: IOUType; + transactionID: string; + reportID: string; + backTo: Routes; + }; [SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO]: { iouType: IOUType; transactionID: string; diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index 3f0512e94c46..a422a52474b5 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -40,6 +40,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_SCAN | typeof SCREENS.MONEY_REQUEST.STEP_CURRENCY | typeof SCREENS.MONEY_REQUEST.STEP_SEND_FROM + | typeof SCREENS.MONEY_REQUEST.STEP_REPORT | typeof SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO | typeof SCREENS.MONEY_REQUEST.STEP_DESTINATION | typeof SCREENS.MONEY_REQUEST.STEP_TIME From 0371346b07151ce8658d6c42f274cfe945cd81b2 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:20:43 +0100 Subject: [PATCH 04/75] update withWritable --- src/pages/iou/request/step/withWritableReportOrNotFound.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index e3ffa11a2e24..c5cb47d7b5d7 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -40,6 +40,7 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT | typeof SCREENS.MONEY_REQUEST.STEP_SCAN | typeof SCREENS.MONEY_REQUEST.STEP_SEND_FROM + | typeof SCREENS.MONEY_REQUEST.STEP_REPORT | typeof SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO | typeof SCREENS.MONEY_REQUEST.STEP_ATTENDEES | typeof SCREENS.MONEY_REQUEST.STEP_UPGRADE From 7168ce69f26f1d7b0290d28661c6c285f88e0fb1 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:21:07 +0100 Subject: [PATCH 05/75] add new report action build --- src/libs/ReportUtils.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cca39fb9ec4a..08ed2bb685dc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5810,6 +5810,44 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine }; } +/** + * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. + * This action is used when we change the workspace of a report. + */ +function buildOptimisticMovedTransactionAction(oldReportID: string, toReportID: string, transactionID: string): ReportAction { + const originalMessage = { + oldReportID, + toReportID, + transactionID, + }; + + const changePolicyReportActionMessage = [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + text: `moved the transaction to the ${toReportID}`, + }, + ]; + + return { + actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + actorAccountID: currentUserAccountID, + avatar: getCurrentUserAvatar(), + created: DateUtils.getDBTime(), + originalMessage, + message: changePolicyReportActionMessage, + person: [ + { + style: 'strong', + text: getCurrentUserDisplayNameOrEmail(), + type: 'TEXT', + }, + ], + reportActionID: rand64(), + shouldShow: true, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; +} + /** * Builds an optimistic SUBMITTED report action with a randomly generated reportActionID. * @@ -9723,6 +9761,8 @@ export { buildOptimisticChangePolicyReportAction, getPolicyChangeMessage, getExpenseReportStateAndStatus, + populateOptimisticReportFormula, + buildOptimisticMovedTransactionAction, }; export type { From b97b0cdf7e4b5ae70d0efaef04c4e9240dcc4531 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:26:59 +0100 Subject: [PATCH 06/75] update confirmation footer --- .../MoneyRequestConfirmationListFooter.tsx | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index e8b23b164b9e..35d2782a646c 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit, isMultiLevelTags as isMultiLevelTagsPolicyUtils, isPaidGroupPolicy} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; +import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, isExpenseReport, populateOptimisticReportFormula} from '@libs/ReportUtils'; import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -239,6 +239,8 @@ function MoneyRequestConfirmationListFooter({ const {translate, toLocaleDigit} = useLocalize(); const {isOffline} = useNetwork(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email}); // A flag and a toggler for showing the rest of the form fields @@ -264,6 +266,25 @@ function MoneyRequestConfirmationListFooter({ return canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]); + const outstandingReport = Object.values(allReports ?? {}) + .filter( + (report) => + isExpenseReport(report) && + report?.stateNum && + report?.statusNum && + report?.policyID === senderWorkspace?.id && + report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && + report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, + ) + .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) + .at(0); + + let reportName = outstandingReport?.reportName; + if (!reportName) { + const optimisticReport = buildOptimisticExpenseReport(reportID, policy?.id, policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID, Number(formattedAmount), currency); + reportName = populateOptimisticReportFormula(policy?.fieldList?.text_title?.defaultValue ?? '', optimisticReport, policy); + } + const isTypeSend = iouType === CONST.IOU.TYPE.PAY; const taxRates = policy?.taxRates ?? null; // In Send Money and Split Bill with Scan flow, we don't allow the Merchant or Date to be edited. For distance requests, don't show the merchant as there's already another "Distance" menu item @@ -616,6 +637,28 @@ function MoneyRequestConfirmationListFooter({ shouldShow: shouldShowBillable, isSupplementary: true, }, + { + item: ( + { + if (!transactionID) { + return; + } + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute())); + }} + interactive + shouldRenderAsHTML + /> + ), + shouldShow: isPolicyExpenseChat, + isSupplementary: true, + }, ]; const subRates = getSubratesFields(perDiemCustomUnit, transaction); From 2666643d82948f64bb90c5667fefa1a0b5d875c8 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:27:11 +0100 Subject: [PATCH 07/75] update moneyRequestView --- .../ReportActionItem/MoneyRequestView.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index eab05a799c4f..c3f9aa7ea760 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -780,6 +780,27 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals /> )} + {!!report?.reportID && ( + + { + if (!report?.reportID || !transaction?.transactionID) { + return; + } + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_REPORT.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID, report.reportID, getReportRHPActiveRoute()), + ); + }} + interactive + shouldRenderAsHTML + /> + + )} ); From 72c84dfb8e6777d61701637a95e21435d2409399 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 17 Mar 2025 23:27:21 +0100 Subject: [PATCH 08/75] create new screen --- .../iou/request/step/IOURequestStepReport.tsx | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/pages/iou/request/step/IOURequestStepReport.tsx diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx new file mode 100644 index 000000000000..c2b75bb7d171 --- /dev/null +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -0,0 +1,99 @@ +import React, {useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import SelectionList from '@components/SelectionList'; +import type {ListItem} from '@components/SelectionList/types'; +import UserListItem from '@components/SelectionList/UserListItem'; +import useLocalize from '@hooks/useLocalize'; +import Navigation from '@libs/Navigation/Navigation'; +import {isExpenseReport} from '@libs/ReportUtils'; +import * as IOU from '@userActions/IOU'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import StepScreenWrapper from './StepScreenWrapper'; +import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; +import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; + +type ReportListItem = ListItem & { + value: string; +}; + +type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & + WithFullTransactionOrNotFoundProps; + +function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { + const {translate} = useLocalize(); + const {transactionID, backTo} = route.params; + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + + const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]); + + const reportOptions: ReportListItem[] = useMemo(() => { + if (!allReports) { + return []; + } + + const expenseReports = Object.values(allReports).filter( + (report) => + isExpenseReport(report) && + report?.stateNum && + report?.statusNum && + report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && + report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, + ); + + return ( + expenseReports + .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) + .map( + (report) => + report && { + text: report.reportName, + value: report.reportID, + keyForList: report.reportID, + }, + ) ?? [] + ); + }, [allReports]); + + const navigateBack = () => { + Navigation.goBack(backTo); + }; + + const selectReport = (item: ReportListItem) => { + const newParticipants = (transaction?.participants ?? []).filter((participant) => participant.accountID); + + newParticipants.push({ + policyID: item.value, + isSender: true, + selected: false, + }); + + IOU.setMoneyRequestParticipants(transactionID, newParticipants); + navigateBack(); + }; + + return ( + + + + ); +} + +IOURequestStepReport.displayName = 'IOURequestStepReport'; + +export default withWritableReportOrNotFound(withFullTransactionOrNotFound(IOURequestStepReport)); From 2f52373cfb2bef7dc8f563ba31085ea2ac062647 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:14:14 +0100 Subject: [PATCH 09/75] add new UNREPORTED_TRANSACTION --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 34c420bcc254..9dc0536c34b8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1211,6 +1211,7 @@ const CONST = { MODIFIED_EXPENSE: 'MODIFIEDEXPENSE', MOVED: 'MOVED', MOVED_TRANSACTION: 'MOVEDTRANSACTION', + UNREPORTED_TRANSACTION: 'UNREPORTEDTRANSACTION', OUTDATED_BANK_ACCOUNT: 'OUTDATEDBANKACCOUNT', // OldDot Action REIMBURSED: 'REIMBURSED', REIMBURSEMENT_ACH_BOUNCE: 'REIMBURSEMENTACHBOUNCE', // OldDot Action From 48c3444f58d4ee3acc677bdcbb15901b1b2f7b0d Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:15:14 +0100 Subject: [PATCH 10/75] add new translations --- src/languages/en.ts | 4 ++++ src/languages/es.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 973d7b4803fd..76a33363429d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -108,6 +108,7 @@ import type { MarkReimbursedFromIntegrationParams, MissingPropertyParams, MovedFromPersonalSpaceParams, + MovedTransactionParams, NeedCategoryForExportToIntegrationParams, NewWorkspaceNameParams, NoLongerHaveAccessParams, @@ -179,6 +180,7 @@ import type { TrialStartedTitleParams, UnapprovedParams, UnapproveWithIntegrationWarningParams, + UnreportedTransactionParams, UnshareParams, UntilTimeParams, UpdatedPolicyCategoryNameParams, @@ -900,6 +902,8 @@ const translations = { posted: 'Posted', deleteReceipt: 'Delete receipt', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `deleted an expense on this report, ${merchant} - ${amount}`, + movedTransaction: ({reportID}: MovedTransactionParams) => `moved this expense to [Expense Report ${reportID}]`, + unreportedTransaction: ({reportID}: UnreportedTransactionParams) => `removed this expense from [Expense Report ${reportID}]`, pendingMatchWithCreditCard: 'Receipt pending match with card transaction', pendingMatch: 'Pending match', pendingMatchWithCreditCardDescription: 'Receipt pending match with card transaction. Mark as cash to cancel.', diff --git a/src/languages/es.ts b/src/languages/es.ts index b47bc8523e96..f71c897e1b9d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -107,6 +107,7 @@ import type { MarkReimbursedFromIntegrationParams, MissingPropertyParams, MovedFromPersonalSpaceParams, + MovedTransactionParams, NeedCategoryForExportToIntegrationParams, NewWorkspaceNameParams, NoLongerHaveAccessParams, @@ -178,6 +179,7 @@ import type { TrialStartedTitleParams, UnapprovedParams, UnapproveWithIntegrationWarningParams, + UnreportedTransactionParams, UnshareParams, UntilTimeParams, UpdatedPolicyCategoryNameParams, @@ -899,6 +901,8 @@ const translations = { markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `eliminó un gasto de este informe, ${merchant} - ${amount}`, + movedTransaction: ({reportID}: MovedTransactionParams) => `movió este gasto a [Gasto ${reportID}]`, + unreportedTransaction: ({reportID}: UnreportedTransactionParams) => `eliminó este gasto de [Gasto ${reportID}]`, receiptIssuesFound: () => ({ one: 'Problema encontrado', other: 'Problemas encontrados', From f8e4e7d4e2591cf60f6addae64ee09ee57e34d48 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:15:24 +0100 Subject: [PATCH 11/75] update params --- src/languages/params.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/languages/params.ts b/src/languages/params.ts index d1c6751e123c..64f1771dd5fe 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -120,6 +120,14 @@ type DeleteTransactionParams = { merchant: string; }; +type MovedTransactionParams = { + reportID: string; +}; + +type UnreportedTransactionParams = { + reportID: string; +}; + type SettleExpensifyCardParams = { formattedAmount: string; }; @@ -783,6 +791,8 @@ export type { RequestAmountParams, RequestCountParams, DeleteTransactionParams, + MovedTransactionParams, + UnreportedTransactionParams, RequestedAmountMessageParams, ResolutionConstraintsParams, RoomNameReservedErrorParams, From 2197850eca24f27917639a61d3b0a10579dba8d2 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:15:49 +0100 Subject: [PATCH 12/75] add and update buildOptimisticUnreportedTransactionAction --- src/libs/ReportUtils.ts | 71 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 08ed2bb685dc..65c130abfb90 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9,7 +9,13 @@ import lodashMaxBy from 'lodash/maxBy'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; -import type {OriginalMessageChangePolicy, OriginalMessageIOU, OriginalMessageModifiedExpense} from 'src/types/onyx/OriginalMessage'; +import type { + OriginalMessageChangePolicy, + OriginalMessageIOU, + OriginalMessageModifiedExpense, + OriginalMessageMovedTransaction, + OriginalMessageUnreportedTransaction, +} from 'src/types/onyx/OriginalMessage'; import type {SetRequired, TupleToUnion, ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import {FallbackAvatar, IntacctSquare, NetSuiteSquare, NSQSSquare, QBOSquare, XeroSquare} from '@components/Icon/Expensicons'; @@ -5455,6 +5461,24 @@ function getDeletedTransactionMessage(action: ReportAction) { return message; } +function getMovedTransactionMessage(action: ReportAction) { + const movedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; + const {toReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; + const message = translateLocal('iou.movedTransaction', { + reportID: toReportID, + }); + return message; +} + +function getUnreportedTransactionMessage(action: ReportAction) { + const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; + const {oldReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; + const message = translateLocal('iou.unreportedTransaction', { + reportID: oldReportID, + }); + return message; +} + function getPolicyChangeMessage(action: ReportAction) { const PolicyChangeOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; const {fromPolicy: fromPolicyID, toPolicy: toPolicyID} = PolicyChangeOriginalMessage as OriginalMessageChangePolicy; @@ -5824,12 +5848,52 @@ function buildOptimisticMovedTransactionAction(oldReportID: string, toReportID: const changePolicyReportActionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: `moved the transaction to the ${toReportID}`, + text: `moved this expense to [Expense Report ${toReportID}]`, }, ]; return { actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + reportID: oldReportID, + actorAccountID: currentUserAccountID, + avatar: getCurrentUserAvatar(), + created: DateUtils.getDBTime(), + originalMessage, + message: changePolicyReportActionMessage, + person: [ + { + style: 'strong', + text: getCurrentUserDisplayNameOrEmail(), + type: 'TEXT', + }, + ], + reportActionID: rand64(), + shouldShow: true, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }; +} + +/** + * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. + * This action is used when we unreport a transaction. + */ +function buildOptimisticUnreportedTransactionAction(oldReportID: string, toReportID: string, transactionID: string): ReportAction { + const originalMessage = { + oldReportID, + toReportID, + transactionID, + }; + + const changePolicyReportActionMessage = [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + text: `removed this expense from [Expense Report ${oldReportID}]`, + }, + ]; + + return { + actionName: CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, + reportID: oldReportID, actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), @@ -9760,9 +9824,12 @@ export { getReportSubtitlePrefix, buildOptimisticChangePolicyReportAction, getPolicyChangeMessage, + getMovedTransactionMessage, + getUnreportedTransactionMessage, getExpenseReportStateAndStatus, populateOptimisticReportFormula, buildOptimisticMovedTransactionAction, + buildOptimisticUnreportedTransactionAction, }; export type { From b5de70f5a6573f93ee4d45ae5ed0f93a6153e7c6 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:16:31 +0100 Subject: [PATCH 13/75] add new api params --- src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 7ab1ab36f86e..a503802eacb6 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -381,3 +381,4 @@ export type {SaveCorpayOnboardingCompanyDetailsParams} from './SaveCorpayOnboard export type {default as AcceptSpotnanaTermsParams} from './AcceptSpotnanaTermsParams'; export type {default as SaveCorpayOnboardingBeneficialOwnerParams} from './SaveCorpayOnboardingBeneficialOwnerParams'; export type {default as ChangeReportPolicyParams} from './ChangeReportPolicyParams'; +export type {default as ChangeTransactionsReportParams} from './ChangeTransactionsReportParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index c6abb1b881af..3613e277c821 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -463,6 +463,7 @@ const WRITE_COMMANDS = { SAVE_CORPAY_ONBOARDING_COMPANY_DETAILS: 'SaveCorpayOnboardingCompanyDetails', SAVE_CORPAY_ONBOARDING_BENEFICIAL_OWNER: 'SaveCorpayOnboardingBeneficialOwner', CHANGE_REPORT_POLICY: 'ChangeReportPolicy', + CHANGE_TRANSACTIONS_REPORT: 'ChangeTransactionsReport', } as const; type WriteCommand = ValueOf; @@ -939,6 +940,9 @@ type WriteCommandParameters = { // Change report policy [WRITE_COMMANDS.CHANGE_REPORT_POLICY]: Parameters.ChangeReportPolicyParams; + + // Change transaction report + [WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT]: Parameters.ChangeTransactionsReportParams; }; const READ_COMMANDS = { From 697db04cb066345f022e6d2d77f7863c781e5d2c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:16:39 +0100 Subject: [PATCH 14/75] ChangeTransactionsReportParams --- src/libs/API/parameters/ChangeTransactionsReportParams.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/libs/API/parameters/ChangeTransactionsReportParams.ts diff --git a/src/libs/API/parameters/ChangeTransactionsReportParams.ts b/src/libs/API/parameters/ChangeTransactionsReportParams.ts new file mode 100644 index 000000000000..71c5745e2070 --- /dev/null +++ b/src/libs/API/parameters/ChangeTransactionsReportParams.ts @@ -0,0 +1,6 @@ +type ChangeTransactionsReportParams = { + transactionList: string; + reportID: string; + reportActionIDToThreadReportIDMap: Record; // A map linking the optimistic MOVEDTRANSACTION or UNREPORTEDTRANSACTION reportActionID to the transaction thread reportID. +}; +export default ChangeTransactionsReportParams; From e93d40e50e04294833305a0140617b9618737cf6 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:17:29 +0100 Subject: [PATCH 15/75] update pure and stepReport --- .../home/report/PureReportActionItem.tsx | 6 ++++ .../iou/request/step/IOURequestStepReport.tsx | 34 +++++++++---------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 2ea656b57cd2..265a95876a3c 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -110,9 +110,11 @@ import { getIOUForwardedMessage, getIOUSubmittedMessage, getIOUUnapprovedMessage, + getMovedTransactionMessage, getPolicyChangeMessage, getReportAutomaticallyApprovedMessage, getReportAutomaticallySubmittedMessage, + getUnreportedTransactionMessage, getWhisperDisplayNames, getWorkspaceNameUpdatedMessage, isArchivedNonExpenseReport, @@ -884,6 +886,10 @@ function PureReportActionItem({ children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION) { children = ; + } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION) { + children = ; + } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION) { + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MERGED_WITH_CASH_TRANSACTION) { children = ; } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION)) { diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index c2b75bb7d171..5cd90a3ada26 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import React, {useMemo, useState} from 'react'; import {useOnyx} from 'react-native-onyx'; import SelectionList from '@components/SelectionList'; import type {ListItem} from '@components/SelectionList/types'; @@ -27,8 +27,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {translate} = useLocalize(); const {transactionID, backTo} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - - const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]); + const [searchValue, setSearchValue] = useState(''); const reportOptions: ReportListItem[] = useMemo(() => { if (!allReports) { @@ -44,20 +43,18 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, ); - return ( - expenseReports - .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) - .map( - (report) => - report && { - text: report.reportName, - value: report.reportID, - keyForList: report.reportID, - }, - ) ?? [] - ); + return expenseReports + .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) + .filter((item) => item !== undefined) + .map((report) => ({ + text: report.reportName, + value: report.reportID, + keyForList: report.reportID, + })); }, [allReports]); + const shouldShowSearch = reportOptions.length > 8; + const navigateBack = () => { Navigation.goBack(backTo); }; @@ -77,18 +74,19 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return ( ); From 73a2a3f626cddbbca565dd02443a58d3ff499043 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 19 Mar 2025 00:17:45 +0100 Subject: [PATCH 16/75] update original message --- src/types/onyx/OriginalMessage.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 29f49f9e7a0b..3a904a20f90b 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -553,6 +553,30 @@ type OriginalMessageChangePolicy = { toPolicy: string; }; +/** Model of `UNREPORTED_TRANSACTION` report action */ +type OriginalMessageUnreportedTransaction = { + /** ID of the old report */ + oldReportID: string; + + /** ID of the new report */ + toReportID: string; + + /** ID of the transaction */ + transactionID: string; +}; + +/** Model of `MOVEDTRANSACTION` report action */ +type OriginalMessageMovedTransaction = { + /** ID of the old report */ + oldReportID: string; + + /** ID of the new report */ + toReportID: string; + + /** ID of the transaction */ + transactionID: string; +}; + /** Model of `moved` report action */ type OriginalMessageMoved = { /** ID of the old policy */ @@ -824,4 +848,6 @@ export type { OriginalMessageExportIntegration, IssueNewCardOriginalMessage, OriginalMessageChangePolicy, + OriginalMessageUnreportedTransaction, + OriginalMessageMovedTransaction, }; From 56dad971ba302109310453cd252c2b2944865e62 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 27 Mar 2025 20:38:27 +0100 Subject: [PATCH 17/75] fix linter --- src/pages/iou/request/step/IOURequestStepReport.tsx | 11 +++-------- .../request/step/withFullTransactionOrNotFound.tsx | 5 ++--- .../iou/request/step/withWritableReportOrNotFound.tsx | 10 ++++------ 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 5cd90a3ada26..920268ef3ead 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -1,4 +1,4 @@ -import React, {useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; import {useOnyx} from 'react-native-onyx'; import SelectionList from '@components/SelectionList'; import type {ListItem} from '@components/SelectionList/types'; @@ -6,7 +6,7 @@ import UserListItem from '@components/SelectionList/UserListItem'; import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import {isExpenseReport} from '@libs/ReportUtils'; -import * as IOU from '@userActions/IOU'; +import {setMoneyRequestParticipants} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -27,7 +27,6 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {translate} = useLocalize(); const {transactionID, backTo} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [searchValue, setSearchValue] = useState(''); const reportOptions: ReportListItem[] = useMemo(() => { if (!allReports) { @@ -53,8 +52,6 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { })); }, [allReports]); - const shouldShowSearch = reportOptions.length > 8; - const navigateBack = () => { Navigation.goBack(backTo); }; @@ -68,7 +65,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { selected: false, }); - IOU.setMoneyRequestParticipants(transactionID, newParticipants); + setMoneyRequestParticipants(transactionID, newParticipants); navigateBack(); }; @@ -82,8 +79,6 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { > diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index c5cb47d7b5d7..81a61ae413c7 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -7,8 +7,8 @@ import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as ReportActions from '@userActions/Report'; +import {canUserPerformWriteAction} from '@libs/ReportUtils'; +import {openReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -67,13 +67,11 @@ export default function { if (!!report?.reportID || !route.params.reportID || !!reportDraft || !isEditing) { return; } - ReportActions.openReport(route.params.reportID); + openReport(route.params.reportID); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); @@ -81,7 +79,7 @@ export default function ; } - if (iouTypeParamIsInvalid || !canUserPerformWriteAction) { + if (iouTypeParamIsInvalid || !canUserPerformWriteAction(report ?? {reportID: ''})) { return ; } From b60caf3c9df35d9a06e1b1fa0316ae1b7eaba06e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 31 Mar 2025 13:45:43 +0200 Subject: [PATCH 18/75] fix import --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index bc1b5c6c4a0b..72974e75f4e4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9928,7 +9928,6 @@ export { buildOptimisticMovedTransactionAction, buildOptimisticUnreportedTransactionAction, buildOptimisticResolvedDuplicatesReportAction, - populateOptimisticReportFormula, getTitleReportField, getReportFieldsByPolicyID, }; From 7b1bea4b6062073465a7efb6f885a13935518a38 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 00:24:12 +0200 Subject: [PATCH 19/75] add new transaction function --- src/libs/actions/Transaction.ts | 170 +++++++++++++++++++++++++++++--- 1 file changed, 158 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index d2f996c3e965..d807fb0fa017 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -5,16 +5,16 @@ import isEqual from 'lodash/isEqual'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import type {DismissViolationParams, GetRouteParams, MarkAsCashParams} from '@libs/API/parameters'; +import type {ChangeTransactionsReportParams, DismissViolationParams, GetRouteParams, MarkAsCashParams} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CollectionUtils from '@libs/CollectionUtils'; import * as NumberUtils from '@libs/NumberUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {buildOptimisticDismissedViolationReportAction} from '@libs/ReportUtils'; +import {buildOptimisticDismissedViolationReportAction, buildOptimisticMovedTransactionAction, buildOptimisticUnreportedTransactionAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, RecentWaypoint, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {PersonalDetails, RecentWaypoint, Report, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; import type {OriginalMessageModifiedExpense} from '@src/types/onyx/OriginalMessage'; import type {OnyxData} from '@src/types/onyx/Request'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; @@ -26,15 +26,27 @@ Onyx.connect({ callback: (val) => (recentWaypoints = val ?? []), }); -const allTransactions: Record = {}; +let allTransactions: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, - callback: (transaction, key) => { - if (!key || !transaction) { + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { return; } - const transactionID = CollectionUtils.extractCollectionItemID(key); - allTransactions[transactionID] = transaction; + allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)) as OnyxCollection; + }, +}); + +let allReports: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { + return; + } + allReports = value; }, }); @@ -356,7 +368,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmissedPersonalDetails: PersonalDetails) { const currentTransactionViolations = transactionIDs.map((id) => ({transactionID: id, violations: allTransactionViolation?.[id] ?? []})); const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]); - const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID)); + const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction?.reportID ?? '', transaction?.transactionID ?? '')); const optimisticDissmidedViolationReportActions = transactionsReportActions.map(() => { return buildOptimisticDismissedViolationReportAction({reason: 'manual', violationName: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}); }); @@ -387,11 +399,11 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const optimisticDataTransactions: OnyxUpdate[] = currentTransactions.map((transaction) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, value: { ...transaction, comment: { - ...transaction.comment, + ...transaction?.comment, dismissedViolations: { duplicatedTransaction: { [dissmissedPersonalDetails.login ?? '']: getUnixTime(new Date()), @@ -411,7 +423,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const failureDataTransaction: OnyxUpdate[] = currentTransactions.map((transaction) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, value: { ...transaction, }, @@ -572,6 +584,139 @@ function getAllTransactions() { return Object.keys(allTransactions ?? {}).length; } +function changeTransactionsReport(transactionIDs: string[], reportID: string, transactionIDToReportActionAndThreadData: Record) { + const optimisticData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; + + const newPolicyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID; + + // 1. Optimistically change the reportID on the passed transactions + const optimisticDataTransactions: OnyxUpdate[] = transactionIDs.map((transactionID) => ({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + reportID, + }, + })); + + const failureDataTransactions: OnyxUpdate[] = transactionIDs.map((transactionID) => { + const transaction = allTransactions?.[transactionID]; + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + reportID: transaction?.reportID, + }, + }; + }); + + optimisticData.push(...optimisticDataTransactions); + failureData.push(...failureDataTransactions); + + // 2. Optimistically update the IOU report action reportID + const optimisticDataReportActions: OnyxUpdate[] = Object.values(transactionIDToReportActionAndThreadData).map((reportActionID) => ({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportActionID}`, + value: { + reportID, + }, + })); + + const failureDataReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, reportActionID]) => { + const transaction = allTransactions?.[transactionID]; + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportActionID}`, + value: { + reportID: transaction?.reportID, + }, + }; + }); + + optimisticData.push(...optimisticDataReportActions); + failureData.push(...failureDataReportActions); + + // 3. Optimistically update the transaction thread and all threads in the transaction thread + const optimisticDataThreadReports: OnyxUpdate[] = Object.values(transactionIDToReportActionAndThreadData).map((threadReportID) => ({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${threadReportID}`, + value: { + parentReportID: reportID, + // If reportID is unreported, we don't need to update policyID + ...(reportID !== CONST.REPORT.UNREPORTED_REPORTID && { + policyID: newPolicyID, + }), + }, + })); + + const failureDataThreadReports: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { + const transaction = allTransactions?.[transactionID]; + const report = transaction?.reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`] : undefined; + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${threadReportID}`, + value: { + parentReportID: transaction?.reportID, + policyID: report?.policyID, + }, + }; + }); + + optimisticData.push(...optimisticDataThreadReports); + failureData.push(...failureDataThreadReports); + + // 4 & 5. Optimistically add new MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions + const optimisticDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { + const transaction = allTransactions?.[transactionID]; + const originalReportID = transaction?.reportID ?? ''; + const reportAction = + reportID === CONST.REPORT.UNREPORTED_REPORTID + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, transactionID); + + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${threadReportID}`, + value: { + [reportAction.reportActionID]: reportAction, + }, + }; + }); + + const failureDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { + const transaction = allTransactions?.[transactionID]; + const originalReportID = transaction?.reportID ?? ''; + const reportAction = + reportID === CONST.REPORT.UNREPORTED_REPORTID + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, transactionID); + + return { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${threadReportID}`, + value: { + [reportAction.reportActionID]: null, + }, + }; + }); + + optimisticData.push(...optimisticDataNewReportActions); + failureData.push(...failureDataNewReportActions); + + const parameters: ChangeTransactionsReportParams = { + transactionList: transactionIDs.join(','), + reportID, + reportActionIDToThreadReportIDMap: transactionIDToReportActionAndThreadData, + }; + + API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { + optimisticData, + successData, + failureData, + }); +} + export { addStop, createInitialWaypoints, @@ -591,4 +736,5 @@ export { getAllTransactions, getLastModifiedExpense, revert, + changeTransactionsReport, }; From 5e528d81eec2393c13b4427fce58e469cddfbf2c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 00:24:28 +0200 Subject: [PATCH 20/75] update requestStep report step --- .../iou/request/step/IOURequestStepReport.tsx | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 920268ef3ead..386cf2bc8dd2 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -3,10 +3,12 @@ import {useOnyx} from 'react-native-onyx'; import SelectionList from '@components/SelectionList'; import type {ListItem} from '@components/SelectionList/types'; import UserListItem from '@components/SelectionList/UserListItem'; +import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; +import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import {isExpenseReport} from '@libs/ReportUtils'; -import {setMoneyRequestParticipants} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -24,9 +26,11 @@ type IOURequestStepReportProps = WithWritableReportOrNotFoundProps; function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { + // TODO: hasForwardedAction const {translate} = useLocalize(); - const {transactionID, backTo} = route.params; + const {backTo} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const reportOptions: ReportListItem[] = useMemo(() => { if (!allReports) { @@ -45,28 +49,36 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .filter((item) => item !== undefined) + .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) .map((report) => ({ text: report.reportName, value: report.reportID, keyForList: report.reportID, })); - }, [allReports]); + }, [allReports, debouncedSearchValue]); const navigateBack = () => { Navigation.goBack(backTo); }; const selectReport = (item: ReportListItem) => { - const newParticipants = (transaction?.participants ?? []).filter((participant) => participant.accountID); + if (!transaction) { + return; + } + + // Get the IOU report action for this transaction + const iouAction = ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID); + if (!iouAction) { + return; + } - newParticipants.push({ - policyID: item.value, - isSender: true, - selected: false, - }); + // Create the mapping of transaction ID to report action and thread data + const transactionIDToReportActionAndThreadData = { + [transaction.transactionID]: iouAction.childReportID ?? '', + }; - setMoneyRequestParticipants(transactionID, newParticipants); - navigateBack(); + changeTransactionsReport([transaction.transactionID], item.value, transactionIDToReportActionAndThreadData); + Navigation.goBack(backTo); }; return ( @@ -80,6 +92,9 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { 8 ? translate('common.search') : undefined} shouldSingleExecuteRowSelect ListItem={UserListItem} /> From 3884eeaa9aa01f4c239a70f2472390bc95bb22e4 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 19:24:27 +0200 Subject: [PATCH 21/75] update message types --- src/libs/ReportUtils.ts | 6 +++--- src/types/onyx/OriginalMessage.ts | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d36089905063..b606805b73c2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5553,7 +5553,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; const {oldReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; const message = translateLocal('iou.unreportedTransaction', { - reportID: oldReportID, + reportID: oldReportID ?? '', }); return message; } @@ -5918,7 +5918,7 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we change the workspace of a report. */ -function buildOptimisticMovedTransactionAction(oldReportID: string, toReportID: string, transactionID: string): ReportAction { +function buildOptimisticMovedTransactionAction(oldReportID: string | undefined, toReportID: string, transactionID: string): ReportAction { const originalMessage = { oldReportID, toReportID, @@ -5957,7 +5957,7 @@ function buildOptimisticMovedTransactionAction(oldReportID: string, toReportID: * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(oldReportID: string, toReportID: string, transactionID: string): ReportAction { +function buildOptimisticUnreportedTransactionAction(oldReportID: string | undefined, toReportID: string, transactionID: string): ReportAction { const originalMessage = { oldReportID, toReportID, diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index b2245d618fdc..cf4b235ea1e9 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -556,7 +556,7 @@ type OriginalMessageChangePolicy = { /** Model of `UNREPORTED_TRANSACTION` report action */ type OriginalMessageUnreportedTransaction = { /** ID of the old report */ - oldReportID: string; + oldReportID: string | undefined; /** ID of the new report */ toReportID: string; @@ -568,7 +568,7 @@ type OriginalMessageUnreportedTransaction = { /** Model of `MOVEDTRANSACTION` report action */ type OriginalMessageMovedTransaction = { /** ID of the old report */ - oldReportID: string; + oldReportID: string | undefined; /** ID of the new report */ toReportID: string; @@ -783,6 +783,8 @@ type OriginalMessageMap = { [CONST.REPORT.ACTIONS.TYPE.MERGED_WITH_CASH_TRANSACTION]: never; [CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE]: OriginalMessageModifiedExpense; [CONST.REPORT.ACTIONS.TYPE.MOVED]: OriginalMessageMoved; + [CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION]: OriginalMessageMovedTransaction; + [CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION]: OriginalMessageUnreportedTransaction; [CONST.REPORT.ACTIONS.TYPE.OUTDATED_BANK_ACCOUNT]: never; [CONST.REPORT.ACTIONS.TYPE.REIMBURSED]: never; [CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENT_ACH_BOUNCE]: never; From 5662a31457a7c80a5ffcdb463993fea4fd0f245c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 19:24:37 +0200 Subject: [PATCH 22/75] fix transaction ts --- src/libs/actions/Transaction.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index d807fb0fa017..47dc5e52a05b 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -26,15 +26,15 @@ Onyx.connect({ callback: (val) => (recentWaypoints = val ?? []), }); -let allTransactions: OnyxCollection = {}; +const allTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { + callback: (transaction, key) => { + if (!key || !transaction) { return; } - allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)) as OnyxCollection; + const transactionID = CollectionUtils.extractCollectionItemID(key); + allTransactions[transactionID] = transaction; }, }); @@ -368,7 +368,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmissedPersonalDetails: PersonalDetails) { const currentTransactionViolations = transactionIDs.map((id) => ({transactionID: id, violations: allTransactionViolation?.[id] ?? []})); const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]); - const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction?.reportID ?? '', transaction?.transactionID ?? '')); + const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID)); const optimisticDissmidedViolationReportActions = transactionsReportActions.map(() => { return buildOptimisticDismissedViolationReportAction({reason: 'manual', violationName: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}); }); @@ -399,11 +399,11 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const optimisticDataTransactions: OnyxUpdate[] = currentTransactions.map((transaction) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { ...transaction, comment: { - ...transaction?.comment, + ...transaction.comment, dismissedViolations: { duplicatedTransaction: { [dissmissedPersonalDetails.login ?? '']: getUnixTime(new Date()), @@ -423,7 +423,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss const failureDataTransaction: OnyxUpdate[] = currentTransactions.map((transaction) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction?.transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { ...transaction, }, @@ -669,7 +669,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr // 4 & 5. Optimistically add new MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions const optimisticDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { const transaction = allTransactions?.[transactionID]; - const originalReportID = transaction?.reportID ?? ''; + const originalReportID = transaction.reportID; const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) @@ -686,7 +686,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr const failureDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { const transaction = allTransactions?.[transactionID]; - const originalReportID = transaction?.reportID ?? ''; + const originalReportID = transaction.reportID; const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) From 4c04878dcc2fab2342e377ae0bae34b8ba605d7d Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 19:24:46 +0200 Subject: [PATCH 23/75] fix requestStep --- src/pages/iou/request/step/IOURequestStepReport.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 386cf2bc8dd2..1438565f305e 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -7,7 +7,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import {getIOUActionForReportID} from '@libs/ReportActionsUtils'; import {isExpenseReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -67,14 +67,14 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { } // Get the IOU report action for this transaction - const iouAction = ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID); - if (!iouAction) { + const iouAction = getIOUActionForReportID(transaction.reportID, transaction.transactionID); + if (!iouAction || !iouAction.childReportID) { return; } // Create the mapping of transaction ID to report action and thread data const transactionIDToReportActionAndThreadData = { - [transaction.transactionID]: iouAction.childReportID ?? '', + [transaction.transactionID]: iouAction.childReportID, }; changeTransactionsReport([transaction.transactionID], item.value, transactionIDToReportActionAndThreadData); From 3c2d75150a125407e3c758d79901073c37e704a9 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 1 Apr 2025 19:38:29 +0200 Subject: [PATCH 24/75] update param --- src/languages/params.ts | 2 +- src/libs/ReportUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/params.ts b/src/languages/params.ts index 5235bcf2cbc9..87647921e55e 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -125,7 +125,7 @@ type MovedTransactionParams = { }; type UnreportedTransactionParams = { - reportID: string; + reportID?: string; }; type SettleExpensifyCardParams = { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b606805b73c2..37e3fb5d126f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5553,7 +5553,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; const {oldReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; const message = translateLocal('iou.unreportedTransaction', { - reportID: oldReportID ?? '', + reportID: oldReportID, }); return message; } From 4162fd87f2742ce0aca243806e14fb16430276bf Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 13:37:18 +0200 Subject: [PATCH 25/75] update selectedParticipants check --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 2d0b6776bdda..d6060de08323 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -280,7 +280,7 @@ function MoneyRequestConfirmationListFooter({ isExpenseReport(report) && report?.stateNum && report?.statusNum && - report?.policyID === senderWorkspace?.id && + report?.policyID === selectedParticipants?.at(0)?.policyID && report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, ) From c7a48257b526b130777557976d2f336044c56d66 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 13:39:46 +0200 Subject: [PATCH 26/75] update reportId to parentReportID --- src/components/ReportActionItem/MoneyRequestView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index cbfda64bcbaa..07b9f982e907 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -801,11 +801,11 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals /> )} - {!!report?.reportID && ( + {!!parentReportID && ( Date: Wed, 2 Apr 2025 17:18:26 +0200 Subject: [PATCH 27/75] update route --- src/ROUTES.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index bc67c6048225..033c44d42d24 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -646,9 +646,9 @@ const ROUTES = { getUrlWithBackToParam(`${action as string}/${iouType as string}/subrate/${transactionID}/${reportID}/edit/${pageIndex}`, backTo), }, MONEY_REQUEST_STEP_REPORT: { - route: ':action/:iouType/report/:transactionID/:reportID/edit', + route: ':action/:iouType/report/:transactionID/:reportID', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/report/${transactionID}/${reportID}/edit`, backTo), + getUrlWithBackToParam(`${action as string}/${iouType as string}/report/${transactionID}/${reportID}`, backTo), }, SETTINGS_TAGS_ROOT: { route: 'settings/:policyID/tags', From ff7729331050d45472ad49a640ee735ecf2ed125 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 17:24:36 +0200 Subject: [PATCH 28/75] update translations --- src/languages/en.ts | 5 +++-- src/languages/es.ts | 4 ++-- src/languages/params.ts | 6 ++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 5c4ab70e1f92..fcf4399b6746 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -920,8 +920,9 @@ const translations = { posted: 'Posted', deleteReceipt: 'Delete receipt', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `deleted an expense on this report, ${merchant} - ${amount}`, - movedTransaction: ({reportID}: MovedTransactionParams) => `moved this expense to [Expense Report ${reportID}]`, - unreportedTransaction: ({reportID}: UnreportedTransactionParams) => `removed this expense from [Expense Report ${reportID}]`, + movedTransaction: ({toReportID, reportName}: MovedTransactionParams) => `moved this expense to ${reportName}`, + unreportedTransaction: ({fromReportID, reportName}: UnreportedTransactionParams) => + `removed this expense from ${reportName}`, pendingMatchWithCreditCard: 'Receipt pending match with card transaction', pendingMatch: 'Pending match', pendingMatchWithCreditCardDescription: 'Receipt pending match with card transaction. Mark as cash to cancel.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 26ccd50121c3..6498c7a62ff9 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -920,8 +920,8 @@ const translations = { markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `eliminó un gasto de este informe, ${merchant} - ${amount}`, - movedTransaction: ({reportID}: MovedTransactionParams) => `movió este gasto a [Gasto ${reportID}]`, - unreportedTransaction: ({reportID}: UnreportedTransactionParams) => `eliminó este gasto de [Gasto ${reportID}]`, + movedTransaction: ({toReportID, reportName}: MovedTransactionParams) => `movió este gasto a ${reportName}`, + unreportedTransaction: ({fromReportID, reportName}: UnreportedTransactionParams) => `eliminó este gasto de ${reportName}`, receiptIssuesFound: () => ({ one: 'Problema encontrado', other: 'Problemas encontrados', diff --git a/src/languages/params.ts b/src/languages/params.ts index 87647921e55e..ec2d642821fd 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -121,11 +121,13 @@ type DeleteTransactionParams = { }; type MovedTransactionParams = { - reportID: string; + toReportID: string; + reportName: string; }; type UnreportedTransactionParams = { - reportID?: string; + reportName: string; + fromReportID?: string; }; type SettleExpensifyCardParams = { From 2a56d0726051197056fe31848009985dbdc4925c Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 17:29:43 +0200 Subject: [PATCH 29/75] update params --- .../parameters/ChangeTransactionsReportParams.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/ChangeTransactionsReportParams.ts b/src/libs/API/parameters/ChangeTransactionsReportParams.ts index 71c5745e2070..6664bab8fe30 100644 --- a/src/libs/API/parameters/ChangeTransactionsReportParams.ts +++ b/src/libs/API/parameters/ChangeTransactionsReportParams.ts @@ -1,6 +1,17 @@ +/** + * A map linking the optimistic MOVEDTRANSACTION or UNREPORTEDTRANSACTION reportActionID to the transactionID. + * If we're creating the transactionThread as part of moving the transaction, we should also send the optimistic + * transactionThreadReportID and transactionThreadCreatedReportActionID + */ +type TransactionThreadInfo = { + transactionThreadReportID?: string; + transactionThreadCreatedReportActionID?: string; + movedReportActionID: string; +}; type ChangeTransactionsReportParams = { transactionList: string; reportID: string; - reportActionIDToThreadReportIDMap: Record; // A map linking the optimistic MOVEDTRANSACTION or UNREPORTEDTRANSACTION reportActionID to the transaction thread reportID. + transactionIDToReportActionAndThreadData: Record; // A map of transactionID to TransactionThreadInfo }; + export default ChangeTransactionsReportParams; From 0bff7321dec4c184c080a3412f9beb923078f14e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 17:54:10 +0200 Subject: [PATCH 30/75] update moved/unreported transactions optimistic --- src/libs/ReportUtils.ts | 34 +++++++++++++++---------------- src/types/onyx/OriginalMessage.ts | 16 +++++---------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 37e3fb5d126f..9d57cb545b42 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5542,18 +5542,20 @@ function getDeletedTransactionMessage(action: ReportAction) { function getMovedTransactionMessage(action: ReportAction) { const movedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; - const {toReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; + const {toReportID, reportName} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; const message = translateLocal('iou.movedTransaction', { - reportID: toReportID, + toReportID, + reportName, }); return message; } function getUnreportedTransactionMessage(action: ReportAction) { const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; - const {oldReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; + const {fromReportID, reportName} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; const message = translateLocal('iou.unreportedTransaction', { - reportID: oldReportID, + fromReportID, + reportName, }); return message; } @@ -5918,28 +5920,27 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we change the workspace of a report. */ -function buildOptimisticMovedTransactionAction(oldReportID: string | undefined, toReportID: string, transactionID: string): ReportAction { +function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string, reportName: string): ReportAction { const originalMessage = { - oldReportID, toReportID, - transactionID, + reportName, }; - const changePolicyReportActionMessage = [ + const movedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: `moved this expense to [Expense Report ${toReportID}]`, + text: `moved this expense to ${reportName}`, }, ]; return { actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, - reportID: oldReportID, + reportID: transactionThreadReportID, actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), originalMessage, - message: changePolicyReportActionMessage, + message: movedTransactionMessage, person: [ { style: 'strong', @@ -5957,23 +5958,22 @@ function buildOptimisticMovedTransactionAction(oldReportID: string | undefined, * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(oldReportID: string | undefined, toReportID: string, transactionID: string): ReportAction { +function buildOptimisticUnreportedTransactionAction(fromReportID: string | undefined, reportName: string): ReportAction { const originalMessage = { - oldReportID, - toReportID, - transactionID, + fromReportID, + reportName, }; const changePolicyReportActionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: `removed this expense from [Expense Report ${oldReportID}]`, + text: `removed this expense from ${reportName}`, }, ]; return { actionName: CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, - reportID: oldReportID, + reportID: fromReportID, actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index cf4b235ea1e9..e34ced909e4f 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -556,25 +556,19 @@ type OriginalMessageChangePolicy = { /** Model of `UNREPORTED_TRANSACTION` report action */ type OriginalMessageUnreportedTransaction = { /** ID of the old report */ - oldReportID: string | undefined; + fromReportID: string | undefined; - /** ID of the new report */ - toReportID: string; - - /** ID of the transaction */ - transactionID: string; + /** Name of the report */ + reportName: string; }; /** Model of `MOVEDTRANSACTION` report action */ type OriginalMessageMovedTransaction = { - /** ID of the old report */ - oldReportID: string | undefined; - /** ID of the new report */ toReportID: string; - /** ID of the transaction */ - transactionID: string; + /** Name of the report */ + reportName: string; }; /** Model of `moved` report action */ From 32a4ef21cb7c607497c5cc8d73bfcabb70b0da11 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 18:05:33 +0200 Subject: [PATCH 31/75] update buildOptimisticUnreportedTransactionAction --- src/libs/ReportUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9d57cb545b42..280107cd3bf5 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5958,13 +5958,13 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(fromReportID: string | undefined, reportName: string): ReportAction { +function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string | undefined, reportName: string): ReportAction { const originalMessage = { fromReportID, reportName, }; - const changePolicyReportActionMessage = [ + const unreportedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, text: `removed this expense from ${reportName}`, @@ -5973,12 +5973,12 @@ function buildOptimisticUnreportedTransactionAction(fromReportID: string | undef return { actionName: CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, - reportID: fromReportID, + reportID: transactionThreadReportID, actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), originalMessage, - message: changePolicyReportActionMessage, + message: unreportedTransactionMessage, person: [ { style: 'strong', From ef2bd050838d217dd666c7fcb5ef7a66d62e0d67 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 18:07:40 +0200 Subject: [PATCH 32/75] add fake policy id --- src/libs/actions/Transaction.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 47dc5e52a05b..577381cd21e7 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -643,10 +643,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr key: `${ONYXKEYS.COLLECTION.REPORT}${threadReportID}`, value: { parentReportID: reportID, - // If reportID is unreported, we don't need to update policyID - ...(reportID !== CONST.REPORT.UNREPORTED_REPORTID && { - policyID: newPolicyID, - }), + policyID: reportID !== CONST.REPORT.UNREPORTED_REPORTID ? newPolicyID : CONST.POLICY.ID_FAKE, }, })); From 868b44f4160ccdcaf48ae61616a8fca2e7a95c52 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 20:47:48 +0200 Subject: [PATCH 33/75] update request step --- .../iou/request/step/IOURequestStepReport.tsx | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 1438565f305e..e9101b1209da 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -7,7 +7,6 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; -import {getIOUActionForReportID} from '@libs/ReportActionsUtils'; import {isExpenseReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -66,18 +65,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return; } - // Get the IOU report action for this transaction - const iouAction = getIOUActionForReportID(transaction.reportID, transaction.transactionID); - if (!iouAction || !iouAction.childReportID) { - return; - } - - // Create the mapping of transaction ID to report action and thread data - const transactionIDToReportActionAndThreadData = { - [transaction.transactionID]: iouAction.childReportID, - }; - - changeTransactionsReport([transaction.transactionID], item.value, transactionIDToReportActionAndThreadData); + changeTransactionsReport([transaction.transactionID], item.value); Navigation.goBack(backTo); }; @@ -94,7 +82,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { onSelectRow={selectReport} textInputValue={searchValue} onChangeText={setSearchValue} - textInputLabel={reportOptions.length > 8 ? translate('common.search') : undefined} + textInputLabel={reportOptions.length >= CONST.STANDARD_LIST_ITEM_LIMIT ? translate('common.search') : undefined} shouldSingleExecuteRowSelect ListItem={UserListItem} /> From c28ad5ca7f40ee72cfa15978655e1817a7c48419 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 20:48:02 +0200 Subject: [PATCH 34/75] update params --- src/libs/API/parameters/ChangeTransactionsReportParams.ts | 4 ++-- src/libs/API/parameters/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/API/parameters/ChangeTransactionsReportParams.ts b/src/libs/API/parameters/ChangeTransactionsReportParams.ts index 6664bab8fe30..79d025505bfb 100644 --- a/src/libs/API/parameters/ChangeTransactionsReportParams.ts +++ b/src/libs/API/parameters/ChangeTransactionsReportParams.ts @@ -4,9 +4,9 @@ * transactionThreadReportID and transactionThreadCreatedReportActionID */ type TransactionThreadInfo = { + movedReportActionID: string; transactionThreadReportID?: string; transactionThreadCreatedReportActionID?: string; - movedReportActionID: string; }; type ChangeTransactionsReportParams = { transactionList: string; @@ -14,4 +14,4 @@ type ChangeTransactionsReportParams = { transactionIDToReportActionAndThreadData: Record; // A map of transactionID to TransactionThreadInfo }; -export default ChangeTransactionsReportParams; +export type {ChangeTransactionsReportParams, TransactionThreadInfo}; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 0b5e43eccf95..7d49a61363a7 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -377,7 +377,7 @@ export type {default as SaveCorpayOnboardingBeneficialOwnerParams} from './SaveC export type {default as MoveIOUReportToPolicyAndInviteSubmitterParams} from './MoveIOUReportToPolicyAndInviteSubmitterParams'; export type {default as MoveIOUReportToExistingPolicyParams} from './MoveIOUReportToExistingPolicyParams'; export type {default as ChangeReportPolicyParams} from './ChangeReportPolicyParams'; -export type {default as ChangeTransactionsReportParams} from './ChangeTransactionsReportParams'; +export type {ChangeTransactionsReportParams, TransactionThreadInfo} from './ChangeTransactionsReportParams'; export type {default as ResetBankAccountSetupParams} from './ResetBankAccountSetupParams'; export type {default as SendRecapInAdminsRoomParams} from './SendRecapInAdminsRoomParams'; export type {default as SetPolicyProhibitedExpensesParams} from './SetPolicyProhibitedExpensesParams'; From 29a7100c1fb48feec419c9170ee4eefa289c4d08 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 20:48:41 +0200 Subject: [PATCH 35/75] add selected report --- src/pages/iou/request/step/IOURequestStepReport.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index e9101b1209da..de0c6e44181e 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -53,8 +53,9 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { text: report.reportName, value: report.reportID, keyForList: report.reportID, + isSelected: report.reportID === transaction?.reportID, })); - }, [allReports, debouncedSearchValue]); + }, [allReports, debouncedSearchValue, transaction?.reportID]); const navigateBack = () => { Navigation.goBack(backTo); From 0c8d6f5ab48eadb8a4399fea1945b1f42da1af59 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 22:11:16 +0200 Subject: [PATCH 36/75] update transaction --- src/libs/actions/Transaction.ts | 86 +++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 577381cd21e7..52022c428301 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -5,7 +5,7 @@ import isEqual from 'lodash/isEqual'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; -import type {ChangeTransactionsReportParams, DismissViolationParams, GetRouteParams, MarkAsCashParams} from '@libs/API/parameters'; +import type {ChangeTransactionsReportParams, DismissViolationParams, GetRouteParams, MarkAsCashParams, TransactionThreadInfo} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CollectionUtils from '@libs/CollectionUtils'; import * as NumberUtils from '@libs/NumberUtils'; @@ -584,12 +584,21 @@ function getAllTransactions() { return Object.keys(allTransactions ?? {}).length; } -function changeTransactionsReport(transactionIDs: string[], reportID: string, transactionIDToReportActionAndThreadData: Record) { +function changeTransactionsReport(transactionIDs: string[], reportID: string) { const optimisticData: OnyxUpdate[] = []; const failureData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; const newPolicyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID; + const transactionIDToReportActionAndThreadData: Record = {}; + + // Get all transactions and their IOU actions upfront + const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]).filter((t): t is NonNullable => t !== undefined); + + const transactionsReportActions = currentTransactions.map((transaction) => ({ + transaction, + iouAction: ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID), + })); // 1. Optimistically change the reportID on the passed transactions const optimisticDataTransactions: OnyxUpdate[] = transactionIDs.map((transactionID) => ({ @@ -600,61 +609,61 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr }, })); - const failureDataTransactions: OnyxUpdate[] = transactionIDs.map((transactionID) => { + const failureDataTransactions: OnyxUpdate[] = []; + transactionIDs.forEach((transactionID) => { const transaction = allTransactions?.[transactionID]; - return { + if (!transaction) { + return; + } + failureDataTransactions.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { reportID: transaction?.reportID, }, - }; + }); }); optimisticData.push(...optimisticDataTransactions); failureData.push(...failureDataTransactions); // 2. Optimistically update the IOU report action reportID - const optimisticDataReportActions: OnyxUpdate[] = Object.values(transactionIDToReportActionAndThreadData).map((reportActionID) => ({ + const optimisticDataReportActions: OnyxUpdate[] = transactionsReportActions.map(({iouAction}) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportActionID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.reportActionID}`, value: { reportID, }, })); - const failureDataReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, reportActionID]) => { - const transaction = allTransactions?.[transactionID]; - return { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportActionID}`, - value: { - reportID: transaction?.reportID, - }, - }; - }); + const failureDataReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => ({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.reportActionID}`, + value: { + reportID: transaction.reportID, + }, + })); optimisticData.push(...optimisticDataReportActions); failureData.push(...failureDataReportActions); // 3. Optimistically update the transaction thread and all threads in the transaction thread - const optimisticDataThreadReports: OnyxUpdate[] = Object.values(transactionIDToReportActionAndThreadData).map((threadReportID) => ({ + const optimisticDataThreadReports: OnyxUpdate[] = transactionsReportActions.map(({iouAction}) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${threadReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouAction?.childReportID}`, value: { parentReportID: reportID, policyID: reportID !== CONST.REPORT.UNREPORTED_REPORTID ? newPolicyID : CONST.POLICY.ID_FAKE, }, })); - const failureDataThreadReports: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { - const transaction = allTransactions?.[transactionID]; - const report = transaction?.reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`] : undefined; + const failureDataThreadReports: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { + const report = transaction.reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`] : undefined; return { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${threadReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouAction?.childReportID}`, value: { - parentReportID: transaction?.reportID, + parentReportID: transaction.reportID, policyID: report?.policyID, }, }; @@ -663,35 +672,40 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr optimisticData.push(...optimisticDataThreadReports); failureData.push(...failureDataThreadReports); - // 4 & 5. Optimistically add new MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions - const optimisticDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { - const transaction = allTransactions?.[transactionID]; + // 4 & 5. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions + const optimisticDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { const originalReportID = transaction.reportID; const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, transactionID); + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transaction.transactionID) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, transaction.transactionID); + + // Store the thread information + transactionIDToReportActionAndThreadData[transaction.transactionID] = { + transactionThreadReportID: iouAction?.childReportID, + transactionThreadCreatedReportActionID: reportAction.created, + movedReportActionID: reportAction.reportActionID, + }; return { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${threadReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.childReportID}`, value: { [reportAction.reportActionID]: reportAction, }, }; }); - const failureDataNewReportActions: OnyxUpdate[] = Object.entries(transactionIDToReportActionAndThreadData).map(([transactionID, threadReportID]) => { - const transaction = allTransactions?.[transactionID]; + const failureDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { const originalReportID = transaction.reportID; const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transactionID) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, transactionID); + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transaction.transactionID) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, transaction.transactionID); return { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${threadReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.childReportID}`, value: { [reportAction.reportActionID]: null, }, @@ -704,7 +718,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, tr const parameters: ChangeTransactionsReportParams = { transactionList: transactionIDs.join(','), reportID, - reportActionIDToThreadReportIDMap: transactionIDToReportActionAndThreadData, + transactionIDToReportActionAndThreadData, }; API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { From 0ef8628871113d13407bea49838e131af5e209bd Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 22:34:45 +0200 Subject: [PATCH 37/75] add hasForwardedAction --- src/pages/iou/request/step/IOURequestStepReport.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index de0c6e44181e..dd5436ff13ca 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -7,6 +7,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import {changeTransactionsReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; +import {getAllReportActions} from '@libs/ReportActionsUtils'; import {isExpenseReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -24,8 +25,15 @@ type ReportListItem = ListItem & { type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & WithFullTransactionOrNotFoundProps; +/** + * Check if a report has any forwarded actions + */ +function hasForwardedAction(reportID: string): boolean { + const reportActions = getAllReportActions(reportID); + return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); +} + function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { - // TODO: hasForwardedAction const {translate} = useLocalize(); const {backTo} = route.params; const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); @@ -42,7 +50,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { report?.stateNum && report?.statusNum && report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && - report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, + report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && + !hasForwardedAction(report.reportID), ); return expenseReports From 715b6991f042063792c6cd8babf00ad40a4a2e0b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 2 Apr 2025 22:38:34 +0200 Subject: [PATCH 38/75] update --- src/languages/params.ts | 4 ++-- src/libs/ReportUtils.ts | 10 ++++++---- src/libs/actions/Transaction.ts | 12 ++++++++---- src/types/onyx/OriginalMessage.ts | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/languages/params.ts b/src/languages/params.ts index ec2d642821fd..73c34a431f66 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -122,11 +122,11 @@ type DeleteTransactionParams = { type MovedTransactionParams = { toReportID: string; - reportName: string; + reportName?: string; }; type UnreportedTransactionParams = { - reportName: string; + reportName?: string; fromReportID?: string; }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 280107cd3bf5..db52e030c390 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5920,7 +5920,7 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we change the workspace of a report. */ -function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string, reportName: string): ReportAction { +function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string, reportName: string | undefined): ReportAction { const originalMessage = { toReportID, reportName, @@ -5929,7 +5929,8 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string const movedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: `moved this expense to ${reportName}`, + html: `moved this expense to ${reportName}`, + text: `moved this expense to ${reportName}`, }, ]; @@ -5958,7 +5959,7 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string | undefined, reportName: string): ReportAction { +function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string | undefined, reportName: string | undefined): ReportAction { const originalMessage = { fromReportID, reportName, @@ -5967,7 +5968,8 @@ function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: s const unreportedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - text: `removed this expense from ${reportName}`, + html: `removed this expense from ${reportName}`, + text: `removed this expense from ${reportName}`, }, ]; diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 52022c428301..d9636d5abd43 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -675,10 +675,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { // 4 & 5. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions const optimisticDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { const originalReportID = transaction.reportID; + const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`]?.reportName; + const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transaction.transactionID) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, transaction.transactionID); + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, reportName) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, reportName); // Store the thread information transactionIDToReportActionAndThreadData[transaction.transactionID] = { @@ -698,10 +700,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { const failureDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { const originalReportID = transaction.reportID; + const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`]?.reportName; + const reportAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, transaction.transactionID) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, transaction.transactionID); + ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, reportName) + : buildOptimisticMovedTransactionAction(originalReportID, reportID, reportName); return { onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index e34ced909e4f..09b8103474c1 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -559,7 +559,7 @@ type OriginalMessageUnreportedTransaction = { fromReportID: string | undefined; /** Name of the report */ - reportName: string; + reportName: string | undefined; }; /** Model of `MOVEDTRANSACTION` report action */ @@ -568,7 +568,7 @@ type OriginalMessageMovedTransaction = { toReportID: string; /** Name of the report */ - reportName: string; + reportName: string | undefined; }; /** Model of `moved` report action */ From a8ad06dd3e9b71bd025076149b0bffb4feddfe34 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 3 Apr 2025 19:45:15 +0200 Subject: [PATCH 39/75] apply changes --- .../ChangeTransactionsReportParams.ts | 3 +- src/libs/ReportUtils.ts | 14 +- src/libs/actions/Transaction.ts | 285 +++++++++++------- .../home/report/PureReportActionItem.tsx | 18 +- src/types/onyx/OriginalMessage.ts | 8 +- 5 files changed, 197 insertions(+), 131 deletions(-) diff --git a/src/libs/API/parameters/ChangeTransactionsReportParams.ts b/src/libs/API/parameters/ChangeTransactionsReportParams.ts index 79d025505bfb..a8168121f091 100644 --- a/src/libs/API/parameters/ChangeTransactionsReportParams.ts +++ b/src/libs/API/parameters/ChangeTransactionsReportParams.ts @@ -8,10 +8,11 @@ type TransactionThreadInfo = { transactionThreadReportID?: string; transactionThreadCreatedReportActionID?: string; }; + type ChangeTransactionsReportParams = { transactionList: string; reportID: string; - transactionIDToReportActionAndThreadData: Record; // A map of transactionID to TransactionThreadInfo + transactionIDToReportActionAndThreadData: string; // A map of transactionID to TransactionThreadInfo }; export type {ChangeTransactionsReportParams, TransactionThreadInfo}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index db52e030c390..5a9d03e18b57 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5542,7 +5542,8 @@ function getDeletedTransactionMessage(action: ReportAction) { function getMovedTransactionMessage(action: ReportAction) { const movedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; - const {toReportID, reportName} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; + const {toReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; + const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`]?.reportName ?? ''; const message = translateLocal('iou.movedTransaction', { toReportID, reportName, @@ -5552,7 +5553,8 @@ function getMovedTransactionMessage(action: ReportAction) { function getUnreportedTransactionMessage(action: ReportAction) { const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; - const {fromReportID, reportName} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; + const {fromReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; + const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]?.reportName ?? ''; const message = translateLocal('iou.unreportedTransaction', { fromReportID, reportName, @@ -5920,12 +5922,12 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we change the workspace of a report. */ -function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string, reportName: string | undefined): ReportAction { +function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string): ReportAction { const originalMessage = { toReportID, - reportName, }; + const reportName = allReports?.[toReportID]?.reportName; const movedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, @@ -5959,12 +5961,12 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string | undefined, reportName: string | undefined): ReportAction { +function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string): ReportAction { const originalMessage = { fromReportID, - reportName, }; + const reportName = allReports?.[fromReportID]?.reportName; const unreportedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index d9636d5abd43..0ad5f39771c3 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -9,9 +9,15 @@ import type {ChangeTransactionsReportParams, DismissViolationParams, GetRoutePar import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CollectionUtils from '@libs/CollectionUtils'; import * as NumberUtils from '@libs/NumberUtils'; -import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {buildOptimisticDismissedViolationReportAction, buildOptimisticMovedTransactionAction, buildOptimisticUnreportedTransactionAction} from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {getAllReportActions, getIOUActionForReportID, getOriginalMessage, isModifiedExpenseAction} from '@libs/ReportActionsUtils'; +import { + buildOptimisticCreatedReportAction, + buildOptimisticDismissedViolationReportAction, + buildOptimisticMovedTransactionAction, + buildOptimisticUnreportedTransactionAction, + buildTransactionThread, +} from '@libs/ReportUtils'; +import {getTransaction, waypointHasValidAddress} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, RecentWaypoint, Report, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; @@ -26,6 +32,15 @@ Onyx.connect({ callback: (val) => (recentWaypoints = val ?? []), }); +let currentUserEmail = ''; + +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (value) => { + currentUserEmail = value?.email ?? ''; + }, +}); + const allTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, @@ -162,7 +177,7 @@ function removeWaypoint(transaction: OnyxEntry, currentIndex: strin return Promise.resolve(); } - const isRemovedWaypointEmpty = removed.length > 0 && !TransactionUtils.waypointHasValidAddress(removed.at(0) ?? {}); + const isRemovedWaypointEmpty = removed.length > 0 && !waypointHasValidAddress(removed.at(0) ?? {}); // When there are only two waypoints we are adding empty waypoint back if (totalWaypoints === 2 && (index === 0 || index === totalWaypoints - 1)) { @@ -368,7 +383,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmissedPersonalDetails: PersonalDetails) { const currentTransactionViolations = transactionIDs.map((id) => ({transactionID: id, violations: allTransactionViolation?.[id] ?? []})); const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]); - const transactionsReportActions = currentTransactions.map((transaction) => ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID)); + const transactionsReportActions = currentTransactions.map((transaction) => getIOUActionForReportID(transaction.reportID, transaction.transactionID)); const optimisticDissmidedViolationReportActions = transactionsReportActions.map(() => { return buildOptimisticDismissedViolationReportAction({reason: 'manual', violationName: CONST.VIOLATIONS.DUPLICATED_TRANSACTION}); }); @@ -490,13 +505,13 @@ function clearError(transactionID: string) { } function getLastModifiedExpense(reportID?: string): OriginalMessageModifiedExpense | undefined { - const modifiedExpenseActions = Object.values(ReportActionsUtils.getAllReportActions(reportID)).filter(ReportActionsUtils.isModifiedExpenseAction); + const modifiedExpenseActions = Object.values(getAllReportActions(reportID)).filter(isModifiedExpenseAction); modifiedExpenseActions.sort((a, b) => Number(a.reportActionID) - Number(b.reportActionID)); - return ReportActionsUtils.getOriginalMessage(modifiedExpenseActions.at(-1)); + return getOriginalMessage(modifiedExpenseActions.at(-1)); } function revert(transactionID?: string, originalMessage?: OriginalMessageModifiedExpense | undefined) { - const transaction = TransactionUtils.getTransaction(transactionID); + const transaction = getTransaction(transactionID); if (transaction && originalMessage?.oldAmount && originalMessage.oldCurrency && 'amount' in originalMessage && 'currency' in originalMessage) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { @@ -585,144 +600,186 @@ function getAllTransactions() { } function changeTransactionsReport(transactionIDs: string[], reportID: string) { - const optimisticData: OnyxUpdate[] = []; - const failureData: OnyxUpdate[] = []; - const successData: OnyxUpdate[] = []; + const newReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + if (!newReport) { + return; + } - const newPolicyID = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]?.policyID; + const transactions = transactionIDs.map((id) => allTransactions?.[id]).filter((t): t is NonNullable => t !== undefined); const transactionIDToReportActionAndThreadData: Record = {}; - // Get all transactions and their IOU actions upfront - const currentTransactions = transactionIDs.map((id) => allTransactions?.[id]).filter((t): t is NonNullable => t !== undefined); - - const transactionsReportActions = currentTransactions.map((transaction) => ({ - transaction, - iouAction: ReportActionsUtils.getIOUActionForReportID(transaction.reportID, transaction.transactionID), - })); - - // 1. Optimistically change the reportID on the passed transactions - const optimisticDataTransactions: OnyxUpdate[] = transactionIDs.map((transactionID) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - reportID, - }, - })); + const optimisticData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; - const failureDataTransactions: OnyxUpdate[] = []; - transactionIDs.forEach((transactionID) => { - const transaction = allTransactions?.[transactionID]; - if (!transaction) { + transactions.forEach((transaction) => { + const oldIOUAction = getIOUActionForReportID(transaction.reportID, transaction.transactionID); + if (!oldIOUAction?.reportActionID) { return; } - failureDataTransactions.push({ + + const newIOUAction = {...oldIOUAction, reportID}; + + // 1. Optimistically change the reportID on the passed transactions + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { - reportID: transaction?.reportID, + reportID, }, }); - }); - optimisticData.push(...optimisticDataTransactions); - failureData.push(...failureDataTransactions); - - // 2. Optimistically update the IOU report action reportID - const optimisticDataReportActions: OnyxUpdate[] = transactionsReportActions.map(({iouAction}) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.reportActionID}`, - value: { - reportID, - }, - })); - - const failureDataReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.reportActionID}`, - value: { - reportID: transaction.reportID, - }, - })); - - optimisticData.push(...optimisticDataReportActions); - failureData.push(...failureDataReportActions); - - // 3. Optimistically update the transaction thread and all threads in the transaction thread - const optimisticDataThreadReports: OnyxUpdate[] = transactionsReportActions.map(({iouAction}) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouAction?.childReportID}`, - value: { - parentReportID: reportID, - policyID: reportID !== CONST.REPORT.UNREPORTED_REPORTID ? newPolicyID : CONST.POLICY.ID_FAKE, - }, - })); - - const failureDataThreadReports: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { - const report = transaction.reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`] : undefined; - return { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouAction?.childReportID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: { - parentReportID: transaction.reportID, - policyID: report?.policyID, + reportID: transaction.reportID, }, - }; - }); - - optimisticData.push(...optimisticDataThreadReports); - failureData.push(...failureDataThreadReports); + }); - // 4 & 5. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions - const optimisticDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { - const originalReportID = transaction.reportID; - const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`]?.reportName; + // 2. Optimistically update the IOU report action reportID + optimisticData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [newIOUAction.reportActionID]: newIOUAction, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldIOUAction.reportID}`, + value: {[oldIOUAction.reportActionID]: null}, + }, + ); - const reportAction = - reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, reportName) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, reportName); + failureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [newIOUAction.reportActionID]: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldIOUAction.reportID}`, + value: {[oldIOUAction.reportActionID]: oldIOUAction}, + }, + ); - // Store the thread information - transactionIDToReportActionAndThreadData[transaction.transactionID] = { - transactionThreadReportID: iouAction?.childReportID, - transactionThreadCreatedReportActionID: reportAction.created, - movedReportActionID: reportAction.reportActionID, - }; + // 3. Optimistically update the transaction thread and all threads in the transaction thread + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${newIOUAction.childReportID}`, + value: { + parentReportID: reportID, + policyID: reportID !== CONST.REPORT.UNREPORTED_REPORTID ? newReport.policyID : CONST.POLICY.ID_FAKE, + }, + }); - return { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.childReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${oldIOUAction.childReportID}`, value: { - [reportAction.reportActionID]: reportAction, + parentReportID: oldIOUAction.reportID, + policyID: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldIOUAction.reportID}`]?.policyID, }, - }; - }); + }); + + // 4. (Optional) Create transactionThread if it doesn't exist + let transactionThreadReportID = newIOUAction.childReportID; + let transactionThreadCreatedReportActionID; + if (!transactionThreadReportID) { + const optimisticTransactionThread = buildTransactionThread(newIOUAction, newReport); + const optimisticCreatedActionForTransactionThread = buildOptimisticCreatedReportAction(currentUserEmail); + transactionThreadReportID = optimisticTransactionThread.reportID; + transactionThreadCreatedReportActionID = optimisticCreatedActionForTransactionThread.reportActionID; + newIOUAction.childReportID = transactionThreadReportID; + + optimisticData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTransactionThread.reportID}`, + value: {...optimisticTransactionThread, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, + value: {[optimisticCreatedActionForTransactionThread.reportActionID]: optimisticCreatedActionForTransactionThread}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newIOUAction.reportID}`, + value: {[newIOUAction.reportActionID]: {childReportID: optimisticTransactionThread.reportID}}, + }, + ); - const failureDataNewReportActions: OnyxUpdate[] = transactionsReportActions.map(({transaction, iouAction}) => { - const originalReportID = transaction.reportID; - const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`]?.reportName; + successData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTransactionThread.reportID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, + value: {[optimisticCreatedActionForTransactionThread.reportActionID]: {pendingAction: null}}, + }, + ); - const reportAction = + failureData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticTransactionThread.reportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticTransactionThread.reportID}`, + value: {[optimisticCreatedActionForTransactionThread.reportActionID]: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newIOUAction.reportID}`, + value: {[newIOUAction.reportActionID]: {childReportID: null}}, + }, + ); + } + + // 5. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions + const movedAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(originalReportID, reportID, reportName) - : buildOptimisticMovedTransactionAction(originalReportID, reportID, reportName); + ? buildOptimisticUnreportedTransactionAction(transactionThreadReportID, transaction.reportID ?? '') + : buildOptimisticMovedTransactionAction(transactionThreadReportID, reportID); - return { + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouAction?.childReportID}`, - value: { - [reportAction.reportActionID]: null, - }, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: {[movedAction?.reportActionID]: movedAction}, + }); + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: {[movedAction?.reportActionID]: null}, + }); + + transactionIDToReportActionAndThreadData[transaction.transactionID] = { + movedReportActionID: movedAction.reportActionID, + ...(oldIOUAction.childReportID + ? {} + : { + transactionThreadReportID, + transactionThreadCreatedReportActionID, + }), }; }); - optimisticData.push(...optimisticDataNewReportActions); - failureData.push(...failureDataNewReportActions); - const parameters: ChangeTransactionsReportParams = { transactionList: transactionIDs.join(','), reportID, - transactionIDToReportActionAndThreadData, + transactionIDToReportActionAndThreadData: JSON.stringify(transactionIDToReportActionAndThreadData), }; API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 5776493463bf..d7b13afaa04d 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -892,11 +892,23 @@ function PureReportActionItem({ } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CHANGE_POLICY) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION) { - children = ; + children = ( + + ${getDeletedTransactionMessage(action)}`} /> + + ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION) { - children = ; + children = ( + + ${getMovedTransactionMessage(action)}`} /> + + ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION) { - children = ; + children = ( + + ${getUnreportedTransactionMessage(action)}`} /> + + ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MERGED_WITH_CASH_TRANSACTION) { children = ; } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION)) { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 09b8103474c1..136d10e1b2ec 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -556,19 +556,13 @@ type OriginalMessageChangePolicy = { /** Model of `UNREPORTED_TRANSACTION` report action */ type OriginalMessageUnreportedTransaction = { /** ID of the old report */ - fromReportID: string | undefined; - - /** Name of the report */ - reportName: string | undefined; + fromReportID: string; }; /** Model of `MOVEDTRANSACTION` report action */ type OriginalMessageMovedTransaction = { /** ID of the new report */ toReportID: string; - - /** Name of the report */ - reportName: string | undefined; }; /** Model of `moved` report action */ From 8e39c24c042ec2b20786a8f1edc79e050959224e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 3 Apr 2025 21:56:34 +0200 Subject: [PATCH 40/75] update transaction --- src/libs/actions/Transaction.ts | 51 +++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 0ad5f39771c3..bb3339651a54 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -17,7 +17,7 @@ import { buildOptimisticUnreportedTransactionAction, buildTransactionThread, } from '@libs/ReportUtils'; -import {getTransaction, waypointHasValidAddress} from '@libs/TransactionUtils'; +import {getAmount, getTransaction, waypointHasValidAddress} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, RecentWaypoint, Report, ReportAction, ReportActions, ReviewDuplicates, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; @@ -607,6 +607,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { const transactions = transactionIDs.map((id) => allTransactions?.[id]).filter((t): t is NonNullable => t !== undefined); const transactionIDToReportActionAndThreadData: Record = {}; + const updatedReportTotals: Record = {}; const optimisticData: OnyxUpdate[] = []; const failureData: OnyxUpdate[] = []; @@ -618,7 +619,9 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { return; } - const newIOUAction = {...oldIOUAction, reportID}; + const newIOUAction = {...oldIOUAction}; + const oldReportID = transaction.reportID; + const oldReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldReportID}`]; // 1. Optimistically change the reportID on the passed transactions optimisticData.push({ @@ -637,7 +640,16 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }, }); - // 2. Optimistically update the IOU report action reportID + // 2. Keep track of the new report totals + const transactionAmount = getAmount(transaction); + if (oldReportID) { + updatedReportTotals[oldReportID] = (updatedReportTotals[oldReportID] ? updatedReportTotals[oldReportID] : oldReport?.total ?? 0) + transactionAmount; + } + if (reportID) { + updatedReportTotals[reportID] = (updatedReportTotals[reportID] ? updatedReportTotals[reportID] : newReport.total ?? 0) - transactionAmount; + } + + // 3. Optimistically update the IOU action reportID optimisticData.push( { onyxMethod: Onyx.METHOD.MERGE, @@ -648,7 +660,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldIOUAction.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldReportID}`, value: {[oldIOUAction.reportActionID]: null}, }, ); @@ -663,12 +675,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldIOUAction.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldReportID}`, value: {[oldIOUAction.reportActionID]: oldIOUAction}, }, ); - // 3. Optimistically update the transaction thread and all threads in the transaction thread + // 4. Optimistically update the transaction thread and all threads in the transaction thread optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${newIOUAction.childReportID}`, @@ -682,12 +694,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${oldIOUAction.childReportID}`, value: { - parentReportID: oldIOUAction.reportID, - policyID: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldIOUAction.reportID}`]?.policyID, + parentReportID: oldReportID, + policyID: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldReportID}`]?.policyID, }, }); - // 4. (Optional) Create transactionThread if it doesn't exist + // 5. (Optional) Create transactionThread if it doesn't exist let transactionThreadReportID = newIOUAction.childReportID; let transactionThreadCreatedReportActionID; if (!transactionThreadReportID) { @@ -710,7 +722,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newIOUAction.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[newIOUAction.reportActionID]: {childReportID: optimisticTransactionThread.reportID}}, }, ); @@ -741,13 +753,13 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newIOUAction.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[newIOUAction.reportActionID]: {childReportID: null}}, }, ); } - // 5. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions + // 6. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions const movedAction = reportID === CONST.REPORT.UNREPORTED_REPORTID ? buildOptimisticUnreportedTransactionAction(transactionThreadReportID, transaction.reportID ?? '') @@ -776,6 +788,21 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { }; }); + // 7. Update the report totals + Object.entries(updatedReportTotals).forEach(([reportIDToUpdate, total]) => { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportIDToUpdate}`, + value: {total}, + }); + + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportIDToUpdate}`, + value: {total: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportIDToUpdate}`]?.total}, + }); + }); + const parameters: ChangeTransactionsReportParams = { transactionList: transactionIDs.join(','), reportID, From 2093ee8da3c7ba56259df4f7a49654a7adf63c01 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 3 Apr 2025 22:03:10 +0200 Subject: [PATCH 41/75] revert deleted --- src/pages/home/report/PureReportActionItem.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index a1b3f1f5e19b..0c46ef2a4361 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -925,11 +925,7 @@ function PureReportActionItem({ } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.CHANGE_POLICY) { children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.DELETED_TRANSACTION) { - children = ( - - ${getDeletedTransactionMessage(action)}`} /> - - ); + children = ; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION) { children = ( From 8b73cc2a54b9959218172fe7b2157f7edd8e0678 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 3 Apr 2025 22:11:51 +0200 Subject: [PATCH 42/75] update correct link --- src/libs/ReportUtils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index da2cd6febc8c..0b302ebe4ea6 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -74,6 +74,7 @@ import {isAnonymousUser as isAnonymousUserSession} from './actions/Session'; import {convertToDisplayString} from './CurrencyUtils'; import DateUtils from './DateUtils'; import {hasValidDraftComment} from './DraftCommentUtils'; +import {getEnvironmentURL} from './Environment/Environment'; import {getMicroSecondOnyxErrorWithTranslationKey} from './ErrorUtils'; import getAttachmentDetails from './fileDownload/getAttachmentDetails'; import isReportMessageAttachment from './isReportMessageAttachment'; @@ -820,6 +821,9 @@ Onyx.connect({ }, }); +let environmentURL: string; +getEnvironmentURL().then((url: string) => (environmentURL = url)); + const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon'; Onyx.connect({ key: ONYXKEYS.SESSION, @@ -5980,7 +5984,7 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string const movedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - html: `moved this expense to ${reportName}`, + html: `moved this expense to ${reportName}`, text: `moved this expense to ${reportName}`, }, ]; @@ -6019,7 +6023,7 @@ function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: s const unreportedTransactionMessage = [ { type: CONST.REPORT.MESSAGE.TYPE.TEXT, - html: `removed this expense from ${reportName}`, + html: `removed this expense from ${reportName}`, text: `removed this expense from ${reportName}`, }, ]; From 59bc33de23e43f700cabee09dd590eb941f775f4 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 3 Apr 2025 22:26:23 +0200 Subject: [PATCH 43/75] update translations --- src/languages/en.ts | 5 ++--- src/languages/es.ts | 4 ++-- src/languages/params.ts | 4 ++-- src/libs/ReportUtils.ts | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index acbb565ee79d..271c9a8bbbaa 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -923,9 +923,8 @@ const translations = { posted: 'Posted', deleteReceipt: 'Delete receipt', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `deleted an expense on this report, ${merchant} - ${amount}`, - movedTransaction: ({toReportID, reportName}: MovedTransactionParams) => `moved this expense to ${reportName}`, - unreportedTransaction: ({fromReportID, reportName}: UnreportedTransactionParams) => - `removed this expense from ${reportName}`, + movedTransaction: ({reportUrl, reportName}: MovedTransactionParams) => `moved this expense to ${reportName}`, + unreportedTransaction: ({reportUrl, reportName}: UnreportedTransactionParams) => `removed this expense from ${reportName}`, pendingMatchWithCreditCard: 'Receipt pending match with card transaction', pendingMatch: 'Pending match', pendingMatchWithCreditCardDescription: 'Receipt pending match with card transaction. Mark as cash to cancel.', diff --git a/src/languages/es.ts b/src/languages/es.ts index febc61361128..66936178e964 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -923,8 +923,8 @@ const translations = { markAsCash: 'Marcar como efectivo', routePending: 'Ruta pendiente...', deletedTransaction: ({amount, merchant}: DeleteTransactionParams) => `eliminó un gasto de este informe, ${merchant} - ${amount}`, - movedTransaction: ({toReportID, reportName}: MovedTransactionParams) => `movió este gasto a ${reportName}`, - unreportedTransaction: ({fromReportID, reportName}: UnreportedTransactionParams) => `eliminó este gasto de ${reportName}`, + movedTransaction: ({reportUrl, reportName}: MovedTransactionParams) => `movió este gasto a ${reportName}`, + unreportedTransaction: ({reportUrl, reportName}: UnreportedTransactionParams) => `eliminó este gasto de ${reportName}`, receiptIssuesFound: () => ({ one: 'Problema encontrado', other: 'Problemas encontrados', diff --git a/src/languages/params.ts b/src/languages/params.ts index f2ebc54bbd1f..e9b086770b1a 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -121,13 +121,13 @@ type DeleteTransactionParams = { }; type MovedTransactionParams = { - toReportID: string; + reportUrl: string; reportName?: string; }; type UnreportedTransactionParams = { + reportUrl: string; reportName?: string; - fromReportID?: string; }; type SettleExpensifyCardParams = { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0b302ebe4ea6..01ace60c95f1 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5598,7 +5598,7 @@ function getMovedTransactionMessage(action: ReportAction) { const {toReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`]?.reportName ?? ''; const message = translateLocal('iou.movedTransaction', { - toReportID, + reportUrl: `${environmentURL}/r/${toReportID}`, reportName, }); return message; @@ -5609,7 +5609,7 @@ function getUnreportedTransactionMessage(action: ReportAction) { const {fromReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]?.reportName ?? ''; const message = translateLocal('iou.unreportedTransaction', { - fromReportID, + reportUrl: `${environmentURL}/r/${fromReportID}`, reportName, }); return message; From 4a9caeae16e2cf55a19bf1506cb491633e692e36 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Fri, 4 Apr 2025 11:21:35 +0200 Subject: [PATCH 44/75] apply changes --- .../ChangeTransactionsReportParams.ts | 1 + src/libs/actions/Transaction.ts | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/libs/API/parameters/ChangeTransactionsReportParams.ts b/src/libs/API/parameters/ChangeTransactionsReportParams.ts index a8168121f091..e9e3a0fb15aa 100644 --- a/src/libs/API/parameters/ChangeTransactionsReportParams.ts +++ b/src/libs/API/parameters/ChangeTransactionsReportParams.ts @@ -5,6 +5,7 @@ */ type TransactionThreadInfo = { movedReportActionID: string; + moneyRequestPreviewReportActionID: string; transactionThreadReportID?: string; transactionThreadCreatedReportActionID?: string; }; diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index bb3339651a54..bf2229730f56 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -8,7 +8,9 @@ import * as API from '@libs/API'; import type {ChangeTransactionsReportParams, DismissViolationParams, GetRouteParams, MarkAsCashParams, TransactionThreadInfo} from '@libs/API/parameters'; import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import * as CollectionUtils from '@libs/CollectionUtils'; +import DateUtils from '@libs/DateUtils'; import * as NumberUtils from '@libs/NumberUtils'; +import {rand64} from '@libs/NumberUtils'; import {getAllReportActions, getIOUActionForReportID, getOriginalMessage, isModifiedExpenseAction} from '@libs/ReportActionsUtils'; import { buildOptimisticCreatedReportAction, @@ -619,7 +621,6 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { return; } - const newIOUAction = {...oldIOUAction}; const oldReportID = transaction.reportID; const oldReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldReportID}`]; @@ -650,6 +651,8 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { } // 3. Optimistically update the IOU action reportID + const optimisticMoneyRequestReportActionID = rand64(); + const newIOUAction = {...oldIOUAction, reportActionID: optimisticMoneyRequestReportActionID, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}; optimisticData.push( { onyxMethod: Onyx.METHOD.MERGE, @@ -661,10 +664,32 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldReportID}`, - value: {[oldIOUAction.reportActionID]: null}, + value: { + [oldIOUAction.reportActionID]: { + actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, + originalMessage: { + deleted: DateUtils.getDBTime(), + }, + message: [ + { + deleted: DateUtils.getDBTime(), + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + text: '', + }, + ], + }, + }, }, ); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: { + [newIOUAction.reportActionID]: {pendingAction: null}, + }, + }); + failureData.push( { onyxMethod: Onyx.METHOD.MERGE, @@ -686,6 +711,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { key: `${ONYXKEYS.COLLECTION.REPORT}${newIOUAction.childReportID}`, value: { parentReportID: reportID, + parentReportActionID: optimisticMoneyRequestReportActionID, policyID: reportID !== CONST.REPORT.UNREPORTED_REPORTID ? newReport.policyID : CONST.POLICY.ID_FAKE, }, }); @@ -695,6 +721,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { key: `${ONYXKEYS.COLLECTION.REPORT}${oldIOUAction.childReportID}`, value: { parentReportID: oldReportID, + optimisticMoneyRequestReportActionID: oldIOUAction.reportActionID, policyID: allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oldReportID}`]?.policyID, }, }); @@ -771,6 +798,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { value: {[movedAction?.reportActionID]: movedAction}, }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: {[movedAction?.reportActionID]: {pendingAction: null}}, + }); + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, @@ -779,6 +812,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { transactionIDToReportActionAndThreadData[transaction.transactionID] = { movedReportActionID: movedAction.reportActionID, + moneyRequestPreviewReportActionID: newIOUAction.reportActionID, ...(oldIOUAction.childReportID ? {} : { From fd0491763b96cd8232e45fd0b4cafa5d48d9fbca Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Sun, 6 Apr 2025 18:37:09 +0200 Subject: [PATCH 45/75] fix linter --- src/libs/actions/Transaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index bf2229730f56..63ee5e7cb467 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -617,7 +617,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { transactions.forEach((transaction) => { const oldIOUAction = getIOUActionForReportID(transaction.reportID, transaction.transactionID); - if (!oldIOUAction?.reportActionID) { + if (!oldIOUAction?.reportActionID || !transaction.reportID) { return; } @@ -789,7 +789,7 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { // 6. Add MOVEDTRANSACTION or UNREPORTEDTRANSACTION report actions const movedAction = reportID === CONST.REPORT.UNREPORTED_REPORTID - ? buildOptimisticUnreportedTransactionAction(transactionThreadReportID, transaction.reportID ?? '') + ? buildOptimisticUnreportedTransactionAction(transactionThreadReportID, transaction.reportID) : buildOptimisticMovedTransactionAction(transactionThreadReportID, reportID); optimisticData.push({ From f6fecf6f62ad615c7af92852cf925968047ed8a8 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Sun, 6 Apr 2025 18:40:45 +0200 Subject: [PATCH 46/75] update key --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 677bd05aa5fb..a88acb15f559 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -652,7 +652,7 @@ function MoneyRequestConfirmationListFooter({ { item: ( Date: Sun, 6 Apr 2025 18:44:16 +0200 Subject: [PATCH 47/75] fix ts --- src/pages/iou/request/step/IOURequestStepReport.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index dd5436ff13ca..4aa028fbf0db 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -59,10 +59,10 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { .filter((item) => item !== undefined) .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) .map((report) => ({ - text: report.reportName, - value: report.reportID, - keyForList: report.reportID, - isSelected: report.reportID === transaction?.reportID, + text: report?.reportName, + value: report?.reportID, + keyForList: report?.reportID, + isSelected: report?.reportID === transaction?.reportID, })); }, [allReports, debouncedSearchValue, transaction?.reportID]); From c6a0505b664dbde691c992d04648f428673b6381 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Sun, 6 Apr 2025 18:50:48 +0200 Subject: [PATCH 48/75] fix ts --- src/pages/iou/request/step/IOURequestStepReport.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 4aa028fbf0db..1d1fcb6a69f1 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -57,12 +57,12 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .filter((item) => item !== undefined) - .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) + .filter((report) => report && (!debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase()))) .map((report) => ({ - text: report?.reportName, - value: report?.reportID, - keyForList: report?.reportID, - isSelected: report?.reportID === transaction?.reportID, + text: report.reportName, + value: report.reportID, + keyForList: report.reportID, + isSelected: report.reportID === transaction?.reportID, })); }, [allReports, debouncedSearchValue, transaction?.reportID]); From 823e40a93e498e53da7b7fff2ab7e48e9f4dbed8 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 7 Apr 2025 11:00:02 +0200 Subject: [PATCH 49/75] fix ts --- src/pages/iou/request/step/IOURequestStepReport.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 1d1fcb6a69f1..cc9736598e9f 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -56,8 +56,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) - .filter((item) => item !== undefined) - .filter((report) => report && (!debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase()))) + .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) + .filter((report) => report !== undefined) .map((report) => ({ text: report.reportName, value: report.reportID, From 2374b45056cf545623cf0601c24cd76b0daafd13 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 7 Apr 2025 11:29:23 +0200 Subject: [PATCH 50/75] fix ts --- src/pages/iou/request/step/IOURequestStepReport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index cc9736598e9f..80a3842746f1 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -57,7 +57,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) - .filter((report) => report !== undefined) + .filter((report): report is NonNullable => report !== undefined) .map((report) => ({ text: report.reportName, value: report.reportID, From aa71e553bda8098cfcb0628658f66858083af04e Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 7 Apr 2025 19:00:13 +0200 Subject: [PATCH 51/75] provide feedback --- src/libs/actions/Transaction.ts | 17 +++++++----- .../iou/request/step/IOURequestStepReport.tsx | 26 +++++++++++++++---- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 63ee5e7cb467..7f0077646a4a 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -601,7 +601,7 @@ function getAllTransactions() { return Object.keys(allTransactions ?? {}).length; } -function changeTransactionsReport(transactionIDs: string[], reportID: string) { +function changeTransactionsReport(transactionIDs: string[], reportID: string, isEditing = false) { const newReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!newReport) { return; @@ -842,12 +842,15 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string) { reportID, transactionIDToReportActionAndThreadData: JSON.stringify(transactionIDToReportActionAndThreadData), }; - - API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { - optimisticData, - successData, - failureData, - }); + if (isEditing) { + API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { + optimisticData, + successData, + failureData, + }); + } else { + Onyx.update(optimisticData); + } } export { diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 80a3842746f1..251c77620c09 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -1,4 +1,5 @@ import React, {useMemo} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import SelectionList from '@components/SelectionList'; import type {ListItem} from '@components/SelectionList/types'; @@ -12,6 +13,8 @@ import {isExpenseReport} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import type {Report} from '@src/types/onyx'; +import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; @@ -22,8 +25,7 @@ type ReportListItem = ListItem & { value: string; }; -type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & - WithFullTransactionOrNotFoundProps; +type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & WithFullTransactionOrNotFoundProps; /** * Check if a report has any forwarded actions @@ -33,11 +35,25 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } +/** + * This function narrows down the data from Onyx to just the properties that we want to trigger a re-render of the component. + * This helps minimize re-rendering and makes the entire component more performant. + */ +const reportSelector = (report: OnyxEntry): OnyxEntry => + report && { + reportID: report.reportID, + reportName: report.reportName, + stateNum: report.stateNum, + statusNum: report.statusNum, + type: report.type, + }; + function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const {translate} = useLocalize(); - const {backTo} = route.params; - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const {backTo, action} = route.params; + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: (c) => mapOnyxCollectionItems(c, reportSelector)}); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); + const isEditing = action === CONST.IOU.ACTION.EDIT; const reportOptions: ReportListItem[] = useMemo(() => { if (!allReports) { @@ -75,7 +91,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return; } - changeTransactionsReport([transaction.transactionID], item.value); + changeTransactionsReport([transaction.transactionID], item.value, isEditing); Navigation.goBack(backTo); }; From d0ecbdc97ba6f1147619afbee4e61843e5f1882a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 7 Apr 2025 21:09:03 +0200 Subject: [PATCH 52/75] update optimistic creation --- src/libs/actions/Transaction.ts | 22 ++++++++++--------- .../iou/request/step/IOURequestStepReport.tsx | 10 ++++++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 7f0077646a4a..f2f55b6b77c7 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -601,7 +601,11 @@ function getAllTransactions() { return Object.keys(allTransactions ?? {}).length; } -function changeTransactionsReport(transactionIDs: string[], reportID: string, isEditing = false) { +function setTransactionReport(transactionID: string, reportID: string, isDraft: boolean) { + Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {reportID}); +} + +function changeTransactionsReport(transactionIDs: string[], reportID: string) { const newReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!newReport) { return; @@ -842,15 +846,12 @@ function changeTransactionsReport(transactionIDs: string[], reportID: string, is reportID, transactionIDToReportActionAndThreadData: JSON.stringify(transactionIDToReportActionAndThreadData), }; - if (isEditing) { - API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { - optimisticData, - successData, - failureData, - }); - } else { - Onyx.update(optimisticData); - } + + API.write(WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT, parameters, { + optimisticData, + successData, + failureData, + }); } export { @@ -873,4 +874,5 @@ export { getLastModifiedExpense, revert, changeTransactionsReport, + setTransactionReport, }; diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 251c77620c09..72fc198d3646 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -6,7 +6,7 @@ import type {ListItem} from '@components/SelectionList/types'; import UserListItem from '@components/SelectionList/UserListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; -import {changeTransactionsReport} from '@libs/actions/Transaction'; +import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; import {getAllReportActions} from '@libs/ReportActionsUtils'; import {isExpenseReport} from '@libs/ReportUtils'; @@ -90,8 +90,12 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { if (!transaction) { return; } - - changeTransactionsReport([transaction.transactionID], item.value, isEditing); + if (item.reportID !== transaction.reportID) { + setTransactionReport(transaction.transactionID, item.value, !isEditing); + if (isEditing) { + changeTransactionsReport([transaction.transactionID], item.value); + } + } Navigation.goBack(backTo); }; From ec32e101a25446cb4f22862a90ef58f3f53c7222 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 15:31:49 +0200 Subject: [PATCH 53/75] fix merge conf --- src/libs/ReportUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 67b1dfc746d6..dd0dcfc63811 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10656,6 +10656,8 @@ export { getGroupChatDraft, getInvoiceReportName, getChatListItemReportName, + buildOptimisticMovedTransactionAction, + navigateToLinkedReportAction, }; export type { From 3f707d5508d644334e95b899aa2b63a043e5efca Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 15:36:18 +0200 Subject: [PATCH 54/75] fix linter --- src/libs/ReportUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index dd0dcfc63811..f30d67348617 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -10658,6 +10658,7 @@ export { getChatListItemReportName, buildOptimisticMovedTransactionAction, navigateToLinkedReportAction, + populateOptimisticReportFormula, }; export type { From 0584d741224b0178afbfe5930ca02760607995e5 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 17:40:02 +0200 Subject: [PATCH 55/75] fix condition --- src/pages/iou/request/step/IOURequestStepReport.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 72fc198d3646..de59ae3eab58 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -63,8 +63,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const expenseReports = Object.values(allReports).filter( (report) => isExpenseReport(report) && - report?.stateNum && - report?.statusNum && + report?.stateNum != null && + report?.statusNum != null && report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && !hasForwardedAction(report.reportID), From 70696b3e569685903374c4f6ccaab23f05c6ef24 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 18:13:19 +0200 Subject: [PATCH 56/75] update condition and update selected --- src/components/MoneyRequestConfirmationListFooter.tsx | 4 ++-- src/pages/iou/request/step/IOURequestStepReport.tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index a88acb15f559..7ebb6ac4cce7 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -282,8 +282,8 @@ function MoneyRequestConfirmationListFooter({ .filter( (report) => isExpenseReport(report) && - report?.stateNum && - report?.statusNum && + report?.stateNum !== undefined && + report?.statusNum !== undefined && report?.policyID === selectedParticipants?.at(0)?.policyID && report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index de59ae3eab58..f554eb8344aa 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -63,8 +63,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const expenseReports = Object.values(allReports).filter( (report) => isExpenseReport(report) && - report?.stateNum != null && - report?.statusNum != null && + report?.stateNum !== undefined && + report?.statusNum !== undefined && report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && !hasForwardedAction(report.reportID), @@ -114,6 +114,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { onChangeText={setSearchValue} textInputLabel={reportOptions.length >= CONST.STANDARD_LIST_ITEM_LIMIT ? translate('common.search') : undefined} shouldSingleExecuteRowSelect + initiallyFocusedOptionKey={transaction?.reportID} ListItem={UserListItem} /> From 3807befec016e7adab0bf9c3bc602c256a89806b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 18:37:33 +0200 Subject: [PATCH 57/75] fix reportName --- src/components/MoneyRequestConfirmationListFooter.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 7ebb6ac4cce7..00b77b38ca4c 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -291,7 +291,14 @@ function MoneyRequestConfirmationListFooter({ .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .at(0); - let reportName = outstandingReport?.reportName; + let reportName: string | undefined; + if (transaction?.reportID) { + const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); + reportName = transactionReport?.reportName; + } else { + reportName = outstandingReport?.reportName; + } + if (!reportName) { const optimisticReport = buildOptimisticExpenseReport(reportID, policy?.id, policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID, Number(formattedAmount), currency); reportName = populateOptimisticReportFormula(policy?.fieldList?.text_title?.defaultValue ?? '', optimisticReport, policy); From 92384cacf5be85fad5eb5b0d20ad35860d987b63 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 19:16:05 +0200 Subject: [PATCH 58/75] update with new util --- .../MoneyRequestConfirmationListFooter.tsx | 13 ++----- src/libs/ReportUtils.ts | 34 +++++++++++++++++++ .../iou/request/step/IOURequestStepReport.tsx | 23 ++----------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 00b77b38ca4c..e6cc8a39a570 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit, isMultiLevelTags as isMultiLevelTagsPolicyUtils, isPaidGroupPolicy} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, isExpenseReport, populateOptimisticReportFormula} from '@libs/ReportUtils'; +import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getOutstandingReports, isExpenseReport, populateOptimisticReportFormula} from '@libs/ReportUtils'; import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -278,16 +278,7 @@ function MoneyRequestConfirmationListFooter({ return canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]); - const outstandingReport = Object.values(allReports ?? {}) - .filter( - (report) => - isExpenseReport(report) && - report?.stateNum !== undefined && - report?.statusNum !== undefined && - report?.policyID === selectedParticipants?.at(0)?.policyID && - report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && - report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED, - ) + const outstandingReport = getOutstandingReports(selectedParticipants?.at(0)?.policyID, allReports ?? {}) .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .at(0); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f30d67348617..d60558143008 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9404,6 +9404,39 @@ function createDraftTransactionAndNavigateToParticipantSelector( return createDraftWorkspaceAndNavigateToConfirmationScreen(transactionID, actionName); } +/** + * Check if a report has any forwarded actions + */ +function hasForwardedAction(reportID: string): boolean { + const reportActions = getAllReportActions(reportID); + return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); +} + +/** + * Get outstanding expense reports for a given policy ID + * @param policyID - The policy ID to filter reports by + * @param reports - Collection of reports to filter + * @returns Array of outstanding expense reports sorted by name + */ +function getOutstandingReports(policyID: string | undefined, reports: OnyxCollection = allReports): Array> { + if (!reports) { + return []; + } + + return Object.values(reports) + .filter( + (report) => + isExpenseReport(report) && + report?.stateNum !== undefined && + report?.statusNum !== undefined && + report?.policyID === policyID && + report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && + report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && + !hasForwardedAction(report.reportID), + ) + .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0); +} + /** * @returns the object to update `report.hasOutstandingChildRequest` */ @@ -10659,6 +10692,7 @@ export { buildOptimisticMovedTransactionAction, navigateToLinkedReportAction, populateOptimisticReportFormula, + getOutstandingReports, }; export type { diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index f554eb8344aa..aeeee70d3b5f 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -8,8 +8,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction'; import Navigation from '@libs/Navigation/Navigation'; -import {getAllReportActions} from '@libs/ReportActionsUtils'; -import {isExpenseReport} from '@libs/ReportUtils'; +import {getOutstandingReports} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -27,14 +26,6 @@ type ReportListItem = ListItem & { type IOURequestStepReportProps = WithWritableReportOrNotFoundProps & WithFullTransactionOrNotFoundProps; -/** - * Check if a report has any forwarded actions - */ -function hasForwardedAction(reportID: string): boolean { - const reportActions = getAllReportActions(reportID); - return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); -} - /** * This function narrows down the data from Onyx to just the properties that we want to trigger a re-render of the component. * This helps minimize re-rendering and makes the entire component more performant. @@ -60,15 +51,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return []; } - const expenseReports = Object.values(allReports).filter( - (report) => - isExpenseReport(report) && - report?.stateNum !== undefined && - report?.statusNum !== undefined && - report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && - report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && - !hasForwardedAction(report.reportID), - ); + const expenseReports = getOutstandingReports(transaction?.participants?.[0]?.policyID, allReports ?? {}); return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) @@ -80,7 +63,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { keyForList: report.reportID, isSelected: report.reportID === transaction?.reportID, })); - }, [allReports, debouncedSearchValue, transaction?.reportID]); + }, [allReports, debouncedSearchValue, transaction?.participants, transaction?.reportID]); const navigateBack = () => { Navigation.goBack(backTo); From 69e56c8124ecc9d36967b5a2762e6d88adbbb69b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 19:24:14 +0200 Subject: [PATCH 59/75] fix lint --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index e6cc8a39a570..4bc48ff2d7dc 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit, isMultiLevelTags as isMultiLevelTagsPolicyUtils, isPaidGroupPolicy} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getOutstandingReports, isExpenseReport, populateOptimisticReportFormula} from '@libs/ReportUtils'; +import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getOutstandingReports, populateOptimisticReportFormula} from '@libs/ReportUtils'; import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, From 8a00c88b6a6edf0cb20b494f9e3de5d2d3f51ea1 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 8 Apr 2025 23:06:24 +0200 Subject: [PATCH 60/75] fix selection issues --- .../MoneyRequestConfirmationListFooter.tsx | 11 ++++++----- src/pages/iou/request/step/IOURequestStepReport.tsx | 12 +++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 4bc48ff2d7dc..a023d28425cb 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -278,16 +278,17 @@ function MoneyRequestConfirmationListFooter({ return canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]); - const outstandingReport = getOutstandingReports(selectedParticipants?.at(0)?.policyID, allReports ?? {}) - .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) - .at(0); + const outstandingReports = getOutstandingReports(selectedParticipants?.at(0)?.policyID, allReports ?? {}).sort( + (a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0, + ); + const shouldUserTransactionReport = outstandingReports.some((report) => report?.reportID === transaction?.reportID); let reportName: string | undefined; - if (transaction?.reportID) { + if (transaction?.reportID && shouldUserTransactionReport) { const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); reportName = transactionReport?.reportName; } else { - reportName = outstandingReport?.reportName; + reportName = outstandingReports.at(0)?.reportName; } if (!reportName) { diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index aeeee70d3b5f..63deed96ca3e 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -33,6 +33,7 @@ type IOURequestStepReportProps = WithWritableReportOrNotFoundProps): OnyxEntry => report && { reportID: report.reportID, + policyID: report.policyID, reportName: report.reportName, stateNum: report.stateNum, statusNum: report.statusNum, @@ -50,9 +51,10 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { if (!allReports) { return []; } - - const expenseReports = getOutstandingReports(transaction?.participants?.[0]?.policyID, allReports ?? {}); - + // We need to get the policyID because it's not defined in the transaction object before we select a report manually. + const policyID = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.participants?.at(0)?.reportID)?.policyID; + const expenseReports = getOutstandingReports(policyID, allReports ?? {}); + const isTransactionReportCorrect = expenseReports.some((report) => report?.reportID === transaction?.reportID); return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) .filter((report) => !debouncedSearchValue || report?.reportName?.toLowerCase().includes(debouncedSearchValue.toLowerCase())) @@ -61,9 +63,9 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { text: report.reportName, value: report.reportID, keyForList: report.reportID, - isSelected: report.reportID === transaction?.reportID, + isSelected: isTransactionReportCorrect ? report.reportID === transaction?.reportID : expenseReports.at(0)?.reportID === report.reportID, })); - }, [allReports, debouncedSearchValue, transaction?.participants, transaction?.reportID]); + }, [allReports, debouncedSearchValue, transaction]); const navigateBack = () => { Navigation.goBack(backTo); From 8cce59208bb79a7a18451c2378085dea0f73143a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 9 Apr 2025 19:52:50 +0200 Subject: [PATCH 61/75] provide feedback --- src/components/MoneyRequestConfirmationListFooter.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index a023d28425cb..1c26a7599089 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -282,9 +282,13 @@ function MoneyRequestConfirmationListFooter({ (a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0, ); - const shouldUserTransactionReport = outstandingReports.some((report) => report?.reportID === transaction?.reportID); + /** + * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. + * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. + */ + const shouldUseTransactionReport = outstandingReports.some((report) => report?.reportID === transaction?.reportID); let reportName: string | undefined; - if (transaction?.reportID && shouldUserTransactionReport) { + if (transaction?.reportID && shouldUseTransactionReport) { const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); reportName = transactionReport?.reportName; } else { From e38c1f958a5168fad7408e7bc857ac06ecb9ffdf Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 9 Apr 2025 20:39:56 +0200 Subject: [PATCH 62/75] fix policyID --- src/pages/iou/request/step/IOURequestStepReport.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 63deed96ca3e..d1d41b0d30c2 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -52,7 +52,9 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { return []; } // We need to get the policyID because it's not defined in the transaction object before we select a report manually. - const policyID = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.participants?.at(0)?.reportID)?.policyID; + const policyID = Object.values(allReports ?? {}).find( + (report) => report?.reportID === transaction?.reportID || report?.reportID === transaction?.participants?.at(0)?.reportID, + )?.policyID; const expenseReports = getOutstandingReports(policyID, allReports ?? {}); const isTransactionReportCorrect = expenseReports.some((report) => report?.reportID === transaction?.reportID); return expenseReports From 54faeac02a6e11f61b425a32986f8fb323cb3600 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 9 Apr 2025 20:43:12 +0200 Subject: [PATCH 63/75] update report utils --- src/libs/ReportUtils.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index adf255b1af1a..6ef410d2572f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5644,12 +5644,20 @@ function getDeletedTransactionMessage(action: ReportAction) { return message; } +function getReportDetails(reportID: string): {reportName: string; reportUrl: string} { + const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + return { + reportName: report?.reportName ?? '', + reportUrl: `${environmentURL}/r/${reportID}`, + }; +} + function getMovedTransactionMessage(action: ReportAction) { const movedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; const {toReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; - const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`]?.reportName ?? ''; + const {reportName, reportUrl} = getReportDetails(toReportID); const message = translateLocal('iou.movedTransaction', { - reportUrl: `${environmentURL}/r/${toReportID}`, + reportUrl, reportName, }); return message; @@ -5658,9 +5666,9 @@ function getMovedTransactionMessage(action: ReportAction) { function getUnreportedTransactionMessage(action: ReportAction) { const unreportedTransactionOriginalMessage = getOriginalMessage(action as ReportAction) ?? {}; const {fromReportID} = unreportedTransactionOriginalMessage as OriginalMessageUnreportedTransaction; - const reportName = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]?.reportName ?? ''; + const {reportName, reportUrl} = getReportDetails(fromReportID); const message = translateLocal('iou.unreportedTransaction', { - reportUrl: `${environmentURL}/r/${fromReportID}`, + reportUrl, reportName, }); return message; From d7de72b914ecde825f80086dca5ec0297beccdb1 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 9 Apr 2025 21:23:22 +0200 Subject: [PATCH 64/75] update reportID --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ec2b86995675..929ceac21e2f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4967,7 +4967,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { comment, created, merchant, - iouReportID: iouReport.reportID, + iouReportID: attendees?.at(0)?.reportID ?? iouReport.reportID, chatReportID: chatReport.reportID, transactionID: transaction.transactionID, reportActionID: iouAction.reportActionID, From 32baeafd3a93b09d671045ca9bf385b600ede1a5 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 9 Apr 2025 23:57:44 +0200 Subject: [PATCH 65/75] remove unused code --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 929ceac21e2f..ec2b86995675 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4967,7 +4967,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { comment, created, merchant, - iouReportID: attendees?.at(0)?.reportID ?? iouReport.reportID, + iouReportID: iouReport.reportID, chatReportID: chatReport.reportID, transactionID: transaction.transactionID, reportActionID: iouAction.reportActionID, From 4f2efa62a79044184f064a1fa6347b42ca26fd5a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 10 Apr 2025 00:54:08 +0200 Subject: [PATCH 66/75] update to use transaction report --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index ba6d29dced19..a68283a3dd3f 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -29,7 +29,7 @@ import Log from '@libs/Log'; import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction'; import Navigation from '@libs/Navigation/Navigation'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; -import {generateReportID, getBankAccountRoute, isSelectedManagerMcTest} from '@libs/ReportUtils'; +import {generateReportID, getBankAccountRoute, getReportOrDraftReport, isSelectedManagerMcTest} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getDefaultTaxCode, getRateID, getRequestType, getValidWaypoints} from '@libs/TransactionUtils'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; @@ -90,7 +90,9 @@ function IOURequestStepConfirmation({ const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${getIOURequestPolicyID(transaction, reportReal)}`); const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); - const report = reportReal ?? reportDraft; + // We want to use a repport from the transaction if it exists + const transactionReport = getReportOrDraftReport(transaction?.reportID); + const report = transactionReport ?? reportReal ?? reportDraft; const policy = policyReal ?? policyDraft; const isDraftPolicy = policy === policyDraft; const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; From 351c22e38c53b015bd135ad3e442b2ec8bde6e23 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 10 Apr 2025 17:42:30 +0200 Subject: [PATCH 67/75] prvide feedback --- .../MoneyRequestConfirmationListFooter.tsx | 17 ++- src/libs/ReportUtils.ts | 118 ++++++++---------- 2 files changed, 61 insertions(+), 74 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index a83f960834de..9702aa8aa17a 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit, isMultiLevelTags as isMultiLevelTagsPolicyUtils, isPaidGroupPolicy} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getOutstandingReports, populateOptimisticReportFormula} from '@libs/ReportUtils'; +import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getFirstOutstandingReport, isReportOutsanding, populateOptimisticReportFormula} from '@libs/ReportUtils'; import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -278,21 +278,18 @@ function MoneyRequestConfirmationListFooter({ return canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant; }, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]); - const outstandingReports = getOutstandingReports(selectedParticipants?.at(0)?.policyID, allReports ?? {}).sort( - (a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0, - ); - /** * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ - const shouldUseTransactionReport = outstandingReports.some((report) => report?.reportID === transaction?.reportID); + const transactionReport = !!transaction?.reportID && allReports?.[transaction.reportID]; + const shouldUseTransactionReport = !!transactionReport && isReportOutsanding(transactionReport, selectedParticipants?.at(0)?.policyID); let reportName: string | undefined; - if (transaction?.reportID && shouldUseTransactionReport) { - const transactionReport = Object.values(allReports ?? {}).find((report) => report?.reportID === transaction?.reportID); - reportName = transactionReport?.reportName; + if (shouldUseTransactionReport) { + reportName = transactionReport.reportName; } else { - reportName = outstandingReports.at(0)?.reportName; + const firstOutstangingReport = getFirstOutstandingReport(selectedParticipants?.at(0)?.policyID, allReports ?? {}); + reportName = firstOutstangingReport?.reportName; } if (!reportName) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 72cf6cfe0e64..01f7e18ddf28 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6053,32 +6053,28 @@ function buildOptimisticChangePolicyReportAction(fromPolicyID: string | undefine }; } -/** - * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. - * This action is used when we change the workspace of a report. - */ -function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string): ReportAction { - const originalMessage = { - toReportID, - }; - - const reportName = allReports?.[toReportID]?.reportName; - const movedTransactionMessage = [ - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - html: `moved this expense to ${reportName}`, - text: `moved this expense to ${reportName}`, - }, - ]; +function buildOptimisticTransactionAction(type: 'MOVEDTRANSACTION' | 'UNREPORTEDTRANSACTION', transactionThreadReportID: string | undefined, targetReportID: string): ReportAction { + const reportName = allReports?.[targetReportID]?.reportName ?? ''; + const url = `${environmentURL}/r/${targetReportID}`; + const [actionText, messageHtml] = + type === CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION + ? [`moved this expense to ${reportName}`, `moved this expense to ${reportName}`] + : [`removed this expense from ${reportName}`, `removed this expense from ${reportName}`]; return { - actionName: CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, + actionName: type, reportID: transactionThreadReportID, actorAccountID: currentUserAccountID, avatar: getCurrentUserAvatar(), created: DateUtils.getDBTime(), - originalMessage, - message: movedTransactionMessage, + originalMessage: type === CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION ? {toReportID: targetReportID} : {fromReportID: targetReportID}, + message: [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + html: messageHtml, + text: actionText, + }, + ], person: [ { style: 'strong', @@ -6092,43 +6088,20 @@ function buildOptimisticMovedTransactionAction(transactionThreadReportID: string }; } +/** + * Builds an optimistic MOVED_TRANSACTION report action with a randomly generated reportActionID. + * This action is used when we change the workspace of a report. + */ +function buildOptimisticMovedTransactionAction(transactionThreadReportID: string | undefined, toReportID: string) { + return buildOptimisticTransactionAction(CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION, transactionThreadReportID, toReportID); +} + /** * Builds an optimistic UNREPORTED_TRANSACTION report action with a randomly generated reportActionID. * This action is used when we unreport a transaction. */ -function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string): ReportAction { - const originalMessage = { - fromReportID, - }; - - const reportName = allReports?.[fromReportID]?.reportName; - const unreportedTransactionMessage = [ - { - type: CONST.REPORT.MESSAGE.TYPE.TEXT, - html: `removed this expense from ${reportName}`, - text: `removed this expense from ${reportName}`, - }, - ]; - - return { - actionName: CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, - reportID: transactionThreadReportID, - actorAccountID: currentUserAccountID, - avatar: getCurrentUserAvatar(), - created: DateUtils.getDBTime(), - originalMessage, - message: unreportedTransactionMessage, - person: [ - { - style: 'strong', - text: getCurrentUserDisplayNameOrEmail(), - type: 'TEXT', - }, - ], - reportActionID: rand64(), - shouldShow: true, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }; +function buildOptimisticUnreportedTransactionAction(transactionThreadReportID: string | undefined, fromReportID: string) { + return buildOptimisticTransactionAction(CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, transactionThreadReportID, fromReportID); } /** @@ -9478,6 +9451,31 @@ function hasForwardedAction(reportID: string): boolean { return Object.values(reportActions).some((action) => action?.actionName === CONST.REPORT.ACTIONS.TYPE.FORWARDED); } +function isReportOutsanding(iouReport: OnyxInputOrEntry, policyID: string | undefined): boolean { + if (!iouReport || isEmptyObject(iouReport)) { + return false; + } + if ( + isExpenseReport(iouReport) && + iouReport?.stateNum !== undefined && + iouReport?.statusNum !== undefined && + iouReport?.policyID === policyID && + iouReport?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && + iouReport?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && + !hasForwardedAction(iouReport.reportID) + ) { + return true; + } + return false; +} + +function getFirstOutstandingReport(policyID: string | undefined, reports: OnyxCollection = allReports): OnyxEntry | undefined { + if (!reports) { + return undefined; + } + return Object.values(reports).find((report) => isReportOutsanding(report, policyID)); +} + /** * Get outstanding expense reports for a given policy ID * @param policyID - The policy ID to filter reports by @@ -9488,18 +9486,8 @@ function getOutstandingReports(policyID: string | undefined, reports: OnyxCollec if (!reports) { return []; } - return Object.values(reports) - .filter( - (report) => - isExpenseReport(report) && - report?.stateNum !== undefined && - report?.statusNum !== undefined && - report?.policyID === policyID && - report?.stateNum <= CONST.REPORT.STATE_NUM.SUBMITTED && - report?.statusNum <= CONST.REPORT.STATUS_NUM.SUBMITTED && - !hasForwardedAction(report.reportID), - ) + .filter((report) => isReportOutsanding(report, policyID)) .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0); } @@ -10761,6 +10749,8 @@ export { navigateToLinkedReportAction, populateOptimisticReportFormula, getOutstandingReports, + isReportOutsanding, + getFirstOutstandingReport, }; export type { From 9b64aac77b9fd40ea20679494b3cc8f4447f7022 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 10 Apr 2025 18:52:24 +0200 Subject: [PATCH 68/75] fix transactionReport --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 9702aa8aa17a..82ca4fe7a32e 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -282,7 +282,7 @@ function MoneyRequestConfirmationListFooter({ * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ - const transactionReport = !!transaction?.reportID && allReports?.[transaction.reportID]; + const transactionReport = !!transaction?.reportID && Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID); const shouldUseTransactionReport = !!transactionReport && isReportOutsanding(transactionReport, selectedParticipants?.at(0)?.policyID); let reportName: string | undefined; if (shouldUseTransactionReport) { From efb86d14ab73b3faef0b80193db8fcd7b20ba194 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Thu, 10 Apr 2025 19:10:48 +0200 Subject: [PATCH 69/75] updates --- src/components/MoneyRequestConfirmationListFooter.tsx | 6 +++--- src/libs/ReportUtils.ts | 8 -------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 82ca4fe7a32e..22bb506bcfe0 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -17,7 +17,7 @@ import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getT import {canSendInvoice, getPerDiemCustomUnit, isMultiLevelTags as isMultiLevelTagsPolicyUtils, isPaidGroupPolicy} from '@libs/PolicyUtils'; import type {ThumbnailAndImageURI} from '@libs/ReceiptUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; -import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getFirstOutstandingReport, isReportOutsanding, populateOptimisticReportFormula} from '@libs/ReportUtils'; +import {buildOptimisticExpenseReport, getDefaultWorkspaceAvatar, getOutstandingReports, isReportOutsanding, populateOptimisticReportFormula} from '@libs/ReportUtils'; import {hasEnabledTags} from '@libs/TagsOptionsListUtils'; import { getTagForDisplay, @@ -282,13 +282,13 @@ function MoneyRequestConfirmationListFooter({ * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ - const transactionReport = !!transaction?.reportID && Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID); + const transactionReport = !!transaction?.reportID && allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`]; const shouldUseTransactionReport = !!transactionReport && isReportOutsanding(transactionReport, selectedParticipants?.at(0)?.policyID); let reportName: string | undefined; if (shouldUseTransactionReport) { reportName = transactionReport.reportName; } else { - const firstOutstangingReport = getFirstOutstandingReport(selectedParticipants?.at(0)?.policyID, allReports ?? {}); + const firstOutstangingReport = getOutstandingReports(selectedParticipants?.at(0)?.policyID, allReports ?? {}).at(0); reportName = firstOutstangingReport?.reportName; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 01f7e18ddf28..9d4c326abddd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9469,13 +9469,6 @@ function isReportOutsanding(iouReport: OnyxInputOrEntry, policyID: strin return false; } -function getFirstOutstandingReport(policyID: string | undefined, reports: OnyxCollection = allReports): OnyxEntry | undefined { - if (!reports) { - return undefined; - } - return Object.values(reports).find((report) => isReportOutsanding(report, policyID)); -} - /** * Get outstanding expense reports for a given policy ID * @param policyID - The policy ID to filter reports by @@ -10750,7 +10743,6 @@ export { populateOptimisticReportFormula, getOutstandingReports, isReportOutsanding, - getFirstOutstandingReport, }; export type { From 5d35c8a0a691652792ef5d796a91f54e6be9af3a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Fri, 11 Apr 2025 12:41:09 +0200 Subject: [PATCH 70/75] fix reassure --- src/components/MoneyRequestConfirmationListFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx index 22bb506bcfe0..09c7f8991336 100644 --- a/src/components/MoneyRequestConfirmationListFooter.tsx +++ b/src/components/MoneyRequestConfirmationListFooter.tsx @@ -282,7 +282,7 @@ function MoneyRequestConfirmationListFooter({ * We need to check if the transaction report exists first in order to prevent the outstanding reports from being used. * Also we need to check if transaction report exists in outstanding reports in order to show a correct report name. */ - const transactionReport = !!transaction?.reportID && allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`]; + const transactionReport = !!transaction?.reportID && Object.values(allReports ?? {}).find((report) => report?.reportID === transaction.reportID); const shouldUseTransactionReport = !!transactionReport && isReportOutsanding(transactionReport, selectedParticipants?.at(0)?.policyID); let reportName: string | undefined; if (shouldUseTransactionReport) { From 658a9ad1f62038c0765a4d68c59809397b30fb3b Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 14 Apr 2025 12:13:42 +0200 Subject: [PATCH 71/75] fix condition --- src/pages/iou/request/step/IOURequestStepReport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index d1d41b0d30c2..3d16f348ab8e 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -77,7 +77,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { if (!transaction) { return; } - if (item.reportID !== transaction.reportID) { + if (item.value !== transaction.reportID) { setTransactionReport(transaction.transactionID, item.value, !isEditing); if (isEditing) { changeTransactionsReport([transaction.transactionID], item.value); From 3688002299cf8d8a874d3f4c36b92c2bf0c505a3 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 14 Apr 2025 15:20:29 +0200 Subject: [PATCH 72/75] update search --- .../iou/request/step/IOURequestStepReport.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index 3d16f348ab8e..c15dd34be1cc 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -46,16 +46,17 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: (c) => mapOnyxCollectionItems(c, reportSelector)}); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); const isEditing = action === CONST.IOU.ACTION.EDIT; + // We need to get the policyID because it's not defined in the transaction object before we select a report manually. + const policyID = Object.values(allReports ?? {}).find( + (report) => report?.reportID === transaction?.reportID || report?.reportID === transaction?.participants?.at(0)?.reportID, + )?.policyID; + const expenseReports = getOutstandingReports(policyID, allReports ?? {}); const reportOptions: ReportListItem[] = useMemo(() => { if (!allReports) { return []; } - // We need to get the policyID because it's not defined in the transaction object before we select a report manually. - const policyID = Object.values(allReports ?? {}).find( - (report) => report?.reportID === transaction?.reportID || report?.reportID === transaction?.participants?.at(0)?.reportID, - )?.policyID; - const expenseReports = getOutstandingReports(policyID, allReports ?? {}); + const isTransactionReportCorrect = expenseReports.some((report) => report?.reportID === transaction?.reportID); return expenseReports .sort((a, b) => a?.reportName?.localeCompare(b?.reportName?.toLowerCase() ?? '') ?? 0) @@ -67,7 +68,7 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { keyForList: report.reportID, isSelected: isTransactionReportCorrect ? report.reportID === transaction?.reportID : expenseReports.at(0)?.reportID === report.reportID, })); - }, [allReports, debouncedSearchValue, transaction]); + }, [allReports, debouncedSearchValue, expenseReports, transaction?.reportID]); const navigateBack = () => { Navigation.goBack(backTo); @@ -86,6 +87,8 @@ function IOURequestStepReport({route, transaction}: IOURequestStepReportProps) { Navigation.goBack(backTo); }; + const headerMessage = useMemo(() => (searchValue && !reportOptions.length ? translate('common.noResultsFound') : ''), [searchValue, reportOptions, translate]); + return ( = CONST.STANDARD_LIST_ITEM_LIMIT ? translate('common.search') : undefined} + textInputLabel={expenseReports.length >= CONST.STANDARD_LIST_ITEM_LIMIT ? translate('common.search') : undefined} shouldSingleExecuteRowSelect + headerMessage={headerMessage} initiallyFocusedOptionKey={transaction?.reportID} ListItem={UserListItem} /> From 84e0536a8c33bd371dbef25af4ea49e4e1ff73e3 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 14 Apr 2025 19:55:32 +0200 Subject: [PATCH 73/75] add comment --- src/pages/iou/request/step/IOURequestStepReport.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/request/step/IOURequestStepReport.tsx b/src/pages/iou/request/step/IOURequestStepReport.tsx index c15dd34be1cc..5faee5fe78d3 100644 --- a/src/pages/iou/request/step/IOURequestStepReport.tsx +++ b/src/pages/iou/request/step/IOURequestStepReport.tsx @@ -21,6 +21,7 @@ import withWritableReportOrNotFound from './withWritableReportOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type ReportListItem = ListItem & { + /** reportID of the report */ value: string; }; From 17647a0fd4097ca60ef4a4b89cd3d8cd6454ff6a Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 14 Apr 2025 20:46:11 +0200 Subject: [PATCH 74/75] fix report passed --- .../iou/request/step/IOURequestStepConfirmation.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 5cc70a0833fc..8fe8b8c2b1f0 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -30,7 +30,7 @@ import navigateAfterInteraction from '@libs/Navigation/navigateAfterInteraction' import Navigation from '@libs/Navigation/Navigation'; import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import Performance from '@libs/Performance'; -import {generateReportID, getBankAccountRoute, getReportOrDraftReport, isSelectedManagerMcTest} from '@libs/ReportUtils'; +import {generateReportID, getBankAccountRoute, getReportOrDraftReport, isProcessingReport, isSelectedManagerMcTest} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import {getDefaultTaxCode, getRateID, getRequestType, getValidWaypoints} from '@libs/TransactionUtils'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; @@ -91,9 +91,13 @@ function IOURequestStepConfirmation({ const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${getIOURequestPolicyID(transaction, reportReal)}`); const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); - // We want to use a repport from the transaction if it exists + /* + * We want to use a repport from the transaction if it exists + * Also if the report was submitted and delayed submittion is on, then we should use an initial report + */ const transactionReport = getReportOrDraftReport(transaction?.reportID); - const report = transactionReport ?? reportReal ?? reportDraft; + const shouldUseTransactionReport = transactionReport && !(isProcessingReport(transactionReport) && !policyReal?.harvesting?.enabled); + const report = shouldUseTransactionReport ? transactionReport : reportReal ?? reportDraft; const policy = policyReal ?? policyDraft; const isDraftPolicy = policy === policyDraft; const policyCategories = policyCategoriesReal ?? policyCategoriesDraft; From 6ec8f3b1d8a1e2a64cb67b9d84d1f41d8410dcd7 Mon Sep 17 00:00:00 2001 From: Artem Makushov <39777589+waterim@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:30:55 +0200 Subject: [PATCH 75/75] Update IOURequestStepConfirmation.tsx Co-authored-by: rayane-d <77965000+rayane-d@users.noreply.github.com> --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 8fe8b8c2b1f0..add8d0123f80 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -92,7 +92,7 @@ function IOURequestStepConfirmation({ const [userLocation] = useOnyx(ONYXKEYS.USER_LOCATION); /* - * We want to use a repport from the transaction if it exists + * We want to use a report from the transaction if it exists * Also if the report was submitted and delayed submittion is on, then we should use an initial report */ const transactionReport = getReportOrDraftReport(transaction?.reportID);