Skip to content

Commit 04e1c07

Browse files
Merge pull request #54048 from paultsimura/fix/53988-share-unit
fix: Handle distance unit update when sharing
2 parents 82bc274 + cb2cca5 commit 04e1c07

File tree

6 files changed

+78
-31
lines changed

6 files changed

+78
-31
lines changed

src/components/MoneyRequestConfirmationList.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ function MoneyRequestConfirmationList({
254254
const rate = mileageRate.rate;
255255
const prevRate = usePrevious(rate);
256256
const unit = mileageRate.unit;
257+
const prevUnit = usePrevious(unit);
257258
const currency = mileageRate.currency ?? CONST.CURRENCY.USD;
258259
const prevCurrency = usePrevious(currency);
259260

@@ -293,7 +294,7 @@ function MoneyRequestConfirmationList({
293294
const distance = getDistanceInMeters(transaction, unit);
294295
const prevDistance = usePrevious(distance);
295296

296-
const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate || prevDistance !== distance || prevCurrency !== currency);
297+
const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate || prevDistance !== distance || prevCurrency !== currency || prevUnit !== unit);
297298

298299
const hasRoute = hasRouteUtil(transaction, isDistanceRequest);
299300
const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate) && !isMovingTransactionFromTrackExpense;

src/libs/TransactionUtils/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,11 @@ function hasWarningTypeViolation(transactionID: string | undefined, transactionV
992992
/**
993993
* Calculates tax amount from the given expense amount and tax percentage
994994
*/
995-
function calculateTaxAmount(percentage: string, amount: number, currency: string) {
995+
function calculateTaxAmount(percentage: string | undefined, amount: number, currency: string) {
996+
if (!percentage) {
997+
return 0;
998+
}
999+
9961000
const divisor = Number(percentage.slice(0, -1)) / 100 + 1;
9971001
const taxAmount = (amount - amount / divisor) / 100;
9981002
const decimals = getCurrencyDecimals(currency);

src/libs/actions/IOU.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -964,10 +964,25 @@ function addSubrate(transaction: OnyxEntry<OnyxTypes.Transaction>, currentIndex:
964964
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction?.transactionID}`, newTransaction);
965965
}
966966

967-
/** Set the distance rate of a new transaction */
968-
function setMoneyRequestDistanceRate(transactionID: string, rateID: string, policyID: string, isDraft: boolean) {
969-
Onyx.merge(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, {[policyID]: rateID});
970-
Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {comment: {customUnit: {customUnitRateID: rateID}}});
967+
/**
968+
* Set the distance rate of a transaction.
969+
* Used when creating a new transaction or moving an existing one from Self DM
970+
*/
971+
function setMoneyRequestDistanceRate(transactionID: string, customUnitRateID: string, policy: OnyxEntry<OnyxTypes.Policy>, isDraft: boolean) {
972+
if (policy) {
973+
Onyx.merge(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES, {[policy.id]: customUnitRateID});
974+
}
975+
976+
const distanceRate = DistanceRequestUtils.getRateByCustomUnitRateID({policy, customUnitRateID});
977+
Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {
978+
comment: {
979+
customUnit: {
980+
customUnitRateID,
981+
...(!!policy && {defaultP2PRate: null}),
982+
...(distanceRate && {distanceUnit: distanceRate.unit}),
983+
},
984+
},
985+
});
971986
}
972987

973988
/** Helper function to get the receipt error for expenses, or the generic error if there's no receipt */
@@ -9049,7 +9064,7 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On
90499064
return participants;
90509065
}
90519066

9052-
function setMoneyRequestTaxRate(transactionID: string, taxCode: string) {
9067+
function setMoneyRequestTaxRate(transactionID: string, taxCode: string | null) {
90539068
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxCode});
90549069
}
90559070

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ function IOURequestStepDistance({
232232
createBackupTransaction(transaction, isDraft);
233233

234234
return () => {
235-
// If the user cancels out of the modal without without saving changes, then the original transaction
235+
// If the user cancels out of the modal without saving changes, then the original transaction
236236
// needs to be restored from the backup so that all changes are removed.
237237
if (transactionWasSaved.current) {
238238
removeBackupTransaction(transaction?.transactionID);

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

+23-23
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import RadioListItem from '@components/SelectionList/RadioListItem';
66
import Text from '@components/Text';
77
import useLocalize from '@hooks/useLocalize';
88
import useThemeStyles from '@hooks/useThemeStyles';
9-
import * as IOU from '@libs/actions/IOU';
10-
import * as CurrencyUtils from '@libs/CurrencyUtils';
9+
import {getIOURequestPolicyID, setMoneyRequestDistanceRate, setMoneyRequestTaxAmount, setMoneyRequestTaxRate, updateMoneyRequestDistanceRate} from '@libs/actions/IOU';
10+
import {convertToBackendAmount} from '@libs/CurrencyUtils';
1111
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
12+
import {shouldUseTransactionDraft} from '@libs/IOUUtils';
1213
import Navigation from '@libs/Navigation/Navigation';
1314
import {getDistanceRateCustomUnitRate, isTaxTrackingEnabled} from '@libs/PolicyUtils';
14-
import * as ReportUtils from '@libs/ReportUtils';
15-
import * as TransactionUtils from '@libs/TransactionUtils';
15+
import {isReportInGroupPolicy} from '@libs/ReportUtils';
16+
import {calculateTaxAmount, getCurrency, getDistanceInMeters, getRateID, getTaxValue, isDistanceRequest as isDistanceRequestTransactionUtils} from '@libs/TransactionUtils';
1617
import CONST from '@src/CONST';
1718
import ONYXKEYS from '@src/ONYXKEYS';
1819
import type SCREENS from '@src/SCREENS';
@@ -35,25 +36,24 @@ function IOURequestStepDistanceRate({
3536
},
3637
transaction,
3738
}: IOURequestStepDistanceRateProps) {
38-
const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${IOU.getIOURequestPolicyID(transaction, reportDraft) ?? '-1'}`);
39+
const [policyDraft] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${getIOURequestPolicyID(transaction, reportDraft)}`);
3940
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
40-
const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID || '-1'}`);
41-
const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID || '-1'}`);
42-
const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${report?.policyID || '-1'}`);
41+
const [policyReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`);
42+
const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report?.policyID}`);
43+
const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${report?.policyID}`);
4344
/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
4445

45-
const policy = policyReal ?? policyDraft;
46+
const policy: OnyxEntry<OnyxTypes.Policy> = policyReal ?? policyDraft;
4647

4748
const styles = useThemeStyles();
4849
const {translate, toLocaleDigit} = useLocalize();
49-
const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction);
50-
const isPolicyExpenseChat = ReportUtils.isReportInGroupPolicy(report);
50+
const isDistanceRequest = isDistanceRequestTransactionUtils(transaction);
51+
const isPolicyExpenseChat = isReportInGroupPolicy(report);
5152
const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest);
5253
const isEditing = action === CONST.IOU.ACTION.EDIT;
5354

54-
const currentRateID = TransactionUtils.getRateID(transaction) ?? '-1';
55-
56-
const transactionCurrency = TransactionUtils.getCurrency(transaction);
55+
const currentRateID = getRateID(transaction);
56+
const transactionCurrency = getCurrency(transaction);
5757

5858
const rates = DistanceRequestUtils.getMileageRates(policy, false, currentRateID);
5959

@@ -84,20 +84,20 @@ function IOURequestStepDistanceRate({
8484
let taxRateExternalID;
8585
if (shouldShowTax) {
8686
const policyCustomUnitRate = getDistanceRateCustomUnitRate(policy, customUnitRateID);
87-
taxRateExternalID = policyCustomUnitRate?.attributes?.taxRateExternalID ?? '-1';
87+
taxRateExternalID = policyCustomUnitRate?.attributes?.taxRateExternalID;
8888
const unit = DistanceRequestUtils.getDistanceUnit(transaction, rates[customUnitRateID]);
89-
const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, TransactionUtils.getDistanceInMeters(transaction, unit));
90-
const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? '';
91-
taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount, rates[customUnitRateID].currency ?? CONST.CURRENCY.USD));
92-
IOU.setMoneyRequestTaxAmount(transactionID, taxAmount);
93-
IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID);
89+
const taxableAmount = DistanceRequestUtils.getTaxableAmount(policy, customUnitRateID, getDistanceInMeters(transaction, unit));
90+
const taxPercentage = taxRateExternalID ? getTaxValue(policy, transaction, taxRateExternalID) : undefined;
91+
taxAmount = convertToBackendAmount(calculateTaxAmount(taxPercentage, taxableAmount, rates[customUnitRateID].currency ?? CONST.CURRENCY.USD));
92+
setMoneyRequestTaxAmount(transactionID, taxAmount);
93+
setMoneyRequestTaxRate(transactionID, taxRateExternalID ?? null);
9494
}
9595

9696
if (currentRateID !== customUnitRateID) {
97-
IOU.setMoneyRequestDistanceRate(transactionID, customUnitRateID, policy?.id ?? '-1', !isEditing);
97+
setMoneyRequestDistanceRate(transactionID, customUnitRateID, policy, shouldUseTransactionDraft(action));
9898

99-
if (isEditing) {
100-
IOU.updateMoneyRequestDistanceRate(transaction?.transactionID ?? '-1', reportID, customUnitRateID, policy, policyTags, policyCategories, taxAmount, taxRateExternalID);
99+
if (isEditing && transaction?.transactionID) {
100+
updateMoneyRequestDistanceRate(transaction.transactionID, reportID, customUnitRateID, policy, policyTags, policyCategories, taxAmount, taxRateExternalID);
101101
}
102102
}
103103

tests/unit/TransactionUtilsTest.ts

+27
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,31 @@ describe('TransactionUtils', () => {
241241
expect(updatedTransaction.taxAmount).toBe(5);
242242
});
243243
});
244+
245+
describe('calculateTaxAmount', () => {
246+
it('returns 0 for undefined percentage', () => {
247+
const result = TransactionUtils.calculateTaxAmount(undefined, 10000, 'USD');
248+
expect(result).toBe(0);
249+
});
250+
251+
it('returns 0 for empty percentage', () => {
252+
const result = TransactionUtils.calculateTaxAmount('', 10000, 'USD');
253+
expect(result).toBe(0);
254+
});
255+
256+
it('returns 0 for zero percentage', () => {
257+
const result = TransactionUtils.calculateTaxAmount('0%', 10000, 'USD');
258+
expect(result).toBe(0);
259+
});
260+
261+
it('returns 0 for zero amount', () => {
262+
const result = TransactionUtils.calculateTaxAmount('10%', 0, 'USD');
263+
expect(result).toBe(0);
264+
});
265+
266+
it('returns correct tax amount for valid percentage and amount', () => {
267+
const result = TransactionUtils.calculateTaxAmount('10%', 10000, 'USD');
268+
expect(result).toBe(9.09);
269+
});
270+
});
244271
});

0 commit comments

Comments
 (0)