Multi program expensify card feeds#96070
Draft
heekinho wants to merge 18 commits into
Draft
Conversation
A domain can be provisioned with more than one Expensify Card program (US and GB) under a single fundID-keyed settings NVP and cards list. Add the helpers and the selected-program Onyx key needed to distinguish them, with no behavior change yet: - getConfiguredExpensifyCardProgramKeys, getProgramKeyForCard, isCardInProgram, filterCardsListByProgram, getExpensifyCardProgramCountrySuffix, getSelectableCardProgramKey, getCardSettingsForSelectedProgram - LAST_SELECTED_EXPENSIFY_CARD_PROGRAM collection key + type - updateSelectedExpensifyCardFeed accepts an optional programKey
Resolves the selected program (US/GB) for a policy's feed: the stored value when present, otherwise the feed's first configured program so a GB-only feed shows its cards on first load. Pairs with useDefaultFundID to identify which program to display.
getAdminExpensifyCardFeedEntries now emits one entry per configured program (each tagged with its programKey) instead of one per fundID. The feed selector renders a row per program, disambiguated by a country suffix on non-US rows (e.g. "deptagency.com" vs "deptagency.com GB"), and persists the selected program alongside the feed.
A single cards_{fundID}_Expensify Card list holds both programs' cards, so filter it by
the selected program's feedCountry and resolve the currency, settings and header suffix
for that program. useCurrencyForExpensifyCard gains a programKey so it reads the selected
program's currency rather than the collapsed, US-first value.
Add a read-only Country row and resolve the card's currency from its own feedCountry, so US and GB cards on the same feed show their correct country and currency.
The issue-card flow captured currency without a program, so a GB feed showed USD through the limit and confirmation steps. Pass the selected program in AssigneeStep, and resolve currency from the card's own program on the edit-limit and limit-type pages.
…llapse Make programKey required on useCurrencyForExpensifyCard so a caller can no longer silently fall back to the collapsed, US-first currency. Fixing the remaining callers surfaced that the settlement label, Settings page and settlement account/frequency pages all resolved the program via getCardProgramKey (US-first): they showed US settings on a GB feed, and the settlement account/frequency edits wrote to the US program even when editing the GB feed. Use the selected program for both display and the update actions.
Cover the new CardUtils helpers (program key resolution, card-to-program mapping, per-program card filtering, configured-program detection) and the feed selector splitting a US+GB feed into one entry per program.
The issue-card flow hardcoded feedCountry as '' or 'US', so selecting the GB feed still issued into US. Pass the selected program's country to CreateExpensifyCard from both the extended-access and magic-code paths. This fixes routing for the physical-card command (CreateExpensifyCard). Virtual cards go through CreateAdminIssuedVirtualCard, which does not yet carry feedCountry end to end (App type omits it, Web-Expensify does not forward it, and the Auth command auto-detects the program) - that routing is a separate backend follow-up.
CREATE_ADMIN_ISSUED_VIRTUAL_CARD omitted feedCountry, so on a domain with more than one program (e.g. both US and GB) an admin-issued virtual card selected for the GB feed was created on the default US program. Allow feedCountry on the command params and pass the selected program in the virtual-card branch of issueExpensifyCard, matching the physical-card branch. The backend uses it to route the card to the correct program (companion Web-Expensify and Auth PRs).
…licy Linking an Expensify Card feed to a policy always sent feedCountry=US (linkCardFeedToPolicy's default), so linking the GB feed of a multi-program domain failed with '404 Feed not found'. Pass the selected program from the feed selector, and thread it through the work-email/verify detour via a new optional feedCountry route param so the program survives email validation before linking.
ExportOnyxStateTest requires every ONYXKEYS collection key to be classified into an export bucket. The new LAST_SELECTED_EXPENSIFY_CARD_PROGRAM key holds a non-sensitive selected-program value, so list it under safeOnyxKeys next to LAST_SELECTED_EXPENSIFY_CARD_FEED.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
The multi-program helper tests added several 'as unknown as' casts, pushing CardUtilsTest over the eslint-seatbelt baseline for no-unsafe-type-assertion. Build the Card and ExpensifyCardSettings mocks with createMock<T>() and set the WorkspaceCardsList cardList meta entry after construction, so the tests add no net unsafe assertions.
When a domain is provisioned with more than one Expensify Card program (e.g. both US and GB), each program's selector row now shows a parenthetical qualifier so the rows are distinguishable: - US shows (USD) — country omitted since USD is eligible in one country - GB shows (COUNTRY - CURRENCY), e.g. (GB - GBP) A single-program domain shows just the domain, and the workspace card list header shows just the domain. Currency is derived per program via the new getExpensifyCardProgramCurrency helper (extracted from getCardOrFeedCurrency), so US rows show (USD) even when the settings NVP carries no explicit currency.
A domain provisioned with both US and GB programs shares one settings NVP, and isFeedPrimaryForPolicy read the merged linkedPolicyIDs across the root and every program block. So linking a workspace to only the US program made both the US and GB rows appear as available (primary) for that workspace. Add getLinkedPolicyIDsForExpensifyCardProgram, which resolves the linked workspaces for a single program (root-level links still apply, for legacy/single-program feeds). isFeedPrimaryForPolicy now checks the entry's own program, so an unlinked program stays under "From other workspaces".
danieldoglas
left a comment
Contributor
There was a problem hiding this comment.
overall lgtm, we need more test steps here for all changes that were created.
e.g. - test the workemail/verify-work-email paths,
|
|
||
| /** | ||
| * A single settings NVP can hold more than one provisioned Expensify Card program (e.g. a domain with both a US and a GB feed). | ||
| * The user-selectable programs are US and GB; CURRENT is deprecated and TRAVEL_US is backend-managed, so neither is offered as its own row. |
Contributor
There was a problem hiding this comment.
Suggested change
| * The user-selectable programs are US and GB; CURRENT is deprecated and TRAVEL_US is backend-managed, so neither is offered as its own row. | |
| * The user-selectable programs are US and GB. |
| return card?.nameValuePairs?.feedCountry === CONST.COUNTRY.GB ? CONST.COUNTRY.GB : CONST.COUNTRY.US; | ||
| } | ||
|
|
||
| /** Whether a card belongs to the given program, so a single `cards_{fundID}_Expensify Card` list can be split per program. */ |
Contributor
There was a problem hiding this comment.
fix the name of the key here and in other ocurrences
Comment on lines
+131
to
+133
| // Surface the card's issuing country so cards on different programs (e.g. US vs GB) of the same feed are distinguishable. | ||
| const cardCountryCode = card?.nameValuePairs?.country; | ||
| const cardCountryName = cardCountryCode ? (CONST.ALL_COUNTRIES as Record<string, string>)[cardCountryCode] : undefined; |
Contributor
There was a problem hiding this comment.
Is this really needed? We're filtering by feeds already, so we shouldn't see the same card for different feeds in the same page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explanation of Change
A domain (or workspace) can be provisioned with more than one Expensify Card program at the same time — for example both the US and UK/EU (GB) programs. The backend stores them under a single
private_expensifyCardSettingsNVP keyed by program ({US: {...}, GB: {...}}) and buckets both programs' cards into onecards_{fundID}_Expensify CardOnyx key. Because the Expensify Card UI was keyed entirely byfundID, the two programs collapsed into one: the feed selector showed a single row, the card list mixed both programs, and currency/limit/settings resolved to whichever program came first (US), so a GB feed showed USD.This PR makes the Expensify Card UI program-aware. The selected program (US/GB) is tracked per policy in a new
lastSelectedExpensifyCardProgramOnyx key alongside the existinglastSelectedExpensifyCardFeed(fundID), so the fundID-keyed storage layer is untouched and single-program domains behave exactly as before.Changes:
CardUtils(getConfiguredExpensifyCardProgramKeys,getProgramKeyForCard,isCardInProgram,filterCardsListByProgram,getExpensifyCardProgramCountrySuffix,getSelectableCardProgramKey,getCardSettingsForSelectedProgram), thelastSelectedExpensifyCardProgramOnyx key, and auseSelectedExpensifyCardProgramhook that resolves the stored program or the feed's first configured program.getAdminExpensifyCardFeedEntriesemits one entry per configured program; the selector renders one row per program, with a country suffix on non-US rows (e.g.deptagency.comvsdeptagency.com GB).cards_{fundID}by the selected program'sfeedCountry, and currency/settings/settlement resolve per program (fixing the "US feed shows a GBP limit" symptom and the settlement/settings pages that showed US values on a GB feed).feedCountry.feedCountryis now sent on both the physicalCreateExpensifyCardand virtualCreateAdminIssuedVirtualCardcommands).linkCardFeedToPolicy(previously defaulted to US, causing "404 Feed not found" when linking a GB feed), threaded through the work-email/verify detour via a new optionalfeedCountryroute param.The virtual-card issuance and linking rely on companion backend PRs (Web-Expensify + Auth) that route the card to the program named by
feedCountry.Fixed Issues
Part of https://github.com/Expensify/Expensify/issues/652047
PROPOSAL:
Tests
deptagency.com) and one suffixedGB(e.g.deptagency.com GB).Offline tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari