Skip to content
3 changes: 3 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3478,6 +3478,9 @@ const CONST = {
LAST_BUSINESS_DAY_OF_MONTH: 'LAST_BUSINESS_DAY_OF_MONTH',
CUSTOM_DAY_OF_MONTH: 'CUSTOM_DAY_OF_MONTH',
},
CARD_NAME: {
CASH: '__CASH__',
},
CARD_LIST_THRESHOLD: 8,
DEFAULT_EXPORT_TYPE: 'default',
EXPORT_CARD_TYPES: {
Expand Down
18 changes: 5 additions & 13 deletions src/components/TransactionItemRow/DataCells/TypeCell.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import React from 'react';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';

Check warning on line 3 in src/components/TransactionItemRow/DataCells/TypeCell.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check warning on line 3 in src/components/TransactionItemRow/DataCells/TypeCell.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used. Direct imports from @components/Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details
import TextWithTooltip from '@components/TextWithTooltip';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {isExpensifyCardTransaction, isPending} from '@libs/TransactionUtils';
import {getTransactionType, isExpensifyCardTransaction, isPending} from '@libs/TransactionUtils';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type TransactionDataCellProps from './TransactionDataCellProps';

// If the transaction is cash, it has the type CONST.EXPENSE.TYPE.CASH_CARD_NAME.
// If there is no credit card name, it means it couldn't be a card transaction,
// so we assume it's cash. Any other type is treated as a card transaction.
// same in getTypeText
const getType = (cardName?: string) => {
if (!cardName || cardName.includes(CONST.EXPENSE.TYPE.CASH_CARD_NAME)) {
return CONST.SEARCH.TRANSACTION_TYPE.CASH;
}
return CONST.SEARCH.TRANSACTION_TYPE.CARD;
};

const getTypeIcon = (type?: string) => {
switch (type) {
case CONST.SEARCH.TRANSACTION_TYPE.CARD:
Expand All @@ -47,8 +38,9 @@

function TypeCell({transactionItem, shouldUseNarrowLayout, shouldShowTooltip}: TransactionDataCellProps) {
const {translate} = useLocalize();
const [cardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
const theme = useTheme();
const type = transactionItem.transactionType ?? getType(transactionItem.cardName);
const type = getTransactionType(transactionItem, cardList);
const isPendingExpensifyCardTransaction = isExpensifyCardTransaction(transactionItem) && isPending(transactionItem);
const typeIcon = isPendingExpensifyCardTransaction ? Expensicons.CreditCardHourglass : getTypeIcon(type);
const typeText = isPendingExpensifyCardTransaction ? 'iou.pending' : getTypeText(type);
Expand Down
3 changes: 0 additions & 3 deletions src/components/TransactionItemRow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import type {ValueOf} from 'type-fest';
import Checkbox from '@components/Checkbox';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';

Check warning on line 7 in src/components/TransactionItemRow/index.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details

Check warning on line 7 in src/components/TransactionItemRow/index.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'@components/Icon/Expensicons' import is restricted from being used. Direct imports from @components/Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details
import type {TransactionWithOptionalHighlight} from '@components/MoneyRequestReportView/MoneyRequestReportTransactionList';
import {PressableWithFeedback} from '@components/Pressable';
import RadioButton from '@components/RadioButton';
Expand Down Expand Up @@ -75,9 +75,6 @@
/** information about whether to show the description, that is provided on Reports page */
shouldShowDescription?: boolean;

/** Type of transaction */
transactionType?: ValueOf<typeof CONST.SEARCH.TRANSACTION_TYPE>;

/** Precomputed violations */
violations?: TransactionViolation[];

Expand Down
34 changes: 34 additions & 0 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import IntlStore from '@src/languages/IntlStore';
import ONYXKEYS from '@src/ONYXKEYS';
import type {
CardList,
OnyxInputOrEntry,
Policy,
PolicyCategories,
Expand Down Expand Up @@ -117,7 +118,7 @@
};

let allReports: OnyxCollection<Report> = {};
Onyx.connect({

Check warning on line 121 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -126,7 +127,7 @@
});

let allTransactionViolations: OnyxCollection<TransactionViolations> = {};
Onyx.connect({

Check warning on line 130 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => (allTransactionViolations = value),
Expand All @@ -134,7 +135,7 @@

let deprecatedCurrentUserEmail = '';
let deprecatedCurrentUserAccountID = -1;
Onyx.connect({

Check warning on line 138 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (val) => {
deprecatedCurrentUserEmail = val?.email ?? '';
Expand Down Expand Up @@ -256,6 +257,38 @@
return getRequestType(transaction);
}

/**
* Determines the transaction type based on custom unit name or card name.
* Returns 'distance' for Distance transactions, 'perDiem' for Per Diem International transactions,
* 'cash' for cash transactions, or 'card' for card transactions.
*
* @param transaction - The transaction to check
* @param cardList - Optional card list to check for cash transactions
* @returns The transaction type: 'distance', 'perDiem', 'cash', or 'card'
*/
function getTransactionType(transaction: OnyxEntry<Transaction>, cardList?: CardList): ValueOf<typeof CONST.SEARCH.TRANSACTION_TYPE> {
const customUnitName = transaction?.comment?.customUnit?.name;

if (customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE) {
return CONST.SEARCH.TRANSACTION_TYPE.DISTANCE;
}

if (customUnitName === CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL) {
return CONST.SEARCH.TRANSACTION_TYPE.PER_DIEM;
}

const cardID = transaction?.cardID;
if (cardID && cardList?.[cardID]?.cardName === CONST.COMPANY_CARDS.CARD_NAME.CASH) {
return CONST.SEARCH.TRANSACTION_TYPE.CASH;
}

if (!transaction?.cardName || transaction?.cardName?.includes(CONST.EXPENSE.TYPE.CASH_CARD_NAME)) {
return CONST.SEARCH.TRANSACTION_TYPE.CASH;
}

return CONST.SEARCH.TRANSACTION_TYPE.CARD;
}

function isManualRequest(transaction: Transaction): boolean {
// This is used during the expense creation flow before the transaction has been saved to the server
if (lodashHas(transaction, 'iouRequestType')) {
Expand Down Expand Up @@ -2059,6 +2092,7 @@
getDescription,
getRequestType,
getExpenseType,
getTransactionType,
isManualRequest,
isScanRequest,
getAmount,
Expand Down
7 changes: 0 additions & 7 deletions src/types/onyx/SearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,6 @@ type SearchTransaction = {
/** The transaction category */
category: string;

/** The type of request */
transactionType: ValueOf<typeof CONST.SEARCH.TRANSACTION_TYPE>;

/** The ID of the parent of the transaction */
parentTransactionID?: string;

Expand Down Expand Up @@ -372,9 +369,6 @@ type SearchWithdrawalIDGroup = {
debitPosted: string;
};

/** Types of searchable transactions */
type SearchTransactionType = ValueOf<typeof CONST.SEARCH.TRANSACTION_TYPE>;

/**
* A utility type that creates a record where all keys are strings that start with a specified prefix.
*/
Expand Down Expand Up @@ -410,7 +404,6 @@ export type {
ListItemDataType,
SearchTask,
SearchTransaction,
SearchTransactionType,
SearchTransactionAction,
SearchDataTypes,
// eslint-disable-next-line @typescript-eslint/no-deprecated
Expand Down
1 change: 0 additions & 1 deletion tests/unit/ReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,6 @@ describe('ReportUtils', () => {
amount: 1000,
currency: 'USD',
merchant: 'Test Merchant',
transactionType: 'cash',
created: testDate,
modifiedMerchant: 'Test Merchant',
} as SearchTransaction;
Expand Down
18 changes: 2 additions & 16 deletions tests/unit/Search/SearchUIUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,6 @@ const searchResults: OnyxTypes.SearchResults = {
tag: '',
transactionID,
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -428,7 +427,6 @@ const searchResults: OnyxTypes.SearchResults = {
tag: '',
transactionID: transactionID2,
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -465,7 +463,6 @@ const searchResults: OnyxTypes.SearchResults = {
tag: '',
transactionID: transactionID3,
transactionThreadReportID: '8287398995021380',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -501,7 +498,6 @@ const searchResults: OnyxTypes.SearchResults = {
tag: '',
transactionID: transactionID4,
transactionThreadReportID: '1014872441234902',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -812,7 +808,6 @@ const transactionsListItems = [
},
transactionID: '1',
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -874,7 +869,6 @@ const transactionsListItems = [
},
transactionID: '2',
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -920,7 +914,6 @@ const transactionsListItems = [
tag: '',
transactionID: '3',
transactionThreadReportID: '8287398995021380',
transactionType: 'cash',
from: {
accountID: 18439984,
avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_3.png',
Expand Down Expand Up @@ -983,7 +976,6 @@ const transactionsListItems = [
tag: '',
transactionID: '4',
transactionThreadReportID: '1014872441234902',
transactionType: 'cash',
from: {
accountID: 18439984,
avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_3.png',
Expand Down Expand Up @@ -1105,7 +1097,6 @@ const transactionReportGroupListItems = [
},
transactionID: '1',
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -1210,7 +1201,6 @@ const transactionReportGroupListItems = [
},
transactionID: '2',
transactionThreadReportID: '456',
transactionType: 'cash',
receipt: undefined,
taxAmount: undefined,
mccGroup: undefined,
Expand Down Expand Up @@ -1786,7 +1776,6 @@ describe('SearchUIUtils', () => {
[`transactions_${distanceTransactionID}`]: {
...searchResults.data[`transactions_${transactionID}`],
transactionID: distanceTransactionID,
transactionType: CONST.SEARCH.TRANSACTION_TYPE.DISTANCE,
iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
},
},
Expand All @@ -1805,7 +1794,7 @@ describe('SearchUIUtils', () => {
expect(distanceTransaction).toBeDefined();
expect(distanceTransaction?.iouRequestType).toBe(CONST.IOU.REQUEST_TYPE.DISTANCE);

const expectedPropertyCount = 52;
const expectedPropertyCount = 51;
expect(Object.keys(distanceTransaction ?? {}).length).toBe(expectedPropertyCount);
});

Expand All @@ -1818,7 +1807,6 @@ describe('SearchUIUtils', () => {
[`transactions_${distanceTransactionID}`]: {
...searchResults.data[`transactions_${transactionID}`],
transactionID: distanceTransactionID,
transactionType: CONST.SEARCH.TRANSACTION_TYPE.DISTANCE,
iouRequestType: CONST.IOU.REQUEST_TYPE.DISTANCE,
},
},
Expand All @@ -1839,7 +1827,7 @@ describe('SearchUIUtils', () => {
expect(distanceTransaction).toBeDefined();
expect(distanceTransaction?.iouRequestType).toBe(CONST.IOU.REQUEST_TYPE.DISTANCE);

const expectedPropertyCount = 52;
const expectedPropertyCount = 51;
expect(Object.keys(distanceTransaction ?? {}).length).toBe(expectedPropertyCount);
});

Expand Down Expand Up @@ -2419,7 +2407,6 @@ describe('SearchUIUtils', () => {
tag: '',
transactionID: '1805965960759424086',
transactionThreadReportID: '4139222832581831',
transactionType: 'cash',
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
convertedAmount: -5000,
convertedCurrency: 'USD',
Expand Down Expand Up @@ -2547,7 +2534,6 @@ describe('SearchUIUtils', () => {
tag: '',
transactionID: '1805965960759424086',
transactionThreadReportID: '4139222832581831',
transactionType: 'cash',
convertedAmount: -5000,
convertedCurrency: 'USD',
},
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/Search/handleActionButtonPressTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ const mockReportItemWithHold = {
tag: '',
transactionID: '1049531721038862176',
transactionThreadReportID: '2957345659269055',
transactionType: 'cash',
from: {
accountID: 1206,
avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/default-avatar_7.png',
Expand Down Expand Up @@ -202,7 +201,6 @@ const mockReportItemWithHold = {
tag: '',
transactionID: '5345995386715609966',
transactionThreadReportID: '740282333335072',
transactionType: 'cash',
from: {
accountID: 1206,
avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/default-avatar_7.png',
Expand Down
1 change: 0 additions & 1 deletion tests/unit/TransactionGroupListItemTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const mockTransaction: TransactionListItemType = {
tag: '',
transactionID: '1',
transactionThreadReportID: '2925191332104975',
transactionType: 'cash',
action: 'approve',
allActions: ['approve'],
formattedFrom: 'Main Applause QA',
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/TransactionUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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, Report, Transaction} from '../../src/types/onyx';
import type {CardList} from '../../src/types/onyx/Card';
import createRandomPolicy, {createCategoryTaxExpenseRules} from '../utils/collections/policies';
import {createRandomReport} from '../utils/collections/reports';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
Expand Down Expand Up @@ -399,6 +400,58 @@ describe('TransactionUtils', () => {
});
});

describe('getTransactionType', () => {
it('returns card when the transaction is null', () => {
expect(TransactionUtils.getTransactionType(null as unknown as Transaction)).toBe(CONST.SEARCH.TRANSACTION_TYPE.CASH);
});

it('returns distance when the transaction has a distance custom unit', () => {
const transaction = generateTransaction({
comment: {
customUnit: {
name: CONST.CUSTOM_UNITS.NAME_DISTANCE,
},
},
});

expect(TransactionUtils.getTransactionType(transaction)).toBe(CONST.SEARCH.TRANSACTION_TYPE.DISTANCE);
});

it('returns per diem when the transaction has an international per diem custom unit', () => {
const transaction = generateTransaction({
comment: {
customUnit: {
name: CONST.CUSTOM_UNITS.NAME_PER_DIEM_INTERNATIONAL,
},
},
});

expect(TransactionUtils.getTransactionType(transaction)).toBe(CONST.SEARCH.TRANSACTION_TYPE.PER_DIEM);
});

it('returns cash when the transaction cardID maps to a cash card in the card list', () => {
const cardID = 101;
const cardList = {
[cardID]: {
cardName: '__CASH__',
},
} as unknown as CardList;
const transaction = generateTransaction({
cardID,
});

expect(TransactionUtils.getTransactionType(transaction, cardList)).toBe(CONST.SEARCH.TRANSACTION_TYPE.CASH);
});

it('returns cash when the transaction card name includes the cash card name substring', () => {
const transaction = generateTransaction({
cardName: `Example ${CONST.EXPENSE.TYPE.CASH_CARD_NAME}`,
});

expect(TransactionUtils.getTransactionType(transaction)).toBe(CONST.SEARCH.TRANSACTION_TYPE.CASH);
});
});

describe('shouldShowRTERViolationMessage', () => {
it('should return true if transaction is receipt being scanned', () => {
const transaction = generateTransaction({
Expand Down
Loading