## Demo
<img width="2624" height="1636" alt="image" src="https://github.com/user-attachments/assets/c0bdc4da-60d9-4edd-90dc-3f124b85a4ee" />
## Summary
Expands the SaaS Budgeting tab with three substantial additions plus two correctness fixes. Branch grew well beyond the original Excluded-Accounts scope; grouping themes below.
---
### 1. AWS Spend — Excluded AWS Accounts band + exclusion registry
Adds a 3rd table to the SaaS Budgeting → AWS Spend tab surfacing every account hidden by the new hardcoded exclusion registry so the methodology stays auditable. Also fixes the Enterprise SaaS account (446735561331) that was missing from the canonical mapping spine despite real Gomembers / IgniteTech spend.
Backend (klair-api/)
- services/saas_budgeting_aws_spend_exclusions.py (new) — registry of ~47 accounts keyed by an ExclusionReason enum (saas_central / engine_yard / wine_cellar / untagged_central / tag_conflict) + ADDITIONAL_MAPPINGS override list.
- services/saas_budgeting_aws_spend_service.py — filters excluded accounts out of the Mapped + Unmapped bands; applies the override; adds get_excluded_accounts_table for the new band.
- routers/saas_budgeting_router.py — new GET /api/aws-spend/saas-budgeting/aws-net-amortized-excluded endpoint.
Frontend (klair-client/)
- services/awsSpendApi.ts + hooks/useSaaSBudgetingAWSExcluded.ts — types, fetcher, multi-quarter merge hook.
- awsSpendCardTransform.ts / AWSSpendCard.tsx — section builder grouping rows by exclusion reason with sort order; new band with per-category subtotals + methodology info callout; CSV export integration.
> Bug fix during impl: initial cut used aws_account_number = ANY(%s) — redshift-connector does not expand Python lists for ANY(%s) and threw ArrayContentNotSupportedError. Switched to explicit IN (%s, %s, ...) placeholders matching the jotform_redshift_service.py pattern.
---
### 2. AWS Spend — DB Servers card (replaces DB Units)
Builds a full BU → Class → DB hierarchy with week-driven cost allocation on the DB Servers card, then retires the parallel DB Units table now that DB Servers covers the same surface. Shared helpers (databaseUnitsTransform, databaseUnitsCostAllocation, centralDbCostAllocation) remain — DB Servers depends on them.
- Metric cards row — Total + per-engine (Postgres / MySQL / MSSQL / Oracle) cards, each with a 4-segment bar (assigned storage | assigned compute | unassigned storage | unassigned compute) summing to total cost. Header shows assigned % and unassigned $ next to the Total figure.
- Full hierarchy + allocation — multi-quarter week picker, Apply gating, shared allocateLeavesForServer helper used by both trees; cost columns sit immediately after instance count; attach-to-budget path + scroll target on the mappings card.
- Unassigned Cost column — slice of each server's totalCost that doesn't roll up to a complete BU+Class pair. Rendered as $X (Y%) with amber tint when non-zero so reviewers see allocation gaps without opening diagnostics.
- Diagnostics popover — click-triggered popover on the server name (AlertTriangle prefix + dotted underline signals problem servers). Replaces the under-table diagnostics block. Added an additive wrapName hook on ColumnDef to decorate the sticky-first-column name without overriding ExpandableRowRenderer.
- Instance-breakdown tooltip — moved into its own info column (the first column ignores column.render when accordionRows: true, so the original placement was dead code). Drops row expansion in favor of the info-icon tooltip on servers with instanceCount > 1.
- All-quarter cost windows — dropped the applied-quarter filter on the window selector; appliedQuarter is no longer user-controlled so the filter only ever exposed the latest quarter.
- Natural sort — server rows by friendlyName, instance children by dbName (so aurora-2 precedes aurora-10).
- Oracle / Non-Oracle attach split — Central DB allocation splits into Oracle vs Non-Oracle subtotals at the BU/Class level (Pricing F6 Oracle Subscription is 10× F7 Non-Oracle); wired through extractBuClassCentralDbCosts, simulatedBudgetMerge, useSimulatedBudget, SimulatedBudgetCard, the CSV export, and quarterProjection.
- Cleanup — removes the DB Units table and hides the Mappings Collapse All affordance.
---
### 3. SaaS Budgeting — Non-Central DBs (new ingest + tab)
End-to-end addition surfacing the $1,000/quarter non-central-DB subscription charge per linked account so Finance can audit who is being billed and why.
Ingest (aws-saas-budget-scripts/pipeline/non_central_dbs_ingest.py)
- Iterates every master payer, pulls net-amortized RDS spend per linked account from Cost Explorer, applies EY / Wine Cellar consolidation, joins to aws_spend_budget_account_mapping for class/BU.
- Writes one row per account per window to core_finance.saas_budgeting_non_central_db_charges.
- Supports --start-date/--end-date and --quarters YYYY-Qn,... for multi-quarter runs.
- Billable rule: any non-zero RDS spend AND NOT in skip-list. The Finance-managed core_finance.saas_budgeting_non_central_db_skip_list is read fresh on every run so Finance can opt accounts out without a code change.
- Bootstrap DDL seeds the skip-list with 75 rows derived from the spreadsheet's billable=No column.
Backend
- routers/non_central_db_charges_router.py + services/non_central_db_charges_service.py — read latest-window rows.
- New endpoint GET /api/aws-spend/saas-budgeting/non-central-db-charges.
Frontend
- New NonCentralDBTab in SaaSBudgetingSection with a BU → Class → Account hierarchy (BUs expanded by default; class rows reveal accounts on demand), billable-only filter (default on) hiding empty classes/BUs, and a $-charge summary.
- Adds a nonCentralDb slot to the simulated-budget rollup so the tab snapshots its billable BU/Class charges (always billable regardless of on-screen filter — the simulated budget represents what Finance actually bills).
---
### 4. Adjustments — two regressions fixed
- success field on write responses — frontend reads response.data.success on upsert/delete and throws Adjustment upsert failed / Adjustment delete failed when missing. Commit 3312958be removed success: bool = True believing it was unused; that assumption skipped the client contract, so every save/delete had been surfacing a misleading error toast since. Restored on both models; updated the delete happy-path test that codified the broken {} body.
- Editor seeding race — on initial mount the registry and adjustments fetch in parallel. If the registry resolved first while savedAdjustments was still [], the seeding effect latched hasSeededRef on the empty state and ignored the eventual adjustments payload — leaving the table empty until a quarter switch reset the latch.
---
## Test plan
- [x] Backend: pytest tests/services/test_saas_budgeting_aws_spend_service.py tests/routers/test_saas_budgeting_aws_spend_router.py tests/non_central_db_charges/ — all green.
- [x] Ingest: pytest aws-saas-budget-scripts/tests/test_non_central_dbs_ingest.py aws-saas-budget-scripts/tests/test_redshift_writer.py aws-saas-budget-scripts/tests/test_main.py — all green.
- [x] Frontend: pnpm test:run on AWSSpendCard, transform, NonCentralDBTab, DatabaseServersTable, engineMetrics, databaseServersHierarchyTransform, useSaaSBudgetingAWSExcluded, simulatedBudgetMerge, simulatedBudgetCsvExport, useAdjustmentRowsEditor — all green.
- [x] pnpm tsc --noEmit, pnpm lint:pr, pnpm prettier --check on changed FE files — clean.
- [x] uv run ruff format/check + uv run pyright on changed BE files — clean.
- [ ] Manual QA: AWS Spend → SaaS Budgeting tab — verify Excluded band renders with subtotals + methodology callout; DB Servers metric cards show 4-segment bar with assigned/unassigned split; DB Servers hierarchy applies week selection and attaches to the simulated budget; diagnostics popover opens from the server name; Non-Central DB tab renders BU/Class/Account hierarchy with billable-only filter; adjustments editor seeds on first load and save/delete no longer surface error toasts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)