Vendor matching CC: Add Vendors tab to Workspace settings#96143
Vendor matching CC: Add Vendors tab to Workspace settings#96143Beamanator wants to merge 25 commits into
Conversation
Adds a read-only Vendors tab to Workspace settings for workspaces with the `vendorMatching` beta enabled and either QBO (Credit/Debit-card export) or Sage Intacct (Credit Card Charge export) connected. Sourced from the existing `connections.<name>.data.vendors` Onyx data used by the vendor selector RHP shipped in #91886, so no backend changes are required. Gate: `hasVendorFeature(policy, isBetaEnabled(BETAS.VENDOR_MATCHING)) && isMatchingVendorListLoaded(policy)`. The list-loaded clause is stricter than the inline surfaces (Default vendor row, per-expense Vendor field) because a read-only externally-synced list with no user CTA is a dead end if it opens empty. Phase 2 (GL code column for QBO) requires a Java `AcctNum` fetch in `QuickbooksOnlineImporter.java` and follows in a separate change. Intacct is fully complete under Phase 1 (no GL code in Intacct vendor imports). See Expensify/Expensify#650595 for the full plan.
|
@Krishna2323 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] |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 79c5adcdb4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- Gate the Vendors menu row on isPolicyAdmin(policy) so it mirrors the page's [ADMIN, PAID] accessVariants. Without this, non-admin members saw the menu row but tap-through rendered NotFound. - Add SCREENS.WORKSPACE.VENDORS to WIDE_LAYOUT_INACTIVE_SCREENS so the keyboard focus trap is disabled on wide side-by-side layouts, matching every other workspace central-pane screen.
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.
|
|
@codex review |
|
@Beamanator does this require C+ review? |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f6a5d12c05
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- Fix isMatchingVendorListLoaded so it returns false for Intacct workspaces that haven't synced yet. Previously the Intacct branch of getActiveVendorMatchingVendors normalized a missing data.vendors to [], which meant the helper returned an array (not undefined) and the tab gate opened onto an empty page — the exact pre-sync state we're trying to avoid. QBO was already correct because it returned data?.vendors directly. - Set shouldHideSelectionButton on the Vendors page rows so SelectableListItem skips the radio button. The list is read-only, so showing radio buttons with a no-op onSelectRow made rows look like editable pickers with no feedback on tap.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4d184b98dd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@Krishna2323 hey hey! I think yes, do you have experience with setting up QBO / other accounting integrations? If so, can you let me know an email address you have for testing this so i can add it to the beta for you to test? 🙏 If not, no worries i can find someone else to help test this! |
Only the active policy has connections data at app start (per withPolicyConnections's own docstring); other workspaces have to fetch it lazily. hasVendorFeature() and isMatchingVendorListLoaded() both read `policy.connections`, so on a non-active vendor-matching workspace the Vendors menu row was staying hidden even after the beta was flipped on, until the user visited the Accounting tab. Add an effect that mirrors withPolicyConnections' fetch trigger inline in WorkspaceInitialPage, gated on the VENDOR_MATCHING beta so we don't add an accounting-page fetch to every workspace visit for non-beta users. Same self-gates as withPolicyConnections: bails if connections are already fetched, or if the workspace has no connections enabled at all.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de45732ec3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The previous commit added a connections-prefetch effect to WorkspaceInitialPage so the sidebar tab renders on non-active workspaces, but that only covers the sidebar entry point. A user opening or refreshing /workspaces/:policyID/vendors directly on a non-active workspace still hit the page before connections were fetched, so hasVendorFeature() returned false and AccessOrNotFoundWrapper rendered NotFound. Wrap WorkspaceVendorsPage in withPolicyConnections so the page itself triggers the fetch and shows a loading indicator until connections are available. Also register VENDORS in the PolicyRouteName union used by withPolicy's route type, matching how CATEGORIES/TAGS/etc. are typed.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a9f0095593
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
useOnyx returns undefined for the value during initial hydration, so my guard on !hasConnectionsDataBeenFetched passed even when the persisted flag was actually true, causing a redundant openPolicyAccountingPage call on every workspace-page rehydrate. Mirror withPolicyConnections by reading the second-tuple result metadata and gating on isLoadingOnyxValue.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ab6b5b010
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
Seems like it's back to you @thelullabyy ! 🙏 |
|
internal QA cuz this is behind a beta! |
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
The lefthook pre-commit hook uses `npx oxfmt` (unpinned), which grabs oxfmt@0.59.0 and formats multi-argument function calls onto separate lines. CI runs `npm run fmt` which uses the lockfile-pinned oxfmt@0.55.0 and formats the same calls onto a single line. My previous fix used the pre-commit hook version and CI still failed. Committing the single-line version (matches CI); bypassing the local hook once so it doesn't revert this.
|
Overall changes look good. I will do final round of testing and adding recordings in next several hours... |
| const {isOffline} = useNetwork(); | ||
| const [hasBeenFetched, hasBeenFetchedResult] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED}${policy?.id}`); | ||
| const isLoadingFetchedFlag = isLoadingOnyxValue(hasBeenFetchedResult); | ||
| const isFetchNeeded = enabled && !isLoadingFetchedFlag && !isOffline && !!policy && (!!policy.areConnectionsEnabled || !isEmptyObject(policy.connections)) && !hasBeenFetched; |
There was a problem hiding this comment.
I think we should add 1 more condition hasVendorFeature(policy, beta) to avoid redundant prefetch for user that has areConnectionsEnabled (no vendor matching)
|
Should we update the cursor pointer type in this case? I think it is quite confusing for read only list currently Screen.Recording.2026-07-17.at.18.20.15.mov |
|
@Beamanator Code changes LGTM. Let's check and resolve these 2 comments then it is good to get approval from me |
heyjennahay
left a comment
There was a problem hiding this comment.
LGTM.
Appreciate this is very simple but we have plans to build this out more, this is the MVP to get it out to customers
|
Oh actually one small change, it should say at the top that the vendors have been imported from QBO. Let me find the mock up |
|
ooh ya that looks much better, will work on that! |
Switch from SelectionList to the generic Table primitive so the page
matches the Categories/Tags visual (column header, tighter rows,
tokenized search, sortable). Add the 'Vendors are managed in your
<integration> settings.' subtitle via the shared
ImportedFromAccountingSoftware component, mirroring Tags. Rows use
interactive={false} so they don't visually invite a click that goes
nowhere.
Also drops the earlier onSelectRow no-op that thelullabyy flagged
(SelectionList's contract required it; Table.Row has no such
requirement).
New translations: workspace.vendors.findVendor,
workspace.vendors.managedInAccountingSoftware. Existing emptyTitle /
emptySubtitle keys reused for Table.EmptyState.
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index e821ba5995c..e009ebfd777 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -4453,7 +4453,7 @@ ${amount} für ${merchant} – ${date}`,
delete: 'Arbeitsbereich löschen',
settings: 'Einstellungen',
categories: 'Kategorien',
- vendors: 'Anbieter',
+ vendors: 'Lieferanten',
tags: 'Tags',
customField1: 'Benutzerdefiniertes Feld 1',
customField2: 'Benutzerdefiniertes Feld 2',
@@ -6286,10 +6286,10 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
genericFailureMessage: 'Beim Aktualisieren des Berichtfelds ist ein Fehler aufgetreten. Bitte versuche es erneut.',
},
vendors: {
- emptyTitle: 'Noch keine Anbieter',
- emptySubtitle: 'Anbieter erscheinen hier, sobald die Synchronisierung mit deiner Buchhaltung abgeschlossen ist.',
- findVendor: 'Anbieter suchen',
- managedInAccountingSoftware: 'Anbieter werden in deinen',
+ emptyTitle: 'Noch keine Lieferanten',
+ emptySubtitle: 'Lieferanten werden hier angezeigt, sobald Ihre Buchhaltungssynchronisierung abgeschlossen ist.',
+ findVendor: 'Anbieter finden',
+ managedInAccountingSoftware: 'Lieferanten werden in Ihrer/Ihrem verwaltet',
},
tags: {
tagName: 'Tag-Name',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index fe79aaa2fdf..725007d32b3 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -6112,9 +6112,9 @@ ${amount} para ${merchant} - ${date}`,
},
vendors: {
emptyTitle: 'Aún no hay proveedores',
- emptySubtitle: 'Los proveedores aparecerán aquí cuando se complete la sincronización con tu software de contabilidad.',
+ emptySubtitle: 'Los proveedores aparecerán aquí cuando se complete la sincronización de contabilidad.',
findVendor: 'Buscar proveedor',
- managedInAccountingSoftware: 'Los proveedores se gestionan en tus',
+ managedInAccountingSoftware: 'Los proveedores se gestionan en tu',
},
tags: {
tagName: 'Nombre de etiqueta',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index cc8bf343d01..518f1ba2b99 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6315,8 +6315,8 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
vendors: {
emptyTitle: 'Aucun fournisseur pour le moment',
emptySubtitle: 'Les fournisseurs apparaîtront ici une fois la synchronisation comptable terminée.',
- findVendor: 'Rechercher un fournisseur',
- managedInAccountingSoftware: 'Les fournisseurs sont gérés dans vos',
+ findVendor: 'Trouver un fournisseur',
+ managedInAccountingSoftware: 'Les fournisseurs sont gérés dans votre',
},
tags: {
tagName: 'Nom du tag',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 95d3af71021..fefd988b5ee 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6275,9 +6275,9 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
},
vendors: {
emptyTitle: 'Nessun fornitore ancora',
- emptySubtitle: 'I fornitori appariranno qui al termine della sincronizzazione con la contabilità.',
+ emptySubtitle: 'I fornitori verranno visualizzati qui al termine della sincronizzazione contabile.',
findVendor: 'Trova fornitore',
- managedInAccountingSoftware: 'I fornitori sono gestiti nelle tue',
+ managedInAccountingSoftware: 'I fornitori vengono gestiti nel tuo',
},
tags: {
tagName: 'Nome tag',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index f558b6b404c..218fbf1c736 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -4402,7 +4402,7 @@ ${integrationName === CONST.ONBOARDING_ACCOUNTING_MAPPING.other ? 'あなたの'
delete: 'ワークスペースを削除',
settings: '設定',
categories: 'カテゴリ',
- vendors: 'ベンダー',
+ vendors: '仕入先',
tags: 'タグ',
customField1: 'カスタムフィールド1',
customField2: 'カスタムフィールド2',
@@ -6193,10 +6193,10 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
genericFailureMessage: 'レポートフィールドの更新中にエラーが発生しました。もう一度お試しください。',
},
vendors: {
- emptyTitle: 'ベンダーはまだありません',
- emptySubtitle: '会計連携の同期が完了すると、ベンダーがここに表示されます。',
- findVendor: 'ベンダーを検索',
- managedInAccountingSoftware: 'ベンダーは',
+ emptyTitle: '取引先はまだありません',
+ emptySubtitle: '会計同期が完了すると、ここにベンダーが表示されます。',
+ findVendor: '取引先を検索',
+ managedInAccountingSoftware: '仕入先は次の場所で管理されます:',
},
tags: {
tagName: 'タグ名',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 83fe0a65e9f..eb88e6145fc 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6257,8 +6257,8 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
},
vendors: {
emptyTitle: 'Nog geen leveranciers',
- emptySubtitle: 'Leveranciers verschijnen hier zodra de synchronisatie met je boekhouding is voltooid.',
- findVendor: 'Zoek leverancier',
+ emptySubtitle: 'Leveranciers verschijnen hier nadat je accounting-synchronisatie is voltooid.',
+ findVendor: 'Leverancier zoeken',
managedInAccountingSoftware: 'Leveranciers worden beheerd in je',
},
tags: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 05e440c9715..a4a86c77782 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6238,9 +6238,9 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
},
vendors: {
emptyTitle: 'Brak dostawców',
- emptySubtitle: 'Dostawcy pojawią się tutaj po zakończeniu synchronizacji z systemem księgowym.',
+ emptySubtitle: 'Dostawcy pojawią się tutaj po zakończeniu synchronizacji księgowej.',
findVendor: 'Znajdź dostawcę',
- managedInAccountingSoftware: 'Dostawcy są zarządzani w Twoich',
+ managedInAccountingSoftware: 'Dostawcy są zarządzani w twoim',
},
tags: {
tagName: 'Nazwa tagu',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index fb127fc8590..2983df789f5 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6249,10 +6249,10 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
genericFailureMessage: 'Ocorreu um erro ao atualizar o campo do relatório. Tente novamente.',
},
vendors: {
- emptyTitle: 'Nenhum fornecedor ainda',
- emptySubtitle: 'Os fornecedores aparecerão aqui após a conclusão da sincronização contábil.',
- findVendor: 'Buscar fornecedor',
- managedInAccountingSoftware: 'Os fornecedores são gerenciados em suas',
+ emptyTitle: 'Ainda não há fornecedores',
+ emptySubtitle: 'Os fornecedores vão aparecer aqui depois que a sua sincronização contábil for concluída.',
+ findVendor: 'Encontrar fornecedor',
+ managedInAccountingSoftware: 'Fornecedores são gerenciados na sua',
},
tags: {
tagName: 'Nome da tag',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 58179fd2817..e90be553c84 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6068,12 +6068,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
reportFieldInitialValueRequiredError: '请选择报表字段的初始值',
genericFailureMessage: '更新报表字段时出错。请重试。',
},
- vendors: {
- emptyTitle: '暂无供应商',
- emptySubtitle: '会计同步完成后,供应商将显示在这里。',
- findVendor: '查找供应商',
- managedInAccountingSoftware: '供应商在您的',
- },
+ vendors: {emptyTitle: '尚无供应商', emptySubtitle: '会计同步完成后,供应商将显示在此处。', findVendor: '查找供应商', managedInAccountingSoftware: '供应商由您在以下位置管理:'},
tags: {
tagName: '标签名称',
requiresTag: '成员必须为所有报销添加标签',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
- Extract WorkspaceVendorsTable to src/components/Tables/, matching Categories/Tags/Taxes/Members/... file layout. - Add illustrations.Buildings icon + shouldDisplayHelpButton on the header, matching Taxes/Categories. - Show the ImportedFromAccountingSoftware subtitle always when a connection is present (both empty and populated states), mirroring Taxes' headerContent pattern. - Apply Polyglot Parrot's polished translations from OSBotify's auto-suggestions (vendors label + emptyTitle/emptySubtitle/ findVendor/managedInAccountingSoftware across the 9 locales).
The type is only referenced internally by the column definition array; no caller imports it. Keeping only WorkspaceVendorTableRowData exported (that one IS used by WorkspaceVendorsPage for typing the rows array).
- Sort the vendor rows via localeCompare in the page's useMemo so the list is alphabetical regardless of server order. - Drop initialSortColumn + compareItems from the Table, and mark the single 'name' column as sortable: false. Table.Header only renders the sort-direction arrow when a column is the active sort key, so removing the initial sort hides the arrow — matches Jenna's mock at #96143 (comment).
Follow-up to feedback: keep the Name column click-to-sort behavior so it matches the Categories/Tags convention. Restores initialSortColumn + compareItems + sortable: true on the column, and drops the redundant page-level sort since the Table now handles ordering.
|
About this image @heyjennahay - it loooks like this may be an old format for tables and such? I checked the categories and members tabs and at least in my dev environment i'm seeing a table like this, so i'm matching that for now:
|
Sidebar menu row: expensifyIcons.Buildings -> expensifyIcons.Suitcase (dropped 'Buildings' from the memoized allow-list, added 'Suitcase'). Page header: illustrations.Buildings -> expensifyIcons.Suitcase. HeaderWithBackButton's icon prop is typed IconAsset which accepts either an illustration or an expensifyIcon, so no other wiring changes were needed. Buildings was my earlier placeholder — Suitcase is the icon Jenna's mock shows.
expensifyIcons.Suitcase has no built-in fill so it rendered as pure black. Other workspace pages (Categories, Members, etc.) all use illustrations.<X> for the header icon, which self-color via the SVG's own <style> block. There is no Suitcase illustration in the codebase, so use illustrations.Luggage — closest briefcase-shaped colored illustration. Sidebar row keeps expensifyIcons.Suitcase (MenuItem tints it via the active-state color, so it looks correct there).




Explanation of Change
Adds a read-only Vendors tab under Workspace settings for workspaces on the
vendorMatchingbeta with either QBO (Credit/Debit-card export) or Sage Intacct (Credit Card Charge export) connected. The list is sourced from the same Onyx data already used by the vendor selector RHP shipped in App #91886, so this change is App-only — no backend work is required. The tab shows one column (vendor name); the GL code column for QBO is a follow-up that also needs a JavaAcctNumfetch inQuickbooksOnlineImporter.java— tracked in the same Expensify #650595 as Phase 2. Intacct never has a GL code so it is fully complete under Phase 1.The visibility gate is
canReadPolicyFeature(POLICY_FEATURE.VENDORS) && hasVendorFeature(policy, isBetaEnabled(BETAS.VENDOR_MATCHING)) && isMatchingVendorListLoaded(policy). The role check reusesROLE_PERMISSION_BUNDLESinPolicyUtils.ts, which derives the ADMIN/AUDITOR bundles fromObject.values(CONST.POLICY.POLICY_FEATURE)— addingPOLICY_FEATURE.VENDORSgives admins WRITE and auditors READ automatically, no server-side change required. The list-loaded clause is stricter than the inline surfaces (Default vendor row, per-expense Vendor field) because a read-only externally-synced destination with no user CTA reads as broken if it opens empty; the page still ships a belt-and-braces empty state for the edge case where the tab is reached before the list populates.For non-active workspaces the effect that triggers
openPolicyAccountingPageruns inWorkspaceInitialPage, gated on the same read-access check, and the page itself is wrapped inwithPolicyConnectionsso direct-URL access hydrates connections before evaluating the wrapper. Offline and Onyx-hydration-race cases are handled by mirroringwithPolicyConnections'sisOfflineandisLoadingOnyxValueguards.Fixed Issues
$ https://github.com/Expensify/Expensify/issues/650595
PROPOSAL: N/A (internal feature, no external proposal)
Tests
Prerequisites: a workspace with the
vendorMatchingbeta enabled and either (a) QBO connected with non-reimbursable export destination set to Credit Card or Debit Card, or (b) Sage Intacct connected with Credit Card Charge export.Happy path
Visibility branches
vendorMatchingbeta on the workspace and verify the Vendors row is hidden regardless of export destination.Role-based access (Control workspace)
POLICY.POLICY_FEATURE.VENDORSauto-picks up READ for Auditors viaROLE_PERMISSION_BUNDLESinPolicyUtils.ts.VENDORSin their permission bundle).Edge cases
isMatchingVendorListLoadedclause of the gate). After the sync completes, the row appears.policy.connectionsisn't hydrated on app start): paste/workspaces/<policyID>/vendorsdirectly into the URL bar. Verify the page shows a loading indicator untilwithPolicyConnectionshydrates the connections, then renders the list.Offline tests
policy.connectionsisn't hydrated), keep the network disabled and open Workspace settings; verify the Vendors row is hidden. Re-enable the network and verify the row appears without requiring a manual navigation to Accounting — the effect refires on theisOfflinetransition.QA Steps
Ping me & @heyjennahay to test!
Same as Tests - Note: tester must have the
vendorMatchingbeta enabledPR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionmainbranch 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