Field Calendar Week-Revenue Gutter (2026-07-31)

What: The “Wk” week-button gutter on the field calendar month view (right edge, after Sunday) shows per-week revenue for up_owner only: invoiced amount (green, top) over a ”+$X” live estimate for the week’s not-yet-invoiced inspections (muted, below). Tooltip and the week detail panel show full amounts with project counts. Other roles and the PPDR calendar keep the plain “Wk” button. Dustin’s ask: “just for Dustin” — deliberately NOT extended to up_admin (financial data).

Backend (entirely-api 268251b):

  • GET /portal/calendar/week-revenue?start_date&end_date in routes/portal_calendar_drag.py, require_role({"up_owner"}).
  • services/calendar_revenue_service.py: visits from the get_site_visits_in_range RPC; a project attributes to the Mon–Sun week of its latest non-cancelled in-range visit (no double-count across weeks). Skips: desk-review projects (PPDR calendar owns those), declined, Closed-without-invoice. Actual = stored projects.invoice_total for invoice_status Sent/Paid + the legacy Closed+invoice_number archival case. Potential = build_invoice_context(record_id, enrich=False).subtotal per the no-duplicate-calculator rule; estimates TTL-cached 10 min, statuses always fresh → amounts move potential→actual as invoices send.
  • build_invoice_context gained enrich: bool = True. enrich=False simulates missing WTR/ENC parent lines in-memory instead of writing create_synthetic_sow_line — a passive calendar view must never create SOW lines or clear ABSoW reconciliation stamps (verified: 0 absow audit events after live smoke tests that priced WTR projects).

Frontend (entirely-portal 48f1aca): CalendarPage fetches revenue alongside events when user.role === 'up_owner' && variant === 'field'; gutter column widens 24px→58px; falls back to “Wk” until loaded.

Tests: tests/test_calendar_week_revenue.py (11 — date parsing incl. UTC→ET shift, Monday bucketing, latest-visit dedupe, classification).

Verified live: endpoint 401 anon / 403 scheduler / 200 owner with 5 weeks of correct data (~3.7s cold, cached after); bundle index-Dh335gvw.js live with the feature string.