diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index d1ea19a37905..e8ae7a8a045b 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -1105,34 +1105,61 @@ function isExpensifyCardFullySetUp(policy?: OnyxEntry, cardSettings?: On return !!(policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID); } -function getCardSettings(cardSettings: OnyxEntry, feedCountry?: string): ExpensifyCardSettingsBase | undefined { +/** + * The set of valid card program keys used to key nested settings in ExpensifyCardSettings. + * 'US' and 'GB' are geo-based programs, 'CURRENT' is the legacy pre-2024 US program, + * and 'TRAVEL_US' is the travel invoicing program. These map directly to the keys + * the backend nests card settings under. + */ +type CardProgramKey = typeof CONST.COUNTRY.US | typeof CONST.EXPENSIFY_CARD.CARD_PROGRAM.CURRENT | typeof CONST.COUNTRY.GB | typeof CONST.TRAVEL.PROGRAM_TRAVEL_US; + +/** + * Detects which card program key exists in the card settings object. + * Returns the first matching program key (US, CURRENT, or GB), or undefined if none found. + * Used to determine the correct nested key for optimistic writes. + */ +function getCardProgramKey(cardSettings: OnyxEntry): CardProgramKey | undefined { + if (!cardSettings) { + return undefined; + } + + const programKeys: CardProgramKey[] = [CONST.COUNTRY.US, CONST.EXPENSIFY_CARD.CARD_PROGRAM.CURRENT, CONST.COUNTRY.GB]; + return programKeys.find((key) => { + const value = cardSettings[key]; + return value !== null && typeof value === 'object' && !Array.isArray(value); + }); +} + +function getCardSettings(cardSettings: OnyxEntry, programKey?: CardProgramKey): ExpensifyCardSettingsBase | undefined { if (!cardSettings) { return undefined; } - const getMergedProgramSettings = (programKey: string): ExpensifyCardSettingsBase | undefined => { - const programSettings = cardSettings[programKey as keyof typeof cardSettings]; + const getMergedProgramSettings = (key: CardProgramKey): ExpensifyCardSettingsBase | undefined => { + const programSettings = cardSettings[key]; if (programSettings && typeof programSettings === 'object' && !Array.isArray(programSettings)) { // Nested program values take precedence — they are the authoritative source for - // program-specific fields once the backend sends the full nested format (Phase 2). - return {...cardSettings, ...(programSettings as ExpensifyCardSettingsBase)} as ExpensifyCardSettingsBase; + // program-specific fields (e.g. paymentBankAccountID, monthlySettlementDate). + return {...cardSettings, ...programSettings} as ExpensifyCardSettingsBase; } return undefined; }; - if (feedCountry) { - return getMergedProgramSettings(feedCountry) ?? cardSettings; + if (programKey) { + return getMergedProgramSettings(programKey); } - // Auto-detect: try known card programs in priority order so callers that - // don't pass feedCountry still get the right program sub-object when the - // backend sends nested settings (Phase 2 of fixing shared Onyx key). - const result = getMergedProgramSettings(CONST.COUNTRY.US) ?? getMergedProgramSettings(CONST.EXPENSIFY_CARD.CARD_PROGRAM.CURRENT) ?? getMergedProgramSettings(CONST.COUNTRY.GB); - if (result) { - return result; - } - - return cardSettings; + // Auto-detect: try known card programs in priority order. + // Newer domains nest settings under US/GB, legacy ones under CURRENT. + // The flat root fallback supports domains that the backend still sends without nesting + // (e.g. older accounts that haven't been migrated). Writes always go to the nested key + // (via getCardProgramKey), so this flat path is read-only display fallback only. + return ( + getMergedProgramSettings(CONST.COUNTRY.US) ?? + getMergedProgramSettings(CONST.EXPENSIFY_CARD.CARD_PROGRAM.CURRENT) ?? + getMergedProgramSettings(CONST.COUNTRY.GB) ?? + (cardSettings as ExpensifyCardSettingsBase) + ); } function isCardPendingIssue(card?: Card) { @@ -1422,21 +1449,21 @@ function getCardCurrency(card?: OnyxEntry, cardSettings?: OnyxEntry, currentFrequency?: Date) { +function updateSettlementFrequency( + workspaceAccountID: number, + programKey: CardProgramKey, + settlementFrequency: ValueOf, + currentFrequency?: Date, +) { const monthlySettlementDate = settlementFrequency === CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY ? null : new Date(); + const settlementValue = {[programKey]: {monthlySettlementDate}}; + const failureValue = {[programKey]: {monthlySettlementDate: currentFrequency}}; + const optimisticData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - monthlySettlementDate, - }, + value: settlementValue, }, ]; @@ -662,9 +669,7 @@ function updateSettlementFrequency(workspaceAccountID: number, settlementFrequen { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - monthlySettlementDate, - }, + value: settlementValue, }, ]; @@ -672,9 +677,7 @@ function updateSettlementFrequency(workspaceAccountID: number, settlementFrequen { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - monthlySettlementDate: currentFrequency, - }, + value: failureValue, }, ]; @@ -686,19 +689,33 @@ function updateSettlementFrequency(workspaceAccountID: number, settlementFrequen API.write(WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY, parameters, {optimisticData, successData, failureData}); } -function updateSettlementAccount(domainName: string, workspaceAccountID: number, policyID: string, settlementBankAccountID?: number, currentSettlementBankAccountID?: number) { +function updateSettlementAccount( + domainName: string, + workspaceAccountID: number, + policyID: string, + programKey: CardProgramKey, + settlementBankAccountID?: number, + currentSettlementBankAccountID?: number, +) { if (!settlementBankAccountID) { return; } + const optimisticValue = {[programKey]: {paymentBankAccountID: settlementBankAccountID}, isLoading: true}; + + const successValue = {[programKey]: {paymentBankAccountID: settlementBankAccountID}, isLoading: false}; + + const failureValue = { + [programKey]: {paymentBankAccountID: currentSettlementBankAccountID}, + isLoading: false, + errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }; + const optimisticData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - paymentBankAccountID: settlementBankAccountID, - isLoading: true, - }, + value: optimisticValue, }, ]; @@ -706,10 +723,7 @@ function updateSettlementAccount(domainName: string, workspaceAccountID: number, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - paymentBankAccountID: settlementBankAccountID, - isLoading: false, - }, + value: successValue, }, ]; @@ -717,11 +731,7 @@ function updateSettlementAccount(domainName: string, workspaceAccountID: number, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`, - value: { - paymentBankAccountID: currentSettlementBankAccountID, - isLoading: false, - errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), - }, + value: failureValue, }, ]; diff --git a/src/libs/actions/TravelInvoicing.ts b/src/libs/actions/TravelInvoicing.ts index 2214c28684f6..ac9eac9d992b 100644 --- a/src/libs/actions/TravelInvoicing.ts +++ b/src/libs/actions/TravelInvoicing.ts @@ -91,9 +91,11 @@ function setTravelInvoicingSettlementAccount(policyID: string, workspaceAccountI onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID, - monthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID, + monthlySettlementDate, + }, isLoading: true, pendingFields: { paymentBankAccountID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -110,9 +112,11 @@ function setTravelInvoicingSettlementAccount(policyID: string, workspaceAccountI onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID: null, - monthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID: null, + monthlySettlementDate, + }, isLoading: false, pendingFields: { paymentBankAccountID: null, @@ -129,10 +133,11 @@ function setTravelInvoicingSettlementAccount(policyID: string, workspaceAccountI onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - // Keep the attempted value visible (grayed out) until error is dismissed - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID, - monthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID, + monthlySettlementDate, + }, isLoading: false, pendingFields: { paymentBankAccountID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -158,8 +163,10 @@ function setTravelInvoicingSettlementAccount(policyID: string, workspaceAccountI */ function clearTravelInvoicingSettlementAccountErrors(workspaceAccountID: number, paymentBankAccountID: number | null) { Onyx.merge(getTravelInvoicingCardSettingsKey(workspaceAccountID), { - paymentBankAccountID, - previousPaymentBankAccountID: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + paymentBankAccountID, + previousPaymentBankAccountID: null, + }, pendingFields: { paymentBankAccountID: null, }, @@ -185,8 +192,10 @@ function updateTravelInvoiceSettlementFrequency(workspaceAccountID: number, freq onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - monthlySettlementDate, - previousMonthlySettlementDate: currentMonthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + monthlySettlementDate, + previousMonthlySettlementDate: currentMonthlySettlementDate, + }, pendingFields: { monthlySettlementDate: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }, @@ -202,8 +211,10 @@ function updateTravelInvoiceSettlementFrequency(workspaceAccountID: number, freq onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - monthlySettlementDate, - previousMonthlySettlementDate: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + monthlySettlementDate, + previousMonthlySettlementDate: null, + }, pendingFields: { monthlySettlementDate: null, }, @@ -219,8 +230,10 @@ function updateTravelInvoiceSettlementFrequency(workspaceAccountID: number, freq onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - monthlySettlementDate, - previousMonthlySettlementDate: currentMonthlySettlementDate ?? null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + monthlySettlementDate, + previousMonthlySettlementDate: currentMonthlySettlementDate ?? null, + }, pendingFields: { monthlySettlementDate: null, }, @@ -244,8 +257,10 @@ function updateTravelInvoiceSettlementFrequency(workspaceAccountID: number, freq */ function clearTravelInvoicingSettlementFrequencyErrors(workspaceAccountID: number, monthlySettlementDate: Date | null | undefined) { Onyx.merge(getTravelInvoicingCardSettingsKey(workspaceAccountID), { - monthlySettlementDate: monthlySettlementDate ?? null, - previousMonthlySettlementDate: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + monthlySettlementDate: monthlySettlementDate ?? null, + previousMonthlySettlementDate: null, + }, pendingFields: { monthlySettlementDate: null, }, @@ -327,7 +342,6 @@ function deactivateTravelInvoicing(policyID: string, workspaceAccountID: number) onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - isEnabled: false, [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { isEnabled: false, }, @@ -342,10 +356,10 @@ function deactivateTravelInvoicing(policyID: string, workspaceAccountID: number) onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - pendingAction: null, [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { isEnabled: false, }, + pendingAction: null, }, }, ]; @@ -355,7 +369,6 @@ function deactivateTravelInvoicing(policyID: string, workspaceAccountID: number) onyxMethod: Onyx.METHOD.MERGE, key: cardSettingsKey, value: { - isEnabled: true, [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { isEnabled: true, }, diff --git a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx index 0cae6b5a8f6d..517506ed80e1 100644 --- a/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx +++ b/src/pages/workspace/accounting/reconciliation/ReconciliationAccountSettingsPage.tsx @@ -12,7 +12,8 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import {getConnectionNameFromRouteParam} from '@libs/AccountingUtils'; import {getLastFourDigits} from '@libs/BankAccountUtils'; -import {getCardSettings, getEligibleBankAccountsForCard} from '@libs/CardUtils'; +import {getCardProgramKey, getCardSettings, getEligibleBankAccountsForCard} from '@libs/CardUtils'; +import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -43,7 +44,8 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); - const settings = getCardSettings(cardSettings); + const programKey = getCardProgramKey(cardSettings); + const settings = getCardSettings(cardSettings, programKey); const paymentBankAccountID = settings?.paymentBankAccountID; const selectedBankAccount = useMemo(() => bankAccountList?.[paymentBankAccountID?.toString() ?? ''], [paymentBankAccountID, bankAccountList]); @@ -74,7 +76,11 @@ function ReconciliationAccountSettingsPage({route}: ReconciliationAccountSetting }, [policyID, backTo, connection]); const selectBankAccount = (newBankAccountID?: number) => { - updateSettlementAccount(domainName, defaultFundID, policyID, newBankAccountID, paymentBankAccountID); + if (!programKey) { + Log.alert('[ReconciliationAccountSettingsPage] selectBankAccount called without a detected card program key'); + return; + } + updateSettlementAccount(domainName, defaultFundID, policyID, programKey, newBankAccountID, paymentBankAccountID); goBack(); }; diff --git a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx index 051740721fd6..c9406a46f5c6 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceSettlementAccountPage.tsx @@ -15,7 +15,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {getRouteParamForConnection} from '@libs/AccountingUtils'; import {openPolicyAccountingPage} from '@libs/actions/PolicyConnections'; import {getLastFourDigits} from '@libs/BankAccountUtils'; -import {getCardSettings, getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from '@libs/CardUtils'; +import {getCardProgramKey, getCardSettings, getEligibleBankAccountsForCard, getEligibleBankAccountsForUkEuCard} from '@libs/CardUtils'; +import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {getDomainNameForPolicy} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; @@ -45,7 +46,8 @@ 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 programKey = getCardProgramKey(cardSettings); + const settings = getCardSettings(cardSettings, programKey); 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); @@ -107,7 +109,11 @@ function WorkspaceSettlementAccountPage({route}: WorkspaceSettlementAccountPageP const listOptions: BankAccountListItem[] = eligibleBankAccountsOptions.length > 0 ? eligibleBankAccountsOptions : [fallbackBankAccountOption]; const handleSelectAccount = (value: number) => { - updateSettlementAccountCard(domainName, defaultFundID, policyID, value, paymentBankAccountID); + if (!programKey) { + Log.alert('[WorkspaceSettlementAccountPage] handleSelectAccount called without a detected card program key'); + return; + } + updateSettlementAccountCard(domainName, defaultFundID, policyID, programKey, value, paymentBankAccountID); Navigation.goBack(); }; diff --git a/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx index d3f442dce982..b2f178b5f1c4 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceSettlementFrequencyPage.tsx @@ -10,7 +10,8 @@ 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 {getCardProgramKey, getCardSettings} from '@libs/CardUtils'; +import Log from '@libs/Log'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -29,7 +30,8 @@ function WorkspaceSettlementFrequencyPage({route}: WorkspaceSettlementFrequencyP const defaultFundID = useDefaultFundID(policyID); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`); - const settings = getCardSettings(cardSettings); + const programKey = getCardProgramKey(cardSettings); + const settings = getCardSettings(cardSettings, programKey); const shouldShowMonthlyOption = settings?.isMonthlySettlementAllowed ?? false; const selectedFrequency = settings?.monthlySettlementDate ? CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.MONTHLY : CONST.EXPENSIFY_CARD.FREQUENCY_SETTING.DAILY; @@ -58,7 +60,11 @@ function WorkspaceSettlementFrequencyPage({route}: WorkspaceSettlementFrequencyP }, [translate, shouldShowMonthlyOption, selectedFrequency]); const updateSettlementFrequency = (value: ValueOf) => { - updateSettlementFrequencyUtil(defaultFundID, value, settings?.monthlySettlementDate); + if (!programKey) { + Log.alert('[WorkspaceSettlementFrequencyPage] updateSettlementFrequency called without a detected card program key'); + return; + } + updateSettlementFrequencyUtil(defaultFundID, programKey, value, settings?.monthlySettlementDate); }; return ( diff --git a/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx b/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx index 9cb1a160cc65..b54d7951346d 100644 --- a/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelInvoicingSection.tsx @@ -311,7 +311,7 @@ function WorkspaceTravelInvoicingSection({policyID}: WorkspaceTravelInvoicingSec clearTravelInvoicingSettlementAccountErrors(workspaceAccountID, cardSettings?.previousPaymentBankAccountID ?? null)} + onClose={() => clearTravelInvoicingSettlementAccountErrors(workspaceAccountID, travelSettings?.previousPaymentBankAccountID ?? null)} errorRowStyles={styles.mh2half} errorRowTextStyles={styles.mr3} > @@ -329,7 +329,7 @@ function WorkspaceTravelInvoicingSection({policyID}: WorkspaceTravelInvoicingSec clearTravelInvoicingSettlementFrequencyErrors(workspaceAccountID, cardSettings?.previousMonthlySettlementDate)} + onClose={() => clearTravelInvoicingSettlementFrequencyErrors(workspaceAccountID, travelSettings?.previousMonthlySettlementDate)} errorRowStyles={styles.mh2half} errorRowTextStyles={styles.mr3} > diff --git a/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx b/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx index 5ba950f46fcd..1203dfce00b2 100644 --- a/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx +++ b/src/pages/workspace/travel/WorkspaceTravelInvoicingSettlementAccountPage.tsx @@ -88,7 +88,7 @@ function WorkspaceTravelInvoicingSettlementAccountPage({route}: WorkspaceTravelI return; } - const previousPaymentBankAccountID = cardSettings?.previousPaymentBankAccountID ?? cardSettings?.paymentBankAccountID; + const previousPaymentBankAccountID = travelSettings?.previousPaymentBankAccountID ?? travelSettings?.paymentBankAccountID; setTravelInvoicingSettlementAccount(policyID, workspaceAccountID, value, previousPaymentBankAccountID); Navigation.goBack(); }; diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 622ab8c6cdb1..46a6fe4d0b5c 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -65,6 +65,7 @@ import { splitMaskedCardNumber, supportsPINManagementFeatures, } from '@src/libs/CardUtils'; +import type {CardProgramKey} from '@src/libs/CardUtils'; import DateUtils from '@src/libs/DateUtils'; import type { BankAccountList, @@ -3612,14 +3613,16 @@ describe('CardUtils', () => { expect(getCardSettings(null as unknown as undefined)).toBeUndefined(); }); - it('should return flat root when feedCountry is not provided and no nested keys exist', () => { + it('should fall back to flat root when no nested keys exist and feedCountry is not provided', () => { const result = getCardSettings(flatSettings); - expect(result).toBe(flatSettings); + expect(result?.paymentBankAccountID).toBe(12345); + expect(result?.limit).toBe(50000); }); - it('should return flat root when feedCountry is undefined and no nested keys exist', () => { + it('should fall back to flat root when no nested keys exist and feedCountry is undefined', () => { const result = getCardSettings(flatSettings, undefined); - expect(result).toBe(flatSettings); + expect(result?.paymentBankAccountID).toBe(12345); + expect(result?.limit).toBe(50000); }); it('should return merged root + nested when feedCountry matches a nested key', () => { @@ -3630,9 +3633,9 @@ describe('CardUtils', () => { expect(result?.domainName).toBe('example.com'); }); - it('should fall back to root when feedCountry key does not exist', () => { - const result = getCardSettings(nestedSettings, 'CA'); - expect(result).toBe(nestedSettings); + it('should return undefined when feedCountry key does not exist', () => { + const result = getCardSettings(nestedSettings, 'CA' as unknown as CardProgramKey); + expect(result).toBeUndefined(); }); it('should return merged root + TRAVEL_US when feedCountry is TRAVEL_US', () => { @@ -3642,9 +3645,9 @@ describe('CardUtils', () => { expect(result?.domainName).toBe('example.com'); }); - it('should not return primitive values as nested settings', () => { - const result = getCardSettings(nestedSettings, 'limit'); - expect(result).toBe(nestedSettings); + it('should return undefined for primitive values as feedCountry', () => { + const result = getCardSettings(nestedSettings, 'limit' as unknown as CardProgramKey); + expect(result).toBeUndefined(); }); it('should auto-detect US program when no feedCountry is provided', () => { diff --git a/tests/unit/PolicyUtilsTest.ts b/tests/unit/PolicyUtilsTest.ts index b1377eb5c584..954426aaa7a5 100644 --- a/tests/unit/PolicyUtilsTest.ts +++ b/tests/unit/PolicyUtilsTest.ts @@ -274,7 +274,9 @@ describe('PolicyUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}2`, policy); await Onyx.set(`${ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED}2`, lastSelectedExpensifyCardFeed); await Onyx.set(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${lastSelectedExpensifyCardFeed}`, { - paymentBankAccountID: 1234, + [CONST.COUNTRY.US]: { + paymentBankAccountID: 1234, + }, }); const {result} = renderHook(() => useDefaultFundID(policy.id)); diff --git a/tests/unit/TravelInvoicingTest.ts b/tests/unit/TravelInvoicingTest.ts index 2e925d6b9ab8..247bc3582de8 100644 --- a/tests/unit/TravelInvoicingTest.ts +++ b/tests/unit/TravelInvoicingTest.ts @@ -46,8 +46,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID, + }), isLoading: true, pendingFields: expect.objectContaining({ paymentBankAccountID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -62,8 +64,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID: null, + }), isLoading: false, pendingFields: expect.objectContaining({ paymentBankAccountID: null, @@ -78,8 +82,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - paymentBankAccountID: settlementBankAccountID, - previousPaymentBankAccountID, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + paymentBankAccountID: settlementBankAccountID, + previousPaymentBankAccountID, + }), isLoading: false, pendingFields: expect.objectContaining({ paymentBankAccountID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -102,8 +108,10 @@ describe('TravelInvoicing', () => { clearTravelInvoicingSettlementAccountErrors(workspaceAccountID, restoredAccountID); expect(spyOnyxMerge).toHaveBeenCalledWith(cardSettingsKey, { - paymentBankAccountID: restoredAccountID, - previousPaymentBankAccountID: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + paymentBankAccountID: restoredAccountID, + previousPaymentBankAccountID: null, + }, pendingFields: { paymentBankAccountID: null, }, @@ -121,8 +129,10 @@ describe('TravelInvoicing', () => { clearTravelInvoicingSettlementFrequencyErrors(workspaceAccountID, monthlySettlementDate); expect(spyOnyxMerge).toHaveBeenCalledWith(cardSettingsKey, { - monthlySettlementDate: monthlySettlementDate ?? null, - previousMonthlySettlementDate: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: { + monthlySettlementDate: monthlySettlementDate ?? null, + previousMonthlySettlementDate: null, + }, pendingFields: { monthlySettlementDate: null, }, @@ -156,8 +166,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - monthlySettlementDate: mockDate, - previousMonthlySettlementDate: currentMonthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + monthlySettlementDate: mockDate, + previousMonthlySettlementDate: currentMonthlySettlementDate, + }), pendingFields: expect.objectContaining({ monthlySettlementDate: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, }), @@ -171,8 +183,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - monthlySettlementDate: mockDate, - previousMonthlySettlementDate: null, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + monthlySettlementDate: mockDate, + previousMonthlySettlementDate: null, + }), pendingFields: expect.objectContaining({ monthlySettlementDate: null, }), @@ -186,8 +200,10 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - monthlySettlementDate: mockDate, - previousMonthlySettlementDate: currentMonthlySettlementDate, + [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ + monthlySettlementDate: mockDate, + previousMonthlySettlementDate: currentMonthlySettlementDate, + }), pendingFields: expect.objectContaining({ monthlySettlementDate: null, }), @@ -280,7 +296,6 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - isEnabled: false, [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ isEnabled: false, }), @@ -304,7 +319,6 @@ describe('TravelInvoicing', () => { expect.objectContaining({ key: cardSettingsKey, value: expect.objectContaining({ - isEnabled: true, [CONST.TRAVEL.PROGRAM_TRAVEL_US]: expect.objectContaining({ isEnabled: true, }),