Skip to content

Commit

Permalink
Merge pull request #43052 from Expensify/monil-fixDistanceTaxRateBug
Browse files Browse the repository at this point in the history
Fix incorrect tax rate selected with distance rate
  • Loading branch information
grgia authored Jun 6, 2024
2 parents 8309d4b + 3744ffb commit 4be277c
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 42 deletions.
18 changes: 11 additions & 7 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down
27 changes: 12 additions & 15 deletions src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +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, 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';
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;
Expand Down Expand Up @@ -272,22 +271,20 @@ function getCustomUnitRateID(reportID: string) {
return customUnitRateID;
}

function calculateTaxAmount(policy: OnyxEntry<Policy>, transaction: OnyxEntry<Transaction>, 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<Policy>, 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 {
Expand All @@ -300,7 +297,7 @@ export default {
getRateForP2P,
getCustomUnitRateID,
convertToDistanceInMeters,
calculateTaxAmount,
getTaxableAmount,
};

export type {MileageRate};
9 changes: 9 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ function getCustomUnit(policy: OnyxEntry<Policy> | 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<Policy> | 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)) {
Expand Down Expand Up @@ -513,6 +521,7 @@ export {
getCurrentXeroOrganizationName,
getXeroBankAccountsWithDefaultSelect,
getCustomUnit,
getCustomUnitRate,
sortWorkspacesBySelected,
};

Expand Down
16 changes: 11 additions & 5 deletions src/libs/TransactionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Transaction> = {};
Onyx.connect({
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -695,10 +695,16 @@ function getRateID(transaction: OnyxEntry<Transaction>): 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<Policy>, transaction: OnyxEntry<Transaction>, 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;
Expand Down Expand Up @@ -743,7 +749,7 @@ function getWorkspaceTaxesSettingsName(policy: OnyxEntry<Policy>, taxCode: strin
}

/**
* Gets the tax name
* Gets the name corresponding to the taxCode that is displayed to the user
*/
function getTaxName(policy: OnyxEntry<Policy>, transaction: OnyxEntry<Transaction>) {
const defaultTaxCode = getDefaultTaxCode(policy, transaction);
Expand Down
18 changes: 9 additions & 9 deletions src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ function IOURequestStepConfirmation({
const {windowWidth} = useWindowDimensions();
const {isOffline} = useNetwork();
const [receiptFile, setReceiptFile] = useState<Receipt>();
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;
Expand All @@ -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');
Expand Down Expand Up @@ -290,7 +291,7 @@ function IOURequestStepConfirmation({
);

const createDistanceRequest = useCallback(
(selectedParticipants: Participant[], trimmedComment: string, customUnitRateID: string) => {
(selectedParticipants: Participant[], trimmedComment: string) => {
if (!transaction) {
return;
}
Expand All @@ -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(
Expand Down Expand Up @@ -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;
}

Expand All @@ -490,7 +490,7 @@ function IOURequestStepConfirmation({
report,
iouType,
receiptFile,
requestType,
isDistanceRequest,
requestMoney,
currentUserPersonalDetails.login,
currentUserPersonalDetails.accountID,
Expand Down Expand Up @@ -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}
Expand Down
12 changes: 6 additions & 6 deletions src/pages/iou/request/step/IOURequestStepDistanceRate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
Expand Down

0 comments on commit 4be277c

Please sign in to comment.