Skip to content

Commit 0d0e0d8

Browse files
Merge pull request #95067 from shubham1206agra/migrate-getRate-3
Refactor: migrate getRate to use usePersonalPolicy hook (part 3)
2 parents ebc40ab + 898064a commit 0d0e0d8

10 files changed

Lines changed: 93 additions & 16 deletions

File tree

src/components/MoneyRequestConfirmationList/DistanceRequestController.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx';
33
import {useCurrencyListActions} from '@hooks/useCurrencyList';
44
import useLocalize from '@hooks/useLocalize';
55
import useOnyx from '@hooks/useOnyx';
6+
import usePersonalPolicy from '@hooks/usePersonalPolicy';
67
import usePrevious from '@hooks/usePrevious';
78
import {clearMoneyRequestRateAutoUpdated, setCustomUnitRateID, setMoneyRequestAmount, setMoneyRequestMerchant, setMoneyRequestPendingFields} from '@libs/actions/IOU/MoneyRequest';
89
import {setSplitShares} from '@libs/actions/IOU/Split';
@@ -78,6 +79,7 @@ function DistanceRequestController({
7879
}: DistanceRequestControllerProps) {
7980
const {translate, toLocaleDigit} = useLocalize();
8081
const {getCurrencySymbol} = useCurrencyListActions();
82+
const personalPolicy = usePersonalPolicy();
8183
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
8284
const lastSelectedRate = policy?.id ? (lastSelectedDistanceRates?.[policy.id] ?? defaultMileageRateCustomUnitRateID) : defaultMileageRateCustomUnitRateID;
8385
const prevPolicy = usePrevious(policy);
@@ -107,7 +109,7 @@ function DistanceRequestController({
107109
// If there is a distance rate in the policy that matches the rate and unit of the currently selected mileage rate, select it automatically
108110
const matchingRate = Object.values(policyRates).find((policyRate) => policyRate.rate === mileageRate.rate && policyRate.unit === mileageRate.unit);
109111
if (matchingRate?.customUnitRateID) {
110-
setCustomUnitRateID(transactionID, matchingRate.customUnitRateID, transaction, policy);
112+
setCustomUnitRateID(transactionID, matchingRate.customUnitRateID, transaction, policy, false, personalPolicy?.outputCurrency);
111113
clearFormErrors([errorKey]);
112114
return;
113115
}
@@ -127,6 +129,7 @@ function DistanceRequestController({
127129
clearFormErrors,
128130
transaction,
129131
prevPolicy?.id,
132+
personalPolicy?.outputCurrency,
130133
]);
131134

132135
useEffect(() => {
@@ -193,7 +196,7 @@ function DistanceRequestController({
193196
rateToUse = bestRate?.customUnitRateID ?? defaultMileageRateCustomUnitRateID ?? lastSelectedRate;
194197
}
195198
}
196-
setCustomUnitRateID(transactionID, rateToUse, transaction, policy);
199+
setCustomUnitRateID(transactionID, rateToUse, transaction, policy, false, personalPolicy?.outputCurrency);
197200
}, [
198201
customUnitRateID,
199202
transactionID,
@@ -205,6 +208,7 @@ function DistanceRequestController({
205208
policy,
206209
selectedParticipants,
207210
defaultMileageRateCustomUnitRateID,
211+
personalPolicy?.outputCurrency,
208212
]);
209213

210214
useEffect(() => {

src/components/MoneyRequestConfirmationList/sections/DateField.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
66
import {useConfirmationFields} from '@components/MoneyRequestConfirmationFields/context';
77
import useLocalize from '@hooks/useLocalize';
88
import useOnyx from '@hooks/useOnyx';
9+
import usePersonalPolicy from '@hooks/usePersonalPolicy';
910
import usePolicy from '@hooks/usePolicy';
1011
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
1112
import useThemeStyles from '@hooks/useThemeStyles';
@@ -60,6 +61,7 @@ function DateField({
6061
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
6162
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
6263
const policy = usePolicy(report?.policyID);
64+
const personalPolicy = usePersonalPolicy();
6365

6466
const dateState = useTransactionSelector(transactionID, dateStateSelector);
6567
const transaction = useTransactionSelector(transactionID, (t) => t);
@@ -104,6 +106,7 @@ function DateField({
104106
policyForTrackExpense,
105107
lastSelectedDistanceRates,
106108
isDraft: shouldUseTransactionDraft(action),
109+
personalPolicyOutputCurrency: personalPolicy?.outputCurrency,
107110
});
108111
}
109112
};

src/hooks/useParticipantSubmission.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import useLocalize from './useLocalize';
3333
import useMappedPolicies from './useMappedPolicies';
3434
import useOnyx from './useOnyx';
3535
import useOptimisticDraftTransactions from './useOptimisticDraftTransactions';
36+
import usePersonalPolicy from './usePersonalPolicy';
3637
import usePolicyForMovingExpenses from './usePolicyForMovingExpenses';
3738
import useTransactionsByID from './useTransactionsByID';
3839

@@ -74,6 +75,7 @@ function useParticipantSubmission({
7475
isFocused,
7576
}: UseParticipantSubmissionParams) {
7677
const {translate} = useLocalize();
78+
const personalPolicy = usePersonalPolicy();
7779

7880
const [allPolicies] = useMappedPolicies(policyMapper);
7981
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
@@ -188,7 +190,7 @@ function useParticipantSubmission({
188190
lastSelectedDistanceRates: distanceRates,
189191
expenseDate: transaction.created,
190192
});
191-
setCustomUnitRateID(transaction.transactionID, rateID, transaction, movingPolicy);
193+
setCustomUnitRateID(transaction.transactionID, rateID, transaction, movingPolicy, false, personalPolicy?.outputCurrency);
192194
const shouldSetParticipantAutoAssignment = iouType === CONST.IOU.TYPE.CREATE;
193195
setMoneyRequestParticipantsFromReport(transaction.transactionID, dmReport, userDetails.accountID, shouldSetParticipantAutoAssignment ? isActiveRequest : false);
194196
setTransactionReport(transaction.transactionID, {reportID: CONST.REPORT.UNREPORTED_REPORT_ID}, true);
@@ -260,7 +262,7 @@ function useParticipantSubmission({
260262
lastSelectedDistanceRates: distanceRates,
261263
expenseDate: transaction.created,
262264
});
263-
setCustomUnitRateID(transaction.transactionID, rateID, transaction, policy);
265+
setCustomUnitRateID(transaction.transactionID, rateID, transaction, policy, false, personalPolicy?.outputCurrency);
264266
}
265267
} else {
266268
// Fallback to using initialTransactionID directly
@@ -270,7 +272,9 @@ function useParticipantSubmission({
270272
policy,
271273
lastSelectedDistanceRates: distanceRates,
272274
});
273-
setCustomUnitRateID(initialTransactionID, rateID, undefined, policy);
275+
// personalPolicyOutputCurrency is intentionally omitted: setCustomUnitRateID only resolves a (P2P) rate when a transaction is passed,
276+
// and no transaction is passed here, so the currency is never read.
277+
setCustomUnitRateID(initialTransactionID, rateID, undefined, policy, false, undefined);
274278
}
275279
}
276280

src/libs/actions/IOU/MoneyRequest.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,15 +618,22 @@ function setMoneyRequestTimeCount(transactionID: string, count: number, isDraft:
618618
* if passed transaction previously had it to make sure that transaction does not have inconsistent
619619
* states (for example distanceUnit not matching distance unit of the new customUnitRateID)
620620
*/
621-
function setCustomUnitRateID(transactionID: string, customUnitRateID: string | undefined, transaction: OnyxEntry<Transaction>, policy: OnyxEntry<Policy>, rateAutoUpdated = false) {
621+
function setCustomUnitRateID(
622+
transactionID: string,
623+
customUnitRateID: string | undefined,
624+
transaction: OnyxEntry<Transaction>,
625+
policy: OnyxEntry<Policy>,
626+
rateAutoUpdated: boolean,
627+
personalPolicyOutputCurrency: string | undefined,
628+
) {
622629
const isFakeP2PRate = customUnitRateID === CONST.CUSTOM_UNITS.FAKE_P2P_ID;
623630

624631
let newDistanceUnit: Unit | undefined;
625632
let newQuantity: number | undefined;
626633

627634
if (customUnitRateID && transaction) {
628635
const distanceRate = isFakeP2PRate
629-
? DistanceRequestUtils.getRate({transaction: undefined, policy: undefined, useTransactionDistanceUnit: false, isFakeP2PRate})
636+
? DistanceRequestUtils.getRate({transaction: undefined, policy: undefined, useTransactionDistanceUnit: false, isFakeP2PRate, personalPolicyOutputCurrency})
630637
: DistanceRequestUtils.getRateByCustomUnitRateID({policy, customUnitRateID});
631638

632639
const transactionDistanceUnit = transaction.comment?.customUnit?.distanceUnit;
@@ -845,6 +852,7 @@ function updateDistanceRateOnExpenseDateChange({
845852
policyForTrackExpense,
846853
lastSelectedDistanceRates,
847854
isDraft,
855+
personalPolicyOutputCurrency,
848856
}: {
849857
transactionID: string;
850858
transaction: OnyxEntry<Transaction>;
@@ -856,6 +864,7 @@ function updateDistanceRateOnExpenseDateChange({
856864
policyForTrackExpense: OnyxEntry<Policy>;
857865
lastSelectedDistanceRates: OnyxEntry<LastSelectedDistanceRates>;
858866
isDraft: boolean;
867+
personalPolicyOutputCurrency: string | undefined;
859868
}) {
860869
if (!isDistanceRequest(transaction) || !(isPolicyExpenseChat || isTrackExpense)) {
861870
return;
@@ -872,7 +881,7 @@ function updateDistanceRateOnExpenseDateChange({
872881
});
873882
const currentRateID = transaction?.comment?.customUnit?.customUnitRateID;
874883
const rateChanged = rateID !== currentRateID;
875-
setCustomUnitRateID(transactionID, rateID, transaction, effectivePolicy, rateChanged);
884+
setCustomUnitRateID(transactionID, rateID, transaction, effectivePolicy, rateChanged, personalPolicyOutputCurrency);
876885

877886
if (rateChanged && rateID && isTaxTrackingEnabled(isPolicyExpenseChat || isTrackExpense || isExpenseUnreported(transaction), effectivePolicy, isDistanceRequest(transaction))) {
878887
const mileageRates = DistanceRequestUtils.getMileageRates(effectivePolicy);

src/pages/iou/request/step/IOURequestStepDate.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useLocalize from '@hooks/useLocalize';
1313
import useNetwork from '@hooks/useNetwork';
1414
import useOnyx from '@hooks/useOnyx';
1515
import usePermissions from '@hooks/usePermissions';
16+
import usePersonalPolicy from '@hooks/usePersonalPolicy';
1617
import usePolicy from '@hooks/usePolicy';
1718
import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses';
1819
import useRestartOnReceiptFailure from '@hooks/useRestartOnReceiptFailure';
@@ -55,6 +56,7 @@ function IOURequestStepDate({
5556
const styles = useThemeStyles();
5657
const {translate} = useLocalize();
5758
const policy = usePolicy(report?.policyID);
59+
const personalPolicy = usePersonalPolicy();
5860
const isTrackExpense = iouType === CONST.IOU.TYPE.TRACK;
5961
const {policyForMovingExpensesID} = usePolicyForMovingExpenses();
6062
const policyForTrackExpense = usePolicy(isTrackExpense ? policyForMovingExpensesID : undefined);
@@ -141,6 +143,7 @@ function IOURequestStepDate({
141143
policyForTrackExpense,
142144
lastSelectedDistanceRates,
143145
isDraft: isTransactionDraft,
146+
personalPolicyOutputCurrency: personalPolicy?.outputCurrency,
144147
});
145148
}
146149

src/pages/iou/request/step/IOURequestStepDestination.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function IOURequestStepDestination({
141141
setCustomUnitID(transactionID, customUnit.customUnitID);
142142
setMoneyRequestCategory(transactionID, customUnit?.defaultCategory ?? '', undefined);
143143
}
144-
setCustomUnitRateID(transactionID, destination.keyForList ?? '', transaction, policy);
144+
setCustomUnitRateID(transactionID, destination.keyForList ?? '', transaction, policy, false, personalPolicy?.outputCurrency);
145145
setMoneyRequestCurrency(transactionID, destination.currency);
146146
clearSubrates(transactionID);
147147
}

src/pages/iou/request/step/IOURequestStepDistance/handleMoneyRequestStepDistanceNavigation.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,21 @@ function buildDistanceAmountAndMerchant({
108108
transaction,
109109
policy,
110110
translate,
111+
personalPolicyOutputCurrency,
111112
}: {
112113
isManualDistance: boolean;
113114
distance: number | undefined;
114115
unit: Unit | undefined;
115116
transaction: Transaction | undefined;
116117
policy: OnyxEntry<Policy>;
117118
translate: <TPath extends TranslationPaths>(path: TPath, ...parameters: TranslationParameters<TPath>) => string;
119+
personalPolicyOutputCurrency: string | undefined;
118120
}): {amount: number; merchant: string} {
119121
if (!isManualDistance || distance === undefined || !unit) {
120122
return {amount: 0, merchant: translate('iou.fieldPending')};
121123
}
122124
const distanceInMeters = DistanceRequestUtils.convertToDistanceInMeters(distance, unit);
123-
const mileageRate = DistanceRequestUtils.getRate({transaction, policy});
125+
const mileageRate = DistanceRequestUtils.getRate({transaction, policy, personalPolicyOutputCurrency});
124126
const amount = DistanceRequestUtils.getDistanceRequestAmount(distanceInMeters, unit, mileageRate?.rate ?? 0);
125127
const merchant = DistanceRequestUtils.getDistanceMerchant(
126128
true,
@@ -248,7 +250,15 @@ function handleMoneyRequestStepDistanceNavigation({
248250

249251
const validWaypoints = !isManualDistance && !isOdometerDistance ? getValidWaypoints(waypoints, true, isGPSDistance) : undefined;
250252

251-
const {amount, merchant} = buildDistanceAmountAndMerchant({isManualDistance, distance, unit, transaction, policy, translate});
253+
const {amount, merchant} = buildDistanceAmountAndMerchant({
254+
isManualDistance,
255+
distance,
256+
unit,
257+
transaction,
258+
policy,
259+
translate,
260+
personalPolicyOutputCurrency: personalOutputCurrency,
261+
});
252262
setMoneyRequestMerchant(transactionID, merchant, false);
253263
const distanceDefaultTaxCode = getDefaultTaxCode(policy, transaction);
254264
const distanceTaxCode = (transaction?.taxCode ? transaction.taxCode : distanceDefaultTaxCode) ?? '';
@@ -443,8 +453,9 @@ function handleMoneyRequestStepDistanceNavigation({
443453
setTransactionReport(transactionID, {reportID: transactionReportID}, true);
444454
// Do not pass transaction and policy so it only updates customUnitRateID without changing distance and distance unit
445455
// as it is set for Manual requests before this function is called and transaction may have
446-
// obsolete customUnit values
447-
setCustomUnitRateID(transactionID, rateID, undefined, undefined);
456+
// obsolete customUnit values. personalPolicyOutputCurrency is intentionally omitted for the same reason:
457+
// without a transaction, setCustomUnitRateID never resolves a rate, so the currency is never read.
458+
setCustomUnitRateID(transactionID, rateID, undefined, undefined, false, undefined);
448459

449460
// Update distance and distance unit in transaction object as it is usually set before this function is called using
450461
// defaultExpensePolicy data which is not accurate in this case as defaultExpensePolicy has autoReporting set to false

src/pages/iou/request/step/IOURequestStepReport/hooks/useReportSelectionActions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ function useReportSelectionActions({
147147
// Clear subrates, and update customUnitID if policy changed for per diem transactions
148148
if (policyChanged && isPerDiemTransaction) {
149149
setCustomUnitID(transaction.transactionID, newCustomUnitID ?? CONST.CUSTOM_UNITS.FAKE_P2P_ID);
150-
setCustomUnitRateID(transaction.transactionID, undefined, transaction, newPolicy);
150+
// personalPolicyOutputCurrency is intentionally omitted: no customUnitRateID is passed, so setCustomUnitRateID never resolves a rate and the currency is never read.
151+
setCustomUnitRateID(transaction.transactionID, undefined, transaction, newPolicy, false, undefined);
151152
clearSubrates(transaction.transactionID);
152153

153154
const newChatReportID = reportOrDraftReportFromValue?.chatReportID ?? reportIDFromRoute;

src/pages/iou/request/step/IOURequestStepUpgrade.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ function IOURequestStepUpgrade({
181181
const shouldKeepOriginalReport = isTrack || iouType === CONST.IOU.TYPE.SPLIT_EXPENSE;
182182
if (!shouldKeepOriginalReport) {
183183
setTransactionReport(transactionID, {reportID: expenseReportID}, true);
184-
// Let the confirmation step decide the distance rate because policy data is not fully available at this step
185-
setCustomUnitRateID(transactionID, '-1', undefined, undefined);
184+
// Let the confirmation step decide the distance rate because policy data is not fully available at this step.
185+
// personalPolicyOutputCurrency is intentionally omitted: no transaction is passed, so setCustomUnitRateID never resolves a rate and the currency is never read.
186+
setCustomUnitRateID(transactionID, '-1', undefined, undefined, false, undefined);
186187
Navigation.setParams({reportID: expenseReportID});
187188
}
188189

tests/unit/DistanceRequestUtilsTest.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,47 @@ describe('DistanceRequestUtils', () => {
390390
const result = DistanceRequestUtils.getRate({policy: FAKE_POLICY, transaction});
391391
expect(result.customUnitRateID).toBeUndefined();
392392
});
393+
394+
describe('output currency resolution', () => {
395+
// getRate resolves the currency as `policy.outputCurrency ?? personalPolicyOutputCurrency ?? personal policy ?? USD`.
396+
// Every caller that threads personalPolicyOutputCurrency relies on this precedence, and it's what lets the
397+
// getPersonalPolicy() fallback be removed later: a caller that passes the currency must get the same result.
398+
// A non-P2P rate ID that doesn't resolve to any policy rate + `isMovingTransactionFromTrackExpense` forces the
399+
// mileage rate to be undefined, so the returned `currency` falls through to the resolved policy currency.
400+
const unresolvedRateTransaction = {
401+
...createRandomTransaction(1),
402+
comment: {customUnit: {customUnitRateID: 'nonexistent-rate-id'}},
403+
} as Transaction;
404+
405+
it('uses personalPolicyOutputCurrency when no policy currency is available', () => {
406+
const result = DistanceRequestUtils.getRate({
407+
transaction: unresolvedRateTransaction,
408+
policy: undefined,
409+
isMovingTransactionFromTrackExpense: true,
410+
personalPolicyOutputCurrency: 'EUR',
411+
});
412+
expect(result.currency).toBe('EUR');
413+
});
414+
415+
it('prefers the policy outputCurrency over personalPolicyOutputCurrency', () => {
416+
const result = DistanceRequestUtils.getRate({
417+
transaction: unresolvedRateTransaction,
418+
policy: {...FAKE_POLICY, outputCurrency: 'GBP'},
419+
isMovingTransactionFromTrackExpense: true,
420+
personalPolicyOutputCurrency: 'EUR',
421+
});
422+
expect(result.currency).toBe('GBP');
423+
});
424+
425+
it('falls back to USD when neither a policy currency nor personalPolicyOutputCurrency is provided', () => {
426+
const result = DistanceRequestUtils.getRate({
427+
transaction: unresolvedRateTransaction,
428+
policy: undefined,
429+
isMovingTransactionFromTrackExpense: true,
430+
});
431+
expect(result.currency).toBe(CONST.CURRENCY.USD);
432+
});
433+
});
393434
});
394435

395436
describe('getRateForP2P', () => {

0 commit comments

Comments
 (0)