diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 40647e33849c..fcb415fc193f 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -25,7 +25,7 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PolicyUtils from '@libs/PolicyUtils'; -import {isTaxTrackingEnabled} from '@libs/PolicyUtils'; +import {getCustomUnitRate, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; @@ -371,17 +371,21 @@ function MoneyRequestConfirmationList({ return; } - let taxAmount; + let taxableAmount: number; + let taxCode: string; if (isDistanceRequest) { - taxAmount = DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? ''); + const customUnitRate = getCustomUnitRate(policy, customUnitRateID); + taxCode = customUnitRate?.attributes?.taxRateExternalID ?? ''; + taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, TransactionUtils.getDistance(transaction)); } else { - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; - taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); + taxableAmount = transaction?.amount ?? 0; + taxCode = transaction?.taxCode ?? TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; } + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; + const taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount); const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits); - }, [policy, shouldShowTax, previousTransactionAmount, previousTransactionCurrency, transaction, isDistanceRequest]); + }, [policy, shouldShowTax, previousTransactionAmount, previousTransactionCurrency, transaction, isDistanceRequest, customUnitRateID]); // 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 7411db493c29..ed4a6a73c9b9 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -4,7 +4,7 @@ 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, Transaction} from '@src/types/onyx'; +import type {LastSelectedDistanceRates, Report} 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'; @@ -12,7 +12,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; import * as ReportUtils from './ReportUtils'; -import * as TransactionUtils from './TransactionUtils'; type MileageRate = { customUnitRateID?: string; @@ -272,22 +271,20 @@ function getCustomUnitRateID(reportID: string) { return customUnitRateID; } -function calculateTaxAmount(policy: OnyxEntry, transaction: OnyxEntry, customUnitRateID: string) { +/** + * Get taxable amount from a specific distance rate, taking into consideration the tax claimable amount configured for the distance rate + */ +function getTaxableAmount(policy: OnyxEntry, customUnitRateID: string, distance: number) { const distanceUnit = PolicyUtils.getCustomUnit(policy); - const customUnitID = distanceUnit?.customUnitID; - if (!policy?.customUnits || !customUnitID) { + const customUnitRate = PolicyUtils.getCustomUnitRate(policy, customUnitRateID); + if (!distanceUnit || !distanceUnit?.customUnitID || !customUnitRate) { 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 unit = distanceUnit?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES; + const rate = customUnitRate?.rate ?? 0; 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) ?? ''; - return TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount); + const taxClaimablePercentage = customUnitRate.attributes?.taxClaimablePercentage ?? 0; + return amount * taxClaimablePercentage; } export default { @@ -300,7 +297,7 @@ export default { getRateForP2P, getCustomUnitRateID, convertToDistanceInMeters, - calculateTaxAmount, + getTaxableAmount, }; export type {MileageRate}; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 321cc5f1c7d0..34ac83405049 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -97,6 +97,14 @@ function getCustomUnit(policy: OnyxEntry | EmptyObject) { return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); } +/** + * Retrieves custom unit rate object from the given customUnitRateID + */ +function getCustomUnitRate(policy: OnyxEntry | EmptyObject, customUnitRateID: string): Rate | EmptyObject { + const distanceUnit = getCustomUnit(policy); + return distanceUnit?.rates[customUnitRateID] ?? {}; +} + function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string): string { const numValue = getNumericValue(value, toLocaleDigit); if (Number.isNaN(numValue)) { @@ -513,6 +521,7 @@ export { getCurrentXeroOrganizationName, getXeroBankAccountsWithDefaultSelect, getCustomUnit, + getCustomUnitRate, sortWorkspacesBySelected, }; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index b230d07715a4..594d47f2d8dd 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -12,7 +12,7 @@ import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; -import {getCleanedTagName} from './PolicyUtils'; +import {getCleanedTagName, getCustomUnitRate} from './PolicyUtils'; let allTransactions: OnyxCollection = {}; Onyx.connect({ @@ -662,7 +662,7 @@ function hasNoticeTypeViolation(transactionID: string, transactionViolations: On } /** - * this is the formulae to calculate tax + * Calculates tax amount from the given expense amount and tax percentage */ function calculateTaxAmount(percentage: string, amount: number) { const divisor = Number(percentage.slice(0, -1)) / 100 + 1; @@ -695,10 +695,16 @@ function getRateID(transaction: OnyxEntry): string | undefined { } /** - * Gets the tax code based on selected currency. - * Returns policy default tax rate if transaction is in policy default currency, otherwise returns foreign default tax rate + * Gets the tax code based on the type of transaction and selected currency. + * If it is distance request, then returns the tax code corresponding to the custom unit rate + * Else returns policy default tax rate if transaction is in policy default currency, otherwise foreign default tax rate */ function getDefaultTaxCode(policy: OnyxEntry, transaction: OnyxEntry, currency?: string | undefined) { + if (isDistanceRequest(transaction)) { + const customUnitRateID = getRateID(transaction) ?? ''; + const customUnitRate = getCustomUnitRate(policy, customUnitRateID); + return customUnitRate?.attributes?.taxRateExternalID ?? ''; + } const defaultExternalID = policy?.taxRates?.defaultExternalID; const foreignTaxDefault = policy?.taxRates?.foreignTaxDefault; return policy?.outputCurrency === (currency ?? getCurrency(transaction)) ? defaultExternalID : foreignTaxDefault; @@ -743,7 +749,7 @@ function getWorkspaceTaxesSettingsName(policy: OnyxEntry, taxCode: strin } /** - * Gets the tax name + * Gets the name corresponding to the taxCode that is displayed to the user */ function getTaxName(policy: OnyxEntry, transaction: OnyxEntry) { const defaultTaxCode = getDefaultTaxCode(policy, transaction); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index dd95e4577442..496a66bef3a8 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -81,10 +81,13 @@ function IOURequestStepConfirmation({ const {windowWidth} = useWindowDimensions(); const {isOffline} = useNetwork(); const [receiptFile, setReceiptFile] = useState(); + const requestType = TransactionUtils.getRequestType(transaction); + const isDistanceRequest = requestType === CONST.IOU.REQUEST_TYPE.DISTANCE; const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; const receiptType = transaction?.receipt?.type; + const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; const transactionTaxAmount = transaction?.taxAmount ?? 0; @@ -108,8 +111,6 @@ function IOURequestStepConfirmation({ }; }, [personalDetails, transaction?.participants, transaction?.splitPayerAccountIDs]); - const requestType = TransactionUtils.getRequestType(transaction); - const headerTitle = useMemo(() => { if (isCategorizingTrackExpense) { return translate('iou.categorize'); @@ -290,7 +291,7 @@ function IOURequestStepConfirmation({ ); const createDistanceRequest = useCallback( - (selectedParticipants: Participant[], trimmedComment: string, customUnitRateID: string) => { + (selectedParticipants: Participant[], trimmedComment: string) => { if (!transaction) { return; } @@ -314,7 +315,7 @@ function IOURequestStepConfirmation({ customUnitRateID, ); }, - [policy, policyCategories, policyTags, report, transaction, transactionTaxCode, transactionTaxAmount], + [policy, policyCategories, policyTags, report, transaction, transactionTaxCode, transactionTaxAmount, customUnitRateID], ); const createTransaction = useCallback( @@ -477,9 +478,8 @@ function IOURequestStepConfirmation({ return; } - if (requestType === CONST.IOU.REQUEST_TYPE.DISTANCE && !isMovingTransactionFromTrackExpense) { - const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; - createDistanceRequest(selectedParticipants, trimmedComment, customUnitRateID); + if (isDistanceRequest && !isMovingTransactionFromTrackExpense) { + createDistanceRequest(selectedParticipants, trimmedComment); return; } @@ -490,7 +490,7 @@ function IOURequestStepConfirmation({ report, iouType, receiptFile, - requestType, + isDistanceRequest, requestMoney, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, @@ -581,7 +581,7 @@ function IOURequestStepConfirmation({ bankAccountRoute={ReportUtils.getBankAccountRoute(report)} iouMerchant={transaction?.merchant} iouCreated={transaction?.created} - isDistanceRequest={requestType === CONST.IOU.REQUEST_TYPE.DISTANCE} + isDistanceRequest={isDistanceRequest} shouldShowSmartScanFields={isMovingTransactionFromTrackExpense ? transaction?.amount !== 0 : requestType !== CONST.IOU.REQUEST_TYPE.SCAN} action={action} payeePersonalDetails={payeePersonalDetails} diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index dda4c862e5fd..f8879a3f4250 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 {getCustomUnit, isTaxTrackingEnabled} from '@libs/PolicyUtils'; +import {getCustomUnitRate, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; @@ -50,8 +50,6 @@ function IOURequestStepDistanceRate({ const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const distanceUnit = getCustomUnit(policy); - const customUnitID = distanceUnit?.customUnitID; const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); @@ -78,10 +76,12 @@ function IOURequestStepDistanceRate({ const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList; function selectDistanceRate(customUnitRateID: string) { - if (policy?.customUnits && customUnitID && shouldShowTax) { - const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID]; + if (shouldShowTax) { + const policyCustomUnitRate = getCustomUnitRate(policy, customUnitRateID); const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? ''; - const taxAmount = CurrencyUtils.convertToBackendAmount(DistanceRequestUtils.calculateTaxAmount(policy, transaction, customUnitRateID)); + const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, TransactionUtils.getDistance(transaction)); + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; + const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); IOU.setMoneyRequestTaxAmount(transactionID, taxAmount); IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID); }