From 59897871ec399a81ef6475ccb4c837e6f14b168b Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 31 May 2024 14:48:40 +0530 Subject: [PATCH 1/3] Set taxRate and taxAmount when amount is not available offline --- src/pages/iou/request/step/IOURequestStepDistanceRate.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 80ddd6567de8..588e54f8347e 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -78,10 +78,10 @@ function IOURequestStepDistanceRate({ const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList; function selectDistanceRate(customUnitRateID: string) { - if (transaction?.amount && policy?.customUnits && customUnitID && shouldShowTax) { + if (policy?.customUnits && customUnitID && shouldShowTax) { const taxClaimablePercentage = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxClaimablePercentage ?? 0; const taxRateExternalID = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxRateExternalID ?? ''; - const taxableAmount = -1 * transaction.amount * taxClaimablePercentage; + const taxableAmount = -1 * (transaction?.amount ?? 0) * taxClaimablePercentage; const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); From 05a105ccf3a42369b8c8f23fe33c0a297d639678 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 31 May 2024 14:53:21 +0530 Subject: [PATCH 2/3] Fix wrong tax amount bug and wrap code in a function --- .../MoneyRequestConfirmationList.tsx | 10 ++++--- src/libs/DistanceRequestUtils.ts | 30 ++++++++++++++++++- .../step/IOURequestStepDistanceRate.tsx | 10 +++---- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b18a98b13304..dff80f45e3f9 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -178,9 +178,11 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData; -const getTaxAmount = (transaction: OnyxEntry, policy: OnyxEntry) => { +const getTaxAmount = (transaction: OnyxEntry, policy: OnyxEntry, isDistanceRequest: boolean) => { + if (isDistanceRequest) { + return DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? ''); + } const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; return TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); }; @@ -371,7 +373,7 @@ function MoneyRequestConfirmationList({ // Calculate and set tax amount in transaction draft useEffect(() => { - const taxAmount = getTaxAmount(transaction, policy).toString(); + const taxAmount = getTaxAmount(transaction, policy, isDistanceRequest).toString(); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); if (transaction?.taxAmount && previousTransactionAmount === transaction?.amount && previousTransactionCurrency === transaction?.currency) { @@ -379,7 +381,7 @@ function MoneyRequestConfirmationList({ } IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); - }, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency]); + }, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency, isDistanceRequest]); // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again if (isEditingSplitBill && didConfirm) { diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 9cb48534214e..06e8b6d2eee9 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -4,13 +4,14 @@ 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, Report} from '@src/types/onyx'; +import type {LastSelectedDistanceRates, Report, Transaction} from '@src/types/onyx'; import type {Unit} from '@src/types/onyx/Policy'; import type Policy from '@src/types/onyx/Policy'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; import * as ReportUtils from './ReportUtils'; +import * as TransactionUtils from "@libs/TransactionUtils"; type MileageRate = { customUnitRateID?: string; @@ -266,6 +267,31 @@ function getCustomUnitRateID(reportID: string) { return customUnitRateID; } +function getCustomUnit(policy: OnyxEntry) { + return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); +} + +function calculateTaxAmount(policy: OnyxEntry, transaction: OnyxEntry, customUnitRateID: string) { + const distanceUnit = getCustomUnit(policy); + const customUnitID = distanceUnit?.customUnitID; + if (!policy?.customUnits || !customUnitID) { + return 0; + } + const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID]; + const unit = policy?.customUnits[customUnitID]?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES; + const rate = policyCustomUnitRate?.rate ?? 0; + const distance = TransactionUtils.getDistance(transaction); + const amount = getDistanceRequestAmount(distance, unit, rate); + const taxClaimablePercentage = policyCustomUnitRate.attributes?.taxClaimablePercentage ?? 0; + const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? ''; + const taxableAmount = amount * taxClaimablePercentage; + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; + console.debug(amount); + console.debug(taxPercentage); + console.debug(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); + return TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount); +} + export default { getDefaultMileageRate, getDistanceMerchant, @@ -276,6 +302,8 @@ export default { getRateForP2P, getCustomUnitRateID, convertToDistanceInMeters, + calculateTaxAmount, + getCustomUnit, }; export type {MileageRate}; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 588e54f8347e..b3af084d014e 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -50,7 +50,7 @@ function IOURequestStepDistanceRate({ const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceUnit = DistanceRequestUtils.getCustomUnit(policy); const customUnitID = distanceUnit?.customUnitID; const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); @@ -79,11 +79,9 @@ function IOURequestStepDistanceRate({ function selectDistanceRate(customUnitRateID: string) { if (policy?.customUnits && customUnitID && shouldShowTax) { - const taxClaimablePercentage = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxClaimablePercentage ?? 0; - const taxRateExternalID = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxRateExternalID ?? ''; - const taxableAmount = -1 * (transaction?.amount ?? 0) * taxClaimablePercentage; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; - const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); + const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID]; + const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? ''; + const taxAmount = CurrencyUtils.convertToBackendAmount(DistanceRequestUtils.calculateTaxAmount(policy, transaction, customUnitRateID)); IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID); } From c83bc2908193534fb89e2e6eccb634c60402274e Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Fri, 31 May 2024 15:50:18 +0530 Subject: [PATCH 3/3] Fix lint and typechecks and refactor code --- src/libs/DistanceRequestUtils.ts | 16 ++++------------ src/libs/PolicyUtils.ts | 10 +++++++++- src/libs/actions/Policy/Policy.ts | 8 ++++---- .../request/step/IOURequestStepDistanceRate.tsx | 4 ++-- .../WorkspaceRateAndUnitPage/InitialPage.tsx | 5 +++-- .../WorkspaceRateAndUnitPage/RatePage.tsx | 5 +++-- .../WorkspaceRateAndUnitPage/UnitPage.tsx | 5 +++-- .../reimburse/WorkspaceReimburseView.tsx | 2 +- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 06e8b6d2eee9..230ad8deb130 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -11,7 +11,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; import * as ReportUtils from './ReportUtils'; -import * as TransactionUtils from "@libs/TransactionUtils"; +import * as TransactionUtils from './TransactionUtils'; type MileageRate = { customUnitRateID?: string; @@ -54,7 +54,7 @@ function getDefaultMileageRate(policy: OnyxEntry | EmptyObject): Mileage return null; } - const distanceUnit = Object.values(policy.customUnits).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceUnit = PolicyUtils.getCustomUnit(policy); if (!distanceUnit?.rates) { return null; } @@ -194,7 +194,7 @@ function getMileageRates(policy: OnyxEntry): Record return mileageRates; } - const distanceUnit = Object.values(policy.customUnits).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceUnit = PolicyUtils.getCustomUnit(policy); if (!distanceUnit?.rates) { return mileageRates; } @@ -267,12 +267,8 @@ function getCustomUnitRateID(reportID: string) { return customUnitRateID; } -function getCustomUnit(policy: OnyxEntry) { - return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); -} - function calculateTaxAmount(policy: OnyxEntry, transaction: OnyxEntry, customUnitRateID: string) { - const distanceUnit = getCustomUnit(policy); + const distanceUnit = PolicyUtils.getCustomUnit(policy); const customUnitID = distanceUnit?.customUnitID; if (!policy?.customUnits || !customUnitID) { return 0; @@ -286,9 +282,6 @@ function calculateTaxAmount(policy: OnyxEntry, transaction: OnyxEntry return numValue.toFixed(CONST.CUSTOM_UNITS.RATE_DECIMALS); } +/** + * Retrieves the distance custom unit object for the given policy + */ +function getCustomUnit(policy: OnyxEntry | EmptyObject) { + return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); +} + function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string): string { const numValue = getNumericValue(value, toLocaleDigit); if (Number.isNaN(numValue)) { @@ -272,7 +279,7 @@ function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { } function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry, isDistanceRequest: boolean): boolean { - const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceUnit = getCustomUnit(policy); const customUnitID = distanceUnit?.customUnitID ?? 0; const isPolicyTaxTrackingEnabled = isPolicyExpenseChat && policy?.tax?.trackingEnabled; const isTaxEnabledForDistance = isPolicyTaxTrackingEnabled && policy?.customUnits?.[customUnitID]?.attributes?.taxEnabled; @@ -483,6 +490,7 @@ export { findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroBankAccountsWithDefaultSelect, + getCustomUnit, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index fc2a8ad7970e..9e019be661c4 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1566,14 +1566,14 @@ function clearAvatarErrors(policyID: string) { */ function updateGeneralSettings(policyID: string, name: string, currencyValue?: string) { const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const customUnitID = distanceUnit?.customUnitID; - const currency = currencyValue ?? policy?.outputCurrency ?? CONST.CURRENCY.USD; - if (!policy) { return; } + const distanceUnit = PolicyUtils.getCustomUnit(policy); + const customUnitID = distanceUnit?.customUnitID; + const currency = currencyValue ?? policy?.outputCurrency ?? CONST.CURRENCY.USD; + const currentRates = distanceUnit?.rates ?? {}; const optimisticRates: Record = {}; const finallyRates: Record = {}; diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index b3af084d014e..762db0ab811c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -11,7 +11,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import type {MileageRate} from '@libs/DistanceRequestUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {isTaxTrackingEnabled} from '@libs/PolicyUtils'; +import {getCustomUnit, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -50,7 +50,7 @@ function IOURequestStepDistanceRate({ const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const distanceUnit = DistanceRequestUtils.getCustomUnit(policy); + const distanceUnit = getCustomUnit(policy); const customUnitID = distanceUnit?.customUnitID; const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx index 6faf23af289e..4e1821c6aad6 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx @@ -13,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; import {getUnitTranslationKey} from '@libs/WorkspacesSettingsUtils'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; @@ -62,7 +63,7 @@ function WorkspaceRateAndUnitPage(props: WorkspaceRateAndUnitPageProps) { }, [props]); const saveUnitAndRate = (newUnit: Unit, newRate: string) => { - const distanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomUnit = PolicyUtils.getCustomUnit(props.policy); if (!distanceCustomUnit) { return; } @@ -82,7 +83,7 @@ function WorkspaceRateAndUnitPage(props: WorkspaceRateAndUnitPageProps) { Policy.updateWorkspaceCustomUnitAndRate(props.policy?.id ?? '', distanceCustomUnit, newCustomUnit, props.policy?.lastModified); }; - const distanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomUnit = PolicyUtils.getCustomUnit(props.policy); const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const unitValue = props.workspaceRateAndUnit?.unit ?? distanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES; diff --git a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx index 9c7eaa82167f..1acd3f501314 100644 --- a/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx +++ b/src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx @@ -10,6 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import {validateRateValue} from '@libs/PolicyDistanceRatesUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections'; @@ -53,10 +54,10 @@ function WorkspaceRatePage(props: WorkspaceRatePageProps) { ); const defaultValue = useMemo(() => { - const defaultDistanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const defaultDistanceCustomUnit = PolicyUtils.getCustomUnit(props.policy); const distanceCustomRate = Object.values(defaultDistanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); return distanceCustomRate?.rate ?? 0; - }, [props.policy?.customUnits]); + }, [props.policy]); return ( { - const defaultDistanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const defaultDistanceCustomUnit = PolicyUtils.getCustomUnit(props.policy); return defaultDistanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES; - }, [props.policy?.customUnits]); + }, [props.policy]); return ( (''); const {isSmallScreenWidth} = useWindowDimensions(); const viewAllReceiptsUrl = `expenses?policyIDList=${policy?.id ?? ''}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`; - const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceCustomUnit = PolicyUtils.getCustomUnit(policy); const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); const {translate, toLocaleDigit} = useLocalize(); const {isOffline} = useNetwork();