Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use transaction distanceUnit to prevent retroactive changes #50001

Merged
merged 34 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b9d9f6b
Use transaction distanceUnit to prevent retroactive changes
neil-marcellini Oct 1, 2024
3cedd46
Display rate with transaction's distance unit
neil-marcellini Oct 1, 2024
d6c88de
Prompt with both units for when transaction unit doesn't match policy
neil-marcellini Oct 1, 2024
778dd23
WIP optimistically set distanceUnit
neil-marcellini Oct 1, 2024
540f11a
Pass existing transaction in all distance cases
neil-marcellini Oct 2, 2024
3961d4c
Optimistically update to current distance unit when editing
neil-marcellini Oct 2, 2024
4520fe3
Prettify
neil-marcellini Oct 2, 2024
7579b73
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 7, 2024
9936790
Remove unused types
neil-marcellini Oct 7, 2024
56c96fb
Missed a spot
neil-marcellini Oct 7, 2024
ebff76e
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 9, 2024
9a06cfe
WIP use util to get transaction distance rate
neil-marcellini Oct 9, 2024
7982c26
Util to get updated distance unit
neil-marcellini Oct 9, 2024
5c43231
Use get rate util in another spot
neil-marcellini Oct 9, 2024
e07a837
Simplify using lodashSet
neil-marcellini Oct 9, 2024
18657fd
WIP convert distance to new rate unit
neil-marcellini Oct 9, 2024
1703edb
Refactor and fix getting updated distance unit
neil-marcellini Oct 9, 2024
466990b
Fix distance conversion with parenthesis
neil-marcellini Oct 9, 2024
da47219
Set distance pending when converted to new rate unit
neil-marcellini Oct 9, 2024
8f10f77
Clear pendingFields that aren't part of transactionChanges
neil-marcellini Oct 9, 2024
5bd5c3a
Fix lint
neil-marcellini Oct 9, 2024
a307c8d
Fix getting mileageRate.rate
neil-marcellini Oct 9, 2024
99e9170
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 11, 2024
aded5c2
Ensure currency is always set
neil-marcellini Oct 11, 2024
7a9d0ea
Use stored P2P rate for existing transactions
neil-marcellini Oct 11, 2024
1e87ca3
Explain why the update distance unit should come from the rate
neil-marcellini Oct 11, 2024
ce80ef1
Apply feedback to clean things up
neil-marcellini Oct 11, 2024
817266b
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 15, 2024
a2f0074
Fix some ts errors
neil-marcellini Oct 15, 2024
00eeb4a
Fix if somehow mileageRate is undefined
neil-marcellini Oct 15, 2024
5b12592
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 16, 2024
01e652a
Ensure policy is defined for optimistic distance unit
neil-marcellini Oct 16, 2024
611ad8b
Fix ts, missing param
neil-marcellini Oct 16, 2024
f0ec762
Merge branch 'main' into neil-distanceUnit
neil-marcellini Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@ import Config from 'react-native-config';
import * as KeyCommand from 'react-native-key-command';
import type {ValueOf} from 'type-fest';
import type {Video} from './libs/actions/Report';
import type {MileageRate} from './libs/DistanceRequestUtils';
import BankAccount from './libs/models/BankAccount';
import * as Url from './libs/Url';
import SCREENS from './SCREENS';
import type PlaidBankAccount from './types/onyx/PlaidBankAccount';
import type {Unit} from './types/onyx/Policy';

type RateAndUnit = {
unit: Unit;
rate: number;
currency: string;
};
type CurrencyDefaultMileageRate = Record<string, RateAndUnit>;

// Creating a default array and object this way because objects ({}) and arrays ([]) are not stable types.
// Freezing the array ensures that it cannot be unintentionally modified.
Expand Down Expand Up @@ -5441,7 +5434,7 @@ const CONST = {
"rate": 2377,
"unit": "km"
}
}`) as CurrencyDefaultMileageRate,
}`) as Record<string, MileageRate>,

