diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 46b89619f76f..274cb5a36856 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -200,8 +200,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals let amountDescription = `${translate('iou.amount')}`; const hasRoute = TransactionUtils.hasRoute(transactionBackup ?? transaction, isDistanceRequest); - const {unit, rate, currency} = DistanceRequestUtils.getRate({transaction, policy}); + const {unit, rate} = DistanceRequestUtils.getRate({transaction, policy}); const distance = TransactionUtils.getDistanceInMeters(transactionBackup ?? transaction, unit); + const currency = transactionCurrency ?? CONST.CURRENCY.USD; const rateToDisplay = DistanceRequestUtils.getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, isOffline); const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate); let merchantTitle = isEmptyMerchant ? '' : transactionMerchant; diff --git a/src/libs/API/parameters/EditMoneyRequestParams.ts b/src/libs/API/parameters/EditMoneyRequestParams.ts deleted file mode 100644 index 6d320510e267..000000000000 --- a/src/libs/API/parameters/EditMoneyRequestParams.ts +++ /dev/null @@ -1,14 +0,0 @@ -type EditMoneyRequestParams = { - transactionID: string; - reportActionID: string; - created?: string; - amount?: number; - currency?: string; - comment?: string; - merchant?: string; - category?: string; - billable?: boolean; - tag?: string; -}; - -export default EditMoneyRequestParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index fb5558fb0350..a6fca0ea19e8 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -157,7 +157,6 @@ export type {default as StartSplitBillParams} from './StartSplitBillParams'; export type {default as SendMoneyParams} from './SendMoneyParams'; export type {default as ApproveMoneyRequestParams} from './ApproveMoneyRequestParams'; export type {default as UnapproveExpenseReportParams} from './UnapproveExpenseReportParams'; -export type {default as EditMoneyRequestParams} from './EditMoneyRequestParams'; export type {default as ReplaceReceiptParams} from './ReplaceReceiptParams'; export type {default as SubmitReportParams} from './SubmitReportParams'; export type {default as DetachReceiptParams} from './DetachReceiptParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index b8b4bb749701..855e0c26950f 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -192,7 +192,6 @@ const WRITE_COMMANDS = { HOLD_MONEY_REQUEST: 'HoldRequest', UPDATE_BILLING_CARD_CURRENCY: 'UpdateBillingCardCurrency', UNHOLD_MONEY_REQUEST: 'UnHoldRequest', - UPDATE_DISTANCE_REQUEST: 'UpdateDistanceRequest', REQUEST_MONEY: 'RequestMoney', SPLIT_BILL: 'SplitBill', SPLIT_BILL_AND_OPEN_REPORT: 'SplitBillAndOpenReport', @@ -203,7 +202,6 @@ const WRITE_COMMANDS = { SEND_MONEY_WITH_WALLET: 'SendMoneyWithWallet', APPROVE_MONEY_REQUEST: 'ApproveMoneyRequest', UNAPPROVE_EXPENSE_REPORT: 'UnapproveExpenseReport', - EDIT_MONEY_REQUEST: 'EditMoneyRequest', REPLACE_RECEIPT: 'ReplaceReceipt', SUBMIT_REPORT: 'SubmitReport', DETACH_RECEIPT: 'DetachReceipt', @@ -616,7 +614,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.HOLD_MONEY_REQUEST]: Parameters.HoldMoneyRequestParams; [WRITE_COMMANDS.UNHOLD_MONEY_REQUEST]: Parameters.UnHoldMoneyRequestParams; - [WRITE_COMMANDS.UPDATE_DISTANCE_REQUEST]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.REQUEST_MONEY]: Parameters.RequestMoneyParams; [WRITE_COMMANDS.SPLIT_BILL]: Parameters.SplitBillParams; @@ -628,7 +625,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SEND_MONEY_WITH_WALLET]: Parameters.SendMoneyParams; [WRITE_COMMANDS.APPROVE_MONEY_REQUEST]: Parameters.ApproveMoneyRequestParams; [WRITE_COMMANDS.UNAPPROVE_EXPENSE_REPORT]: Parameters.UnapproveExpenseReportParams; - [WRITE_COMMANDS.EDIT_MONEY_REQUEST]: Parameters.EditMoneyRequestParams; [WRITE_COMMANDS.REPLACE_RECEIPT]: Parameters.ReplaceReceiptParams; [WRITE_COMMANDS.SUBMIT_REPORT]: Parameters.SubmitReportParams; [WRITE_COMMANDS.DETACH_RECEIPT]: Parameters.DetachReceiptParams; diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 86e9c23af97b..3b8e26c9cd33 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -364,6 +364,14 @@ function getUpdatedDistanceUnit({transaction, policy, policyDraft}: {transaction return getRate({transaction, policy, policyDraft, useTransactionDistanceUnit: false}).unit; } +/** + * Get the mileage rate by its ID in the form it's configured for the policy. + * If not found, return undefined. + */ +function getRateByCustomUnitRateID({customUnitRateID, policy}: {customUnitRateID: string; policy: OnyxEntry}): MileageRate | undefined { + return getMileageRates(policy, true, customUnitRateID)[customUnitRateID]; +} + export default { getDefaultMileageRate, getDistanceMerchant, @@ -378,6 +386,7 @@ export default { getDistanceUnit, getUpdatedDistanceUnit, getRate, + getRateByCustomUnitRateID, }; export type {MileageRate}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 247b02ea8c33..ce510d0c9624 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3709,12 +3709,10 @@ function getModifiedExpenseOriginalMessage( originalMessage.oldCurrency = TransactionUtils.getCurrency(oldTransaction); originalMessage.oldMerchant = TransactionUtils.getMerchant(oldTransaction); - const modifiedDistanceFields = TransactionUtils.calculateAmountForUpdatedWaypointOrRate(updatedTransaction, transactionChanges, policy, isFromExpenseReport); - // For the originalMessage, we should use the non-negative amount, similar to what TransactionUtils.getAmount does for oldAmount - originalMessage.amount = Math.abs(modifiedDistanceFields.modifiedAmount); - originalMessage.currency = modifiedDistanceFields.modifiedCurrency ?? CONST.CURRENCY.USD; - originalMessage.merchant = modifiedDistanceFields.modifiedMerchant; + originalMessage.amount = Math.abs(updatedTransaction.modifiedAmount ?? 0); + originalMessage.currency = updatedTransaction.modifiedCurrency ?? CONST.CURRENCY.USD; + originalMessage.merchant = updatedTransaction.modifiedMerchant; } return originalMessage; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 363748ff4118..25d0d93c99c7 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -249,7 +249,19 @@ function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { /** * Given the edit made to the expense, return an updated transaction object. */ -function getUpdatedTransaction(transaction: Transaction, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, shouldUpdateReceiptState = true): Transaction { +function getUpdatedTransaction({ + transaction, + transactionChanges, + isFromExpenseReport, + shouldUpdateReceiptState = true, + policy = undefined, +}: { + transaction: Transaction; + transactionChanges: TransactionChanges; + isFromExpenseReport: boolean; + shouldUpdateReceiptState?: boolean; + policy?: OnyxEntry; +}): Transaction { // Only changing the first level fields so no need for deep clone now const updatedTransaction = lodashDeepClone(transaction); let shouldStopSmartscan = false; @@ -281,7 +293,29 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra if (Object.hasOwn(transactionChanges, 'waypoints')) { updatedTransaction.modifiedWaypoints = transactionChanges.waypoints; + updatedTransaction.isLoading = true; shouldStopSmartscan = true; + + if (!transactionChanges.routes?.route0?.geometry?.coordinates) { + // The waypoints were changed, but there is no route – it is pending from the BE and we should mark the fields as pending + updatedTransaction.amount = CONST.IOU.DEFAULT_AMOUNT; + updatedTransaction.modifiedAmount = CONST.IOU.DEFAULT_AMOUNT; + updatedTransaction.modifiedMerchant = Localize.translateLocal('iou.fieldPending'); + } else { + const mileageRate = DistanceRequestUtils.getRate({transaction: updatedTransaction, policy}); + const {unit, rate} = mileageRate; + + const distanceInMeters = getDistanceInMeters(transaction, unit); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, rate ?? 0); + const updatedAmount = isFromExpenseReport ? -amount : amount; + const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, Localize.translateLocal, (digit) => + toLocaleDigit(preferredLocale, digit), + ); + + updatedTransaction.amount = updatedAmount; + updatedTransaction.modifiedAmount = updatedAmount; + updatedTransaction.modifiedMerchant = updatedMerchant; + } } if (Object.hasOwn(transactionChanges, 'customUnitRateID')) { @@ -290,20 +324,38 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra shouldStopSmartscan = true; const existingDistanceUnit = transaction?.comment?.customUnit?.distanceUnit; - const allReports = ReportConnection.getAllReports(); - const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`] ?? null; - const policyID = report?.policyID ?? ''; - const policy = PolicyUtils.getPolicy(policyID); // Get the new distance unit from the rate's unit const newDistanceUnit = DistanceRequestUtils.getUpdatedDistanceUnit({transaction: updatedTransaction, policy}); + lodashSet(updatedTransaction, 'comment.customUnit.distanceUnit', newDistanceUnit); // If the distanceUnit is set and the rate is changed to one that has a different unit, convert the distance to the new unit if (existingDistanceUnit && newDistanceUnit !== existingDistanceUnit) { const conversionFactor = existingDistanceUnit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? CONST.CUSTOM_UNITS.MILES_TO_KILOMETERS : CONST.CUSTOM_UNITS.KILOMETERS_TO_MILES; const distance = NumberUtils.roundToTwoDecimalPlaces((transaction?.comment?.customUnit?.quantity ?? 0) * conversionFactor); lodashSet(updatedTransaction, 'comment.customUnit.quantity', distance); - lodashSet(updatedTransaction, 'pendingFields.merchant', CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); + } + + if (!isFetchingWaypointsFromServer(transaction)) { + // When the waypoints are being fetched from the server, we have no information about the distance, and cannot recalculate the updated amount. + // Otherwise, recalculate the fields based on the new rate. + + const oldMileageRate = DistanceRequestUtils.getRate({transaction, policy}); + const updatedMileageRate = DistanceRequestUtils.getRate({transaction: updatedTransaction, policy, useTransactionDistanceUnit: false}); + const {unit, rate} = updatedMileageRate; + + const distanceInMeters = getDistanceInMeters(transaction, oldMileageRate?.unit); + const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, rate ?? 0); + const updatedAmount = isFromExpenseReport ? -amount : amount; + const updatedCurrency = updatedMileageRate.currency ?? CONST.CURRENCY.USD; + const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, updatedCurrency, Localize.translateLocal, (digit) => + toLocaleDigit(preferredLocale, digit), + ); + + updatedTransaction.amount = updatedAmount; + updatedTransaction.modifiedAmount = updatedAmount; + updatedTransaction.modifiedMerchant = updatedMerchant; + updatedTransaction.modifiedCurrency = updatedCurrency; } } @@ -861,49 +913,6 @@ function calculateTaxAmount(percentage: string, amount: number, currency: string return parseFloat(taxAmount.toFixed(decimals)); } -/** - * Calculates updated amount, currency, and merchant for a distance request with modified waypoints or customUnitRateID - */ -function calculateAmountForUpdatedWaypointOrRate( - transaction: OnyxInputOrEntry, - transactionChanges: TransactionChanges, - policy: OnyxInputOrEntry, - isFromExpenseReport: boolean, -) { - const hasModifiedRouteWithPendingWaypoints = !isEmptyObject(transactionChanges.waypoints) && isEmptyObject(transactionChanges?.routes?.route0?.geometry); - const hasModifiedRateWithPendingWaypoints = !!transactionChanges?.customUnitRateID && isFetchingWaypointsFromServer(transaction); - if (hasModifiedRouteWithPendingWaypoints || hasModifiedRateWithPendingWaypoints) { - return { - amount: CONST.IOU.DEFAULT_AMOUNT, - modifiedAmount: CONST.IOU.DEFAULT_AMOUNT, - modifiedMerchant: Localize.translateLocal('iou.fieldPending'), - }; - } - - const customUnitRateID = transactionChanges.customUnitRateID ?? getRateID(transaction) ?? ''; - const mileageRates = DistanceRequestUtils.getMileageRates(policy, true); - const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; - const mileageRate = isCustomUnitRateIDForP2P(transaction) - ? DistanceRequestUtils.getRateForP2P(policyCurrency, transaction ?? undefined) - : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); - const {unit, rate, currency} = mileageRate; - - const distanceInMeters = getDistanceInMeters(transaction, unit); - const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, rate ?? 0); - const updatedAmount = isFromExpenseReport ? -amount : amount; - const updatedCurrency = currency ?? CONST.CURRENCY.USD; - const updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, updatedCurrency, Localize.translateLocal, (digit) => - toLocaleDigit(preferredLocale, digit), - ); - - return { - amount: updatedAmount, - modifiedAmount: updatedAmount, - modifiedMerchant: updatedMerchant, - modifiedCurrency: updatedCurrency, - }; -} - /** * Calculates count of all tax enabled options */ @@ -1216,7 +1225,6 @@ function buildTransactionsMergeParams(reviewDuplicates: OnyxEntry, policyTagList: OnyxTypes.OnyxInputOrEntry, policyCategories: OnyxTypes.OnyxInputOrEntry, - onlyIncludeChangedFields: boolean, violations?: OnyxEntry, ): UpdateMoneyRequestData { const optimisticData: OnyxUpdate[] = []; @@ -2507,7 +2500,8 @@ function getUpdateMoneyRequestParams( const failureData: OnyxUpdate[] = []; // Step 1: Set any "pending fields" (ones updated while the user was offline) to have error messages in the failureData - let pendingFields: OnyxTypes.Transaction['pendingFields'] = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])); + const pendingFields: OnyxTypes.Transaction['pendingFields'] = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])); + const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); const allReports = ReportConnection.getAllReports(); @@ -2517,24 +2511,22 @@ function getUpdateMoneyRequestParams( const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); - let updatedTransaction: OnyxEntry = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : undefined; + const updatedTransaction: OnyxEntry = transaction + ? TransactionUtils.getUpdatedTransaction({ + transaction, + transactionChanges, + isFromExpenseReport, + policy, + }) + : undefined; const transactionDetails = ReportUtils.getTransactionDetails(updatedTransaction); - if (updatedTransaction?.pendingFields) { - pendingFields = { - ...pendingFields, - ...updatedTransaction?.pendingFields, - }; - } - if (transactionDetails?.waypoints) { // This needs to be a JSON string since we're sending this to the MapBox API transactionDetails.waypoints = JSON.stringify(transactionDetails.waypoints); } - const dataToIncludeInParams: Partial | undefined = onlyIncludeChangedFields - ? Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) - : transactionDetails; + const dataToIncludeInParams: Partial = Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))); const params: UpdateMoneyRequestParams = { ...dataToIncludeInParams, @@ -2545,14 +2537,6 @@ function getUpdateMoneyRequestParams( const hasPendingWaypoints = 'waypoints' in transactionChanges; const hasModifiedDistanceRate = 'customUnitRateID' in transactionChanges; if (transaction && updatedTransaction && (hasPendingWaypoints || hasModifiedDistanceRate)) { - updatedTransaction = { - ...updatedTransaction, - ...TransactionUtils.calculateAmountForUpdatedWaypointOrRate(updatedTransaction, transactionChanges, policy, ReportUtils.isExpenseReport(iouReport)), - }; - - // Update the distanceUnit - lodashSet(updatedTransaction, 'comment.customUnit.distanceUnit', DistanceRequestUtils.getUpdatedDistanceUnit({transaction: updatedTransaction, policy})); - // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors successData.push({ onyxMethod: Onyx.METHOD.SET, @@ -2569,6 +2553,7 @@ function getUpdateMoneyRequestParams( amount: transaction.amount, modifiedAmount: transaction.modifiedAmount, modifiedMerchant: transaction.modifiedMerchant, + modifiedCurrency: transaction.modifiedCurrency, }, }); } @@ -2673,7 +2658,6 @@ function getUpdateMoneyRequestParams( value: { ...updatedTransaction, pendingFields, - isLoading: hasPendingWaypoints, errorFields: null, }, }); @@ -2764,8 +2748,6 @@ function getUpdateMoneyRequestParams( } } - const clearedPendingFields = Object.fromEntries(Object.keys(updatedTransaction?.pendingFields ?? transactionChanges).map((key) => [key, null])); - // Clear out the error fields and loading states on success successData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -2835,18 +2817,13 @@ function getUpdateMoneyRequestParams( * @param transactionThreadReportID * @param transactionChanges * @param [transactionChanges.created] Present when updated the date field - * @param onlyIncludeChangedFields - * When 'true', then the returned params will only include the transaction details for the fields that were changed. - * When `false`, then the returned params will include all the transaction details, regardless of which fields were changed. - * This setting is necessary while the UpdateDistanceRequest API is refactored to be fully 1:1:1 in https://github.com/Expensify/App/issues/28358 * @param policy May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) */ function getUpdateTrackExpenseParams( transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges, - onlyIncludeChangedFields: boolean, - policy: OnyxTypes.OnyxInputOrEntry, + policy: OnyxEntry, ): UpdateMoneyRequestData { const optimisticData: OnyxUpdate[] = []; const successData: OnyxUpdate[] = []; @@ -2854,6 +2831,7 @@ function getUpdateTrackExpenseParams( // Step 1: Set any "pending fields" (ones updated while the user was offline) to have error messages in the failureData const pendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])); + const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); const allReports = ReportConnection.getAllReports(); @@ -2862,7 +2840,14 @@ function getUpdateTrackExpenseParams( const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); - let updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, false) : null; + const updatedTransaction = transaction + ? TransactionUtils.getUpdatedTransaction({ + transaction, + transactionChanges, + isFromExpenseReport: false, + policy, + }) + : null; const transactionDetails = ReportUtils.getTransactionDetails(updatedTransaction); if (transactionDetails?.waypoints) { @@ -2870,9 +2855,7 @@ function getUpdateTrackExpenseParams( transactionDetails.waypoints = JSON.stringify(transactionDetails.waypoints); } - const dataToIncludeInParams: Partial | undefined = onlyIncludeChangedFields - ? Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) - : transactionDetails; + const dataToIncludeInParams: Partial = Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))); const params: UpdateMoneyRequestParams = { ...dataToIncludeInParams, @@ -2883,11 +2866,6 @@ function getUpdateTrackExpenseParams( const hasPendingWaypoints = 'waypoints' in transactionChanges; const hasModifiedDistanceRate = 'customUnitRateID' in transactionChanges; if (transaction && updatedTransaction && (hasPendingWaypoints || hasModifiedDistanceRate)) { - updatedTransaction = { - ...updatedTransaction, - ...TransactionUtils.calculateAmountForUpdatedWaypointOrRate(transaction, transactionChanges, policy, false), - }; - // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors successData.push({ onyxMethod: Onyx.METHOD.SET, @@ -2914,7 +2892,7 @@ function getUpdateTrackExpenseParams( // - we're updating the distance rate while the waypoints are still pending // In these cases, there isn't a valid optimistic mileage data we can use, // and the report action is created on the server with the distance-related response from the MapBox API - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, false, policy); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, false, policy, updatedTransaction); if (!hasPendingWaypoints && !(hasModifiedDistanceRate && TransactionUtils.isFetchingWaypointsFromServer(transaction))) { params.reportActionID = updatedReportAction.reportActionID; @@ -2952,7 +2930,6 @@ function getUpdateTrackExpenseParams( value: { ...updatedTransaction, pendingFields, - isLoading: hasPendingWaypoints, errorFields: null, }, }); @@ -2979,8 +2956,6 @@ function getUpdateTrackExpenseParams( }); } - const clearedPendingFields = Object.fromEntries(Object.keys(updatedTransaction?.pendingFields ?? transactionChanges).map((key) => [key, null])); - // Clear out the error fields and loading states on success successData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -3034,9 +3009,9 @@ function updateMoneyRequestDate( const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories); } const {params, onyxData} = data; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DATE, params, onyxData); @@ -3054,7 +3029,7 @@ function updateMoneyRequestBillable( const transactionChanges: TransactionChanges = { billable: value, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE, params, onyxData); } @@ -3075,9 +3050,9 @@ function updateMoneyRequestMerchant( const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); } const {params, onyxData} = data; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_MERCHANT, params, onyxData); @@ -3096,7 +3071,7 @@ function updateMoneyRequestAttendees( const transactionChanges: TransactionChanges = { attendees, }; - const data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true, violations); + const data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, violations); const {params, onyxData} = data; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_ATTENDEES, params, onyxData); } @@ -3113,7 +3088,7 @@ function updateMoneyRequestTag( const transactionChanges: TransactionChanges = { tag, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAG, params, onyxData); } @@ -3129,7 +3104,7 @@ function updateMoneyRequestTaxAmount( const transactionChanges = { taxAmount, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories); API.write('UpdateMoneyRequestTaxAmount', params, onyxData); } @@ -3149,7 +3124,7 @@ function updateMoneyRequestTaxRate({transactionID, optimisticReportActionID, tax taxCode, taxAmount, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, optimisticReportActionID, transactionChanges, policy, policyTagList, policyCategories); API.write('UpdateMoneyRequestTaxRate', params, onyxData); } @@ -3182,9 +3157,9 @@ function updateMoneyRequestDistance({ const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); } const {params, onyxData} = data; @@ -3210,7 +3185,7 @@ function updateMoneyRequestCategory( const transactionChanges: TransactionChanges = { category, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_CATEGORY, params, onyxData); } @@ -3231,9 +3206,9 @@ function updateMoneyRequestDescription( const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); } const {params, onyxData} = data; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION, params, onyxData); @@ -3258,11 +3233,23 @@ function updateMoneyRequestDistanceRate( const allReports = ReportConnection.getAllReports(); const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; + + const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + if (transaction) { + const existingDistanceUnit = transaction?.comment?.customUnit?.distanceUnit; + const newDistanceUnit = DistanceRequestUtils.getRateByCustomUnitRateID({customUnitRateID: rateID, policy})?.unit; + + // If the distanceUnit is set and the rate is changed to one that has a different unit, mark the merchant as modified to make the distance field pending + if (existingDistanceUnit && newDistanceUnit && newDistanceUnit !== existingDistanceUnit) { + transactionChanges.merchant = TransactionUtils.getMerchant(transaction); + } + } + let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories); } const {params, onyxData} = data; // `taxAmount` & `taxCode` only needs to be updated in the optimistic data, so we need to remove them from the params @@ -3270,19 +3257,6 @@ function updateMoneyRequestDistanceRate( API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE_RATE, paramsWithoutTaxUpdated, onyxData); } -/** Edits an existing distance expense */ -function updateDistanceRequest( - transactionID: string, - transactionThreadReportID: string, - transactionChanges: TransactionChanges, - policy: OnyxTypes.Policy, - policyTagList: OnyxTypes.PolicyTagLists, - policyCategories: OnyxTypes.PolicyCategories, -) { - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, false); - API.write(WRITE_COMMANDS.UPDATE_DISTANCE_REQUEST, params, onyxData); -} - const getConvertTrackedExpenseInformation = ( transactionID: string, actionableWhisperReportActionID: string, @@ -5175,7 +5149,14 @@ function setDraftSplitTransaction(transactionID: string, transactionChanges: Tra draftSplitTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; } - const updatedTransaction = draftSplitTransaction ? TransactionUtils.getUpdatedTransaction(draftSplitTransaction, transactionChanges, false, false) : null; + const updatedTransaction = draftSplitTransaction + ? TransactionUtils.getUpdatedTransaction({ + transaction: draftSplitTransaction, + transactionChanges, + isFromExpenseReport: false, + shouldUpdateReceiptState: false, + }) + : null; Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction); } @@ -5339,296 +5320,6 @@ function createDistanceRequest( Report.notifyNewAction(activeReportID, userAccountID); } -function editRegularMoneyRequest( - transactionID: string, - transactionThreadReportID: string, - transactionChanges: TransactionChanges, - policy: OnyxTypes.Policy, - policyTags: OnyxTypes.PolicyTagLists, - policyCategories: OnyxTypes.PolicyCategories, -) { - const allReports = ReportConnection.getAllReports(); - // STEP 1: Get all collections we're updating - const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; - const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; - const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`] ?? null; - const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); - - // STEP 2: Build new modified expense report action. - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport, policy); - const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; - - // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct - // Should only update if the transaction matches the currency of the report, else we wait for the update - // from the server with the currency conversion - let updatedMoneyRequestReport = {...iouReport}; - const updatedChatReport = {...chatReport}; - const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); - if (updatedTransaction?.currency === iouReport?.currency && updatedTransaction?.modifiedAmount && diff !== 0) { - if (ReportUtils.isExpenseReport(iouReport) && typeof updatedMoneyRequestReport.total === 'number') { - updatedMoneyRequestReport.total += diff; - } else { - updatedMoneyRequestReport = iouReport - ? IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false) - : {}; - } - - updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); - - // Update the last message of the IOU report - const lastMessage = ReportUtils.getIOUReportActionMessage( - iouReport?.reportID ?? '-1', - CONST.IOU.REPORT_ACTION_TYPE.CREATE, - updatedMoneyRequestReport.total ?? 0, - '', - updatedTransaction.currency, - '', - false, - ); - updatedMoneyRequestReport.lastMessageText = ReportActionsUtils.getTextFromHtml(lastMessage.at(0)?.html); - updatedMoneyRequestReport.lastMessageHtml = lastMessage.at(0)?.html; - - // Update the last message of the chat report - const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); - const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { - payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID ?? -1).login ?? '', - amount: CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedMoneyRequestReport.currency), - }); - updatedChatReport.lastMessageText = messageText; - updatedChatReport.lastMessageHtml = messageText; - } - - const isScanning = TransactionUtils.hasReceipt(updatedTransaction) && TransactionUtils.isReceiptBeingScanned(updatedTransaction); - - // STEP 4: Compose the optimistic data - const currentTime = DateUtils.getDBTime(); - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, - value: { - [updatedReportAction.reportActionID]: updatedReportAction as OnyxTypes.ReportAction, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: updatedTransaction, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - value: updatedMoneyRequestReport, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, - value: updatedChatReport, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, - value: { - lastReadTime: currentTime, - lastVisibleActionCreated: currentTime, - }, - }, - ]; - - if (!isScanning) { - optimisticData.push( - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, - value: { - [transactionThread?.parentReportActionID ?? '-1']: { - originalMessage: { - whisperedTo: [], - }, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.parentReportID}`, - value: { - [iouReport?.parentReportActionID ?? '-1']: { - originalMessage: { - whisperedTo: [], - }, - }, - }, - }, - ); - } - - // Update recently used categories if the category is changed - if ('category' in transactionChanges) { - const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); - if (optimisticPolicyRecentlyUsedCategories.length) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport?.policyID}`, - value: optimisticPolicyRecentlyUsedCategories, - }); - } - } - - // Update recently used currencies if the currency is changed - if ('currency' in transactionChanges) { - const optimisticRecentlyUsedCurrencies = Policy.buildOptimisticRecentlyUsedCurrencies(transactionChanges.currency); - if (optimisticRecentlyUsedCurrencies.length) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.RECENTLY_USED_CURRENCIES, - value: optimisticRecentlyUsedCurrencies, - }); - } - } - - // Update recently used categories if the tag is changed - if ('tag' in transactionChanges) { - const optimisticPolicyRecentlyUsedTags = Tag.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag); - if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport?.policyID}`, - value: optimisticPolicyRecentlyUsedTags, - }); - } - } - - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, - value: { - [updatedReportAction.reportActionID]: {pendingAction: null}, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - pendingFields: { - comment: null, - amount: null, - created: null, - currency: null, - merchant: null, - billable: null, - category: null, - tag: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - value: {pendingAction: null}, - }, - ]; - - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, - value: { - [updatedReportAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - ...transaction, - modifiedCreated: transaction?.modifiedCreated ? transaction.modifiedCreated : null, - modifiedAmount: transaction?.modifiedAmount ? transaction.modifiedAmount : null, - modifiedCurrency: transaction?.modifiedCurrency ? transaction.modifiedCurrency : null, - modifiedMerchant: transaction?.modifiedMerchant ? transaction.modifiedMerchant : null, - modifiedWaypoints: transaction?.modifiedWaypoints ? transaction.modifiedWaypoints : null, - pendingFields: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - value: { - ...iouReport, - cachedTotal: iouReport?.cachedTotal ? iouReport?.cachedTotal : null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, - value: chatReport, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, - value: { - lastReadTime: transactionThread?.lastReadTime, - lastVisibleActionCreated: transactionThread?.lastVisibleActionCreated, - }, - }, - ]; - - // Add transaction violations if we have a paid policy and an updated transaction - if (policy && PolicyUtils.isPaidGroupPolicy(policy) && updatedTransaction) { - const currentTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? []; - const updatedViolationsOnyxData = ViolationsUtils.getViolationsOnyxData( - updatedTransaction, - currentTransactionViolations, - policy, - policyTags, - policyCategories, - PolicyUtils.hasDependentTags(policy, policyTags), - ); - optimisticData.push(updatedViolationsOnyxData); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, - value: currentTransactionViolations, - }); - } - - // STEP 6: Call the API endpoint - const {created, amount, currency, comment, merchant, category, billable, tag} = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; - - const parameters: EditMoneyRequestParams = { - transactionID, - reportActionID: updatedReportAction.reportActionID, - created, - amount, - currency, - comment, - merchant, - category, - billable, - tag, - }; - - API.write(WRITE_COMMANDS.EDIT_MONEY_REQUEST, parameters, {optimisticData, successData, failureData}); -} - -function editMoneyRequest( - transaction: OnyxTypes.Transaction, - transactionThreadReportID: string, - transactionChanges: TransactionChanges, - policy: OnyxTypes.Policy, - policyTags: OnyxTypes.PolicyTagLists, - policyCategories: OnyxTypes.PolicyCategories, -) { - if (TransactionUtils.isDistanceRequest(transaction)) { - updateDistanceRequest(transaction.transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories); - } else { - editRegularMoneyRequest(transaction.transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories); - } -} - type UpdateMoneyRequestAmountAndCurrencyParams = { transactionID: string; transactionThreadReportID: string; @@ -5664,9 +5355,9 @@ function updateMoneyRequestAmountAndCurrency({ const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReport?.parentReportID}`] ?? null; let data: UpdateMoneyRequestData; if (ReportUtils.isTrackExpenseReport(transactionThreadReport) && ReportUtils.isSelfDM(parentReport)) { - data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy ?? null); + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, policy); } else { - data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList ?? null, policyCategories ?? null, true); + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList ?? null, policyCategories ?? null); } const {params, onyxData} = data; API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY, params, onyxData); @@ -8600,7 +8291,6 @@ export { deleteTrackExpense, detachReceipt, dismissHoldUseExplanation, - editMoneyRequest, getIOURequestPolicyID, initMoneyRequest, navigateToStartStepIfScanFileCannotBeRead, diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index e7a3465f1d25..3cb6e3dc44ba 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -323,6 +323,9 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i return Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { comment: { waypoints, + customUnit: { + quantity: null, + }, }, // We want to reset the amount only for draft transactions (when creating the expense). // When modifying an existing transaction, the amount will be updated on the actual IOU update operation. diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index c2005d221273..1c26390f7b09 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1608,321 +1608,6 @@ describe('actions/IOU', () => { }); }); - describe('edit expense', () => { - const amount = 10000; - const comment = 'πŸ’ΈπŸ’ΈπŸ’ΈπŸ’Έ'; - const merchant = 'NASDAQ'; - - afterEach(() => { - mockFetch?.resume?.(); - }); - - it('updates the IOU request and IOU report when offline', () => { - let thread: OptimisticChatReport; - let iouReport: OnyxEntry; - let iouAction: OnyxEntry>; - let transaction: OnyxEntry; - - mockFetch?.pause?.(); - IOU.requestMoney({reportID: ''}, amount, [], CONST.CURRENCY.USD, '', merchant, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment, {}); - return waitForBatchedUpdates() - .then(() => { - Onyx.set(ONYXKEYS.SESSION, {email: RORY_EMAIL, accountID: RORY_ACCOUNT_ID}); - return waitForBatchedUpdates(); - }) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (allReports) => { - Onyx.disconnect(connection); - iouReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.IOU); - - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, - waitForCollectionCallback: false, - callback: (reportActionsForIOUReport) => { - Onyx.disconnect(connection); - - [iouAction] = Object.values(reportActionsForIOUReport ?? {}).filter( - (reportAction): reportAction is OnyxTypes.ReportAction => ReportActionsUtils.isMoneyRequestAction(reportAction), - ); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (allTransactions) => { - Onyx.disconnect(connection); - - transaction = Object.values(allTransactions ?? {}).find((t) => !isEmptyObject(t)); - resolve(); - }, - }); - }), - ) - .then(() => { - thread = ReportUtils.buildTransactionThread(iouAction, iouReport) ?? null; - Onyx.set(`report_${thread?.reportID ?? '-1'}`, thread); - return waitForBatchedUpdates(); - }) - .then(() => { - if (transaction) { - IOU.editMoneyRequest( - transaction, - thread.reportID, - {amount: 20000, comment: 'Double the amount!'}, - { - id: '123', - role: 'user', - type: CONST.POLICY.TYPE.TEAM, - name: '', - owner: '', - outputCurrency: '', - isPolicyExpenseChatEnabled: false, - }, - {}, - {}, - ); - } - return waitForBatchedUpdates(); - }) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (allTransactions) => { - Onyx.disconnect(connection); - - const updatedTransaction = Object.values(allTransactions ?? {}).find((t) => !isEmptyObject(t)); - expect(updatedTransaction?.modifiedAmount).toBe(20000); - expect(updatedTransaction?.comment).toMatchObject({comment: 'Double the amount!'}); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${thread.reportID}`, - waitForCollectionCallback: false, - callback: (allActions) => { - Onyx.disconnect(connection); - const updatedAction = Object.values(allActions ?? {}).find((reportAction) => !isEmptyObject(reportAction)); - expect(updatedAction?.actionName).toEqual('MODIFIEDEXPENSE'); - expect(updatedAction && ReportActionsUtils.getOriginalMessage(updatedAction)).toEqual( - expect.objectContaining({amount: 20000, newComment: 'Double the amount!', oldAmount: amount, oldComment: comment}), - ); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (allReports) => { - Onyx.disconnect(connection); - const updatedIOUReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.IOU); - const updatedChatReport = Object.values(allReports ?? {}).find((report) => report?.reportID === iouReport?.chatReportID); - expect(updatedIOUReport).toEqual( - expect.objectContaining({ - total: 20000, - cachedTotal: '$200.00', - lastMessageHtml: 'submitted $200.00', - lastMessageText: 'submitted $200.00', - }), - ); - expect(updatedChatReport).toEqual( - expect.objectContaining({ - lastMessageHtml: `${CARLOS_EMAIL} owes $200.00`, - lastMessageText: `${CARLOS_EMAIL} owes $200.00`, - }), - ); - resolve(); - }, - }); - }), - ) - .then(() => { - mockFetch?.resume?.(); - }); - }); - - it('resets the IOU request and IOU report when api returns an error', () => { - let thread: OptimisticChatReport; - let iouReport: OnyxEntry; - let iouAction: OnyxEntry>; - let transaction: OnyxEntry; - - IOU.requestMoney({reportID: ''}, amount, [], CONST.CURRENCY.USD, '', merchant, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment, {}); - return waitForBatchedUpdates() - .then(() => { - Onyx.set(ONYXKEYS.SESSION, {email: RORY_EMAIL, accountID: RORY_ACCOUNT_ID}); - return waitForBatchedUpdates(); - }) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (allReports) => { - Onyx.disconnect(connection); - [iouReport] = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.IOU); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, - waitForCollectionCallback: false, - callback: (reportActionsForIOUReport) => { - Onyx.disconnect(connection); - - [iouAction] = Object.values(reportActionsForIOUReport ?? {}).filter( - (reportAction): reportAction is OnyxTypes.ReportAction => ReportActionsUtils.isMoneyRequestAction(reportAction), - ); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (allTransactions) => { - Onyx.disconnect(connection); - - transaction = Object.values(allTransactions ?? {}).find((t) => !isEmptyObject(t)); - resolve(); - }, - }); - }), - ) - .then(() => { - thread = ReportUtils.buildTransactionThread(iouAction, iouReport); - Onyx.set(`report_${thread.reportID}`, thread); - return waitForBatchedUpdates(); - }) - .then(() => { - mockFetch?.fail?.(); - - if (transaction) { - IOU.editMoneyRequest( - transaction, - thread.reportID, - {amount: 20000, comment: 'Double the amount!'}, - { - id: '123', - role: 'user', - type: CONST.POLICY.TYPE.TEAM, - name: '', - owner: '', - outputCurrency: '', - isPolicyExpenseChatEnabled: false, - }, - {}, - {}, - ); - } - return waitForBatchedUpdates(); - }) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (allTransactions) => { - Onyx.disconnect(connection); - - const updatedTransaction = Object.values(allTransactions ?? {}).find((t) => !isEmptyObject(t)); - expect(updatedTransaction?.modifiedAmount).toBe(undefined); - expect(updatedTransaction?.amount).toBe(10000); - expect(updatedTransaction?.comment).toMatchObject({comment}); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${thread.reportID}`, - waitForCollectionCallback: false, - callback: (allActions) => { - Onyx.disconnect(connection); - const updatedAction = Object.values(allActions ?? {}).find((reportAction) => !isEmptyObject(reportAction)); - expect(updatedAction?.actionName).toEqual('MODIFIEDEXPENSE'); - expect(Object.values(updatedAction?.errors ?? {}).at(0)).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); - resolve(); - }, - }); - }), - ) - .then( - () => - new Promise((resolve) => { - const connection = Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, - callback: (allReports) => { - Onyx.disconnect(connection); - const updatedIOUReport = Object.values(allReports ?? {}).find((report) => report?.type === CONST.REPORT.TYPE.IOU); - const updatedChatReport = Object.values(allReports ?? {}).find((report) => report?.reportID === iouReport?.chatReportID); - expect(updatedIOUReport).toEqual( - expect.objectContaining({ - total: 10000, - cachedTotal: '$100.00', - lastMessageHtml: `submitted $${amount / 100}.00 for ${comment}`, - lastMessageText: `submitted $${amount / 100}.00 for ${comment}`, - }), - ); - expect(updatedChatReport).toEqual( - expect.objectContaining({ - lastMessageHtml: '', - }), - ); - resolve(); - }, - }); - }), - ); - }); - }); - describe('pay expense report via ACH', () => { const amount = 10000; const comment = 'πŸ’ΈπŸ’ΈπŸ’ΈπŸ’Έ'; @@ -2596,11 +2281,14 @@ describe('actions/IOU', () => { jest.advanceTimersByTime(10); if (transaction && createIOUAction) { - IOU.editMoneyRequest( - transaction, - thread.reportID, - {amount: 20000, comment: 'Double the amount!'}, - { + IOU.updateMoneyRequestAmountAndCurrency({ + transactionID: transaction.transactionID, + transactionThreadReportID: thread.reportID, + amount: 20000, + currency: CONST.CURRENCY.USD, + taxAmount: 0, + taxCode: '', + policy: { id: '123', role: 'user', type: CONST.POLICY.TYPE.TEAM, @@ -2609,9 +2297,9 @@ describe('actions/IOU', () => { outputCurrency: '', isPolicyExpenseChatEnabled: false, }, - {}, - {}, - ); + policyTagList: {}, + policyCategories: {}, + }); } await waitForBatchedUpdates();