Skip to content
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

Fix incorrect tax rate selected with distance rate #43052

Merged
merged 9 commits into from
Jun 6, 2024
Merged
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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For distance policy?.customUnits && customUnitID are always set, so I found it redundant and hence removed and cleaned up the code

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
Loading