Skip to content

Commit 1d5f83d

Browse files
authored
Merge pull request Expensify#42909 from Expensify/monil-fixDistanceTaxOffline
Fix distance tax rate and amount not updating offline and inaccurate tax amount and refactor code
2 parents d3e3920 + 5a61263 commit 1d5f83d

File tree

9 files changed

+58
-27
lines changed

9 files changed

+58
-27
lines changed

src/components/MoneyRequestConfirmationList.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,11 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps &
179179

180180
type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData;
181181

182-
const getTaxAmount = (transaction: OnyxEntry<OnyxTypes.Transaction>, policy: OnyxEntry<OnyxTypes.Policy>) => {
182+
const getTaxAmount = (transaction: OnyxEntry<OnyxTypes.Transaction>, policy: OnyxEntry<OnyxTypes.Policy>, isDistanceRequest: boolean) => {
183+
if (isDistanceRequest) {
184+
return DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? '');
185+
}
183186
const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? '';
184-
185187
const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? '';
186188
return TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0);
187189
};
@@ -372,15 +374,15 @@ function MoneyRequestConfirmationList({
372374

373375
// Calculate and set tax amount in transaction draft
374376
useEffect(() => {
375-
const taxAmount = getTaxAmount(transaction, policy).toString();
377+
const taxAmount = getTaxAmount(transaction, policy, isDistanceRequest).toString();
376378
const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount));
377379

378380
if (transaction?.taxAmount && previousTransactionAmount === transaction?.amount && previousTransactionCurrency === transaction?.currency) {
379381
return IOU.setMoneyRequestTaxAmount(transactionID, transaction?.taxAmount ?? 0, true);
380382
}
381383

382384
IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true);
383-
}, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency]);
385+
}, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency, isDistanceRequest]);
384386

385387
// If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again
386388
if (isEditingSplitBill && didConfirm) {

src/libs/DistanceRequestUtils.ts

+23-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import type {LocaleContextProps} from '@components/LocaleContextProvider';
44
import type {RateAndUnit} from '@src/CONST';
55
import CONST from '@src/CONST';
66
import ONYXKEYS from '@src/ONYXKEYS';
7-
import type {LastSelectedDistanceRates, Report} from '@src/types/onyx';
7+
import type {LastSelectedDistanceRates, Report, Transaction} from '@src/types/onyx';
88
import type {Unit} from '@src/types/onyx/Policy';
99
import type Policy from '@src/types/onyx/Policy';
1010
import type {EmptyObject} from '@src/types/utils/EmptyObject';
1111
import * as CurrencyUtils from './CurrencyUtils';
1212
import * as PolicyUtils from './PolicyUtils';
1313
import * as ReportUtils from './ReportUtils';
14+
import * as TransactionUtils from './TransactionUtils';
1415

1516
type MileageRate = {
1617
customUnitRateID?: string;
@@ -53,7 +54,7 @@ function getDefaultMileageRate(policy: OnyxEntry<Policy> | EmptyObject): Mileage
5354
return null;
5455
}
5556

56-
const distanceUnit = Object.values(policy.customUnits).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
57+
const distanceUnit = PolicyUtils.getCustomUnit(policy);
5758
if (!distanceUnit?.rates) {
5859
return null;
5960
}
@@ -193,7 +194,7 @@ function getMileageRates(policy: OnyxEntry<Policy>): Record<string, MileageRate>
193194
return mileageRates;
194195
}
195196

196-
const distanceUnit = Object.values(policy.customUnits).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
197+
const distanceUnit = PolicyUtils.getCustomUnit(policy);
197198
if (!distanceUnit?.rates) {
198199
return mileageRates;
199200
}
@@ -266,6 +267,24 @@ function getCustomUnitRateID(reportID: string) {
266267
return customUnitRateID;
267268
}
268269

