Skip to content

[Due for payment 2026-10-01] Spend - After disconnecting accounting, Vendor column remains visible on Spend table #101859

Closed

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


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.
  1. Go to staging.new.expensify.com
  2. Go to workspace chat.
  3. Create an expense.
  4. Go to Spend > Expenses.
  5. Click Display > Edit columns.
  6. Select Vendor > Save.
  7. Go to workspace settings > Accounting.
  8. Disconnect Sage Intacct.
  9. Go to Spend > Expenses.
  10. 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.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome Safari

Screenshots/Videos

Bug7266051_1790060841564.14.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Julia Ponikarchuk
Bug ID: 7266051

Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 22, 2026
  2. applause-bot commented on Sep 22, 2026

    @applause-bot
    Author

    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

  3. melvin-bot commented on Sep 22, 2026

    @melvin-bot

    You have been assigned to this deploy blocker because you recently merged this PR: #101410

  4. melvin-bot commented on Sep 22, 2026

    @melvin-bot

    @Beamanator you have been assigned to this deploy blocker because you reviewed the offending PR: #101410

  5. github-actions commented on Sep 22, 2026

    @github-actions
    Contributor

    👋 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:

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  6. melvin-bot commented on Sep 22, 2026

    @melvin-bot

    @c3024 you have been assigned to this deploy blocker because you authored the offending PR: #101410

  7. melvin-bot commented on Sep 22, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  8. MelvinBot commented on Sep 22, 2026

    @MelvinBot
    Contributor

    🔍 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 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):

    1. 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.
    2. 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."
    3. src/components/Search/hooks/useSearchSnapshot.ts#L386-L401 — NOT gated. This is the Spend/Search table. It passes visibleColumns, read raw from SEARCH_ADVANCED_FILTERS_FORM at #L129, straight into getColumnsToShow with no vendor-availability filter.

    Root Cause

    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:

    App/src/libs/SearchUIUtils.ts

    Lines 6543 to 6546 in 538837e

    const allowedColumns: string[] = isExpenseReportView ? Object.values(CONST.SEARCH.REPORT_DETAILS_CUSTOM_COLUMNS) : Object.values(CONST.SEARCH.TYPE_CUSTOM_COLUMNS.EXPENSE);
    const filteredVisibleColumns = visibleColumns.filter((column) => allowedColumns.includes(column));
    const isDefaultExpenseColumnSelection = arraysEqual(Object.values(CONST.SEARCH.TYPE_DEFAULT_COLUMNS.EXPENSE), filteredVisibleColumns);
    const shouldUseCustomResult = !isDefaultExpenseColumnSelection && filteredVisibleColumns.length > 0;

    VENDOR is a member of TYPE_CUSTOM_COLUMNS.EXPENSE, so it survives into customResult and is returned verbatim:

    App/src/libs/SearchUIUtils.ts

    Lines 6761 to 6768 in 538837e

    if (customResult) {
    if (!shouldShowViolationsColumn) {
    return customResult.filter((column) => column !== CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS);
    }
    if (columns[CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS]) {
    insertColumnBeforeTotalAmount(customResult, CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS);
    }
    return customResult;

    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).
    • Offending code (❌):

      App/src/libs/SearchUIUtils.ts

      Lines 6761 to 6768 in 538837e

      if (customResult) {
      if (!shouldShowViolationsColumn) {
      return customResult.filter((column) => column !== CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS);
      }
      if (columns[CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS]) {
      insertColumnBeforeTotalAmount(customResult, CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS);
      }
      return customResult;
      (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.

  9. c3024 commented on Sep 22, 2026

    @c3024
    Contributor

    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.

  10. 19 remaining items

  11. c3024 commented on Sep 28, 2026

    @c3024
    Contributor

    Not overdue.

  12. melvin-bot commented on Sep 28, 2026

    @melvin-bot

    @mallenexpensify Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

  13. 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
  14. 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
  15. melvin-bot commented on Sep 28, 2026

    @melvin-bot

    Current assignee @mallenexpensify is eligible for the Awaiting Payment assigner, not assigning anyone new.

  16. 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
  17. mallenexpensify commented on Sep 28, 2026

    @mallenexpensify
    Contributor

    @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)

  18. melvin-bot commented on Sep 30, 2026

    @melvin-bot

    Payment Summary

    Resolving PRs:

    BugZero Checklist (@mallenexpensify)

    • I have confirmed assignees, roles, and Upwork contracts look correct
    • I have paid out Upwork contracts / manual NewDot requests
    • [BugZero Assignee] I have created a GH issue for creating/updating the regression test once above steps have been agreed upon
  19. mallenexpensify commented on Oct 1, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @eh2077 due $175 via NewDot

  20. laurenreidexpensify commented on Oct 15, 2026

    @laurenreidexpensify
    Contributor

    Approved @eh2077 $175 New Expensify

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.DailyKSv2Engineering

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions