diff --git a/src/CONST.ts b/src/CONST.ts index ed4de999c78cd..736d51be456bb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -13,6 +13,7 @@ import type {Unit} from './types/onyx/Policy'; type RateAndUnit = { unit: Unit; rate: number; + currency: string; }; type CurrencyDefaultMileageRate = Record; @@ -2507,6 +2508,7 @@ const CONST = { CATEGORY: 'category', RECEIPT: 'receipt', DISTANCE: 'distance', + DISTANCE_RATE: 'distanceRate', TAG: 'tag', TAX_RATE: 'taxRate', TAX_AMOUNT: 'taxAmount', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 330b5dda4aac9..65ffc4848f96c 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -191,6 +191,7 @@ function MoneyRequestView({ const isReceiptBeingScanned = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction); const didReceiptScanSucceed = hasReceipt && TransactionUtils.didReceiptScanSucceed(transaction); const canEditDistance = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE); + const canEditDistanceRate = canUserPerformWriteAction && ReportUtils.canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE); const isAdmin = policy?.role === 'admin'; const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID; @@ -225,7 +226,7 @@ function MoneyRequestView({ let amountDescription = `${translate('iou.amount')}`; const hasRoute = TransactionUtils.hasRoute(transactionBackup ?? transaction, isDistanceRequest); - const rateID = transaction?.comment?.customUnit?.customUnitRateID ?? '-1'; + const rateID = TransactionUtils.getRateID(transaction) ?? '-1'; const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; @@ -344,17 +345,16 @@ function MoneyRequestView({ } /> - {/* TODO: correct the pending field action https://github.com/Expensify/App/issues/36987 */} - + {}} + onPress={() => + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_DISTANCE_RATE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '-1', report?.reportID ?? '-1')) + } /> diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index bc34cf32e8850..3f19c719fdd77 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -161,6 +161,7 @@ const WRITE_COMMANDS = { UPDATE_MONEY_REQUEST_TAX_AMOUNT: 'UpdateMoneyRequestTaxAmount', UPDATE_MONEY_REQUEST_TAX_RATE: 'UpdateMoneyRequestTaxRate', UPDATE_MONEY_REQUEST_DISTANCE: 'UpdateMoneyRequestDistance', + UPDATE_MONEY_REQUEST_DISTANCE_RATE: 'UpdateMoneyRequestDistanceRate', UPDATE_MONEY_REQUEST_CATEGORY: 'UpdateMoneyRequestCategory', UPDATE_MONEY_REQUEST_DESCRIPTION: 'UpdateMoneyRequestDescription', UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY: 'UpdateMoneyRequestAmountAndCurrency', @@ -475,6 +476,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAX_AMOUNT]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAX_RATE]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE]: Parameters.UpdateMoneyRequestParams; + [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE_RATE]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_CATEGORY]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION]: Parameters.UpdateMoneyRequestParams; [WRITE_COMMANDS.HOLD_MONEY_REQUEST]: Parameters.HoldMoneyRequestParams; diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 76d595dc69769..fa98cf32ee399 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -210,7 +210,11 @@ function getDistanceMerchant( * @returns The rate and unit in RateAndUnit object. */ function getRateForP2P(currency: string): RateAndUnit { - return CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ?? CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE.USD; + const currencyWithExistingRate = CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ? currency : CONST.CURRENCY.USD; + return { + ...CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currencyWithExistingRate], + currency: currencyWithExistingRate, + }; } /** diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index f4d65a16fd713..c527a02c16af9 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -891,6 +891,7 @@ type MoneyRequestNavigatorParamList = { currency?: string; }; [SCREENS.MONEY_REQUEST.STEP_DISTANCE_RATE]: { + action: IOUAction; iouType: ValueOf; transactionID: string; backTo: Routes; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9a2c3089e6235..cf9fcf68a8006 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -362,6 +362,7 @@ type TransactionDetails = { currency: string; merchant: string; waypoints?: WaypointCollection | string; + customUnitRateID?: string; comment: string; category: string; billable: boolean; @@ -2719,6 +2720,7 @@ function getTransactionDetails(transaction: OnyxInputOrEntry, creat comment: TransactionUtils.getDescription(transaction), merchant: TransactionUtils.getMerchant(transaction), waypoints: TransactionUtils.getWaypoints(transaction), + customUnitRateID: TransactionUtils.getRateID(transaction), category: TransactionUtils.getCategory(transaction), billable: TransactionUtils.getBillable(transaction), tag: TransactionUtils.getTag(transaction), @@ -2813,6 +2815,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry CONST.EDIT_REQUEST_FIELD.DATE, CONST.EDIT_REQUEST_FIELD.RECEIPT, CONST.EDIT_REQUEST_FIELD.DISTANCE, + CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE, ]; if (!ReportActionsUtils.isMoneyRequestAction(reportAction) || !canEditMoneyRequest(reportAction)) { @@ -2852,6 +2855,11 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry return !isInvoiceReport(moneyRequestReport) && !TransactionUtils.isReceiptBeingScanned(transaction) && !TransactionUtils.isDistanceRequest(transaction) && isRequestor; } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE) { + // The distance rate can be modified only on the distance expense reports + return isExpenseReport(moneyRequestReport) && TransactionUtils.isDistanceRequest(transaction); + } + return true; } @@ -3291,6 +3299,19 @@ function getModifiedExpenseOriginalMessage( originalMessage.billable = transactionChanges?.billable ? Localize.translateLocal('common.billable').toLowerCase() : Localize.translateLocal('common.nonBillable').toLowerCase(); } + if ('customUnitRateID' in transactionChanges) { + originalMessage.oldAmount = TransactionUtils.getAmount(oldTransaction, isFromExpenseReport); + originalMessage.oldCurrency = TransactionUtils.getCurrency(oldTransaction); + originalMessage.oldMerchant = TransactionUtils.getMerchant(oldTransaction); + + const modifiedData = TransactionUtils.calculateAmountForUpdatedWaypointOrRate(oldTransaction, 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(modifiedData.modifiedAmount); + originalMessage.currency = modifiedData.modifiedCurrency; + originalMessage.merchant = modifiedData.modifiedMerchant; + } + return originalMessage; } diff --git a/src/libs/TransactionUtils/getDistanceInMeters.ts b/src/libs/TransactionUtils/getDistanceInMeters.ts index 776ae0abb8b2c..0efe72759d5d7 100644 --- a/src/libs/TransactionUtils/getDistanceInMeters.ts +++ b/src/libs/TransactionUtils/getDistanceInMeters.ts @@ -2,8 +2,10 @@ import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import type {OnyxInputOrEntry, Transaction} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; +// Get the distance in meters from the transaction. +// This function is placed in a separate file to avoid circular dependencies. function getDistanceInMeters(transaction: OnyxInputOrEntry, unit: Unit | undefined) { - // If we are creating a new distance request, the distance is available in routes.route0.distance and it's already in meter. + // If we are creating a new distance request, the distance is available in routes.route0.distance and it's already in meters. if (transaction?.routes?.route0?.distance) { return transaction.routes.route0.distance; } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index a57ff7f940d24..dd1dee99988e8 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -21,6 +21,10 @@ import type {Beta, OnyxInputOrEntry, Policy, RecentWaypoint, ReviewDuplicates, T import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import getDistanceInMeters from './getDistanceInMeters'; +import DistanceRequestUtils from '@libs/DistanceRequestUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import {toLocaleDigit} from '@libs/LocaleDigitUtils'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; let allTransactions: OnyxCollection = {}; Onyx.connect({ @@ -41,6 +45,17 @@ Onyx.connect({ callback: (value) => (allTransactionViolations = value), }); +let preferredLocale: DeepValueOf = CONST.LOCALES.DEFAULT; +Onyx.connect({ + key: ONYXKEYS.NVP_PREFERRED_LOCALE, + callback: (value) => { + if (!value) { + return; + } + preferredLocale = value; + }, +}); + let currentUserEmail = ''; let currentUserAccountID = -1; Onyx.connect({ @@ -203,7 +218,7 @@ function areRequiredFieldsEmpty(transaction: OnyxEntry): boolean { } /** - * Given the edit made to the expnse, return an updated transaction object. + * Given the edit made to the expense, return an updated transaction object. */ function getUpdatedTransaction(transaction: Transaction, transactionChanges: TransactionChanges, isFromExpenseReport: boolean, shouldUpdateReceiptState = true): Transaction { // Only changing the first level fields so no need for deep clone now @@ -240,6 +255,11 @@ function getUpdatedTransaction(transaction: Transaction, transactionChanges: Tra shouldStopSmartscan = true; } + if (Object.hasOwn(transactionChanges, 'customUnitRateID')) { + updatedTransaction.modifiedCustomUnitRateID = transactionChanges.customUnitRateID; + shouldStopSmartscan = true; + } + if (Object.hasOwn(transactionChanges, 'taxAmount') && typeof transactionChanges.taxAmount === 'number') { updatedTransaction.taxAmount = isFromExpenseReport ? -transactionChanges.taxAmount : transactionChanges.taxAmount; } @@ -724,6 +744,48 @@ 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, +) { + if (isEmptyObject(transactionChanges?.routes?.route0?.geometry) && isEmptyObject(transactionChanges.customUnitRateID)) { + return { + amount: CONST.IOU.DEFAULT_AMOUNT, + modifiedAmount: CONST.IOU.DEFAULT_AMOUNT, + modifiedMerchant: Localize.translateLocal('iou.fieldPending'), + modifiedCurrency: 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) + : 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 */ @@ -743,10 +805,10 @@ function hasReservationList(transaction: Transaction | undefined | null): boolea } /** - * Get rate ID from the transaction object + * Get custom unit rate (distance rate) ID from the transaction object */ function getRateID(transaction: OnyxInputOrEntry): string | undefined { - return transaction?.comment?.customUnit?.customUnitRateID?.toString(); + return transaction?.modifiedCustomUnitRateID ?? transaction?.comment?.customUnit?.customUnitRateID?.toString(); } /** @@ -980,6 +1042,7 @@ function buildTransactionsMergeParams(reviewDuplicates: OnyxEntry, - transactionChanges: TransactionChanges, - policy: OnyxTypes.OnyxInputOrEntry, - iouReport: OnyxTypes.OnyxInputOrEntry, -) { - let updatedAmount: number = CONST.IOU.DEFAULT_AMOUNT; - let updatedMerchant = Localize.translateLocal('iou.fieldPending'); - if (!isEmptyObject(transactionChanges?.routes?.route0?.geometry)) { - const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; - const mileageRates = DistanceRequestUtils.getMileageRates(policy, true); - const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; - const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) - ? DistanceRequestUtils.getRateForP2P(policyCurrency) - : mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy); - const {unit, rate} = mileageRate; - const distance = TransactionUtils.getDistanceInMeters(transaction, unit); - const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate ?? 0); - updatedAmount = ReportUtils.isExpenseReport(iouReport) ? -amount : amount; - updatedMerchant = DistanceRequestUtils.getDistanceMerchant(true, distance, unit, rate, transaction?.currency ?? CONST.CURRENCY.USD, Localize.translateLocal, (digit) => - toLocaleDigit(preferredLocale, digit), - ); - } - return {amount: updatedAmount, modifiedAmount: updatedAmount, modifiedMerchant: updatedMerchant}; -} - /** * @param transactionID * @param transactionThreadReportID @@ -2564,10 +2538,11 @@ function getUpdateMoneyRequestParams( }; const hasPendingWaypoints = 'waypoints' in transactionChanges; - if (transaction && updatedTransaction && hasPendingWaypoints) { + const hasModifiedDistanceRate = 'customUnitRateID' in transactionChanges; + if (transaction && updatedTransaction && (hasPendingWaypoints || hasModifiedDistanceRate)) { updatedTransaction = { ...updatedTransaction, - ...calculateAmountForUpdatedWaypoint(transaction, transactionChanges, policy, iouReport), + ...TransactionUtils.calculateAmountForUpdatedWaypointOrRate(transaction, transactionChanges, policy, ReportUtils.isExpenseReport(iouReport)), }; // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors @@ -2869,10 +2844,11 @@ function getUpdateTrackExpenseParams( }; const hasPendingWaypoints = 'waypoints' in transactionChanges; - if (transaction && updatedTransaction && hasPendingWaypoints) { + const hasModifiedDistanceRate = 'customUnitRateID' in transactionChanges; + if (transaction && updatedTransaction && (hasPendingWaypoints || hasModifiedDistanceRate)) { updatedTransaction = { ...updatedTransaction, - ...calculateAmountForUpdatedWaypoint(transaction, transactionChanges, policy, transactionThread), + ...TransactionUtils.calculateAmountForUpdatedWaypointOrRate(transaction, transactionChanges, policy, ReportUtils.isExpenseReport(transactionThread)), }; // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors @@ -3196,6 +3172,31 @@ function updateMoneyRequestDescription( API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION, params, onyxData); } +/** Updates the distance rate of an expense */ +function updateMoneyRequestDistanceRate( + transactionID: string, + transactionThreadReportID: string, + rateID: string, + policy: OnyxEntry, + policyTagList: OnyxEntry, + policyCategories: OnyxEntry, +) { + const transactionChanges: TransactionChanges = { + customUnitRateID: rateID, + }; + const allReports = ReportConnection.getAllReports(); + const transactionThreadReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; + + let data: UpdateMoneyRequestData; + if (ReportUtils.isTrackExpenseReport(transactionThreadReport)) { + data = getUpdateTrackExpenseParams(transactionID, transactionThreadReportID, transactionChanges, true, policy); + } else { + data = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories, true); + } + const {params, onyxData} = data; + API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE_RATE, params, onyxData); +} + /** Edits an existing distance expense */ function updateDistanceRequest( transactionID: string, @@ -7834,6 +7835,7 @@ export { setMoneyRequestCreated, setMoneyRequestCurrency, setMoneyRequestDescription, + setMoneyRequestDistanceRate, setMoneyRequestMerchant, setMoneyRequestParticipants, setMoneyRequestParticipantsFromReport, @@ -7852,13 +7854,13 @@ export { trackExpense, unapproveExpenseReport, unholdRequest, - updateDistanceRequestRate, updateMoneyRequestAmountAndCurrency, updateMoneyRequestBillable, updateMoneyRequestCategory, updateMoneyRequestDate, updateMoneyRequestDescription, updateMoneyRequestDistance, + updateMoneyRequestDistanceRate, updateMoneyRequestMerchant, updateMoneyRequestTag, updateMoneyRequestTaxAmount, diff --git a/src/libs/migrations/TransactionBackupsToCollection.ts b/src/libs/migrations/TransactionBackupsToCollection.ts index 53fa7e855e724..806a4a5999f56 100644 --- a/src/libs/migrations/TransactionBackupsToCollection.ts +++ b/src/libs/migrations/TransactionBackupsToCollection.ts @@ -7,7 +7,7 @@ import type {Transaction} from '@src/types/onyx'; /** * This migration moves all the transaction backups stored in the transaction collection, ONYXKEYS.COLLECTION.TRANSACTION, to a reserved collection that only * stores draft transactions, ONYXKEYS.COLLECTION.TRANSACTION_DRAFT. The purpose of the migration is that there is a possibility that transaction backups are - * not filtered by most functions, e.g, getAllReportTransactions (src/libs/TransactionUtils.ts). One problem that arose from storing transaction backups with + * not filtered by most functions, e.g, getAllReportTransactions (src/libs/TransactionUtils/index.ts). One problem that arose from storing transaction backups with * the other transactions is that for every distance expense which have their waypoints updated offline, we expect the ReportPreview component to display the * default image of a pending map. However, due to the presence of the transaction backup, the previous map image will be displayed alongside the pending map. * The problem was further discussed in this PR. https://github.com/Expensify/App/pull/30232#issuecomment-178110172 diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index a55d491ea834a..d21edcf9518c9 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -17,7 +17,7 @@ import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {Policy, Transaction} from '@src/types/onyx'; +import type * as OnyxTypes from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -26,7 +26,13 @@ import withWritableReportOrNotFound from './withWritableReportOrNotFound'; type IOURequestStepDistanceRateOnyxProps = { /** Policy details */ - policy: OnyxEntry; + policy: OnyxEntry; + + /** Collection of categories attached to the policy */ + policyCategories: OnyxEntry; + + /** Collection of tags attached to the policy */ + policyTags: OnyxEntry; /** Mileage rates */ rates: Record; @@ -35,25 +41,28 @@ type IOURequestStepDistanceRateOnyxProps = { type IOURequestStepDistanceRateProps = IOURequestStepDistanceRateOnyxProps & WithWritableReportOrNotFoundProps & { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - transaction: OnyxEntry; + transaction: OnyxEntry; }; function IOURequestStepDistanceRate({ policy, report, route: { - params: {backTo, transactionID}, + params: {action, reportID, backTo, transactionID}, }, transaction, rates, + policyTags, + policyCategories, }: IOURequestStepDistanceRateProps) { const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); + const isEditing = action === CONST.IOU.ACTION.EDIT; - const lastSelectedRateID = TransactionUtils.getRateID(transaction) ?? '-1'; + const currentRateID = TransactionUtils.getRateID(transaction) ?? '-1'; const navigateBack = () => { Navigation.goBack(backTo); @@ -67,7 +76,7 @@ function IOURequestStepDistanceRate({ alternateText: rate.name ? rateForDisplay : '', keyForList: rate.customUnitRateID, value: rate.customUnitRateID, - isSelected: lastSelectedRateID ? lastSelectedRateID === rate.customUnitRateID : !!(rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE), + isSelected: currentRateID ? currentRateID === rate.customUnitRateID : rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE, }; }); @@ -85,7 +94,15 @@ function IOURequestStepDistanceRate({ IOU.setMoneyRequestTaxAmount(transactionID, taxAmount); IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID); } - IOU.updateDistanceRequestRate(transactionID, customUnitRateID, policy?.id ?? '-1'); + + if (currentRateID !== customUnitRateID) { + IOU.setMoneyRequestDistanceRate(transactionID, customUnitRateID, policy?.id ?? '-1', !isEditing); + + if (isEditing) { + IOU.updateMoneyRequestDistanceRate(transaction?.transactionID ?? '-1', reportID, customUnitRateID, policy, policyTags, policyCategories); + } + } + navigateBack(); } @@ -93,8 +110,8 @@ function IOURequestStepDistanceRate({ {translate('iou.chooseARate', {unit})} @@ -115,9 +132,15 @@ const IOURequestStepDistanceRateWithOnyx = withOnyx `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '-1'}`, }, + policyCategories: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, + }, + policyTags: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, + }, rates: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? '-1'}`, - selector: (policy: OnyxEntry) => DistanceRequestUtils.getMileageRates(policy), + selector: (policy: OnyxEntry) => DistanceRequestUtils.getMileageRates(policy), }, })(IOURequestStepDistanceRate); diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 7515e2f5a8d05..607b9b808b858 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -354,6 +354,9 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback< /** The edited waypoints for the distance expense */ modifiedWaypoints?: WaypointCollection; + /** The edited distance rate for the distance request */ + modifiedCustomUnitRateID?: string; + /** * Used during the creation flow before the transaction is saved to the server and helps dictate where * the user is navigated to when pressing the back button on the confirmation step @@ -446,7 +449,7 @@ type Transaction = OnyxCommon.OnyxValueWithOfflineFeedback< /** Whether the transaction is linked to a managed card */ managedCard?: boolean; }, - keyof Comment + keyof Comment | keyof TransactionCustomUnit >; /** Keys of pending transaction fields */ @@ -460,6 +463,9 @@ type AdditionalTransactionChanges = { /** Collection of modified waypoints */ waypoints?: WaypointCollection; + /** The ID of the distance rate */ + customUnitRateID?: string; + /** Previous amount before changes */ oldAmount?: number;