-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[Payment due @daledah] Load single default P2P mileage rate #84791
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
Changes from all commits
7adaf4c
23f03e3
ba039cd
c782f2c
ce02083
189c558
7d3b1d1
a81ff36
c8c6799
c4b82e9
74048dd
816b22e
2d0ba40
50318b8
c6f4595
017e001
01db696
138b22d
48723e3
4fc3083
f3e0771
4762bb5
8cf5b4e
b8e01f7
d78835a
02b88a2
ed9ee57
f95b4cb
cb08f95
f8ce1ec
58af665
c054ec1
2e19313
19a8693
00ebc42
f5cf791
611a26a
6de9120
4b7896a
56e75b2
3a64479
331a602
335966a
9610209
4fa91a4
7b43d16
046eb43
39502d3
082fdd3
05bcbac
fdc5429
5d91232
15e1ccb
78a6ed7
9e4b9c6
c0c073a
8b0d7a3
740bb76
ff667f0
181ede3
722d337
79aaf84
9440958
5590a1b
a5e2427
153f0b2
c59d552
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,9 +5,11 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider'; | |
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type {LastSelectedDistanceRates, OnyxInputOrEntry, Transaction} from '@src/types/onyx'; | ||
| import type DefaultP2PMileageRate from '@src/types/onyx/DefaultP2PMileageRate'; | ||
| import type {Unit} from '@src/types/onyx/Policy'; | ||
| import type Policy from '@src/types/onyx/Policy'; | ||
| import {isEmptyObject} from '@src/types/utils/EmptyObject'; | ||
| import getStoredDefaultP2PMileageRate from './getStoredDefaultP2PMileageRate'; | ||
| import {replaceAllDigits} from './MoneyRequestUtils'; | ||
| import {getDistanceRateCustomUnit, getDistanceRateCustomUnitRate, getUnitRateValue} from './PolicyUtils'; | ||
| import {getCurrency, getRateID, isCustomUnitRateIDForP2P, isExpenseUnreported} from './TransactionUtils'; | ||
|
|
@@ -33,6 +35,7 @@ Onyx.connectWithoutView({ | |
|
|
||
| const METERS_TO_KM = 0.001; // 1 kilometer is 1000 meters | ||
| const METERS_TO_MILES = 0.000621371; // There are approximately 0.000621371 miles in a meter | ||
| const DEFAULT_P2P_RATE_CENTS_PER_MILE = 67; | ||
|
|
||
| function getMileageRates(policy: OnyxInputOrEntry<Policy>, includeDisabledRates = false, selectedRateID?: string): Record<string, MileageRate> { | ||
| const mileageRates: Record<string, MileageRate> = {}; | ||
|
|
@@ -276,13 +279,6 @@ function getDistanceMerchant( | |
| return `${distanceInUnits} ${CONST.DISTANCE_MERCHANT_SEPARATOR} ${ratePerUnit}`; | ||
| } | ||
|
|
||
| function ensureRateDefined(rate: number | undefined): asserts rate is number { | ||
| if (rate !== undefined) { | ||
| return; | ||
| } | ||
| throw new Error('All default P2P rates should have a rate defined'); | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves the rate and unit for a P2P distance expense for a given currency. | ||
| * | ||
|
|
@@ -292,16 +288,16 @@ function ensureRateDefined(rate: number | undefined): asserts rate is number { | |
| * @returns The rate and unit in MileageRate object. | ||
| */ | ||
| function getRateForP2P(currency: string, transaction: OnyxEntry<Transaction>): MileageRate { | ||
| const currencyWithExistingRate = CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currency] ? currency : CONST.CURRENCY.USD; | ||
| const mileageRate = CONST.CURRENCY_TO_DEFAULT_MILEAGE_RATE[currencyWithExistingRate]; | ||
| ensureRateDefined(mileageRate.rate); | ||
| const defaultRate = getStoredDefaultP2PMileageRate(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because this reads the new Onyx key through a Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm yes this is a known limitation of the approach, and maybe a good reason to load it in OpenApp instead. I think in practice though the rate will usually load before a user reaches the screen where it displays. |
||
| const p2pRate: DefaultP2PMileageRate = defaultRate ?? {rate: DEFAULT_P2P_RATE_CENTS_PER_MILE, unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}; | ||
| const rate = transaction && getCurrency(transaction) === currency ? (transaction.comment?.customUnit?.defaultP2PRate ?? p2pRate.rate) : p2pRate.rate; | ||
|
|
||
| // Ensure the rate is updated when the currency changes, otherwise use the stored rate | ||
| const rate = getCurrency(transaction) === currency ? (transaction?.comment?.customUnit?.defaultP2PRate ?? mileageRate.rate) : mileageRate.rate; | ||
| // If a distance expense is being edited, the defaultP2PRate may not have been loaded yet, so use data from the existing transaction. | ||
| const fallbackUnit = transaction?.comment?.customUnit?.distanceUnit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES; | ||
| return { | ||
| ...mileageRate, | ||
| currency: currencyWithExistingRate, | ||
| rate, | ||
| unit: defaultRate ? p2pRate.unit : fallbackUnit, | ||
| currency: defaultRate ? currency : getCurrency(transaction), | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // This module is used to load the default P2P mileage rate for a user based on their personal policy outputCurrency (default / reporting currency). | ||
| // Whenever a user starts the "Track distance" flow the getDefaultP2PMileageRate action will fetch the rate and unit from the hard coded mapping stored in Auth | ||
| // (CURRENCY_TO_DEFAULT_MILEAGE_RATE), via the API read command GetDefaultP2PMileageRate. | ||
| // The rate will be stored in Onyx and loaded into a variable here via Onyx.connectWithoutView. Normally useOnyx should be used instead, but because | ||
| // the default P2P mileage rate is used across many library functions an exception is allowed to prevent having to pass the value through many functions | ||
| // across the codebase. | ||
| // DO NOT use this pattern for other Onyx data unless you get authorization from the internal Expensify team in Slack. | ||
| import Onyx from 'react-native-onyx'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type DefaultP2PMileageRate from '@src/types/onyx/DefaultP2PMileageRate'; | ||
|
|
||
| let defaultP2PMileageRate: DefaultP2PMileageRate | undefined; | ||
| Onyx.connectWithoutView({ | ||
| key: ONYXKEYS.DEFAULT_P2P_MILEAGE_RATE, | ||
| callback: (value) => { | ||
| defaultP2PMileageRate = value; | ||
| }, | ||
| }); | ||
|
|
||
| function getStoredDefaultP2PMileageRate() { | ||
| return defaultP2PMileageRate; | ||
| } | ||
|
|
||
| export default getStoredDefaultP2PMileageRate; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import type {Unit} from './Policy'; | ||
|
|
||
| /** Default P2P mileage rate fetched from Auth for the user's personal policy outputCurrency (default / report currency) */ | ||
| type DefaultP2PMileageRate = { | ||
| /** Rate in cents per unit (e.g. 67 = $0.67/mile) */ | ||
| rate: number; | ||
|
|
||
| /** Distance unit: "mi" or "km" */ | ||
| unit: Unit; | ||
| }; | ||
|
|
||
| export default DefaultP2PMileageRate; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import Onyx from 'react-native-onyx'; | ||
| import {getDefaultP2PMileageRate} from '@libs/actions/Transaction'; | ||
| import * as API from '@libs/API'; | ||
| import {READ_COMMANDS} from '@libs/API/types'; | ||
| import DistanceRequestUtils from '@libs/DistanceRequestUtils'; | ||
| import getStoredDefaultP2PMileageRate from '@libs/getStoredDefaultP2PMileageRate'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import createRandomTransaction from '../utils/collections/transaction'; | ||
| import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; | ||
|
|
||
| describe('Default P2P mileage rate', () => { | ||
| beforeAll(() => { | ||
| Onyx.init({ | ||
| keys: ONYXKEYS, | ||
| }); | ||
| }); | ||
|
|
||
| beforeEach(() => { | ||
| return Onyx.clear().then(waitForBatchedUpdates); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| jest.restoreAllMocks(); | ||
| }); | ||
|
|
||
| describe('getDefaultP2PMileageRate', () => { | ||
| it('calls API.read with GetDefaultP2PMileageRate', () => { | ||
| const readSpy = jest.spyOn(API, 'read').mockImplementation(() => {}); | ||
|
|
||
| getDefaultP2PMileageRate(); | ||
|
|
||
| expect(readSpy).toHaveBeenCalledWith(READ_COMMANDS.GET_DEFAULT_P2P_MILEAGE_RATE, null); | ||
| }); | ||
| }); | ||
|
|
||
| describe('getRateForP2P', () => { | ||
| it('falls back to USD 67¢/mile when no rate has been stored in Onyx', () => { | ||
| const result = DistanceRequestUtils.getRateForP2P('EUR', undefined); | ||
|
|
||
| expect(result).toEqual({rate: 67, unit: 'mi', currency: CONST.CURRENCY.USD}); | ||
| }); | ||
|
|
||
| it('uses the stored Onyx rate with the caller currency once a rate is available', async () => { | ||
| await Onyx.set(ONYXKEYS.DEFAULT_P2P_MILEAGE_RATE, {rate: 5500, unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS}); | ||
| await waitForBatchedUpdates(); | ||
|
|
||
| expect(getStoredDefaultP2PMileageRate()).toEqual({rate: 5500, unit: 'km'}); | ||
|
|
||
| const result = DistanceRequestUtils.getRateForP2P('EUR', undefined); | ||
|
|
||
| expect(result).toEqual({rate: 5500, unit: 'km', currency: 'EUR'}); | ||
| }); | ||
|
|
||
| it('uses the transaction defaultP2PRate when the transaction currency matches', async () => { | ||
| await Onyx.set(ONYXKEYS.DEFAULT_P2P_MILEAGE_RATE, {rate: 5500, unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES}); | ||
| await waitForBatchedUpdates(); | ||
|
|
||
| const transaction = { | ||
| ...createRandomTransaction(1), | ||
| currency: CONST.CURRENCY.USD, | ||
| comment: {customUnit: {defaultP2PRate: 99}}, | ||
| }; | ||
|
|
||
| const result = DistanceRequestUtils.getRateForP2P(CONST.CURRENCY.USD, transaction); | ||
|
|
||
| expect(result).toEqual({rate: 99, unit: 'mi', currency: CONST.CURRENCY.USD}); | ||
| }); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.