270+
function calculateTaxAmount(policy: OnyxEntry<Policy>, transaction: OnyxEntry<Transaction>, customUnitRateID: string) {
271+
const distanceUnit = PolicyUtils.getCustomUnit(policy);
272+
const customUnitID = distanceUnit?.customUnitID;
273+
if (!policy?.customUnits || !customUnitID) {
274+
return 0;
275+
}
276+
const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID];
277+
const unit = policy?.customUnits[customUnitID]?.attributes?.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES;
278+
const rate = policyCustomUnitRate?.rate ?? 0;
279+
const distance = TransactionUtils.getDistance(transaction);
280+
const amount = getDistanceRequestAmount(distance, unit, rate);
281+
const taxClaimablePercentage = policyCustomUnitRate.attributes?.taxClaimablePercentage ?? 0;
282+
const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? '';
283+
const taxableAmount = amount * taxClaimablePercentage;
284+
const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? '';
285+
return TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount);
286+
}
287+
269288
export default {
270289
getDefaultMileageRate,
271290
getDistanceMerchant,
@@ -276,6 +295,7 @@ export default {
276295
getRateForP2P,
277296
getCustomUnitRateID,
278297
convertToDistanceInMeters,
298+
calculateTaxAmount,
279299
};
280300

281301
export type {MileageRate};

src/libs/PolicyUtils.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ function getNumericValue(value: number | string, toLocaleDigit: (arg: string) =>
9090
return numValue.toFixed(CONST.CUSTOM_UNITS.RATE_DECIMALS);
9191
}
9292

93+
/**
94+
* Retrieves the distance custom unit object for the given policy
95+
*/
96+
function getCustomUnit(policy: OnyxEntry<Policy> | EmptyObject) {
97+
return Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
98+
}
99+
93100
function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => string): string {
94101
const numValue = getNumericValue(value, toLocaleDigit);
95102
if (Number.isNaN(numValue)) {
@@ -278,7 +285,7 @@ function isPaidGroupPolicy(policy: OnyxEntry<Policy> | EmptyObject): boolean {
278285
}
279286

280287
function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry<Policy>, isDistanceRequest: boolean): boolean {
281-
const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
288+
const distanceUnit = getCustomUnit(policy);
282289
const customUnitID = distanceUnit?.customUnitID ?? 0;
283290
const isPolicyTaxTrackingEnabled = isPolicyExpenseChat && policy?.tax?.trackingEnabled;
284291
const isTaxEnabledForDistance = isPolicyTaxTrackingEnabled && policy?.customUnits?.[customUnitID]?.attributes?.taxEnabled;
@@ -505,6 +512,7 @@ export {
505512
findCurrentXeroOrganization,
506513
getCurrentXeroOrganizationName,
507514
getXeroBankAccountsWithDefaultSelect,
515+
getCustomUnit,
508516
sortWorkspacesBySelected,
509517
};
510518

src/libs/actions/Policy/Policy.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1567,14 +1567,14 @@ function clearAvatarErrors(policyID: string) {
15671567
*/
15681568
function updateGeneralSettings(policyID: string, name: string, currencyValue?: string) {
15691569
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
1570-
const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
1571-
const customUnitID = distanceUnit?.customUnitID;
1572-
const currency = currencyValue ?? policy?.outputCurrency ?? CONST.CURRENCY.USD;
1573-
15741570
if (!policy) {
15751571
return;
15761572
}
15771573

1574+
const distanceUnit = PolicyUtils.getCustomUnit(policy);
1575+
const customUnitID = distanceUnit?.customUnitID;
1576+
const currency = currencyValue ?? policy?.outputCurrency ?? CONST.CURRENCY.USD;
1577+
15781578
const currentRates = distanceUnit?.rates ?? {};
15791579
const optimisticRates: Record<string, Rate> = {};
15801580
const finallyRates: Record<string, Rate> = {};

src/pages/iou/request/step/IOURequestStepDistanceRate.tsx

+6-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils';
1111
import type {MileageRate} from '@libs/DistanceRequestUtils';
1212
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
1313
import Navigation from '@libs/Navigation/Navigation';
14-
import {isTaxTrackingEnabled} from '@libs/PolicyUtils';
14+
import {getCustomUnit, isTaxTrackingEnabled} from '@libs/PolicyUtils';
1515
import * as ReportUtils from '@libs/ReportUtils';
1616
import * as TransactionUtils from '@libs/TransactionUtils';
1717
import CONST from '@src/CONST';
@@ -50,7 +50,7 @@ function IOURequestStepDistanceRate({
5050
const styles = useThemeStyles();
5151
const {translate, toLocaleDigit} = useLocalize();
5252
const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction);
53-
const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
53+
const distanceUnit = getCustomUnit(policy);
5454
const customUnitID = distanceUnit?.customUnitID;
5555
const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report);
5656
const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest);
@@ -78,12 +78,10 @@ function IOURequestStepDistanceRate({
7878
const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList;
7979

8080
function selectDistanceRate(customUnitRateID: string) {
81-
if (transaction?.amount && policy?.customUnits && customUnitID && shouldShowTax) {
82-
const taxClaimablePercentage = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxClaimablePercentage ?? 0;
83-
const taxRateExternalID = policy?.customUnits[customUnitID].rates[customUnitRateID].attributes?.taxRateExternalID ?? '';
84-
const taxableAmount = -1 * transaction.amount * taxClaimablePercentage;
85-
const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? '';
86-
const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount));
81+
if (policy?.customUnits && customUnitID && shouldShowTax) {
82+
const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID];
83+
const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? '';
84+
const taxAmount = CurrencyUtils.convertToBackendAmount(DistanceRequestUtils.calculateTaxAmount(policy, transaction, customUnitRateID));
8785
IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true);
8886
IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID);
8987
}

src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/InitialPage.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
1313
import compose from '@libs/compose';
1414
import * as CurrencyUtils from '@libs/CurrencyUtils';
1515
import Navigation from '@libs/Navigation/Navigation';
16+
import * as PolicyUtils from '@libs/PolicyUtils';
1617
import {getUnitTranslationKey} from '@libs/WorkspacesSettingsUtils';
1718
import withPolicy from '@pages/workspace/withPolicy';
1819
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
@@ -62,7 +63,7 @@ function WorkspaceRateAndUnitPage(props: WorkspaceRateAndUnitPageProps) {
6263
}, [props]);
6364

6465
const saveUnitAndRate = (newUnit: Unit, newRate: string) => {
65-
const distanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
66+
const distanceCustomUnit = PolicyUtils.getCustomUnit(props.policy);
6667
if (!distanceCustomUnit) {
6768
return;
6869
}
@@ -82,7 +83,7 @@ function WorkspaceRateAndUnitPage(props: WorkspaceRateAndUnitPageProps) {
8283
Policy.updateWorkspaceCustomUnitAndRate(props.policy?.id ?? '', distanceCustomUnit, newCustomUnit, props.policy?.lastModified);
8384
};
8485

85-
const distanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
86+
const distanceCustomUnit = PolicyUtils.getCustomUnit(props.policy);
8687
const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE);
8788

8889
const unitValue = props.workspaceRateAndUnit?.unit ?? distanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES;

src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/RatePage.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
1010
import compose from '@libs/compose';
1111
import Navigation from '@libs/Navigation/Navigation';
1212
import {validateRateValue} from '@libs/PolicyDistanceRatesUtils';
13+
import * as PolicyUtils from '@libs/PolicyUtils';
1314
import withPolicy from '@pages/workspace/withPolicy';
1415
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
1516
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
@@ -53,10 +54,10 @@ function WorkspaceRatePage(props: WorkspaceRatePageProps) {
5354
);
5455

5556
const defaultValue = useMemo(() => {
56-
const defaultDistanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
57+
const defaultDistanceCustomUnit = PolicyUtils.getCustomUnit(props.policy);
5758
const distanceCustomRate = Object.values(defaultDistanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE);
5859
return distanceCustomRate?.rate ?? 0;
59-
}, [props.policy?.customUnits]);
60+
}, [props.policy]);
6061

