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 distance tax rate and amount not updating offline and inaccurate tax amount and refactor code #42909

Merged
merged 4 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,11 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps &

type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData;

const getTaxAmount = (transaction: OnyxEntry<OnyxTypes.Transaction>, policy: OnyxEntry<OnyxTypes.Policy>) => {
const getTaxAmount = (transaction: OnyxEntry<OnyxTypes.Transaction>, policy: OnyxEntry<OnyxTypes.Policy>, 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);
};
Expand Down Expand Up @@ -371,15 +373,15 @@ 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) {
return IOU.setMoneyRequestTaxAmount(transactionID, transaction?.taxAmount ?? 0, true);
}

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) {
Expand Down
26 changes: 23 additions & 3 deletions src/libs/DistanceRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 './TransactionUtils';

type MileageRate = {
customUnitRateID?: string;
Expand Down Expand Up @@ -53,7 +54,7 @@ function getDefaultMileageRate(policy: OnyxEntry<Policy> | 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;
}
Expand Down Expand Up @@ -193,7 +194,7 @@ function getMileageRates(policy: OnyxEntry<Policy>): Record<string, MileageRate>
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;
}
Expand Down Expand Up @@ -266,6 +267,24 @@ function getCustomUnitRateID(reportID: string) {
return customUnitRateID;
}

function calculateTaxAmount(policy: OnyxEntry<Policy>, transaction: OnyxEntry<Transaction>, customUnitRateID: string) {
const distanceUnit = PolicyUtils.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) ?? '';
return TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount);
}

export default {
getDefaultMileageRate,
getDistanceMerchant,
Expand All @@ -276,6 +295,7 @@ export default {
getRateForP2P,
getCustomUnitRateID,
convertToDistanceInMeters,
calculateTaxAmount,
};

export type {MileageRate};
10 changes: 9 additions & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ function getNumericValue(value: number | string, toLocaleDigit: (arg: string) =>
return numValue.toFixed(CONST.CUSTOM_UNITS.RATE_DECIMALS);
}

/**
* Retrieves the distance custom unit object for the given policy
*/
function getCustomUnit(policy: OnyxEntry<Policy> | 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)) {
Expand Down Expand Up @@ -272,7 +279,7 @@ function isPaidGroupPolicy(policy: OnyxEntry<Policy> | EmptyObject): boolean {
}

function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry<Policy>, 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;
Expand Down Expand Up @@ -483,6 +490,7 @@ export {
findCurrentXeroOrganization,
getCurrentXeroOrganizationName,
getXeroBankAccountsWithDefaultSelect,
getCustomUnit,
};

export type {MemberEmailsToAccountIDs};
8 changes: 4 additions & 4 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Rate> = {};
const finallyRates: Record<string, Rate> = {};
Expand Down
14 changes: 6 additions & 8 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 {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';
Expand Down Expand Up @@ -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 = getCustomUnit(policy);
const customUnitID = distanceUnit?.customUnitID;
const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report);
const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest);
Expand Down Expand Up @@ -78,12 +78,10 @@ function IOURequestStepDistanceRate({
const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList;

function selectDistanceRate(customUnitRateID: string) {
if (transaction?.amount && 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 taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? '';
const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount));
if (policy?.customUnits && customUnitID && shouldShowTax) {
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 (
<WorkspacePageWithSections
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
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';
Expand Down Expand Up @@ -42,9 +43,9 @@ function WorkspaceUnitPage(props: WorkspaceUnitPageProps) {
};

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);
return defaultDistanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES;
}, [props.policy?.customUnits]);
}, [props.policy]);

return (
<WorkspacePageWithSections
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/reimburse/WorkspaceReimburseView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimbur
const [currentRatePerUnit, setCurrentRatePerUnit] = useState<string>('');
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();
Expand Down
Loading