From 16ae04dd0fa2acfe8cc1d7ebbff712ed72f9c069 Mon Sep 17 00:00:00 2001 From: allgandaf Date: Fri, 27 Feb 2026 15:55:09 +0530 Subject: [PATCH 1/6] Add getCardSettings utility for nested card settings format --- src/libs/CardUtils.ts | 17 +++++++++++++++++ src/types/onyx/ExpensifyCardSettings.ts | 3 +++ src/types/onyx/index.ts | 2 ++ 3 files changed, 22 insertions(+) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 3ee077344b31..8c93e2402e9c 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -20,6 +20,7 @@ import type { CompanyCardFeed, CurrencyList, ExpensifyCardSettings, + ExpensifyCardSettingsBase, PersonalDetailsList, Policy, PrivatePersonalDetails, @@ -1019,6 +1020,21 @@ function isExpensifyCardFullySetUp(policy?: OnyxEntry, cardSettings?: On return !!(policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID); } +function getCardSettings(cardSettings: OnyxEntry, feedCountry?: string): ExpensifyCardSettingsBase | undefined { + if (!cardSettings) { + return undefined; + } + + if (feedCountry) { + const nested = cardSettings[feedCountry as keyof typeof cardSettings]; + if (nested && typeof nested === 'object' && !Array.isArray(nested)) { + return nested as ExpensifyCardSettingsBase; + } + } + + return cardSettings; +} + function isCardPendingIssue(card?: Card) { return card?.state === CONST.EXPENSIFY_CARD.STATE.STATE_NOT_ISSUED; } @@ -1315,6 +1331,7 @@ export { normalizeCardName, hasIssuedExpensifyCard, isExpensifyCardFullySetUp, + getCardSettings, filterAllInactiveCards, filterInactiveCards, isCardPendingIssue, diff --git a/src/types/onyx/ExpensifyCardSettings.ts b/src/types/onyx/ExpensifyCardSettings.ts index e8685d36f83d..aeee1ad6643b 100644 --- a/src/types/onyx/ExpensifyCardSettings.ts +++ b/src/types/onyx/ExpensifyCardSettings.ts @@ -69,6 +69,9 @@ type ExpensifyCardSettingsBase = { /** Model of Expensify card settings for a workspace - can have nested feed types from backend */ type ExpensifyCardSettings = OnyxCommon.OnyxValueWithOfflineFeedback< ExpensifyCardSettingsBase & { + /** Nested Expensify Card settings keyed by feed country from backend */ + // eslint-disable-next-line @typescript-eslint/naming-convention + US?: ExpensifyCardSettingsBase; /** Nested Travel Invoicing settings from backend */ // eslint-disable-next-line @typescript-eslint/naming-convention TRAVEL_US?: ExpensifyCardSettingsBase; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index f86ee284b367..82cc8f4d6b43 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -64,6 +64,7 @@ import type DuplicateWorkspace from './DuplicateWorkspace'; import type ExpenseRule from './ExpenseRule'; import type ExpensifyCardBankAccountMetadata from './ExpensifyCardBankAccountMetadata'; import type ExpensifyCardSettings from './ExpensifyCardSettings'; +import type {ExpensifyCardSettingsBase} from './ExpensifyCardSettings'; import type ExportTemplate from './ExportTemplate'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; @@ -200,6 +201,7 @@ export type { WorkspaceCardsList, ExpenseRule, ExpensifyCardSettings, + ExpensifyCardSettingsBase, ExpensifyCardBankAccountMetadata, FrequentlyUsedEmoji, Fund, From 762a0b53683fc35e5bf69c68a773e37f99348f88 Mon Sep 17 00:00:00 2001 From: allgandaf Date: Fri, 27 Feb 2026 15:58:54 +0530 Subject: [PATCH 2/6] Add tests for getCardSettings utility --- tests/unit/CardUtilsTest.ts | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 6317fd53b82c..b11596da4ba7 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -25,6 +25,7 @@ import { getCardFeedIcon, getCardFeedWithDomainID, getCardsByCardholderName, + getCardSettings, getCompanyCardDescription, getCompanyCardFeed, getCompanyFeeds, @@ -3182,4 +3183,72 @@ describe('CardUtils', () => { expect(result).toBe(`${environmentURL}/settings/wallet/personal-card/99999`); }); }); + + describe('getCardSettings', () => { + const flatSettings = { + paymentBankAccountID: 12345, + limit: 50000, + currentBalance: 1000, + remainingLimit: 49000, + } as ExpensifyCardSettings; + + const nestedSettings = { + paymentBankAccountID: 12345, + limit: 50000, + US: { + paymentBankAccountID: 67890, + limit: 30000, + currentBalance: 500, + }, + TRAVEL_US: { + paymentBankAccountID: 11111, + isEnabled: true, + }, + } as ExpensifyCardSettings; + + it('should return undefined when cardSettings is undefined', () => { + expect(getCardSettings(undefined)).toBeUndefined(); + }); + + it('should return undefined when cardSettings is null', () => { + expect(getCardSettings(null)).toBeUndefined(); + }); + + it('should return flat root when feedCountry is not provided', () => { + const result = getCardSettings(flatSettings); + expect(result).toBe(flatSettings); + }); + + it('should return flat root when feedCountry is undefined', () => { + const result = getCardSettings(flatSettings, undefined); + expect(result).toBe(flatSettings); + }); + + it('should return nested object when feedCountry matches a nested key', () => { + const result = getCardSettings(nestedSettings, 'US'); + expect(result).toEqual({ + paymentBankAccountID: 67890, + limit: 30000, + currentBalance: 500, + }); + }); + + it('should fall back to flat root when feedCountry key does not exist', () => { + const result = getCardSettings(nestedSettings, 'CA'); + expect(result).toBe(nestedSettings); + }); + + it('should return TRAVEL_US nested settings when feedCountry is TRAVEL_US', () => { + const result = getCardSettings(nestedSettings, 'TRAVEL_US'); + expect(result).toEqual({ + paymentBankAccountID: 11111, + isEnabled: true, + }); + }); + + it('should not return primitive values as nested settings', () => { + const result = getCardSettings(nestedSettings, 'limit'); + expect(result).toBe(nestedSettings); + }); + }); }); From d23f75f5d6e26872d6e2a277ae34609ed9f4757e Mon Sep 17 00:00:00 2001 From: allgandaf Date: Fri, 27 Feb 2026 15:58:59 +0530 Subject: [PATCH 3/6] Update card pages and hooks to use getCardSettings utility --- src/hooks/useDefaultFundID.ts | 5 +++-- src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 5 +++-- src/pages/workspace/WorkspaceOverviewPage.tsx | 5 +++-- .../ReconciliationAccountSettingsPage.tsx | 7 ++++--- .../expensifyCard/WorkspaceCardListLabels.tsx | 4 ++-- .../expensifyCard/WorkspaceCardSettingsPage.tsx | 10 ++++++---- .../expensifyCard/WorkspaceCardsListLabel.tsx | 8 +++++--- .../expensifyCard/WorkspaceExpensifyCardListPage.tsx | 7 ++++--- .../expensifyCard/WorkspaceExpensifyCardPage.tsx | 7 ++++--- .../expensifyCard/WorkspaceSettlementAccountPage.tsx | 11 ++++++----- .../WorkspaceSettlementFrequencyPage.tsx | 8 +++++--- 11 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/hooks/useDefaultFundID.ts b/src/hooks/useDefaultFundID.ts index 64ae63732eb3..7db4a5876cb9 100644 --- a/src/hooks/useDefaultFundID.ts +++ b/src/hooks/useDefaultFundID.ts @@ -1,6 +1,6 @@ import {useCallback} from 'react'; import type {OnyxCollection} from 'react-native-onyx'; -import {getFundIdFromSettingsKey} from '@libs/CardUtils'; +import {getCardSettings, getFundIdFromSettingsKey} from '@libs/CardUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ExpensifyCardSettings} from '@src/types/onyx'; @@ -15,6 +15,7 @@ function useDefaultFundID(policyID: string | undefined) { const workspaceAccountID = useWorkspaceAccountID(policyID); const [lastSelectedExpensifyCardFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED}${policyID}`); const [lastSelectedCardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${lastSelectedExpensifyCardFeed}`); + const lastSelectedSettings = getCardSettings(lastSelectedCardSettings); const getDomainFundID = useCallback( (cardSettings: OnyxCollection) => { @@ -36,7 +37,7 @@ function useDefaultFundID(policyID: string | undefined) { [getDomainFundID], ); - if (lastSelectedExpensifyCardFeed && lastSelectedCardSettings?.paymentBankAccountID) { + if (lastSelectedExpensifyCardFeed && lastSelectedSettings?.paymentBankAccountID) { return lastSelectedExpensifyCardFeed; } diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index 84fa8adcd2ca..4038726ca4ab 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -20,7 +20,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {enablePolicyTravel} from '@libs/actions/Policy/Travel'; -import {filterInactiveCards, getAllCardsForWorkspace, getCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; +import {filterInactiveCards, getAllCardsForWorkspace, getCardSettings, getCompanyFeeds, isSmartLimitEnabled as isSmartLimitEnabledUtil} from '@libs/CardUtils'; import {getLatestErrorField} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -115,7 +115,8 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro const policyData = usePolicyData(policyID); const defaultFundID = useDefaultFundID(policyID); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); - const paymentBankAccountID = cardSettings?.paymentBankAccountID; + const settings = getCardSettings(cardSettings); + const paymentBankAccountID = settings?.paymentBankAccountID; const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); diff --git a/src/pages/workspace/WorkspaceOverviewPage.tsx b/src/pages/workspace/WorkspaceOverviewPage.tsx index 070924079483..97706ae35c17 100644 --- a/src/pages/workspace/WorkspaceOverviewPage.tsx +++ b/src/pages/workspace/WorkspaceOverviewPage.tsx @@ -42,7 +42,7 @@ import { setIsComingFromGlobalReimbursementsFlow, updateWorkspaceAvatar, } from '@libs/actions/Policy/Policy'; -import {filterInactiveCards} from '@libs/CardUtils'; +import {filterInactiveCards, getCardSettings} from '@libs/CardUtils'; import {getLatestErrorField, getLatestErrorMessage} from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -97,7 +97,8 @@ function WorkspaceOverviewPage({policyDraft, policy: policyProp, route}: Workspa const policyID = policy?.id; const defaultFundID = useDefaultFundID(policyID); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); - const isBankAccountVerified = !!cardSettings?.paymentBankAccountID; + const settings = getCardSettings(cardSettings); + const isBankAccountVerified = !!settings?.paymentBankAccountID; const isPolicyAdmin = isPolicyAdminPolicyUtils(policy); const outputCurrency = policy?.outputCurrency ?? ''; diff --git a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx index 172bd948d0b0..0cae6b5a8f6d 100644 --- a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx +++ b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx @@ -12,7 +12,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getConnectionNameFromRouteParam} from '@libs/AccountingUtils'; import {getLastFourDigits} from '@libs/BankAccountUtils'; -import {getEligibleBankAccountsForCard} from '@libs/CardUtils'; +import {getCardSettings, getEligibleBankAccountsForCard} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -43,12 +43,13 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); - const paymentBankAccountID = cardSettings?.paymentBankAccountID; + const settings = getCardSettings(cardSettings); + const paymentBankAccountID = settings?.paymentBankAccountID; const selectedBankAccount = useMemo(() => bankAccountList?.[paymentBankAccountID?.toString() ?? ''], [paymentBankAccountID, bankAccountList]); const bankAccountNumber = useMemo(() => selectedBankAccount?.accountData?.accountNumber ?? '', [selectedBankAccount?.accountData?.accountNumber]); const settlementAccountEnding = getLastFourDigits(bankAccountNumber); - const domainName = cardSettings?.domainName ?? getDomainNameForPolicy(policyID); + const domainName = settings?.domainName ?? getDomainNameForPolicy(policyID); const {environmentURL} = useEnvironment(); const options = useMemo(() => { diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx index 7d5f71ae7f33..0bddc64ad1f3 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardListLabels.tsx @@ -7,7 +7,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ExpensifyCardSettings} from '@src/types/onyx'; +import type {ExpensifyCardSettingsBase} from '@src/types/onyx'; import WorkspaceCardsListLabel from './WorkspaceCardsListLabel'; type WorkspaceCardListLabelsProps = { @@ -15,7 +15,7 @@ type WorkspaceCardListLabelsProps = { policyID: string; /** Card settings */ - cardSettings: ExpensifyCardSettings | undefined; + cardSettings: ExpensifyCardSettingsBase | undefined; }; function WorkspaceCardListLabels({policyID, cardSettings}: WorkspaceCardListLabelsProps) { diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardSettingsPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardSettingsPage.tsx index c09234290093..4c314f971fa8 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardSettingsPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardSettingsPage.tsx @@ -11,6 +11,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getLastFourDigits} from '@libs/BankAccountUtils'; +import {getCardSettings} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -30,11 +31,12 @@ function WorkspaceCardSettingsPage({route}: WorkspaceCardSettingsPageProps) { const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); + const settings = getCardSettings(cardSettings); - const paymentBankAccountID = cardSettings?.paymentBankAccountID; - const paymentBankAccountNumber = cardSettings?.paymentBankAccountNumber; - const isMonthlySettlementAllowed = cardSettings?.isMonthlySettlementAllowed ?? false; - const settlementFrequency = cardSettings?.monthlySettlementDate ? CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.MONTHLY : CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; + const paymentBankAccountID = settings?.paymentBankAccountID; + const paymentBankAccountNumber = settings?.paymentBankAccountNumber; + const isMonthlySettlementAllowed = settings?.isMonthlySettlementAllowed ?? false; + const settlementFrequency = settings?.monthlySettlementDate ? CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.MONTHLY : CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; const isSettlementFrequencyBlocked = !isMonthlySettlementAllowed && settlementFrequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; const bankAccountNumber = bankAccountList?.[paymentBankAccountID?.toString() ?? '']?.accountData?.accountNumber ?? paymentBankAccountNumber ?? ''; diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx index a5d5fadae8ec..69286f45eeff 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardsListLabel.tsx @@ -18,6 +18,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import {getCardSettings} from '@libs/CardUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -59,9 +60,10 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr const settlementCurrency = useCurrencyForExpensifyCard({policyID}); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); + const settings = getCardSettings(cardSettings); const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${defaultFundID}`); const icons = useMemoizedLazyExpensifyIcons(['Info'] as const); - const paymentBankAccountID = cardSettings?.paymentBankAccountID; + const paymentBankAccountID = settings?.paymentBankAccountID; const isLessThanMediumScreen = isMediumScreenWidth || shouldUseNarrowLayout; @@ -88,13 +90,13 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr }, [isVisible, windowWidth]); const requestLimitIncrease = () => { - requestExpensifyCardLimitIncrease(cardSettings?.paymentBankAccountID); + requestExpensifyCardLimitIncrease(settings?.paymentBankAccountID); setVisible(false); navigateToConciergeChat(conciergeReportID, false); }; const isCurrentBalanceType = type === CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE; - const isSettleBalanceButtonDisplayed = !!cardSettings?.isMonthlySettlementAllowed && !cardManualBilling && isCurrentBalanceType; + const isSettleBalanceButtonDisplayed = !!settings?.isMonthlySettlementAllowed && !cardManualBilling && isCurrentBalanceType; const isSettleDateTextDisplayed = !!cardManualBilling && isCurrentBalanceType; const settlementDate = isSettleDateTextDisplayed ? format(addDays(new Date(), 1), CONST.DATE.FNS_FORMAT_STRING) : ''; diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx index e99afac82961..0c24698550c0 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx @@ -31,7 +31,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {clearIssueNewCardFormData, setIssueNewCardStepAndData} from '@libs/actions/Card'; import {clearDeletePaymentMethodError} from '@libs/actions/PaymentMethods'; -import {filterCardsByPersonalDetails, getCardsByCardholderName, sortCardsByCardholderName} from '@libs/CardUtils'; +import {filterCardsByPersonalDetails, getCardsByCardholderName, getCardSettings, sortCardsByCardholderName} from '@libs/CardUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDescriptionForPolicyDomainCard, getMemberAccountIDsForWorkspace} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -71,6 +71,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [cardOnWaitlist] = useOnyx(`${ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST}${policyID}`); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${fundID}`); + const settings = getCardSettings(cardSettings); const allExpensifyCardFeeds = useExpensifyCardFeeds(policyID); const shouldShowSelector = Object.keys(allExpensifyCardFeeds ?? {}).length > 1; @@ -195,7 +196,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList, fundID}: WorkspaceExp {allCards.length > CONST.SEARCH_ITEM_LIMIT && ( Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_SELECT_FEED.getRoute(policyID))} CardFeedIcon={cardFeedIcon} feedName={translate('workspace.common.expensifyCard')} - supportingText={getDescriptionForPolicyDomainCard(cardSettings?.domainName ?? '', policyCollection)} + supportingText={getDescriptionForPolicyDomainCard(settings?.domainName ?? '', policyCollection)} /> {isBankAccountVerified && getHeaderButtons()} diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx index 252fdba24625..2608f3c5ad3f 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx @@ -4,7 +4,7 @@ import useDefaultFundID from '@hooks/useDefaultFundID'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import {updateSelectedExpensifyCardFeed} from '@libs/actions/Card'; -import {filterInactiveCards} from '@libs/CardUtils'; +import {filterInactiveCards, getCardSettings} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -22,6 +22,7 @@ function WorkspaceExpensifyCardPage({route}: WorkspaceExpensifyCardPageProps) { const defaultFundID = useDefaultFundID(policyID); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); + const settings = getCardSettings(cardSettings); const [cardsList] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${defaultFundID}_${CONST.EXPENSIFY_CARD.BANK}`, {selector: filterInactiveCards}); const fetchExpensifyCards = useCallback(() => { @@ -35,8 +36,8 @@ function WorkspaceExpensifyCardPage({route}: WorkspaceExpensifyCardPageProps) { fetchExpensifyCards(); }, [fetchExpensifyCards]); - const paymentBankAccountID = cardSettings?.paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID; - const isLoading = !isOffline && (!cardSettings || cardSettings.isLoading); + const paymentBankAccountID = settings?.paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID; + const isLoading = !isOffline && (!cardSettings || settings?.isLoading); const renderContent = () => { if (!!isLoading && !paymentBankAccountID) { diff --git a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx index ea3a3ef70776..051740721fd6 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx @@ -15,7 +15,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {getRouteParamForConnection} from '@libs/AccountingUtils'; import {openPolicyAccountingPage} from '@libs/actions/PolicyConnections'; import {getLastFourDigits} from '@libs/BankAccountUtils'; -import {getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from '@libs/CardUtils'; +import {getCardSettings, getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -45,13 +45,14 @@ function WorkspaceSettlementAccountPage({route}: WorkspaceSettlementAccountPageP const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const [bankAccountsList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); + const settings = getCardSettings(cardSettings); const [continuousReconciliation] = useOnyx(`${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION}${defaultFundID}`); const [reconciliationConnection] = useOnyx(`${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION}${defaultFundID}`); const isUkEuCurrencySupported = useExpensifyCardUkEuSupported(policyID); - const paymentBankAccountID = cardSettings?.paymentBankAccountID; - const paymentBankAccountNumberFromCardSettings = cardSettings?.paymentBankAccountNumber; - const paymentBankAccountAddressName = cardSettings?.paymentBankAccountAddressName; + const paymentBankAccountID = settings?.paymentBankAccountID; + const paymentBankAccountNumberFromCardSettings = settings?.paymentBankAccountNumber; + const paymentBankAccountAddressName = settings?.paymentBankAccountAddressName; const paymentBankAccountNumber = bankAccountsList?.[paymentBankAccountID?.toString() ?? '']?.accountData?.accountNumber ?? paymentBankAccountNumberFromCardSettings ?? ''; const getEligibleBankAccounts = () => { @@ -62,7 +63,7 @@ function WorkspaceSettlementAccountPage({route}: WorkspaceSettlementAccountPageP }; const eligibleBankAccounts = getEligibleBankAccounts(); - const domainName = cardSettings?.domainName ?? getDomainNameForPolicy(policyID); + const domainName = settings?.domainName ?? getDomainNameForPolicy(policyID); const hasActiveAccountingConnection = !!(policy?.connections && Object.keys(policy.connections).length > 0); const fetchPolicyAccountingData = useCallback(() => { diff --git a/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx index 951e964c45f3..d3f442dce982 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx @@ -10,6 +10,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {updateSettlementFrequency as updateSettlementFrequencyUtil} from '@libs/actions/Card'; +import {getCardSettings} from '@libs/CardUtils'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -28,9 +29,10 @@ function WorkspaceSettlementFrequencyPage({route}: WorkspaceSettlementFrequencyP const defaultFundID = useDefaultFundID(policyID); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); + const settings = getCardSettings(cardSettings); - const shouldShowMonthlyOption = cardSettings?.isMonthlySettlementAllowed ?? false; - const selectedFrequency = cardSettings?.monthlySettlementDate ? CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.MONTHLY : CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; + const shouldShowMonthlyOption = settings?.isMonthlySettlementAllowed ?? false; + const selectedFrequency = settings?.monthlySettlementDate ? CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.MONTHLY : CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; const isSettlementFrequencyBlocked = !shouldShowMonthlyOption && selectedFrequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; const data = useMemo(() => { @@ -56,7 +58,7 @@ function WorkspaceSettlementFrequencyPage({route}: WorkspaceSettlementFrequencyP }, [translate, shouldShowMonthlyOption, selectedFrequency]); const updateSettlementFrequency = (value: ValueOf) => { - updateSettlementFrequencyUtil(defaultFundID, value, cardSettings?.monthlySettlementDate); + updateSettlementFrequencyUtil(defaultFundID, value, settings?.monthlySettlementDate); }; return ( From 3156a2d5e785769047e5d11a06db409dbf0708fe Mon Sep 17 00:00:00 2001 From: allgandaf Date: Fri, 27 Feb 2026 16:04:04 +0530 Subject: [PATCH 4/6] Fix null type in getCardSettings test --- tests/unit/CardUtilsTest.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index b11596da4ba7..a20ae5807bb2 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -3211,7 +3211,9 @@ describe('CardUtils', () => { }); it('should return undefined when cardSettings is null', () => { - expect(getCardSettings(null)).toBeUndefined(); + // OnyxEntry may resolve to undefined rather than null, + // but we cast to cover runtime safety + expect(getCardSettings(null as unknown as undefined)).toBeUndefined(); }); it('should return flat root when feedCountry is not provided', () => { From b0eb48312986c08492aa2e4334c27925b76e7abd Mon Sep 17 00:00:00 2001 From: allgandaf Date: Sat, 28 Feb 2026 19:42:25 +0530 Subject: [PATCH 5/6] Rename nested to feedCountryCardSettings --- src/libs/CardUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index 8c93e2402e9c..da9b0b5a4caa 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -1026,9 +1026,9 @@ function getCardSettings(cardSettings: OnyxEntry, feedCou } if (feedCountry) { - const nested = cardSettings[feedCountry as keyof typeof cardSettings]; - if (nested && typeof nested === 'object' && !Array.isArray(nested)) { - return nested as ExpensifyCardSettingsBase; + const feedCountryCardSettings = cardSettings[feedCountry as keyof typeof cardSettings]; + if (feedCountryCardSettings && typeof feedCountryCardSettings === 'object' && !Array.isArray(feedCountryCardSettings)) { + return feedCountryCardSettings as ExpensifyCardSettingsBase; } } From f314957a4f885b1cdbd65e50d67eadf446f219a4 Mon Sep 17 00:00:00 2001 From: allgandaf Date: Mon, 2 Mar 2026 10:53:16 +0530 Subject: [PATCH 6/6] Fix hasOnceLoaded access to use cardSettings instead of settings --- .../workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx index a3cdddd8142a..ece7d62148ff 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardPage.tsx @@ -37,7 +37,7 @@ function WorkspaceExpensifyCardPage({route}: WorkspaceExpensifyCardPageProps) { }, [fetchExpensifyCards]); const paymentBankAccountID = settings?.paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID; - const isLoading = !isOffline && (!cardSettings || settings?.isLoading) && !settings?.hasOnceLoaded; + const isLoading = !isOffline && (!cardSettings || settings?.isLoading) && !cardSettings?.hasOnceLoaded; const renderContent = () => { if (isLoading) {