Skip to content

Commit 1d0a987

Browse files
authored
Merge pull request #81936 from Expensify/cm-company-cards-last4-matching
Fix OAuth company cards showing as unassigned
2 parents 5d4aa8d + b6adeff commit 1d0a987

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

src/libs/CardUtils.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,31 @@ function lastFourNumbersFromCardName(cardName: string | undefined): string {
274274
return match[1];
275275
}
276276

277+
/**
278+
* Checks if a card matches a given card identifier (encrypted card number or card name).
279+
* Uses exact match for encrypted card numbers, and normalized string comparison
280+
* for card names to handle special character differences from OAuth providers (e.g., ® vs no ®).
281+
*
282+
* @param card - The card to check
283+
* @param encryptedCardNumber - The encrypted card number to match against
284+
* @param cardName - The card name to match against
285+
* @returns true if the card matches either identifier
286+
*/
287+
function isMatchingCard(card: Card, encryptedCardNumber: string, cardName: string): boolean {
288+
if (card.encryptedCardNumber === encryptedCardNumber) {
289+
return true;
290+
}
291+
292+
if (!card.cardName || !cardName) {
293+
return false;
294+
}
295+
296+
// Normalize both strings to remove special characters (®, ™, ©, etc.)
297+
// This handles differences between OAuth provider card names and stored card names
298+
const normalize = (str: string) => str.replaceAll(/[^\w\s-]/g, '').trim();
299+
return normalize(card.cardName) === normalize(cardName);
300+
}
301+
277302
function getMCardNumberString(cardNumber: string): string {
278303
return cardNumber.replaceAll(/\s/g, '');
279304
}
@@ -965,7 +990,7 @@ function isCardAlreadyAssigned(cardNumberToCheck: string, workspaceCardFeeds: On
965990
}
966991
const {cardList, ...assignedCards} = workspaceCards;
967992
return Object.values(assignedCards).some(
968-
(card) => card?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && (card?.encryptedCardNumber === cardNumberToCheck || card?.cardName === cardNumberToCheck),
993+
(card) => card && card.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isMatchingCard(card, cardNumberToCheck, cardNumberToCheck),
969994
);
970995
});
971996
}
@@ -1043,6 +1068,7 @@ export {
10431068
isCardConnectionBroken,
10441069
isSmartLimitEnabled,
10451070
lastFourNumbersFromCardName,
1071+
isMatchingCard,
10461072
hasIssuedExpensifyCard,
10471073
isExpensifyCardFullySetUp,
10481074
filterInactiveCards,

src/pages/workspace/companyCards/WorkspaceCompanyCardsTable/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import useOnyx from '@hooks/useOnyx';
1717
import useResponsiveLayout from '@hooks/useResponsiveLayout';
1818
import useThemeStyles from '@hooks/useThemeStyles';
1919
import {resetFailedWorkspaceCompanyCardUnassignment} from '@libs/actions/CompanyCards';
20-
import {getDefaultCardName, getPlaidInstitutionId} from '@libs/CardUtils';
20+
import {getDefaultCardName, getPlaidInstitutionId, isMatchingCard} from '@libs/CardUtils';
2121
import tokenizedSearch from '@libs/tokenizedSearch';
2222
import WorkspaceCompanyCardPageEmptyState from '@pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState';
2323
import WorkspaceCompanyCardsFeedAddedEmptyPage from '@pages/workspace/companyCards/WorkspaceCompanyCardsFeedAddedEmptyPage';
@@ -151,7 +151,7 @@ function WorkspaceCompanyCardsTable({
151151
const cardsData: WorkspaceCompanyCardTableItemData[] = isLoadingCards
152152
? []
153153
: (Object.entries(cardNamesToEncryptedCardNumberMapping ?? {}).map(([cardName, encryptedCardNumber]) => {
154-
const assignedCard = Object.values(assignedCards ?? {}).find((card: Card) => card.encryptedCardNumber === encryptedCardNumber || card.cardName === cardName);
154+
const assignedCard = Object.values(assignedCards ?? {}).find((card: Card) => isMatchingCard(card, encryptedCardNumber, cardName));
155155
const cardholder = assignedCard?.accountID ? personalDetails?.[assignedCard.accountID] : undefined;
156156

157157
return {

0 commit comments

Comments
 (0)