Skip to content

Commit f77b5c8

Browse files
authored
Merge pull request #88103 from Expensify/georgia-cards-filter
[Home Page] Expensify card - filter out $0 limit cards
2 parents 9b85c31 + a14cd98 commit f77b5c8

3 files changed

Lines changed: 85 additions & 3 deletions

File tree

src/libs/CardUtils.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,10 @@ function isCardPendingActivate(card?: Card) {
13271327
return card?.state === CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED;
13281328
}
13291329

1330+
function isCardWithCustomZeroLimit(card: Card): boolean {
1331+
return !!card.nameValuePairs?.hasCustomUnapprovedExpenseLimit && card.nameValuePairs?.unapprovedExpenseLimit === 0;
1332+
}
1333+
13301334
/**
13311335
* Check if a card has potential fraud that needs review.
13321336
* Returns true if the card has fraud type 'domain' or 'individual'.
@@ -1548,7 +1552,8 @@ function hasDisplayableAssignedCards(cardList: CardList | undefined): boolean {
15481552
CONST.EXPENSIFY_CARD.ACTIVE_STATES.includes(card.state ?? 0) &&
15491553
(isExpensifyCard(card) || !!card.domainName || isPersonalCard(card)) &&
15501554
card.cardName !== CONST.COMPANY_CARDS.CARD_NAME.CASH &&
1551-
(!isExpensifyCard(card) || !isExpiredCard(card)),
1555+
(!isExpensifyCard(card) || !isExpiredCard(card)) &&
1556+
(!isExpensifyCard(card) || !isCardWithCustomZeroLimit(card)),
15521557
);
15531558
}
15541559

@@ -1597,7 +1602,7 @@ function getDisplayableExpensifyCards(cardList: CardList | undefined): Card[] {
15971602

15981603
const activeCards = filterAllInactiveCards(cardList);
15991604
const activeExpensifyCards = Object.values(activeCards).filter(
1600-
(card) => isExpensifyCard(card) && !isExpiredCard(card) && card.cardName !== CONST.COMPANY_CARDS.CARD_NAME.CASH && !isTravelCard(card),
1605+
(card) => isExpensifyCard(card) && !isExpiredCard(card) && card.cardName !== CONST.COMPANY_CARDS.CARD_NAME.CASH && !isTravelCard(card) && !isCardWithCustomZeroLimit(card),
16011606
);
16021607

16031608
const sortedCards = lodashSortBy(activeExpensifyCards, getAssignedCardSortKey);
@@ -1805,6 +1810,7 @@ export {
18051810
getPersonalBankCardDetailsImage,
18061811
isCardPendingIssue,
18071812
isCardPendingActivate,
1813+
isCardWithCustomZeroLimit,
18081814
hasPendingExpensifyCardAction,
18091815
isExpensifyCardPendingAction,
18101816
getFundIdFromSettingsKey,

src/pages/home/TimeSensitiveSection/hooks/useTimeSensitiveCards.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import useOnyx from '@hooks/useOnyx';
2-
import {isCard, isCardPendingActivate, isCardPendingIssue, isCardWithPotentialFraud, isExpensifyCard} from '@libs/CardUtils';
2+
import {isCard, isCardPendingActivate, isCardPendingIssue, isCardWithCustomZeroLimit, isCardWithPotentialFraud, isExpensifyCard} from '@libs/CardUtils';
33
import ONYXKEYS from '@src/ONYXKEYS';
44
import type {Card} from '@src/types/onyx';
55

@@ -23,6 +23,10 @@ function useTimeSensitiveCards() {
2323
cardsWithFraud.push(card);
2424
}
2525

26+
if (isCardWithCustomZeroLimit(card)) {
27+
continue;
28+
}
29+
2630
const isPhysicalCard = !card.nameValuePairs?.isVirtual;
2731
if (!isPhysicalCard) {
2832
continue;

tests/unit/hooks/useTimeSensitiveCards.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,76 @@ describe('useTimeSensitiveCards', () => {
220220
expect(result.current.cardsWithFraud.at(0)?.cardID).toBe(1);
221221
expect(result.current.shouldShowReviewCardFraud).toBe(true);
222222
});
223+
224+
it('should exclude cards with custom $0 limit from shipping address to-dos', async () => {
225+
const zeroLimitCard: Card = {
226+
...createRandomExpensifyCard(1, {state: CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED}),
227+
nameValuePairs: {hasCustomUnapprovedExpenseLimit: true, unapprovedExpenseLimit: 0} as Card['nameValuePairs'],
228+
};
229+
const cardList: CardList = {'1': zeroLimitCard};
230+
231+
await Onyx.merge(ONYXKEYS.CARD_LIST, cardList);
232+
await waitForBatchedUpdates();
233+
234+
const {result} = renderHook(() => useTimeSensitiveCards());
235+
236+
expect(result.current.cardsNeedingShippingAddress).toHaveLength(0);
237+
expect(result.current.shouldShowAddShippingAddress).toBe(false);
238+
});
239+
240+
it('should exclude cards with custom $0 limit from activation to-dos', async () => {
241+
const zeroLimitCard: Card = {
242+
...createRandomExpensifyCard(1, {state: CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED}),
243+
nameValuePairs: {hasCustomUnapprovedExpenseLimit: true, unapprovedExpenseLimit: 0} as Card['nameValuePairs'],
244+
};
245+
const cardList: CardList = {'1': zeroLimitCard};
246+
247+
await Onyx.merge(ONYXKEYS.CARD_LIST, cardList);
248+
await waitForBatchedUpdates();
249+
250+
const {result} = renderHook(() => useTimeSensitiveCards());
251+
252+
expect(result.current.cardsNeedingActivation).toHaveLength(0);
253+
expect(result.current.shouldShowActivateCard).toBe(false);
254+
});
255+
256+
it('should not exclude cards without custom limit even if unapprovedExpenseLimit is 0', async () => {
257+
const groupLimitCard: Card = {
258+
...createRandomExpensifyCard(1, {state: CONST.EXPENSIFY_CARD.STATE.NOT_ACTIVATED}),
259+
nameValuePairs: {hasCustomUnapprovedExpenseLimit: false, unapprovedExpenseLimit: 0} as Card['nameValuePairs'],
260+
};
261+
const cardList: CardList = {'1': groupLimitCard};
262+
263+
await Onyx.merge(ONYXKEYS.CARD_LIST, cardList);
264+
await waitForBatchedUpdates();
265+
266+
const {result} = renderHook(() => useTimeSensitiveCards());
267+
268+
expect(result.current.cardsNeedingActivation).toHaveLength(1);
269+
expect(result.current.shouldShowActivateCard).toBe(true);
270+
});
271+
272+
it('should still show fraud alerts for cards with custom $0 limit', async () => {
273+
const zeroLimitFraudCard: Card = {
274+
...createRandomExpensifyCard(1, {
275+
state: CONST.EXPENSIFY_CARD.STATE.OPEN,
276+
fraud: CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN,
277+
possibleFraud: {triggerAmount: 1000, triggerMerchant: 'SUSPICIOUS MERCHANT', currency: 'USD', fraudAlertReportID: 123456},
278+
}),
279+
nameValuePairs: {
280+
hasCustomUnapprovedExpenseLimit: true,
281+
unapprovedExpenseLimit: 0,
282+
possibleFraud: {triggerAmount: 1000, triggerMerchant: 'SUSPICIOUS MERCHANT', currency: 'USD', fraudAlertReportID: 123456},
283+
} as Card['nameValuePairs'],
284+
};
285+
const cardList: CardList = {'1': zeroLimitFraudCard};
286+
287+
await Onyx.merge(ONYXKEYS.CARD_LIST, cardList);
288+
await waitForBatchedUpdates();
289+
290+
const {result} = renderHook(() => useTimeSensitiveCards());
291+
292+
expect(result.current.cardsWithFraud).toHaveLength(1);
293+
expect(result.current.shouldShowReviewCardFraud).toBe(true);
294+
});
223295
});

0 commit comments

Comments
 (0)