Holding Pool: No-Show Chips + hold_since Backfill (2026-07-31)
Context: Dustin asked where hold tracking lives (answer: Holding Pool,
sidebar Monitoring, entirely.it.com/holding-pool, up_owner/up_admin), then
asked for two things: backfill the 12 legacy holds missing hold_since,
and surface missed-appointment association per hold — a no-show hold aged
past 30 days is capturable revenue (trip-charge it, close it out).
Backfill (data, via supabase_write_service.update_project in-container): 12 on-hold rows had NULL hold_since. Stamped from last real activity: 8 from last site visit (2025-01-31 … 2026-07-24), 2 from date-invoice-sent (blkhse.44725→2024-07-10, blkhse.45193→2024-12-27), 3 from the PMS migration floor 2026-03-15 (blkhse.45083/45031/45087 — no real activity date exists anywhere; “held since at least when it entered Mostly”). MAN-9132 was NOT legacy — a current project held without a stamp. Verified: 0 NULLs remain among 24 on-hold rows.
No-show feature (entirely-api 9c77c6f, portal 4347716):
GET /portal/holding-pooladds per-projectno_show_count+last_no_show(batch site_visits visit_status=‘No-Show’ lookup) andtrip_charge_rate(ADM-MISS unit_price, $400 — never hardcoded in UI).- HoldingPool page: gold outline “No-Show” chip under 30 days on hold; solid red “Trip-Charge Ready” at 30+ (BILLABLE_HOLD_DAYS=30). Summary bar adds “No-Show Holds” and “Billable Now” (count + ~$ estimate). Toolbar gains “No-Shows Only” filter. Old generic 30-day “Invoice” chip remains for holds without a no-show.
- Live at ship: MAN-9007 (125d) + MAN-9030 (113d) trip-charge ready (~$800); MAN-9147/9153 fresh no-show holds that ripen at 30 days.
One-click closeout (entirely-api 0f30a8c, portal 4e775d3):
POST /portal/holding-pool/{id}/trip-charge-closeout (owner only) —
guards (on hold + No-Show visit + not already invoiced), ensures the
ADM-MISS SOW line (dedup, NO rescheduling RFI), persists invoice
overrides stripping every non-ADM-MISS line ("" kills auto PRD-DAY/
ADD-BKFL; context built enrich=True first so WTR/ENC parents land in the
remove-list), drafts with override_validation=True, verifies drafted
subtotal == trip charge (mismatch aborts + clears overrides), sends via
the normal pipeline with new send_invoice(suppress_cert_notice=True),
releases the hold, stamps portal_state.trip_charge_closeout
{invoice_number,total,at,by} as the re-intake breadcrumb. UI: red
“Bill Trip Charge & Close Out” button with inline confirm on ripe cards.
CAUTION for re-intake: the persisted remove-all-but-ADM-MISS override
MUST be swapped (not left) if the job is ever reactivated for a real
inspection, or the final invoice will bill only the trip charge.
Reactivation flow (entirely-api 47fe494+f5d4890, portal b3125a5):
Branch resubmits a trip-charged job → intake dup-check annotates matches
with trip_charge_closeout AND exempts them from the terminal-status
filter (paid trip charge auto-closes the project, which previously
dropped it from dup matches — a resubmission would have spawned a real
duplicate). Wizard shows a “Trip-Charge Closed Out” banner; the
add-visit action calls POST /portal/holding-pool/{id}/trip-charge- reactivate (roles = HOLD_ROLES) first: (1) inspection invoice number =
{base}-B (-C on re-reactivation) via new
invoice_numbering.register_suffixed_number; _lookup_existing now
returns the LATEST row per record so idempotent numbering reuses the
suffix; trip-charge invoice keeps {base} + its ledger row; (2)
override swap — closeout’s “bill only ADM-MISS” replaced with its
inverse (exclude billed ADM-MISS + memo citing the trip-charge invoice);
(3) status → Intake via allow_backward; scope re-confirmation happens
naturally (reparse edits clear the ABSoW stamp). Idempotent on retries.
KNOWN EDGE: after reactivation, projects.invoice_number = {base}-B,
so a remittance paying {base} may park in pending_matches for manual
confirm (existing recon flow) rather than auto-matching.
tests/test_trip_charge_reactivate.py (5). scheduler-intake.mmd updated.
Latent bug found + fixed (entirely-api 7cb67f2):
ss_read._get_client() does not exist (correct:
supabase_service.get_client). The PRE-EXISTING cancel-future-visits
block in place_on_hold has been silently failing on it (caught as a
warning) — portal holds were not cancelling future scheduled visits.
Fixed all 3 call sites. Damage check: 0 on-hold projects currently have
future non-cancelled visits, so no cleanup was needed.