You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-10-01] Spend - After disconnecting accounting, Vendor column remains visible on Spend table #101859
Version Number: 9.4.91-0 Reproducible in staging?: Yes Reproducible in production?: N/A - new feature, doesn't exist in prod If this was caught during regression testing, add the test name, ID and link from BrowserStack:#101410 Email or phone of affected tester (no customers):jsdoijnodsdisojo@gmail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Mac 26.5 / Chrome App Component: Search
Action Performed:
Precondition:
Connect workspace to Sage Intacct.
Go to staging.new.expensify.com
Go to workspace chat.
Create an expense.
Go to Spend > Expenses.
Click Display > Edit columns.
Select Vendor > Save.
Go to workspace settings > Accounting.
Disconnect Sage Intacct.
Go to Spend > Expenses.
Click Display > Edit columns.
Expected Result:
After disconnecting Sage Intacct, Vendor column will be hidden on Spend table.
Actual Result:
After disconnecting Sage Intacct, Vendor column remains visible on Spend table.
In Columns list, Vendor column is hidden.
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
The Vendor column availability gate was added to the column picker and to the report table, but not to the Search (Spend) table's render path, so the saved column list keeps rendering Vendor after the integration is disconnected. A targeted gate in getColumnsToShow (mirroring how VIOLATIONS is already handled a few lines below) is far smaller and safer than reverting the whole feature.
Assigned: @c3024 (PR author), @Beamanator (approving reviewer) from highest-confidence causing PR only — already assigned by melvin-bot, left as-is Labels: No changes. DeployBlockerCash kept (App PR caused it). DeployBlocker was not present, so nothing to remove.
📋 Detailed Analysis
Evidence
PR #101410 is the PR that introduced CONST.SEARCH.TABLE_COLUMNS.VENDOR and every render path for it (src/CONST/index.ts, SearchTableHeader.tsx, TransactionItemRowWide.tsx, SearchUIUtils.ts, SearchColumnsPage.tsx, ReportDetailsColumnsPage.tsx). Merged 2026-09-20, and it is item 8 on Deploy Checklist: New Expensify 2026-09-21. The Vendor column does not exist in production, which matches "N/A - new feature, doesn't exist in prod".
Sage Intacct is one of the non-beta-gated vendor-matching integrations, so disconnecting it flips hasVendorFeature from true to false for that workspace — see PolicyUtils.ts#L2782-L2790.
That flip is honoured in exactly two of the three places it needs to be, which is precisely the split symptom reported ("Columns list says hidden, table still shows it").
Verification
Affected files (all touched or introduced by PR #101410):
src/pages/Search/SearchColumnsPage.tsx#L40-L48 — gated. isColumnAvailable strips VENDOR from both allTypeCustomColumns and currentColumns when no policy has the vendor feature. This is why step 10 shows Vendor as hidden in the Columns list.
src/components/MoneyRequestReportView/useMoneyRequestReportColumns.ts#L77-L84 — gated. The report table filters VENDOR out of savedColumns before calling getColumnsToShow, with the comment "The saved column list is account-wide, so drop the vendor column on reports whose workspace lacks the vendor feature."
The user's saved column list is account-wide and persists after the integration is disconnected. In SearchUIUtils.getColumnsToShow the saved list is only filtered against the type's allowed columns:
Note that the VIOLATIONS column immediately above solves the identical problem correctly: it is gated in the picker and stripped from customResult here via shouldShowViolationsColumn. VENDOR got the picker half of that pattern but not the table half. The only other vendor logic in getColumnsToShow is the data-presence check at #L6660-L6662, which mutates the columns map and is therefore dead on the customResult path.
Suggested fix — add an isVendorColumnAvailable parameter to getColumnsToShow and strip VENDOR from customResult exactly the way VIOLATIONS is stripped, then pass it from the Search call sites using the hasVendorFeatureOnAnyPolicy selector already added by this PR. Doing it inside getColumnsToShow covers every unguarded consumer in one place; besides useSearchSnapshot.ts#L390 these read the same unfiltered saved list:
Workaround: open Display > Edit columns and hit Save. The picker saves its already-filtered currentColumns, which rewrites the persisted list without Vendor.
💡 Suggested AI code review rule
This bug looks like a recurring, mechanically detectable pattern, so it may be worth a coding-standards rule so the AI reviewer catches the next one. Proposed for Expensify/App:
Rule:CONSISTENCY-18 Re-apply availability gates where a persisted preference is consumed, not only where it is picked
Condition: When a PR adds a feature/permission/beta gate that hides an option from a settings or picker page, flag it unless the same gate is applied on the path that reads the persisted value back for rendering or export. Persisted preferences outlive the entitlement that created them, so the consumption site must re-validate.
Why:PR #101410 gated the Vendor column in SearchColumnsPage and in the report table but not in the Search table, so after disconnecting Sage Intacct the saved column list kept rendering Vendor while the picker showed it as hidden (#101859).
Search pattern: in a PR diff, column !== CONST.SEARCH.TABLE_COLUMNS.<X> (or an equivalent isXAvailable && guard) added under src/pages/**/*ColumnsPage.tsx / other picker pages, with no matching <X> guard added in the corresponding read path (getColumnsToShow in src/libs/SearchUIUtils.ts, or wherever the persisted list is consumed).
(compare the VIOLATIONS guard, which does it right, with the missing VENDOR guard)
Fix (✅): gate VENDOR in customResult the same way shouldShowViolationsColumn gates VIOLATIONS, and thread isVendorColumnAvailable from the callers listed above.
The full rule file, with the ❌/✅ code inline in Expensify/App's coding-standards format, would go in Expensify/App itself (.claude/skills/app-coding-standards/rules/consistency-18-*.md plus a SKILL.md index bullet). If it's worth adding, a maintainer can open it there as a draft PR.
I think we should demote this. It only shows up when a user has added the Vendor column and the workspace later loses the vendor feature (integration disconnected etc.). This is a new feature and it does not exist in production.
A user can remove the column by clicking on Edit columns and saving the list. Other feature-dependent column (tax, category, tag, card, exported) stays in the table after its feature is turned off.
I will open a PR to make the Spend table behave the same as the picker, but it does not need to hold the deploy or be CP'd IMO.
changed the title [-][Payment Due Oct 1, 2026] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+]Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+]on Sep 28, 2026
changed the title [-]Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+][Due for payment 2026-09-28] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+]on Sep 28, 2026
changed the title [-][Due for payment 2026-09-28] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+][Due for payment 2026-10-01] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+]on Sep 28, 2026
@Beamanator use the Awaiting Payment label first, it'll add 'due for payment' in the title with today's date, then update that date, if needed (cuz I need formatting to be consistent to not miss issues when paying)
Activity
applause-bot commentedon Sep 22, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/708ad9cc4da8de71d2852c870d772f84cf1a490f64babbd5651212a4a021a181
melvin-bot commentedon Sep 22, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #101410
melvin-bot commentedon Sep 22, 2026
@Beamanator you have been assigned to this deploy blocker because you reviewed the offending PR: #101410
github-actions commentedon Sep 22, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
melvin-bot commentedon Sep 22, 2026
@c3024 you have been assigned to this deploy blocker because you authored the offending PR: #101410
melvin-bot commentedon Sep 22, 2026
💬 A slack conversation has been started in #expensify-open-source
MelvinBot commentedon Sep 22, 2026
🔍 Investigation Summary
Classification: Frontend bug
Causing PR: [Internal QA] Add Vendor column to expense Search and report tables - by @c3024 (High confidence)
Related Issues: None
Recommendation: ROLL FORWARD
The Vendor column availability gate was added to the column picker and to the report table, but not to the Search (Spend) table's render path, so the saved column list keeps rendering Vendor after the integration is disconnected. A targeted gate in
getColumnsToShow(mirroring howVIOLATIONSis already handled a few lines below) is far smaller and safer than reverting the whole feature.Assigned: @c3024 (PR author), @Beamanator (approving reviewer) from highest-confidence causing PR only — already assigned by melvin-bot, left as-is
Labels: No changes.
DeployBlockerCashkept (App PR caused it).DeployBlockerwas not present, so nothing to remove.📋 Detailed Analysis
Evidence
CONST.SEARCH.TABLE_COLUMNS.VENDORand every render path for it (src/CONST/index.ts,SearchTableHeader.tsx,TransactionItemRowWide.tsx,SearchUIUtils.ts,SearchColumnsPage.tsx,ReportDetailsColumnsPage.tsx). Merged 2026-09-20, and it is item 8 on Deploy Checklist: New Expensify 2026-09-21. The Vendor column does not exist in production, which matches "N/A - new feature, doesn't exist in prod".hasVendorFeaturefromtruetofalsefor that workspace — seePolicyUtils.ts#L2782-L2790.Verification
Affected files (all touched or introduced by PR #101410):
src/pages/Search/SearchColumnsPage.tsx#L40-L48— gated.isColumnAvailablestripsVENDORfrom bothallTypeCustomColumnsandcurrentColumnswhen no policy has the vendor feature. This is why step 10 shows Vendor as hidden in the Columns list.src/components/MoneyRequestReportView/useMoneyRequestReportColumns.ts#L77-L84— gated. The report table filtersVENDORout ofsavedColumnsbefore callinggetColumnsToShow, with the comment "The saved column list is account-wide, so drop the vendor column on reports whose workspace lacks the vendor feature."src/components/Search/hooks/useSearchSnapshot.ts#L386-L401— NOT gated. This is the Spend/Search table. It passesvisibleColumns, read raw fromSEARCH_ADVANCED_FILTERS_FORMat#L129, straight intogetColumnsToShowwith no vendor-availability filter.Root Cause
The user's saved column list is account-wide and persists after the integration is disconnected. In
SearchUIUtils.getColumnsToShowthe saved list is only filtered against the type's allowed columns:App/src/libs/SearchUIUtils.ts
Lines 6543 to 6546 in 538837e
VENDORis a member ofTYPE_CUSTOM_COLUMNS.EXPENSE, so it survives intocustomResultand is returned verbatim:App/src/libs/SearchUIUtils.ts
Lines 6761 to 6768 in 538837e
Note that the
VIOLATIONScolumn immediately above solves the identical problem correctly: it is gated in the picker and stripped fromcustomResulthere viashouldShowViolationsColumn.VENDORgot the picker half of that pattern but not the table half. The only other vendor logic ingetColumnsToShowis the data-presence check at#L6660-L6662, which mutates thecolumnsmap and is therefore dead on thecustomResultpath.Suggested fix — add an
isVendorColumnAvailableparameter togetColumnsToShowand stripVENDORfromcustomResultexactly the wayVIOLATIONSis stripped, then pass it from the Search call sites using thehasVendorFeatureOnAnyPolicyselector already added by this PR. Doing it insidegetColumnsToShowcovers every unguarded consumer in one place; besidesuseSearchSnapshot.ts#L390these read the same unfiltered saved list:src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx#L143src/components/Search/SearchList/ListItem/GroupHeader.tsx#L141src/components/Search/FilterDropdowns/SortByPopup.tsx#L58src/hooks/useSearchOverlay.tsx#L117src/hooks/useSearchBulkActions.ts#L1033(CSV export would also emit a Vendor column)Workaround: open Display > Edit columns and hit Save. The picker saves its already-filtered
currentColumns, which rewrites the persisted list without Vendor.💡 Suggested AI code review rule
This bug looks like a recurring, mechanically detectable pattern, so it may be worth a coding-standards rule so the AI reviewer catches the next one. Proposed for
Expensify/App:CONSISTENCY-18Re-apply availability gates where a persisted preference is consumed, not only where it is pickedSearchColumnsPageand in the report table but not in the Search table, so after disconnecting Sage Intacct the saved column list kept rendering Vendor while the picker showed it as hidden (#101859).column !== CONST.SEARCH.TABLE_COLUMNS.<X>(or an equivalentisXAvailable &&guard) added undersrc/pages/**/*ColumnsPage.tsx/ other picker pages, with no matching<X>guard added in the corresponding read path (getColumnsToShowinsrc/libs/SearchUIUtils.ts, or wherever the persisted list is consumed).App/src/libs/SearchUIUtils.ts
Lines 6761 to 6768 in 538837e
VIOLATIONSguard, which does it right, with the missingVENDORguard)VENDORincustomResultthe same wayshouldShowViolationsColumngatesVIOLATIONS, and threadisVendorColumnAvailablefrom the callers listed above.The full rule file, with the ❌/✅ code inline in
Expensify/App's coding-standards format, would go inExpensify/Appitself (.claude/skills/app-coding-standards/rules/consistency-18-*.mdplus aSKILL.mdindex bullet). If it's worth adding, a maintainer can open it there as a draft PR.c3024 commentedon Sep 22, 2026
I think we should demote this. It only shows up when a user has added the Vendor column and the workspace later loses the vendor feature (integration disconnected etc.). This is a new feature and it does not exist in production.
A user can remove the column by clicking on Edit columns and saving the list. Other feature-dependent column (tax, category, tag, card, exported) stays in the table after its feature is turned off.
I will open a PR to make the Spend table behave the same as the picker, but it does not need to hold the deploy or be CP'd IMO.
19 remaining items
c3024 commentedon Sep 28, 2026
Not overdue.
melvin-bot commentedon Sep 28, 2026
@mallenexpensify Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
[-][Payment Due Oct 1, 2026] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+]Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+][-]Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+][Due for payment 2026-09-28] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+]melvin-bot commentedon Sep 28, 2026
Current assignee @mallenexpensify is eligible for the Awaiting Payment assigner, not assigning anyone new.
[-][Due for payment 2026-09-28] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/-][+][Due for payment 2026-10-01] Spend - After disconnecting accounting, Vendor column remains visible on Spend table[/+]mallenexpensify commentedon Sep 28, 2026
@Beamanator use the
Awaiting Paymentlabel first, it'll add 'due for payment' in the title with today's date, then update that date, if needed (cuz I need formatting to be consistent to not miss issues when paying)melvin-bot commentedon Sep 30, 2026
Payment Summary
Resolving PRs:
Drop the Vendor column from the Spend table when no workspace has the vendor feature #101864
Contributor: @c3024 does not require payment (Contractor)
Reviewer: @eh2077 owed $175 via NewDot
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Oct 1, 2026
Payment Summary
Contributor+: @eh2077 due $175 via NewDot
laurenreidexpensify commentedon Oct 15, 2026
Approved @eh2077 $175 New Expensify