Skip to main content

Bug: accounts.stats investmentTotal ignores investments table (Itaú OF shows R$0 invested)

Context (pt-BR)

Post Featurebase 6aea1bfcc28a57fa423bb6fd (MCP pluma_accounts_stats, gravidade alta, 1 voto). Relator UUID 3573845b-21b5-47d7-8d23-dae23393231c (mesmo de PLU-844). Cliente MCP relatou "investimentos do Itaú não importados" porque investmentTotal = 0 e só há conta corrente + cartão Itaú em accounts.

Não é falha de sync Pluggy neste caso. Posições já estão em investments. O buraco é agregação financeira (accounts.stats.investmentTotal / netWorth e MCP) que ignora a tabela investments. SWE não começa sem decisão do Lucca (A/B/C abaixo) — matriz: cálculo financeiro.

Problem

Open Finance Itaú (connector 601) syncs investment positions into investments linked to the checking/credit connected_account. It does not create accounts rows with type=investment (0 such rows for connector 601 in prod).

accounts.stats (apps/web/api/trpc/_services/accounts.ts) computes:

investmentTotal = Σ balance WHERE accounts.type ∈ {investment, INVESTMENT}
netWorth = investmentTotal + cashTotal − creditTotal

So for every user whose wealth lives only in investments (the Itaú / OF pattern), MCP pluma_accounts_stats and any UI bound to accounts.stats report R$ 0 invested and an understated net worth — even when Contas UI (AssetCategoriesSection → fetchInvestmentsWithAccounts) already sums investments.balance correctly.

The Featurebase filing is an MCP false-negative on import; the product bug is stats ≠ Contas / Investimentos.

Evidence (read-only, anonymized)

Reporter

  • UUID 3573845b-21b5-47d7-8d23-dae23393231c
  • Itaú connected_account active, connector 601, last_sync 2026-11-03, no sync_error
  • accounts on that item: checking + credit only (no type=investment)
  • investments on that CA: 3 rows, all FIXED_INCOME / ACTIVE / not hidden, with positive balance/amount/value (sum > R$ 50k; aligns with ">~90 mil" claim without exposing exact amounts)
  • MCP pluma_accounts_stats → investmentTotal = 0 (by design of the buggy aggregator)

System impact (COUNT only)

| Metric | Count | | -- | -- | | Active Itaú items (connector 601) | ~344 | | …with ≥1 investment position | 185 | | …with positive position value | 131 | | …with zero positions | 159 | | accounts with type=investment (all connectors) | 1 (1 user) | | Users with visible investments rows | 909 | | Users with positions but no investment account | 908 |

Bank profile 601 / 201 Itaú: known_issues=[], needs_correction=false.

Secondary cohort: ~159 Itaú items with no positions may be consent / Pluggy empty / product INV gap (see historical PLU-107). Out of scope for this issue until Lucca asks for a Pluggy read on a sample itemId.

Expected behavior

  • accounts.stats.investmentTotal (and therefore netWorth / MCP pluma_accounts_stats) must include the sum of visible non-manual investments positions (same population Contas already shows), not only accounts.type=investment.
  • Contas / Investimentos Beta and stats must agree for the same user scope.
  • Creating a synthetic accounts.type=investment row is not required for the minimum fix (option A).

Acceptance criteria (Gherkin)

Feature: accounts.stats includes investments table positions

  Scenario: Itaú-style user with positions and no investment account
    Given a user has BRL cash C and credit debt D
    And N ACTIVE non-manual investments with positive balance totaling I
    And zero accounts with type=investment
    When accounts.stats runs
    Then investmentTotal equals I
    And netWorth equals C + I - D (with credit sign rules from PLU-844)

  Scenario: Contas and stats agree
    Given the same user as above
    When Contas AssetCategoriesSection investment header and accounts.stats run
    Then both investment totals equal I within rounding

  Scenario: Manual investments are not double-counted
    Given a manual investment already counted via manualPortfolio
    When accounts.stats aggregates investments
    Then is_manual=true rows are excluded (same as fetchInvestmentsWithAccounts)

  Scenario: Hidden / inactive positions excluded
    Given investments with is_hidden=true or status≠ACTIVE
    When accounts.stats runs
    Then those rows do not enter investmentTotal

  Scenario: MCP mirrors the fix
    Given the same authenticated user
    When pluma_accounts_stats is called
    Then investmentTotal is no longer zero solely because accounts.type≠investment

Out of scope

  • Pluggy upstream ticket / reconnect for the ~159 Itaú items with zero positions (separate investigation).
  • Creating accounts.type=investment mirror rows from sync (option B only).
  • Dashboard totalBalance credit sign bug (PLU-844) — coordinate so both aggregators end consistent.
  • Investment returns / Dia-Mês columns (PLU-801), cost basis (PLU-784).

Risks

  • Raising investmentTotal / netWorth for ~908 users changes headline wealth (correct, but CX/comms).
  • Double-count if some connectors still write both accounts.type=investment and investments for the same money — today only 1 investment account exists; still need a join rule (prefer positions; or subtract overlapping account ids).
  • Must stay aligned with PLU-844 credit liability rules when both land.

Likely files

  • apps/web/api/trpc/_services/accounts.ts (stats — add investments sum)
  • Possibly apps/web/api/trpc/_services/dashboard.ts if summary must include positions too
  • apps/web/src/lib/accounts/accounts-data.ts (fetchInvestmentsWithAccounts — reference filters)
  • Unit tests under apps/web/__tests__/accounts/api/
  • MCP docs if field semantics change

Effort

S for option A (query + tests). Option B = M (sync + possible migration) → Lucca.

Priority

High (P1) — core net-worth / MCP figure understates invested wealth for ~908 users (~131 Itaú with positive value alone). Not P0: data present, app up, Contas path already shows positions for this reporter. Not a Pluggy outage.

Decision needed (Lucca) — bloqueante

A — Recommended. Extend accounts.stats (and dashboard wealth total if it still ignores positions) to add SUM(investments.balance) for status=ACTIVE, is_manual=false, not hidden, same household/user scope as Contas. Document MCP. Coordinate with PLU-844 so netWorth formula becomes cash + investmentsPositions[+investmentAccounts if non-overlapping] − abs(credit). No migration / no sync change.

B. Do A plus have Pluggy sync create/update accounts.type=investment aggregate rows per connected_account so list/stats stay account-centric. Touches lib/pluggy / sync — Lucca required. Effort M.

C. Treat MCP filing as invalid: keep investmentTotal = accounts only; update MCP tool copy to point at Investimentos / Contas investments section; no aggregator change. Leaves netWorth systematically low for OF banks.

Recommendation: A. Sync already works for the reporter; fix the aggregator Contas already bypassed. Park zero-position Itaú cohort as a follow-up Pluggy sample if Lucca wants.

Status

Todo — blocked on Lucca A/B/C. Do not assign SWE until decided. Related: PLU-844 (same reporter, same financial-aggregation matrix).

Status: Planned

No comments yet

Be the first to share your thoughts.