6162
return (
6263
<WorkspacePageWithSections

src/pages/workspace/reimburse/WorkspaceRateAndUnitPage/UnitPage.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import useLocalize from '@hooks/useLocalize';
88
import useThemeStyles from '@hooks/useThemeStyles';
99
import compose from '@libs/compose';
1010
import Navigation from '@libs/Navigation/Navigation';
11+
import * as PolicyUtils from '@libs/PolicyUtils';
1112
import withPolicy from '@pages/workspace/withPolicy';
1213
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
1314
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
@@ -42,9 +43,9 @@ function WorkspaceUnitPage(props: WorkspaceUnitPageProps) {
4243
};
4344

4445
const defaultValue = useMemo(() => {
45-
const defaultDistanceCustomUnit = Object.values(props.policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
46+
const defaultDistanceCustomUnit = PolicyUtils.getCustomUnit(props.policy);
4647
return defaultDistanceCustomUnit?.attributes.unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES;
47-
}, [props.policy?.customUnits]);
48+
}, [props.policy]);
4849

4950
return (
5051
<WorkspacePageWithSections

src/pages/workspace/reimburse/WorkspaceReimburseView.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function WorkspaceReimburseView({policy, reimbursementAccount}: WorkspaceReimbur
4040
const [currentRatePerUnit, setCurrentRatePerUnit] = useState<string>('');
4141
const {isSmallScreenWidth} = useWindowDimensions();
4242
const viewAllReceiptsUrl = `expenses?policyIDList=${policy?.id ?? ''}&billableReimbursable=reimbursable&submitterEmail=%2B%2B`;
43-
const distanceCustomUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE);
43+
const distanceCustomUnit = PolicyUtils.getCustomUnit(policy);
4444
const distanceCustomRate = Object.values(distanceCustomUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE);
4545
const {translate, toLocaleDigit} = useLocalize();
4646
const {isOffline} = useNetwork();

0 commit comments

Comments
 (0)