[Payment due @abzokhattab] Add vendorID action field to merchant coding rules (vendor matching R5, Phase B) - #97811
Conversation
Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
🦜 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 d0991c549c7..75af3475e5d 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -7696,6 +7696,8 @@ Fordern Sie Spesendetails wie Belege und Beschreibungen an, legen Sie Limits und
expenseDefaultsSubtitle: 'Felder aktualisieren, ohne dass die einreichende Person etwas tun muss',
ifAnyExpenseMatches: 'Wenn eine Ausgabe übereinstimmt:',
thenApplyFollowingDefaults: 'Wenden Sie dann die folgenden Standardwerte an:',
+ vendorUnavailable: 'Anbieter nicht verfügbar',
+ supplierUnavailable: 'Lieferant nicht verfügbar',
},
categoryRules: {
title: 'Kategorienregeln',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index 4391e63ad2c..5176e22b090 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -8027,6 +8027,8 @@ ${reportName}`,
importColumnUpdatedCategory: 'Ενημερωμένη κατηγορία',
importColumnUpdatedTag: 'Ενημερωμένη ετικέτα',
importColumnUpdatedDescription: 'Ενημερωμένη περιγραφή',
+ vendorUnavailable: 'Ο προμηθευτής δεν είναι διαθέσιμος',
+ supplierUnavailable: 'Ο προμηθευτής δεν είναι διαθέσιμος',
},
newRule: {
title: 'Νέος κανόνας',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index e27e999b89d..9c4069c444e 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -7723,6 +7723,8 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
expenseDefaultsSubtitle: 'Mettre à jour les champs sans que le déclarant ne fasse quoi que ce soit',
ifAnyExpenseMatches: 'Si une dépense correspond :',
thenApplyFollowingDefaults: 'Appliquez ensuite les valeurs par défaut suivantes :',
+ vendorUnavailable: 'Fournisseur indisponible',
+ supplierUnavailable: 'Fournisseur indisponible',
},
categoryRules: {
title: 'Règles de catégorie',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index cf6c4b48a40..0b7ac73f1bf 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -7668,6 +7668,8 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
expenseDefaultsSubtitle: 'Aggiorna i campi senza che chi invia debba fare nulla',
ifAnyExpenseMatches: 'Se una qualsiasi spesa corrisponde a:',
thenApplyFollowingDefaults: 'Quindi applica le seguenti impostazioni predefinite:',
+ vendorUnavailable: 'Fornitore non disponibile',
+ supplierUnavailable: 'Fornitore non disponibile',
},
categoryRules: {
title: 'Regole di categoria',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index b5cb7e25463..87a6d7d2bab 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -7575,6 +7575,8 @@ ${reportName}`,
expenseDefaultsSubtitle: '申請者が何も操作しなくてもフィールドを更新する',
ifAnyExpenseMatches: 'いずれかの経費が次の条件に一致する場合:',
thenApplyFollowingDefaults: '次に、以下のデフォルトを適用します。',
+ vendorUnavailable: 'ベンダーを利用できません',
+ supplierUnavailable: 'サプライヤーを利用できません',
},
categoryRules: {
title: 'カテゴリルール',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 0ef89e0a32c..137d17ce334 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -7651,6 +7651,8 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
expenseDefaultsSubtitle: 'Velden bijwerken zonder dat de indiener iets hoeft te doen',
ifAnyExpenseMatches: 'Als een uitgave overeenkomt:',
thenApplyFollowingDefaults: 'Pas vervolgens de volgende standaardinstellingen toe:',
+ vendorUnavailable: 'Leverancier niet beschikbaar',
+ supplierUnavailable: 'Leverancier niet beschikbaar',
},
categoryRules: {
title: 'Categorisatieregels',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index cbe6f9a6372..fbe2e673251 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -7630,6 +7630,8 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
expenseDefaultsSubtitle: 'Aktualizuj pola bez wymagania działania od osoby zgłaszającej',
ifAnyExpenseMatches: 'Jeśli którykolwiek wydatek pasuje:',
thenApplyFollowingDefaults: 'Następnie zastosuj następujące domyślne ustawienia:',
+ vendorUnavailable: 'Dostawca niedostępny',
+ supplierUnavailable: 'Dostawca niedostępny',
},
categoryRules: {
title: 'Reguły kategorii',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index d755a20e994..9d92516656a 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -7646,6 +7646,8 @@ Exija dados de despesas como recibos e descrições, defina limites e padrões e
expenseDefaultsSubtitle: 'Atualizar campos sem o responsável pelo envio fazer nada',
ifAnyExpenseMatches: 'Se alguma despesa corresponder:',
thenApplyFollowingDefaults: 'Em seguida, aplica os seguintes padrões:',
+ vendorUnavailable: 'Fornecedor indisponível',
+ supplierUnavailable: 'Fornecedor indisponível',
},
categoryRules: {
title: 'Regras de categoria',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index ee4798c1282..5452f7f8265 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -7399,6 +7399,8 @@ ${reportName}`,
expenseDefaultsSubtitle: '在提交人无须执行任何操作的情况下更新字段',
ifAnyExpenseMatches: '如果任一报销符合以下条件:',
thenApplyFollowingDefaults: '然后应用以下默认设置:',
+ vendorUnavailable: '供应商不可用',
+ supplierUnavailable: '供应商不可用',
},
categoryRules: {
title: '类别规则',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
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 fix the polyglot changes by running the correct script Then in a new commit fix Oxfmt and typescript checks |
Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
|
Done — pushed two commits to 1. Polyglot translations ( 2. Oxfmt (
|
|
@MelvinBot fix the formatting of the PR template text, then consider what kind of tests we need to write for #97811 (comment) |
|
1. PR template formatting — fixed. The body had a stray reviewer-checklist block and a 2. Tests for the Codecov drop The Codecov report flags two genuine gaps and a lot of noise. The existing a)
b)
c) The "Set vendor to" row in Skip (noise): the deltas on Reply |
|
from Alex's Claude agent I looked at each proposal against the current tree. Disagree with (a), agree with (b) + (c), agree skipping the nav wiring. (a) PolicyUtils vendor helpers — skipAlready covered on
These landed with R2 / R4. The Codecov "gap" on them in this PR is measurement noise from the impacted-files view, not a real coverage hole — nothing here would exercise those branches better than the existing R2/R4 suite. Adding a duplicate matrix in this PR would be pure scope creep. (b) AddVendorPage.tsx — agreeNew file, 53 lines, 0% coverage. RTL test file Fixtures — QBO policy (with vendor list), Xero policy (with contacts), empty-list policy. Seed Cases to assert:
(c) "Set vendor to" row in
|
Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
|
Done — pushed What landed
Deviation: pure-logic unit tests instead of RTL renders — and why
What that leaves uncovered: the parts of your plan that genuinely need a render — If you'd rather have the full RTL suite exactly as scoped, say the word and I'll write it — with the caveat that I can't run it locally, so it'll rely on CI to validate.
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b91c1e25c
ℹ️ 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".
Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com>
|
MelvinBot has made 5 consecutive commits trying to fix CI failures. Automatic CI fix dispatches are now paused for this PR. Next steps: A human should review the recent commits and resolve the conflicting CI errors manually. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cded6ec63d
ℹ️ 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".
|
@MelvinBot consider the unresolved codex comments and if they need to be addressed - if so, please address. Also fix the spell check failing check |
…and fix spellcheck Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
|
Both unresolved codex comments were valid and are now addressed, and the 1. [P2] Gate direct vendor picker access behind the beta — valid. 2. [P3] Show the stored vendor value before vendor data loads — valid. The editor row titled itself with a bare 3. Spellcheck — the failure was Local verification
The vendor picker is beta + integration (QBO/Intacct/Xero) gated, so this was validated via the unit suite and static checks rather than a live app run. The two remaining failing checks are |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 64be6e704f
ℹ️ 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".
Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com>
- AddVendorPage: show FullScreenLoadingIndicator while the connections prefetch is in flight instead of falling through to NotFoundPage on a deep-link cold-load. Matches how withPolicyConnections gates. - Rule summary: when the workspace has no active vendor-matching source (e.g. admin switched non-reimbursable export mode to Vendor Bill after authoring the rule), fall back to findVendorByID so the historical vendor name still renders instead of the raw external ID. Keeps the active-source strict scoping when a source is active, so a rule that only resolves against a stale/inactive connection still surfaces as "unavailable". - Add regression test for the export-mode-change case.
…orPage Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
|
Fixed the failing Compare knip issues against main check. Root cause: Fix: Dropped the type import and passed the object as an inline literal to return <FullScreenLoadingIndicator reasonAttributes={{context: 'AddVendorPage', isFetchNeeded, isLoadingFetchedFlag}} />;This matches the established convention across the codebase (e.g. Verification
The other red check, |
…line literal Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
…ingIndicator Co-authored-by: Alex Beaman <Beamanator@users.noreply.github.com>
|
Fixed the failing typecheck check (commit Root cause: This branch predates a refactor on Fix: Dropped the removed prop — Details & verification
The only remaining red check is |
|
All code checks are now green — my earlier fix resolved the Compare knip issues against main failure, and ESLint, typecheck, the full The only remaining red check is Check independent approval, which isn't a code failure — it just needs an independent reviewer to approve the PR. There's nothing for me to push a fix for there. |
|
@MelvinBot @codex review the PR |
Code reviewOverall this is clean and well-tested — the beta + connected-integration gating, the deeplink 🟡 Vendor-name fallback is inconsistent across the two rule surfacesThe same coding rule is rendered in two places, and they resolve
Concretely: a QBO workspace authors a rule while non-reimbursable export = Credit Card (vendor matching active), then the admin switches export to Vendor Bill. Now You clearly intended the historical-name behavior — it has a dedicated passing test ( Note on the editor rowThe editor's 🔵 Test-plan wording nit (already flagged by
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5281f28f2
ℹ️ 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 revamp table (getMerchantCodingRulesTableData) was already updated so the rule summary keeps rendering the stored vendor's name after an admin switches the workspace's non-reimbursable export mode away from vendor-matching mode. The legacy pre-revamp path in MerchantRulesSection (rendered whenever the RULES_REVAMP beta is off) still had the two-tier lookup and fell through to the raw external ID in the same state. Add findVendorByID as a tier-3 permissive fallback so both summaries stay consistent, keeping the raw external ID only as a last resort while connection data hasn't hydrated. Export getRuleDescription so the same regression tests we added for the revamp table can run against the legacy path.
|
🎯 @abzokhattab, thanks for reviewing and testing this PR! 🎉 A payment issue will be created for your review once this PR is deployed to production. If payment is not needed (e.g., regression PR review fix etc), react with 👎 to this comment to prevent the payment issue from being created. |
|
Asked @mollfpr to help review since an internal engineer didn't get assigned 🤷 |
|
🚧 mollfpr has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/mollfpr in version: 9.4.53-0 🚀
|
|
🤖 Help site review — changes required This PR adds a Vendor action field to Merchant Rules (labeled Supplier on Xero), letting admins set a specific vendor on every expense a rule matches. The Merchant Rules help article lists the fields Merchant Rules can update but did not include Vendor, and the QuickBooks Online vendor matching article already references "if a workspace merchant rule specifies a vendor" — so there was a real gap. Draft help site PR: #98481 It updates
@Beamanator, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
|
Working well in staging! Screen.Recording.2026-08-13.at.9.16.07.AM.mov |
|
Deploy Blocker #98602 was identified to be related to this PR. |
Explanation of Change
Adds
vendorIDas an action field on merchant coding rules so admins can set a specific vendor on every expense that matches a rule. This is the App side of vendor matching R5 Phase B; Auth and Web-Expensify pieces ship separately.Everything is gated behind the
vendorMatchingbeta AND the policy having a connected vendor-matching integration (QuickBooks Online, Sage Intacct, or Xero) — off-beta or unconnected workspaces see no vendor UI. Only the external vendor ID is stored; the display name is resolved at render time from the policy's active vendor list so renamed vendors stay in sync. On Xero workspaces the copy flips to "Supplier".CSV importer, Concierge action UI, and any cached vendor name are explicitly out of scope.
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/638612
PROPOSAL:
Tests
Prereqs: admin of a workspace with either a QBO, Sage Intacct, or Xero connection that has synced vendors. Enable the
vendorMatchingbeta (Staging Settings → Betas, orOnyx.set('betas', ['vendorMatching', ...])in dev tools).getMatchingVendors(policy)(same list the transaction-level picker uses). Xero workspace → header says "Supplier".set vendor to <name>(orset supplier to <name>on Xero).policy_<id>.connections) — verify the rules list re-renders the new name without editing the rule (name is resolved at render, not stored).vendorMatchingbeta. Verify:/workspaces/<policyID>/rules/merchant-rules/new/vendorrenders theNotFoundPage(does not render the picker).NotFoundPage.rules.codingRules.<ruleID>.vendorIDin Onyx to a value that isn't in the active vendor list. Verify the summary showsvendor unavailable(orsupplier unavailableon Xero) once the vendor list has loaded, and shows the raw ID while the list is still syncing.Offline tests
Onyx.set('network', {isOffline: true})).QA Steps
Feel free to ping me & @heyjennahay to test!
Same as Tests section 1–8 and 12, using a Staging workspace with a synced QBO / Intacct / Xero connection and the
vendorMatchingbeta enabled.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./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screenshots/Videosundefined