EXIT_SURVEY: {
REASONS: {
Expand Down Expand Up @@ -5810,6 +5803,6 @@ type FeedbackSurveyOptionID = ValueOf<Pick<ValueOf<typeof CONST.FEEDBACK_SURVEY_
type SubscriptionType = ValueOf<typeof CONST.SUBSCRIPTION.TYPE>;
type CancellationType = ValueOf<typeof CONST.CANCELLATION_TYPE>;

export type {Country, IOUAction, IOUType, RateAndUnit, OnboardingPurposeType, IOURequestType, SubscriptionType, FeedbackSurveyOptionID, CancellationType, OnboardingInviteType};
export type {Country, IOUAction, IOUType, OnboardingPurposeType, IOURequestType, SubscriptionType, FeedbackSurveyOptionID, CancellationType, OnboardingInviteType};

export default CONST;
9 changes: 3 additions & 6 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,11 @@ function MoneyRequestConfirmationList({
}, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID, isDistanceRequest]);

const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD;

const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(policyCurrency) : mileageRates?.[customUnitRateID] ?? defaultMileageRate;

const {unit, rate} = mileageRate ?? {};

const unit = DistanceRequestUtils.getDistanceUnit(transaction, mileageRate);
const {rate} = mileageRate ?? {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two sources of truth mileageRate.unit and unit which could be confusing and error-prone. Can we have the returned mileageRate already have the correct unit. i.e. write a function DistanceRequestUtils.getRate and in that function pass both the policy and the transaction and set the unit accordingly.

Also I think we can remove the isCustomUnitRateIDForP2P, getRateForP2P and getCustomUnitRateID (not sure about this one) functions or at least not export them. The getRate function should be enough

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a pretty good idea, thanks. I'll work on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up adding two new util functions for getting the rate of existing transactions and then getting the distance unit for use when updating a transaction.

I left one use of these other utils you recommended removing, because making the current code work with the new utils would require creating a temporary transaction, setting the customUnitRateID from the transaction changes into it, then using one util for the rate and another for the unit. It's just as messy as the existing code. Not to mention that I would also need to change a lot of the OnyxEntryOrInput types to work with the OnyxEntry types. So I'm going to leave that as is.

const mileageRate = isCustomUnitRateIDForP2P(transaction)
? DistanceRequestUtils.getRateForP2P(policyCurrency)
: mileageRates?.[customUnitRateID] ?? DistanceRequestUtils.getDefaultMileageRate(policy);

Please let me know what you think in your next review.

const prevRate = usePrevious(rate);

const currency = (mileageRate as MileageRate)?.currency ?? policyCurrency;
const currency = mileageRate?.currency ?? policyCurrency;

// A flag for showing the categories field
const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice) && (!!iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {})));
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
const currency = policy ? policy.outputCurrency : PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD;

const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) ? DistanceRequestUtils.getRateForP2P(currency) : distanceRates[rateID] ?? {};
const {unit} = mileageRate;
const unit = DistanceRequestUtils.getDistanceUnit(transaction, mileageRate);
const rate = transaction?.comment?.customUnit?.defaultP2PRate ?? mileageRate.rate;
neil-marcellini marked this conversation as resolved.
Show resolved Hide resolved

