From 963a160fa9d7bb2e38f8fd35b0cdb82dde9a189f Mon Sep 17 00:00:00 2001
From: dmkt9 <demoskratos9x@gmail.com>
Date: Thu, 19 Jun 2025 16:02:32 +0700
Subject: [PATCH 1/4] Fix - After moving distance expense to Self DM,
 "Distance" field is shown as "Pending"

---
 .../ReportActionItem/MoneyRequestView.tsx     |  7 ++++-
 src/components/TransactionItemRow/index.tsx   |  6 ++++-
 src/libs/DistanceRequestUtils.ts              |  2 +-
 src/libs/SearchUIUtils.ts                     |  7 ++++-
 src/libs/TransactionPreviewUtils.ts           | 11 +++++---
 src/libs/TransactionUtils/index.ts            | 27 ++++++++++++++++---
 6 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx
index 891809b77e94..b592db84ac92 100644
--- a/src/components/ReportActionItem/MoneyRequestView.tsx
+++ b/src/components/ReportActionItem/MoneyRequestView.tsx
@@ -250,7 +250,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
     const {unit, rate} = DistanceRequestUtils.getRate({transaction, policy});
     const distance = getDistanceInMeters(transactionBackup ?? transaction, unit);
     const currency = transactionCurrency ?? CONST.CURRENCY.USD;
-    const isCustomUnitOutOfPolicy = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.CUSTOM_UNIT_OUT_OF_POLICY);
+    const isCustomUnitOutOfPolicy = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.CUSTOM_UNIT_OUT_OF_POLICY) || !rate;
     const rateToDisplay = isCustomUnitOutOfPolicy ? translate('common.rateOutOfPolicy') : DistanceRequestUtils.getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, isOffline);
     const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate);
     let merchantTitle = isEmptyMerchant ? '' : transactionMerchant;
@@ -345,6 +345,10 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
                 return translate(translationPath);
             }
 
+            if (isCustomUnitOutOfPolicy && field === 'customUnitRateID') {
+                return translate('violations.customUnitOutOfPolicy');
+            }
+
             // Return violations if there are any
             if (field !== 'merchant' && hasViolations(field, data, policyHasDependentTags, tagValue)) {
                 const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue);
@@ -373,6 +377,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
             canEditDate,
             canEditMerchant,
             canEdit,
+            isCustomUnitOutOfPolicy,
         ],
     );
 
diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx
index f59a58f6e25c..aa8c80c2a501 100644
--- a/src/components/TransactionItemRow/index.tsx
+++ b/src/components/TransactionItemRow/index.tsx
@@ -30,6 +30,7 @@ import {
     isMerchantMissing,
     isReceiptBeingScanned,
     isTransactionPendingDelete,
+    isUnreportedAndHasInvalidDistanceRateTransaction,
 } from '@libs/TransactionUtils';
 import variables from '@styles/variables';
 import CONST from '@src/CONST';
