Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,16 +430,31 @@ function isSelectedFeedExpired(directFeed: DirectCardFeedData | undefined): bool
}

/** Returns list of cards which can be assigned */
function getFilteredCardList(list: WorkspaceCardsList | undefined, directFeed: DirectCardFeedData | undefined) {
function getFilteredCardList(list: WorkspaceCardsList | undefined, directFeed: DirectCardFeedData | undefined, workspaceCardFeeds: OnyxCollection<WorkspaceCardsList> = allWorkspaceCards) {
const {cardList: customFeedCardsToAssign, ...cards} = list ?? {};
const assignedCards = Object.values(cards).map((card) => card.cardName);

// Get cards assigned across all workspaces
const allWorkspaceAssignedCards = new Set<string>();
Object.values(workspaceCardFeeds ?? {}).forEach((workspaceCards) => {
if (!workspaceCards) {
return;
}
const {cardList, ...workspaceCardItems} = workspaceCards;
Object.values(workspaceCardItems).forEach((card) => {
if (!card.cardName) {
return;
}
allWorkspaceAssignedCards.add(card.cardName);
});
});

if (directFeed) {
const unassignedDirectFeedCards = directFeed.accountList.filter((cardNumber) => !assignedCards.includes(cardNumber));
const unassignedDirectFeedCards = directFeed.accountList.filter((cardNumber) => !assignedCards.includes(cardNumber) && !allWorkspaceAssignedCards.has(cardNumber));
return Object.fromEntries(unassignedDirectFeedCards.map((cardNumber) => [cardNumber, cardNumber]));
}

return Object.fromEntries(Object.entries(customFeedCardsToAssign ?? {}).filter(([cardNumber]) => !assignedCards.includes(cardNumber)));
return Object.fromEntries(Object.entries(customFeedCardsToAssign ?? {}).filter(([cardNumber]) => !assignedCards.includes(cardNumber) && !allWorkspaceAssignedCards.has(cardNumber)));
}

function hasOnlyOneCardToAssign(list: FilteredCardList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) {
const [searchText, setSearchText] = useState('');
const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD);
const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${feed}`, {selector: filterInactiveCards});
const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`);

const isEditing = assignCard?.isEditing;
const assigneeDisplayName = getPersonalDetailByEmail(assignCard?.data?.email ?? '')?.displayName ?? '';
const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed]);
const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[feed], workspaceCardFeeds);

const [cardSelected, setCardSelected] = useState(assignCard?.data?.encryptedCardNumber ?? '');
const [shouldShowError, setShouldShowError] = useState(false);
Expand Down
105 changes: 105 additions & 0 deletions tests/unit/CardUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,111 @@ describe('CardUtils', () => {
const cardsList = getFilteredCardList(undefined, undefined);
expect(cardsList).toStrictEqual({});
});

it('Should handle the case when all cards are already assigned in other workspaces', () => {
const assignedCard1 = 'CREDIT CARD...5566';
const assignedCard2 = 'CREDIT CARD...6677';

const mockAllWorkspaceCards = {
cards_888888_feed: {
'11111': {
accountID: 999999,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
cardID: 11111,
cardName: assignedCard1,
domainName: 'other-workspace.exfy',
state: 3,
},
'22222': {
accountID: 999999,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE,
cardID: 22222,
cardName: assignedCard2,
domainName: 'other-workspace.exfy',
state: 3,
},
},
} as unknown as OnyxCollection<WorkspaceCardsList>;

const customFeedWithAllAssignedCards = {
cardList: {
[assignedCard1]: 'ENCRYPTED_DATA',
[assignedCard2]: 'ENCRYPTED_DATA',
},
} as unknown as WorkspaceCardsList;
const filteredCards = getFilteredCardList(customFeedWithAllAssignedCards, undefined, mockAllWorkspaceCards);
expect(filteredCards).toStrictEqual({});
});

it('Should filter out cards that are already assigned in another workspace (custom feed)', () => {
const customFeedWorkspaceCardsList = {
'21310091': {
accountID: 18439984,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA,
cardID: 21310091,
cardName: '480801XXXXXX2554',
domainName: 'expensify-policy41314f4dc5ce25af.exfy',
fraud: 'none',
lastFourPAN: '2554',
lastUpdated: '',
lastScrape: '2024-11-27 11:00:53',
scrapeMinDate: '2024-10-17',
state: 3,
},
'21310092': {
accountID: 18439985,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.VISA,
cardID: 21310092,
cardName: '480801XXXXXX2666',
domainName: 'expensify-policy41314f4dc5ce25af.exfy',
fraud: 'none',
lastFourPAN: '2666',
lastUpdated: '',
lastScrape: '2024-11-27 11:00:53',
scrapeMinDate: '2024-10-17',
state: 3,
},
cardList: {
'480801XXXXXX2554': 'ENCRYPTED_CARD_NUMBER',
'480801XXXXXX2666': 'ENCRYPTED_CARD_NUMBER',
},
} as unknown as WorkspaceCardsList;

const filteredCards = getFilteredCardList(customFeedWorkspaceCardsList, undefined);
expect(filteredCards).toStrictEqual({});
});

it('Should filter out cards that are already assigned in another workspace (direct feed)', () => {
const assignedCard1 = 'CREDIT CARD...3344';
const assignedCard2 = 'CREDIT CARD...3355';
const unassignedCard = 'CREDIT CARD...6666';

const mockAllWorkspaceCards = {
cards_888888_feed: {
'67889': {
accountID: 999998,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE,
cardID: 67889,
cardName: assignedCard1,
domainName: 'other-workspace.exfy',
state: 3,
},
'67890': {
accountID: 999999,
bank: CONST.COMPANY_CARD.FEED_BANK_NAME.CAPITAL_ONE,
cardID: 67890,
cardName: assignedCard2,
domainName: 'other-workspace.exfy',
state: 3,
},
},
} as unknown as OnyxCollection<WorkspaceCardsList>;
const directFeedWithAssignedCard = {
accountList: [assignedCard1, assignedCard2, unassignedCard],
} as unknown as (typeof oAuthAccountDetails)[typeof CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE];
const filteredCards = getFilteredCardList(undefined, directFeedWithAssignedCard, mockAllWorkspaceCards);
expect(filteredCards).toStrictEqual({[`${unassignedCard}`]: unassignedCard});
});
});

describe('getFeedType', () => {
Expand Down