Skip to content

I-031 — GCP charge shown in both sub-tabs + broken date sorting

Symptoms (owner, 2026-07-01)

  1. Service Invoices sub-tab: the Invoice Date column (MMM DD, YYYY) sorts in alphabetical (month-name) order, not by date.
  2. GCP charge 5626347939 (a Google Cloud Platform charge; should be a Service Invoice) shows in both the Expense Receipt sub-tab AND the Service Invoices sub-tab.
  3. Expense Receipt sub-tab sorts rows unpredictably when sorting by date.

Investigation (live tebs-erl / aote-system)

  • All date fields are ISO (YYYY-MM-DD): 86 receipts, the GCP billing invoices, the 3 manual + 46 workspace invoices — none non-ISO. So a raw date.localeCompare() would sort chronologically; the values aren't the problem.
  • No GCP receipt exists — 0 of 86 receipts match by amount (879.56 / 301.02), date (2026-06-30) or a Google/GCP/Cloud merchant. Not literally duplicated into the receipt store.
  • Confirmed root cause — duplicate React key: the GCP billing invoices collection holds two docs for June 2026, both invoiceMonth: "202606" — the Cost Table invoice 5626347939 ($879.56, 2026-06-30) and a BigQuery-only aggregate 202606 ($301.02, no invoice #). buildGcpRows keys each row by invoiceMonth, so both map to recordId 202606ExpenseRecordRow.id = "invoice:gcp:202606" for both. Duplicate React key → month rendered twice + AntD sort/render erratic (row bleeds into the other sub-tab's view = the "both tabs").

Fix (commit below)

  1. lib/accounting/vendorInvoiceFeed.server.ts (buildGcpRows) — collapse GCP rows to one per billing month, preferring the Cost Table invoice over the aggregate. Kills the duplicate June row + the key collision.
  2. components/records/ExpenseRecordsTab.tsx — the Date column sorts by the parsed date (new Date(x).getTime(), undated last), not string localeCompare.

Verification

Replicated listStoredInvoices + buildGcpRows on live data: 2026-06 went from 2 rows (both recordId 202606) → 1 (5626347939, $879.56). Project-pinned tsc clean. Owner to re-verify on deploy (if the Receipt sub-tab still mis-sorts with no GCP row present, a screenshot would help — no receipt-side dup was found).

Decision log

2026-07-01 — ✅ Read AGENTS.md before working this ticket. Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD

Renumbered I-024 → I-029 → I-031 across successive merge collisions (I-024/I-026 are the fork's NAS GCP-scraper tickets that fix the ingest side; I-029 was taken by the coaching-links recovery; I-030 by the payee-block hot-fix). This ticket is the read-time Records-feed tolerance for the duplicate June docs — complementary, not overlapping. Ticket-only. Next free I-032.