@@ -176,7 +177,8 @@ function TransactionItemRow({
 
     const merchantOrDescriptionName = useMemo(() => getMerchantNameWithFallback(transactionItem, translate, shouldUseNarrowLayout), [shouldUseNarrowLayout, transactionItem, translate]);
     const missingFieldError = useMemo(() => {
-        const hasFieldErrors = hasMissingSmartscanFields(transactionItem);
+        const isCustomUnitOutOfPolicy = isUnreportedAndHasInvalidDistanceRateTransaction(transactionItem);
+        const hasFieldErrors = hasMissingSmartscanFields(transactionItem) || isCustomUnitOutOfPolicy;
         if (hasFieldErrors) {
             const amountMissing = isAmountMissing(transactionItem);
             const merchantMissing = isMerchantMissing(transactionItem);
@@ -188,6 +190,8 @@ function TransactionItemRow({
                 error = translate('iou.missingAmount');
             } else if (merchantMissing) {
                 error = translate('iou.missingMerchant');
+            } else if (isCustomUnitOutOfPolicy) {
+                error = translate('violations.customUnitOutOfPolicy');
             }
             return error;
         }
diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts
index f1de938c492b..e121a752ebcb 100644
--- a/src/libs/DistanceRequestUtils.ts
+++ b/src/libs/DistanceRequestUtils.ts
@@ -186,7 +186,7 @@ function getDistanceForDisplay(
     translate: LocaleContextProps['translate'],
     useShortFormUnit?: boolean,
 ): string {
-    if (!hasRoute || !rate || !unit || !distanceInMeters) {
+    if (!hasRoute || !unit || !distanceInMeters) {
         return translate('iou.fieldPending');
     }
 
diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts
index 1bba91e69ca9..8987f6e59e48 100644
--- a/src/libs/SearchUIUtils.ts
+++ b/src/libs/SearchUIUtils.ts
@@ -71,6 +71,7 @@ import {
     getCreated as getTransactionCreatedDate,
     getMerchant as getTransactionMerchant,
     isPendingCardOrScanningTransaction,
+    isUnreportedAndHasInvalidDistanceRateTransaction,
     isViolationDismissed,
 } from './TransactionUtils';
 import shouldShowTransactionYear from './TransactionUtils/shouldShowTransactionYear';
@@ -647,6 +648,11 @@ function getAction(data: OnyxTypes.SearchResults['data'], allViolations: OnyxCol
     if (!isTransaction && !isReportEntry(key)) {
         return CONST.SEARCH.ACTION_TYPES.VIEW;
     }
+    
+    const transaction = isTransaction ? data[key] : undefined;
+    if (isUnreportedAndHasInvalidDistanceRateTransaction(transaction)) {
+        return CONST.SEARCH.ACTION_TYPES.REVIEW;
+    }
     // Tracked and unreported expenses don't have a report, so we return early.
     if (!report) {
         return CONST.SEARCH.ACTION_TYPES.VIEW;
@@ -656,7 +662,6 @@ function getAction(data: OnyxTypes.SearchResults['data'], allViolations: OnyxCol
         return CONST.SEARCH.ACTION_TYPES.PAID;
     }
 
-    const transaction = isTransaction ? data[key] : undefined;
     // We need to check both options for a falsy value since the transaction might not have an error but the report associated with it might. We return early if there are any errors for performance reasons, so we don't need to compute any other possible actions.
     // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
     if (transaction?.errors || report?.errors) {
diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts
index b4ba1e71f866..05b7653395d8 100644
--- a/src/libs/TransactionPreviewUtils.ts
+++ b/src/libs/TransactionPreviewUtils.ts
@@ -33,6 +33,7 @@ import {
     isPending,
     isPerDiemRequest,
     isScanning,
+    isUnreportedAndHasInvalidDistanceRateTransaction,
 } from './TransactionUtils';
 
 const emptyPersonalDetails: OnyxTypes.PersonalDetails = {
@@ -204,12 +205,14 @@ function getTransactionPreviewTextAndTranslationPaths({
         RBRMessage = actionsWithErrors.length > 1 ? {translationPath: 'violations.reviewRequired'} : {text: actionsWithErrors.at(0)};
     }
 
-    RBRMessage ??= {text: ''};
-
     let previewHeaderText: TranslationPathOrText[] = [showCashOrCard];
 
     if (isDistanceRequest(transaction)) {
         previewHeaderText = [{translationPath: 'common.distance'}];
+
+        if (RBRMessage === undefined && isUnreportedAndHasInvalidDistanceRateTransaction(transaction)) {
+            RBRMessage = {translationPath: 'violations.customUnitOutOfPolicy'};
+        }
     } else if (isPerDiemRequest(transaction)) {
         previewHeaderText = [{translationPath: 'common.perDiem'}];
     } else if (isTransactionScanning) {
@@ -218,6 +221,8 @@ function getTransactionPreviewTextAndTranslationPaths({
         previewHeaderText = [{translationPath: 'iou.split'}];
     }
 
+    RBRMessage ??= {text: ''};
+
     if (!isCreatedMissing(transaction)) {
         const created = getFormattedCreated(transaction);
         const date = DateUtils.formatWithUTCTimeZone(created, DateUtils.doesDateBelongToAPastYear(created) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT);
@@ -310,7 +315,7 @@ function createTransactionPreviewConditionals({
     const shouldShowCategory = !!category && isReportAPolicyExpenseChat;
 
     // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-    const hasAnyViolations = hasViolationsOfTypeNotice || hasWarningTypeViolation(transaction?.transactionID, violations, true) || hasViolation(transaction, violations, true);
+    const hasAnyViolations = isUnreportedAndHasInvalidDistanceRateTransaction(transaction) || hasViolationsOfTypeNotice || hasWarningTypeViolation(transaction?.transactionID, violations, true) || hasViolation(transaction, violations, true);
     const hasErrorOrOnHold = hasFieldErrors || (!isFullySettled && !isFullyApproved && isTransactionOnHold);
     const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionsWithErrors(iouReport?.reportID);
     const shouldShowRBR = hasAnyViolations || hasErrorOrOnHold || hasReportViolationsOrActionErrors;
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index 6f5605a87cfe..476d9ff0f659 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -656,6 +656,24 @@ function isFetchingWaypointsFromServer(transaction: OnyxInputOrEntry<Transaction
     return !!transaction?.pendingFields?.waypoints;
 }
 
+/**
+ * Verify that the transaction is in Self DM and that its distance rate is invalid.
+ */
+function isUnreportedAndHasInvalidDistanceRateTransaction(transaction: OnyxInputOrEntry<Transaction>, policyParam: OnyxEntry<Policy> = undefined) {
+    if (transaction && isDistanceRequest(transaction)) {
+        const report = getReportOrDraftReport(transaction.reportID);
+        const policy = policyParam ?? getPolicy(report?.policyID);
+        const {rate} = DistanceRequestUtils.getRate({transaction, policy});
+        const isUnreported = !transaction.reportID || transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
+        
+        if (isUnreported && !rate) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 /**
  * Return the merchant field from the transaction, return the modifiedMerchant if present.
  */
@@ -668,9 +686,11 @@ function getMerchant(transaction: OnyxInputOrEntry<Transaction>, policyParam: On
         const mileageRate = DistanceRequestUtils.getRate({transaction, policy});
         const {unit, rate} = mileageRate;
         const distanceInMeters = getDistanceInMeters(transaction, unit);
-        return DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, translateLocal, (digit) =>
-            toLocaleDigit(TranslationStore.getCurrentLocale(), digit),
-        );
+        if (!isUnreportedAndHasInvalidDistanceRateTransaction(transaction, policy)) {
+            return DistanceRequestUtils.getDistanceMerchant(true, distanceInMeters, unit, rate, transaction.currency, translateLocal, (digit) =>
+                toLocaleDigit(TranslationStore.getCurrentLocale(), digit),
+            );
+        }
     }
     return transaction?.modifiedMerchant ? transaction.modifiedMerchant : (transaction?.merchant ?? '');
 }
@@ -1728,6 +1748,7 @@ export {
     getTransactionPendingAction,
     isTransactionPendingDelete,
     createUnreportedExpenseSections,
+    isUnreportedAndHasInvalidDistanceRateTransaction,
 };
 
 export type {TransactionChanges};

From 2c04419230347639e9ff1bb1b88bf54582037aac Mon Sep 17 00:00:00 2001
From: dmkt9 <demoskratos9x@gmail.com>
Date: Wed, 2 Jul 2025 08:09:47 +0700
Subject: [PATCH 2/4] update condition check for isCustomUnitOutOfPolicy

---
 src/components/ReportActionItem/MoneyRequestView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx
index b592db84ac92..182262ebceb2 100644
--- a/src/components/ReportActionItem/MoneyRequestView.tsx
+++ b/src/components/ReportActionItem/MoneyRequestView.tsx
@@ -250,7 +250,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
     const {unit, rate} = DistanceRequestUtils.getRate({transaction, policy});
     const distance = getDistanceInMeters(transactionBackup ?? transaction, unit);
     const currency = transactionCurrency ?? CONST.CURRENCY.USD;
-    const isCustomUnitOutOfPolicy = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.CUSTOM_UNIT_OUT_OF_POLICY) || !rate;
+    const isCustomUnitOutOfPolicy = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.CUSTOM_UNIT_OUT_OF_POLICY) || (isDistanceRequest && !rate);
     const rateToDisplay = isCustomUnitOutOfPolicy ? translate('common.rateOutOfPolicy') : DistanceRequestUtils.getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, isOffline);
     const distanceToDisplay = DistanceRequestUtils.getDistanceForDisplay(hasRoute, distance, unit, rate, translate);
     let merchantTitle = isEmptyMerchant ? '' : transactionMerchant;

From d9b869e098dac8898abe120410fadf11bf7fb8ee Mon Sep 17 00:00:00 2001
From: dmkt9 <demoskratos9x@gmail.com>
Date: Wed, 2 Jul 2025 09:22:24 +0700
Subject: [PATCH 3/4] Fix lint and prettier

---
 src/libs/SearchUIUtils.ts           | 2 +-
 src/libs/TransactionPreviewUtils.ts | 8 ++++++--
 src/libs/TransactionUtils/index.ts  | 3 ++-
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts
index 090f921450f0..0df7b232829f 100644
--- a/src/libs/SearchUIUtils.ts
+++ b/src/libs/SearchUIUtils.ts
@@ -681,7 +681,7 @@ function getAction(data: OnyxTypes.SearchResults['data'], allViolations: OnyxCol
     if (!isTransaction && !isReportEntry(key)) {
         return CONST.SEARCH.ACTION_TYPES.VIEW;
     }
-    
+
     const transaction = isTransaction ? data[key] : undefined;
     if (isUnreportedAndHasInvalidDistanceRateTransaction(transaction)) {
         return CONST.SEARCH.ACTION_TYPES.REVIEW;
diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts
index d4b1f1093a9b..0461399aa9b5 100644
--- a/src/libs/TransactionPreviewUtils.ts
+++ b/src/libs/TransactionPreviewUtils.ts
@@ -320,8 +320,12 @@ function createTransactionPreviewConditionals({
 
     const shouldShowCategory = !!categoryForDisplay && isReportAPolicyExpenseChat;
 
-    // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-    const hasAnyViolations = isUnreportedAndHasInvalidDistanceRateTransaction(transaction) || hasViolationsOfTypeNotice || hasWarningTypeViolation(transaction?.transactionID, violations, true) || hasViolation(transaction, violations, true);
+    const hasAnyViolations =
+        isUnreportedAndHasInvalidDistanceRateTransaction(transaction) ||
+        // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+        hasViolationsOfTypeNotice ||
+        hasWarningTypeViolation(transaction?.transactionID, violations, true) ||
+        hasViolation(transaction, violations, true);
     const hasErrorOrOnHold = hasFieldErrors || (!isFullySettled && !isFullyApproved && isTransactionOnHold);
     const hasReportViolationsOrActionErrors = (isReportOwner(iouReport) && hasReportViolations(iouReport?.reportID)) || hasActionsWithErrors(iouReport?.reportID);
     const shouldShowRBR = hasAnyViolations || hasErrorOrOnHold || hasReportViolationsOrActionErrors;
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index 353435ec335a..06a2320e11ae 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -663,10 +663,11 @@ function isFetchingWaypointsFromServer(transaction: OnyxInputOrEntry<Transaction
 function isUnreportedAndHasInvalidDistanceRateTransaction(transaction: OnyxInputOrEntry<Transaction>, policyParam: OnyxEntry<Policy> = undefined) {
     if (transaction && isDistanceRequest(transaction)) {
         const report = getReportOrDraftReport(transaction.reportID);
+        // eslint-disable-next-line deprecation/deprecation
         const policy = policyParam ?? getPolicy(report?.policyID);
         const {rate} = DistanceRequestUtils.getRate({transaction, policy});
         const isUnreported = !transaction.reportID || transaction.reportID === CONST.REPORT.UNREPORTED_REPORT_ID;
-        
+
         if (isUnreported && !rate) {
             return true;
         }

From 4e48e889f260b09eaccb89c49745f32bdb575cac Mon Sep 17 00:00:00 2001
From: dmkt9 <demoskratos9x@gmail.com>
Date: Fri, 4 Jul 2025 11:08:55 +0700
Subject: [PATCH 4/4] add unit tests for
 isUnreportedAndHasInvalidDistanceRateTransaction

---
 tests/unit/TransactionUtilsTest.ts | 126 +++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/tests/unit/TransactionUtilsTest.ts b/tests/unit/TransactionUtilsTest.ts
index 9a2c93408de9..96e7432b85ff 100644
--- a/tests/unit/TransactionUtilsTest.ts
+++ b/tests/unit/TransactionUtilsTest.ts
@@ -4,7 +4,9 @@ import CONST from '@src/CONST';
 import IntlStore from '@src/languages/IntlStore';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Attendee} from '@src/types/onyx/IOU';
+import type {CustomUnit, Rate} from '@src/types/onyx/Policy';
 import type {ReportCollectionDataSet} from '@src/types/onyx/Report';
+import type {TransactionCustomUnit} from '@src/types/onyx/Transaction';
 import * as TransactionUtils from '../../src/libs/TransactionUtils';
 import type {Policy, Transaction} from '../../src/types/onyx';
 import createRandomPolicy, {createCategoryTaxExpenseRules} from '../utils/collections/policies';
@@ -69,6 +71,40 @@ const reportCollectionDataSet: ReportCollectionDataSet = {
     [`${ONYXKEYS.COLLECTION.REPORT}${FAKE_APPROVED_REPORT_ID}`]: approvedReport,
     [`${ONYXKEYS.COLLECTION.REPORT}${FAKE_OPEN_REPORT_SECOND_USER_ID}`]: secondUserOpenReport,
 };
+const defaultDistanceRatePolicyID1: Record<string, Rate> = {
+    customUnitRateID1: {
+        currency: 'USD',
+        customUnitRateID: 'customUnitRateID1',
+        enabled: true,
+        name: 'Default Rate',
+        rate: 70,
+        subRates: [],
+    },
+};
+const distanceRateTransactionID1: TransactionCustomUnit = {
+    customUnitID: 'customUnitID1',
+    customUnitRateID: 'customUnitRateID1',
+    distanceUnit: 'mi',
+    name: 'Distance',
+};
+const distanceRateTransactionID2: TransactionCustomUnit = {
+    customUnitID: 'customUnitID2',
+    customUnitRateID: 'customUnitRateID2',
+    distanceUnit: 'mi',
+    name: 'Distance',
+};
+const defaultCustomUnitPolicyID1: Record<string, CustomUnit> = {
+    customUnitID1: {
+        attributes: {
+            unit: 'mi',
+        },
+        customUnitID: 'customUnitID1',
+        defaultCategory: 'Car',
+        enabled: true,
+        name: 'Distance',
+        rates: defaultDistanceRatePolicyID1,
+    },
+};
 
 describe('TransactionUtils', () => {
     beforeAll(() => {
@@ -520,4 +556,94 @@ describe('TransactionUtils', () => {
             expect(result).toEqual(expected);
         });
     });
+
+    describe('isUnreportedAndHasInvalidDistanceRateTransaction', () => {
+        it('should be false when transaction is null', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: defaultCustomUnitPolicyID1,
+            };
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(null, fakePolicy);
+            expect(result).toBe(false);
+        });
+        it('should be false when transaction is not distance type transaction', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: defaultCustomUnitPolicyID1,
+            };
+            const transaction: Transaction = {
+                ...generateTransaction(),
+                iouRequestType: CONST.IOU.REQUEST_TYPE.MANUAL,
+            };
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(transaction, fakePolicy);
+            expect(result).toBe(false);
+        });
+        it('should be false when transaction is reported', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: defaultCustomUnitPolicyID1,
+            };
+            const transaction: Transaction = {
+                ...generateTransaction(),
+                iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
+                reportID: '1',
+            };
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(transaction, fakePolicy);
+            expect(result).toBe(false);
+        });
+        it('should be false when transaction is unreported and has valid rate', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: defaultCustomUnitPolicyID1,
+            };
+            const transaction: Transaction = {
+                ...generateTransaction(),
+                iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
+                reportID: '0',
+                comment: {
+                    customUnit: distanceRateTransactionID1,
+                    type: 'customUnit',
+                },
+            };
+
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(transaction, fakePolicy);
+            expect(result).toBe(false);
+        });
+        it('should be false when transaction is unreported, has invalid rate but policy has default rate', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: defaultCustomUnitPolicyID1,
+            };
+            const transaction: Transaction = {
+                ...generateTransaction(),
+                iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
+                reportID: '0',
+                comment: {
+                    customUnit: distanceRateTransactionID2,
+                    type: 'customUnit',
+                },
+            };
+
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(transaction, fakePolicy);
+            expect(result).toBe(false);
+        });
+        it('should be true when transaction is unreported, has invalid rate and policy has no default rate', () => {
+            const fakePolicy: Policy = {
+                ...createRandomPolicy(0),
+                customUnits: {},
+            };
+            const transaction: Transaction = {
+                ...generateTransaction(),
+                iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
+                reportID: '0',
+                comment: {
+                    customUnit: distanceRateTransactionID2,
+                    type: 'customUnit',
+                },
+            };
+
+            const result = TransactionUtils.isUnreportedAndHasInvalidDistanceRateTransaction(transaction, fakePolicy);
+            expect(result).toBe(true);
+        });
+    });
 });
