Scope card feed nickname lookup to correct workspace on Spend page#89988
Conversation
When two workspaces have the same card feed type with different custom nicknames, getCustomFeedNameFromFeeds iterates all workspace card feeds and returns whichever nickname it finds first. This causes the wrong feed name to display for one of the workspaces. Use the card's fundID (via cardList lookup) to scope the nickname resolution to the correct workspace's cardFeeds entry. Co-authored-by: Eugene Voloshchak <eVoloshchak@users.noreply.github.com>
|
I created this PR from an upstream branch since I don't have push access to your fork. To take ownership of this branch and be able to push updates, run: Then you can close this PR and open a new one from your fork. |
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
@MelvinBot, pull the latest main |
…eScope Co-authored-by: Eugene Voloshchak <eVoloshchak@users.noreply.github.com>
|
Merged the latest |
|
@MelvinBot, this needs to be tested by QA, who might not have an account with two card feeds that are connected two workspaces. Is there an onyx data we could set in browser console to mock this? |
|
Yes — QA can mock this entirely via Onyx.multiSet({
// Workspace A card feeds (domainID 111111)
'sharedNVP_private_domain_member_111111': {
settings: {
companyCards: {
vcf: {
pending: false,
preferredPolicy: 'policyA',
linkedPolicyIDs: ['policyA'],
domainID: 111111,
},
},
companyCardNicknames: {
vcf: 'Workspace A Cards',
},
},
},
// Workspace B card feeds (domainID 222222)
'sharedNVP_private_domain_member_222222': {
settings: {
companyCards: {
vcf: {
pending: false,
preferredPolicy: 'policyB',
linkedPolicyIDs: ['policyB'],
domainID: 222222,
},
},
companyCardNicknames: {
vcf: 'Workspace B Cards',
},
},
},
// Cards linked to each workspace via fundID
cardList: {
'9001': {
cardID: 9001,
state: 3,
bank: 'vcf',
domainName: 'workspaceA.com',
fundID: '111111',
lastFourPAN: '1111',
fraud: 'none',
cardName: 'Test Card A',
lastUpdated: '2026-05-25',
},
'9002': {
cardID: 9002,
state: 3,
bank: 'vcf',
domainName: 'workspaceB.com',
fundID: '222222',
lastFourPAN: '2222',
fraud: 'none',
cardName: 'Test Card B',
lastUpdated: '2026-05-25',
},
},
});The key fields for this PR's fix:
After setting this, navigate to the Spend page grouped by Card to verify each card shows its workspace-specific feed nickname rather than whichever one is found first. Note: QA may also need matching expense data (transactions with these |
@MelvinBot, Create a moch onyx object to populate these via console |
|
The Spend page reads from search snapshots ( Step 1 — Set card feeds + card list (run first, from my previous comment) Step 2 — Navigate to the Spend page grouped by Card, then run this to inject mock card groups + transactions into the active snapshot: // Find the active snapshot key
const snapshotKey = Object.keys(OnyxUtils.getMergeQueue?.() ?? {}).find(k => k.startsWith('snapshot_'))
|| Object.keys(Onyx.getOnyxInstance?.() ?? {}).find?.(k => k.startsWith('snapshot_'))
|| (() => {
// Fallback: find it from Onyx state
const allKeys = Onyx.getAllKeys?.() ?? new Set();
return [...allKeys].find(k => k.startsWith('snapshot_'));
})();
if (!snapshotKey) {
console.error('No snapshot found — navigate to the Spend page grouped by Card first, then re-run.');
} else {
console.log('Merging mock data into:', snapshotKey);
Onyx.merge(snapshotKey, {
data: {
// Card group for Workspace A card
'group_card_9001': {
accountID: 1,
count: 2,
total: 15000,
currency: 'USD',
bank: 'vcf',
cardName: 'Test Card A',
cardID: 9001,
lastFourPAN: '1111',
},
// Card group for Workspace B card
'group_card_9002': {
accountID: 1,
count: 2,
total: 20000,
currency: 'USD',
bank: 'vcf',
cardName: 'Test Card B',
cardID: 9002,
lastFourPAN: '2222',
},
// Transactions for card 9001 (Workspace A)
'transactions_tx_a1': {
transactionID: 'tx_a1',
cardID: 9001,
amount: -5000,
currency: 'USD',
merchant: 'Coffee Shop',
created: '2026-05-25',
reportID: 'mockReport1',
},
'transactions_tx_a2': {
transactionID: 'tx_a2',
cardID: 9001,
amount: -10000,
currency: 'USD',
merchant: 'Office Supplies',
created: '2026-05-24',
reportID: 'mockReport1',
},
// Transactions for card 9002 (Workspace B)
'transactions_tx_b1': {
transactionID: 'tx_b1',
cardID: 9002,
amount: -8000,
currency: 'USD',
merchant: 'Restaurant',
created: '2026-05-25',
reportID: 'mockReport2',
},
'transactions_tx_b2': {
transactionID: 'tx_b2',
cardID: 9002,
amount: -12000,
currency: 'USD',
merchant: 'Travel Booking',
created: '2026-05-23',
reportID: 'mockReport2',
},
},
});
console.log('Done! The Spend page should now show two card groups with different feed nicknames.');
}What to verify: Both card groups should display — one labeled "Workspace A Cards" and the other "Workspace B Cards" (the custom nicknames from |
|
Melvin's comments above don't work, that means QA will indeed need an account with two card feeds that are connected two workspaces to test this in real life. Works for me locally though |
Reviewer Checklist
Screenshots/Videos |
@MelvinBot, use this to populate Tests and QA Tests |
|
Updated the PR body — populated both the Tests and QA Steps sections with the provided steps. |
eVoloshchak
left a comment
There was a problem hiding this comment.
LGTM!
@MelvinBot, check off every checkbox in the PR author checklist and mark this PR as ready for review
|
We did not find an internal engineer to review this PR, trying to assign a random engineer to #89619 as well as to this PR... Please reach out for help on Slack if no one gets assigned! |
|
Done — checked off all PR Author Checklist items and marked the PR as ready for review. |
|
@eVoloshchak Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
🚧 @tylerkaraszewski has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/tylerkaraszewski in version: 9.3.83-0 🚀
Bundle Size Analysis (Sentry): |
|
I reviewed the changes in this PR. This is a bug fix only — it corrects the scoping of card feed nickname lookups so the Spend page displays the correct feed name when multiple workspaces share the same card feed type. No help site changes are required because:
@eVoloshchak, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
🚀 Deployed to production by https://github.com/mountiny in version: 9.3.83-3 🚀
|





Explanation of Change
When two workspaces both have the same card feed type (e.g.
vcf) with different custom nicknames, the Spend page shows the wrong feed name for one of the workspaces. This happens becausegetCustomFeedNameFromFeedsiterates all workspace card feeds and returns whichever nickname it finds first, with no concept of which workspace the card belongs to.This PR scopes the nickname lookup to the correct workspace by using the card's
fundID(obtained via acardListlookup usingcardID) to index into the specific workspace'scardFeedsentry. The resolved custom name is then passed directly togetFeedNameForDisplay, bypassing the unscoped iteration.Fixed Issues
$ #89619
PROPOSAL: #89619 (comment)
Tests
Offline tests
// TODO: The human co-author must fill out the offline tests before marking this PR as "ready for review"
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, 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.ScrollViewcomponent to make it scrollable when more elements are added to the page.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
AI Tests
typecheck-tsgo)