const distance = TransactionUtils.getDistanceInMeters(transactionBackup ?? transaction, unit);
Expand Down
3 changes: 1 addition & 2 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ import type {
PayerSettledParams,
PaySomeoneParams,
ReconciliationWorksParams,
ReimbursementRateParams,
RemovedFromApprovalWorkflowParams,
RemovedTheRequestParams,
RemoveMemberPromptParams,
Expand Down Expand Up @@ -1010,7 +1009,7 @@ const translations = {
changed: 'changed',
removed: 'removed',
transactionPending: 'Transaction pending.',
chooseARate: ({unit}: ReimbursementRateParams) => `Select a workspace reimbursement rate per ${unit}`,
chooseARate: 'Select a workspace reimbursement rate per mile or kilometer',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works better when the selected rate is in a different unit than the current policy distance unit. I think it works fine when the unit is all the same too.

unapprove: 'Unapprove',
unapproveReport: 'Unapprove report',
headsUp: 'Heads up!',
Expand Down
3 changes: 1 addition & 2 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ import type {
PayerSettledParams,
PaySomeoneParams,
ReconciliationWorksParams,
ReimbursementRateParams,
RemovedFromApprovalWorkflowParams,
RemovedTheRequestParams,
RemoveMemberPromptParams,
Expand Down Expand Up @@ -1005,7 +1004,7 @@ const translations = {
changed: 'cambió',
removed: 'eliminó',
transactionPending: 'Transacción pendiente.',
chooseARate: ({unit}: ReimbursementRateParams) => `Selecciona una tasa de reembolso por ${unit} del espacio de trabajo`,
chooseARate: 'Selecciona una tasa de reembolso por milla o kilómetro para el espacio de trabajo',
unapprove: 'Desaprobar',
unapproveReport: 'Anular la aprobación del informe',
headsUp: 'Atención!',
Expand Down
5 changes: 1 addition & 4 deletions src/languages/params.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {OnyxInputOrEntry, ReportAction} from '@src/types/onyx';
import type {DelegateRole} from '@src/types/onyx/Account';
import type {AllConnectionName, ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName, Unit} from '@src/types/onyx/Policy';
import type {AllConnectionName, ConnectionName, PolicyConnectionSyncStage, SageIntacctMappingName} from '@src/types/onyx/Policy';
import type {ViolationDataType} from '@src/types/onyx/TransactionViolation';

type AddressLineParams = {
Expand Down Expand Up @@ -279,8 +279,6 @@ type LogSizeAndDateParams = {size: number; date: string};

type HeldRequestParams = {comment: string};

type ReimbursementRateParams = {unit: Unit};

type ChangeFieldParams = {oldValue?: string; newValue: string; fieldName: string};

type ChangePolicyParams = {fromPolicy: string; toPolicy: string};
Expand Down Expand Up @@ -648,7 +646,6 @@ export type {
PayerPaidAmountParams,
PayerPaidParams,
PayerSettledParams,
ReimbursementRateParams,
RemovedTheRequestParams,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
Expand Down
12 changes: 8 additions & 4 deletions src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type {OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {RateAndUnit} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {LastSelectedDistanceRates, OnyxInputOrEntry} from '@src/types/onyx';
import type {LastSelectedDistanceRates, OnyxInputOrEntry, Transaction} from '@src/types/onyx';
import type {Unit} from '@src/types/onyx/Policy';
import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand Down Expand Up @@ -223,9 +222,9 @@ function getDistanceMerchant(
* Retrieves the rate and unit for a P2P distance expense for a given currency.
*
* @param currency
* @returns The rate and unit in RateAndUnit object.
* @returns The rate and unit in MileageRate object.
*/
function getRateForP2P(currency: string): RateAndUnit {
function getRateForP2P(currency: string): MileageRate {
const currencyWithExistingRate = CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ? currency : CONST.CURRENCY.USD;
return {
...CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currencyWithExistingRate],
Expand Down Expand Up @@ -301,6 +300,10 @@ function getTaxableAmount(policy: OnyxEntry<Policy>, customUnitRateID: string, d
return amount * taxClaimablePercentage;
}

function getDistanceUnit(transaction: OnyxEntry<Transaction>, mileageRate: OnyxEntry<MileageRate>) {
return transaction?.comment?.customUnit?.distanceUnit ?? mileageRate?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES;
}

export default {
getDefaultMileageRate,
getDistanceMerchant,
Expand All @@ -312,6 +315,7 @@ export default {
getCustomUnitRateID,
convertToDistanceInMeters,
getTaxableAmount,
getDistanceUnit,
};

export type {MileageRate};
17 changes: 17 additions & 0 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import lodashHas from 'lodash/has';
import lodashIsEqual from 'lodash/isEqual';
import lodashSet from 'lodash/set';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
Expand Down Expand Up @@ -139,6 +140,7 @@ function buildOptimisticTransaction(
billable = false,
pendingFields: Partial<{[K in TransactionPendingFieldsKey]: ValueOf<typeof CONST.RED_BRICK_ROAD_PENDING_ACTION>}> | undefined = undefined,
reimbursable = true,
existingTransaction: OnyxEntry<Transaction> | undefined = undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function (buildOptimisticTransaction) now have two parameters that are similar in name but different existingTransactionID and existingTransaction; existingTransaction could exist and at the same time existingTransactionID does not which is a little confusing because you'd think the later id is the coming from that transaction but it's not necessary the case as we can pass the draft transaction

): Transaction {
// transactionIDs are random, positive, 64-bit numeric strings.
// Because JS can only handle 53-bit numbers, transactionIDs are strings in the front-end (just like reportActionID)
Expand All @@ -152,6 +154,21 @@ function buildOptimisticTransaction(
commentJSON.originalTransactionID = originalTransactionID;
}

const isDistanceTransaction = (pendingFields?.waypoints ?? '') !== '';
neil-marcellini marked this conversation as resolved.
Show resolved Hide resolved
if (isDistanceTransaction) {
// Get the policy of this transaction from the report.policyID
const allReports = ReportConnection.getAllReports();
const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null;
const policyID = report?.policyID ?? '';
const policy = PolicyUtils.getPolicy(policyID);
neil-marcellini marked this conversation as resolved.
Show resolved Hide resolved

// Set the distance unit, which comes from the policy distance unit or the P2P rate data
const distanceRates = DistanceRequestUtils.getMileageRates(policy, true);
const customUnitRateID = existingTransaction?.comment?.customUnit?.customUnitRateID ?? CONST.CUSTOM_UNITS.FAKE_P2P_ID;
const mileageRate = customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID ? DistanceRequestUtils.getRateForP2P(currency) : distanceRates[customUnitRateID] ?? {};
lodashSet(commentJSON, 'customUnit.distanceUnit', mileageRate.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES);
}

return {
...(!isEmptyObject(pendingFields) ? {pendingFields} : {}),
transactionID,
Expand Down
18 changes: 17 additions & 1 deletion src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {format} from 'date-fns';
import {fastMerge, Str} from 'expensify-common';
import lodashSet from 'lodash/set';
import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxInputValue, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {PartialDeep, SetRequired, ValueOf} from 'type-fest';
Expand Down Expand Up @@ -2020,6 +2021,7 @@ function getMoneyRequestInformation(
payeeEmail = currentUserEmail,
moneyRequestReportID = '',
linkedTrackedExpenseReportAction?: OnyxTypes.ReportAction,
existingTransaction: OnyxEntry<OnyxTypes.Transaction> | undefined = undefined,
): MoneyRequestInformation {
const payerEmail = PhoneNumber.addSMSDomainIfPhoneNumber(participant.login ?? '');
const payerAccountID = Number(participant.accountID);
Expand Down Expand Up @@ -2072,7 +2074,6 @@ function getMoneyRequestInformation(
}

// STEP 3: Build an optimistic transaction with the receipt
const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${existingTransactionID ?? CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`];
const isDistanceRequest = existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
let optimisticTransaction = TransactionUtils.buildOptimisticTransaction(
ReportUtils.isExpenseReport(iouReport) ? -amount : amount,
Expand All @@ -2092,6 +2093,8 @@ function getMoneyRequestInformation(
ReportUtils.isExpenseReport(iouReport) ? -(taxAmount ?? 0) : taxAmount,
billable,
isDistanceRequest ? {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : undefined,
undefined,
existingTransaction,
);

const optimisticPolicyRecentlyUsedCategories = Category.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category);
Expand Down Expand Up @@ -2330,6 +2333,7 @@ function getTrackExpenseInformation(
billable,
isDistanceRequest ? {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : undefined,
false,
existingTransaction,
);

// If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction
Expand Down Expand Up @@ -2521,6 +2525,13 @@ function getUpdateMoneyRequestParams(
...TransactionUtils.calculateAmountForUpdatedWaypointOrRate(updatedTransaction, transactionChanges, policy, ReportUtils.isExpenseReport(iouReport)),
};

// Update the distanceUnit
const distanceRates = DistanceRequestUtils.getMileageRates(policy, true);
const customUnitRateID = updatedTransaction?.comment?.customUnit?.customUnitRateID ?? CONST.CUSTOM_UNITS.FAKE_P2P_ID;
const mileageRate =
customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID ? DistanceRequestUtils.getRateForP2P(updatedTransaction?.currency ?? 'USD') : distanceRates[customUnitRateID] ?? {};
lodashSet(updatedTransaction, 'comment.customUnit.distanceUnit', mileageRate.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES);

// Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors
successData.push({
onyxMethod: Onyx.METHOD.SET,
Expand Down Expand Up @@ -3972,6 +3983,8 @@ function createSplitsAndOnyxData(
taxAmount,
billable,
isDistanceRequest ? {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : undefined,
undefined,
existingTransaction,
);

// Important data is set on the draft distance transaction, such as the iouRequestType marking it as a distance request, so merge it into the optimistic split transaction
Expand Down Expand Up @@ -5093,6 +5106,7 @@ function createDistanceRequest(
currentUserAccountID = -1,
splitShares: SplitShares = {},
iouType: ValueOf<typeof CONST.IOU.TYPE> = CONST.IOU.TYPE.SUBMIT,
existingTransaction: OnyxEntry<OnyxTypes.Transaction> | undefined = undefined,
) {
// If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function
const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report);
Expand Down Expand Up @@ -5185,6 +5199,8 @@ function createDistanceRequest(
userAccountID,
currentUserEmail,
moneyRequestReportID,
undefined,
existingTransaction,
);
onyxData = moneyRequestOnyxData;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ function IOURequestStepConfirmation({
currentUserPersonalDetails.accountID,
transaction.splitShares,
iouType,
transaction,
);
},
[policy, policyCategories, policyTags, report, transaction, transactionTaxCode, transactionTaxAmount, customUnitRateID, currentUserPersonalDetails, iouType],
Expand Down
1 change: 1 addition & 0 deletions src/pages/iou/request/step/IOURequestStepDistance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ function IOURequestStepDistance({
currentUserPersonalDetails.accountID,
transaction?.splitShares,
iouType,
transaction,
);
return;
}
Expand Down
11 changes: 6 additions & 5 deletions src/pages/iou/request/step/IOURequestStepDistanceRate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {Unit} from '@src/types/onyx/Policy';
import StepScreenWrapper from './StepScreenWrapper';
import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
Expand Down Expand Up @@ -71,7 +70,10 @@ function IOURequestStepDistanceRate({
};

const sections = Object.values(rates).map((rate) => {
const rateForDisplay = DistanceRequestUtils.getRateForDisplay(rate.unit, rate.rate, rate.currency, translate, toLocaleDigit);
// If the rate is currently used by the transaction, display the rate with the transaction's distance unit. Otherwise, display the rate's unit.
// Using the transaction's distance unit prevents the case where changing the policy distance unit, and therefore all rate units, effects the units of existing transactions.
const unit = transaction?.comment?.customUnit?.customUnitRateID === rate.customUnitRateID ? DistanceRequestUtils.getDistanceUnit(transaction, rate) : rate.unit;
const rateForDisplay = DistanceRequestUtils.getRateForDisplay(unit, rate.rate, rate.currency, translate, toLocaleDigit);

return {
text: rate.name ?? rateForDisplay,
Expand All @@ -83,8 +85,6 @@ function IOURequestStepDistanceRate({
};
});

const unit = (Object.values(rates).at(0)?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit;

const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList;

function selectDistanceRate(customUnitRateID: string) {
Expand All @@ -93,6 +93,7 @@ function IOURequestStepDistanceRate({
if (shouldShowTax) {
const policyCustomUnitRate = getCustomUnitRate(policy, customUnitRateID);
taxRateExternalID = policyCustomUnitRate?.attributes?.taxRateExternalID ?? '-1';
const unit = DistanceRequestUtils.getDistanceUnit(transaction, rates[customUnitRateID]);
const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, TransactionUtils.getDistanceInMeters(transaction, unit));
const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? '';
taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount, rates[customUnitRateID].currency ?? CONST.CURRENCY.USD));
Expand All @@ -118,7 +119,7 @@ function IOURequestStepDistanceRate({
shouldShowWrapper
testID={IOURequestStepDistanceRate.displayName}
>
<Text style={[styles.mh5, styles.mv4]}>{translate('iou.chooseARate', {unit})}</Text>
<Text style={[styles.mh5, styles.mv4]}>{translate('iou.chooseARate')}</Text>

<SelectionList
sections={[{data: sections}]}
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ type TransactionCustomUnit = {

/** Default rate for custom unit */
defaultP2PRate?: number | null;

/** The unit for the distance/quantity */
distanceUnit?: 'mi' | 'km';
};

/** Types of geometry */
Expand Down
Loading