From e3a47e470e170b45bd15549c3badae7810609ca4 Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:27:53 +0200 Subject: [PATCH 1/2] fix: use common function to display rate --- .../MoneyRequestConfirmationList.js | 6 +-- src/libs/DistanceRequestUtils.js | 6 ++- src/libs/PolicyUtils.js | 38 +++++++++++++++++++ .../reimburse/WorkspaceRateAndUnitPage.js | 25 ++---------- .../reimburse/WorkspaceReimburseView.js | 29 ++------------ 5 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index da4e8d69682a..b085da27a2a6 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -184,7 +184,7 @@ function MoneyRequestConfirmationList(props) { // Destructure functions from props to pass it as a dependecy to useCallback/useMemo hooks. // Prop functions pass props itself as a "this" value to the function which means they change every time props change. const {onSendMoney, onConfirm, onSelectParticipant, transaction} = props; - const {translate} = useLocalize(); + const {translate, toLocaleDigit} = useLocalize(); // A flag and a toggler for showing the rest of the form fields const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false); @@ -328,9 +328,9 @@ function MoneyRequestConfirmationList(props) { if (!props.isDistanceRequest) { return; } - const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate); + const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate, toLocaleDigit); IOU.setMoneyRequestMerchant(distanceMerchant); - }, [hasRoute, distance, unit, rate, currency, translate, props.isDistanceRequest]); + }, [hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, props.isDistanceRequest]); /** * @param {Object} option diff --git a/src/libs/DistanceRequestUtils.js b/src/libs/DistanceRequestUtils.js index 34fa14163835..7744ee3d8bb1 100644 --- a/src/libs/DistanceRequestUtils.js +++ b/src/libs/DistanceRequestUtils.js @@ -1,6 +1,7 @@ import _ from 'underscore'; import CONST from '../CONST'; import * as CurrencyUtils from './CurrencyUtils'; +import * as PolicyUtils from './PolicyUtils'; /** * Retrieves the default mileage rate based on a given policy. @@ -79,16 +80,17 @@ const getRoundedDistanceInUnits = (distanceInMeters, unit) => { * @param {Number} rate Expensable amount allowed per unit * @param {String} currency The currency associated with the rate * @param {Function} translate Translate function + * @param {Function} toLocaleDigit Function to convert to localized digit * @returns {String} A string that describes the distance traveled and the rate used for expense calculation */ -const getDistanceMerchant = (hasRoute, distanceInMeters, unit, rate, currency, translate) => { +const getDistanceMerchant = (hasRoute, distanceInMeters, unit, rate, currency, translate, toLocaleDigit) => { const distanceInUnits = hasRoute ? getRoundedDistanceInUnits(distanceInMeters, unit) : translate('common.tbd'); const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); const unitString = distanceInUnits === 1 ? singularDistanceUnit : distanceUnit; - const ratePerUnit = rate * 0.01; + const ratePerUnit = PolicyUtils.getUnitRateValue({rate}, toLocaleDigit); const currencySymbol = CurrencyUtils.getCurrencySymbol(currency) || `${currency} `; return `${distanceInUnits} ${unitString} @ ${currencySymbol}${ratePerUnit} / ${singularDistanceUnit}`; diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 164f284a4ef5..85031c0464eb 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -57,6 +57,42 @@ function hasCustomUnitsError(policy) { return !_.isEmpty(_.pick(lodashGet(policy, 'customUnits', {}), 'errors')); } +/** + * @param {Number} value + * @param {Function} toLocaleDigit + * @returns {Number} + */ +function getNumericValue(value, toLocaleDigit) { + const numValue = parseFloat(value.toString().replace(toLocaleDigit('.'), '.')); + if (Number.isNaN(numValue)) { + return NaN; + } + return numValue.toFixed(3); +} + +/** + * @param {Number} value + * @param {Function} toLocaleDigit + * @returns {String} + */ +function getRateDisplayValue(value, toLocaleDigit) { + const numValue = getNumericValue(value, toLocaleDigit); + if (Number.isNaN(numValue)) { + return ''; + } + return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.length); +} + +/** + * @param {Object} customUnitRate + * @param {Number} customUnitRate.rate + * @param {Function} toLocaleDigit + * @returns {String} + */ +function getUnitRateValue(customUnitRate, toLocaleDigit) { + return getRateDisplayValue(lodashGet(customUnitRate, 'rate', 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET, toLocaleDigit); +} + /** * Get the brick road indicator status for a policy. The policy has an error status if there is a policy member error, a custom unit error or a field error. * @@ -172,6 +208,8 @@ export { hasPolicyError, hasPolicyErrorFields, hasCustomUnitsError, + getNumericValue, + getUnitRateValue, getPolicyBrickRoadIndicatorStatus, shouldShowPolicy, isExpensifyTeam, diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js index e551e0d6d1b9..fc862234515a 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage.js @@ -7,6 +7,7 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal import styles from '../../../styles/styles'; import compose from '../../../libs/compose'; import * as Policy from '../../../libs/actions/Policy'; +import * as PolicyUtils from '../../../libs/PolicyUtils'; import CONST from '../../../CONST'; import Picker from '../../../components/Picker'; import TextInput from '../../../components/TextInput'; @@ -35,10 +36,6 @@ class WorkspaceRateAndUnitPage extends React.Component { this.validate = this.validate.bind(this); } - getUnitRateValue(customUnitRate) { - return this.getRateDisplayValue(lodashGet(customUnitRate, 'rate', 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET); - } - getUnitItems() { return [ {label: this.props.translate('workspace.reimburse.kilometers'), value: CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS}, @@ -46,22 +43,6 @@ class WorkspaceRateAndUnitPage extends React.Component { ]; } - getRateDisplayValue(value) { - const numValue = this.getNumericValue(value); - if (Number.isNaN(numValue)) { - return ''; - } - return numValue.toString().replace('.', this.props.toLocaleDigit('.')).substring(0, value.length); - } - - getNumericValue(value) { - const numValue = parseFloat(value.toString().replace(',', '.')); - if (Number.isNaN(numValue)) { - return NaN; - } - return numValue.toFixed(3); - } - saveUnitAndRate(unit, rate) { const distanceCustomUnit = _.find(lodashGet(this.props, 'policy.customUnits', {}), (u) => u.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); if (!distanceCustomUnit) { @@ -70,7 +51,7 @@ class WorkspaceRateAndUnitPage extends React.Component { const currentCustomUnitRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (r) => r.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const unitID = lodashGet(distanceCustomUnit, 'customUnitID', ''); const unitName = lodashGet(distanceCustomUnit, 'name', ''); - const rateNumValue = this.getNumericValue(rate); + const rateNumValue = PolicyUtils.getNumericValue(rate, this.props.toLocaleDigit); const newCustomUnit = { customUnitID: unitID, @@ -137,7 +118,7 @@ class WorkspaceRateAndUnitPage extends React.Component { accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} inputID="rate" containerStyles={[styles.mt4]} - defaultValue={this.getUnitRateValue(distanceCustomRate)} + defaultValue={PolicyUtils.getUnitRateValue(distanceCustomRate, this.props.toLocaleDigit)} label={this.props.translate('workspace.reimburse.trackDistanceRate')} accessibilityLabel={this.props.translate('workspace.reimburse.trackDistanceRate')} placeholder={lodashGet(this.props, 'policy.outputCurrency', CONST.CURRENCY.USD)} diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseView.js b/src/pages/workspace/reimburse/WorkspaceReimburseView.js index 6a4a3ecc207b..206d627e72bd 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseView.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseView.js @@ -16,6 +16,7 @@ import CopyTextToClipboard from '../../../components/CopyTextToClipboard'; import * as Link from '../../../libs/actions/Link'; import compose from '../../../libs/compose'; import * as Policy from '../../../libs/actions/Policy'; +import * as PolicyUtils from '../../../libs/PolicyUtils'; import CONST from '../../../CONST'; import ROUTES from '../../../ROUTES'; import ONYXKEYS from '../../../ONYXKEYS'; @@ -71,39 +72,15 @@ function WorkspaceReimburseView(props) { const distanceCustomRate = _.find(lodashGet(distanceCustomUnit, 'rates', {}), (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const {translate, toLocaleDigit} = props; - const getNumericValue = useCallback( - (value) => { - const numValue = parseFloat(value.toString().replace(toLocaleDigit('.'), '.')); - if (Number.isNaN(numValue)) { - return NaN; - } - return numValue.toFixed(3); - }, - [toLocaleDigit], - ); - - const getRateDisplayValue = useCallback( - (value) => { - const numValue = getNumericValue(value); - if (Number.isNaN(numValue)) { - return ''; - } - return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.length); - }, - [getNumericValue, toLocaleDigit], - ); - - const getRateLabel = useCallback((customUnitRate) => getRateDisplayValue(lodashGet(customUnitRate, 'rate', 0) / CONST.POLICY.CUSTOM_UNIT_RATE_BASE_OFFSET), [getRateDisplayValue]); - const getUnitLabel = useCallback((value) => translate(`common.${value}`), [translate]); const getCurrentRatePerUnitLabel = useCallback(() => { const customUnitRate = _.find(lodashGet(distanceCustomUnit, 'rates', '{}'), (rate) => rate && rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const currentUnit = getUnitLabel(lodashGet(distanceCustomUnit, 'attributes.unit', CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES)); - const currentRate = getRateLabel(customUnitRate); + const currentRate = PolicyUtils.getUnitRateValue(customUnitRate, toLocaleDigit); const perWord = translate('common.per'); return `${currentRate} ${perWord} ${currentUnit}`; - }, [translate, distanceCustomUnit, getUnitLabel, getRateLabel]); + }, [translate, distanceCustomUnit, toLocaleDigit, getUnitLabel]); const fetchData = useCallback(() => { // Instead of setting the reimbursement account loading within the optimistic data of the API command, use a separate action so that the Onyx value is updated right away. From 2fc47382c4979e4e78682c2420bae30a3f0e956e Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:11:12 +0200 Subject: [PATCH 2/2] refactor: added constant --- src/CONST.ts | 1 + src/libs/PolicyUtils.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index dd192f1b257c..37630d33d72a 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1140,6 +1140,7 @@ const CONST = { DISTANCE_UNIT_KILOMETERS: 'km', MILEAGE_IRS_RATE: 0.655, DEFAULT_RATE: 'Default Rate', + RATE_DECIMALS: 3, }, TERMS: { diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 85031c0464eb..eed2d5617788 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -67,7 +67,7 @@ function getNumericValue(value, toLocaleDigit) { if (Number.isNaN(numValue)) { return NaN; } - return numValue.toFixed(3); + return numValue.toFixed(CONST.CUSTOM_UNITS.RATE_DECIMALS); } /**