InfraForge Docs

InfraNotes Project Finance · v0

Welcome

Select a document from the sidebar to read it.

Release 3 Dev 2 Handoff: Project Controls

Routes

  • GET /api/project-finance/{id}/command-center
  • GET /api/project-finance/portfolio/overview

No new route or query parameter was added. Both existing read models now include control_summary.
Responses use the existing Project Finance JSON DTO shape; control_summary is embedded in the command-center and portfolio read-model payloads.

Command Center control_summary

Fields:

  • total_budget, burn_amount, committed_amount, total_revenue, forecast_variance_amount
  • burn_percent, margin_percent, commitment_percent, forecast_variance_percent
  • billing_risk: unknown, low, medium, or high
  • unbilled_milestones, overdue_unbilled_milestones
  • currency, last_updated

Billing risk rules:

  • unknown: project has no milestones.
  • high: at least one non-billed/non-cancelled milestone is past due.
  • medium: project has unbilled milestones but none overdue.
  • low: every active milestone is billed.

Portfolio control_summary

Fields:

  • total_budget, burn_amount, committed_amount, total_revenue, forecast_variance_amount
  • burn_percent, margin_percent, commitment_percent, forecast_variance_percent
  • project_count, currency, last_updated, mixed_currency_warning

Mixed-currency projects are skipped from single-currency portfolio totals and set mixed_currency_warning=true.

Empty And Degraded States

  • Missing money fields are treated as zero in the project currency.
  • Empty portfolio returns zero-valued USD control totals.
  • control_summary is expected to be present on service-built responses; frontend should still tolerate null for older backend versions.
  • Freshness is exposed through control_summary.last_updated; there is no separate freshness collection for PF control facts in this slice.

Manual-Test Seed

Use the opt-in seed command after selecting the tenant and user that should own manual-test data:

DATABASE_URL="$DATABASE_URL" \
RELEASE3_SEED_TENANT_ID="<tenant-uuid>" \
RELEASE3_SEED_USER_ID="<user-uuid>" \
make seed-release3-control-facts

Optional overrides:

  • RELEASE3_SEED_PROJECT_ID: use a specific UUID instead of the deterministic per-tenant fixture ID.
  • RELEASE3_SEED_PROJECT_NAME: defaults to Release 3 Project Controls Fixture.
  • RELEASE3_SEED_TIMEOUT: defaults to 15s.

The command is idempotent and tenant-scoped. It sets transaction-local RLS context, upserts one project, two milestones, and one active budget commitment, then prints the seeded project, milestone, and commitment IDs. Expected manual-test state:

  • Portfolio overview contains at least one project in control_summary.project_count.
  • Command center for the printed project ID shows non-zero budget, burn, commitment, revenue, and forecast-variance fields.
  • Command center control_summary.billing_risk is high, with two unbilled milestones and one overdue unbilled milestone.

Playwright hint: select the printed project ID from /business/project-finance, then assert /business/project-finance/{id} renders the control cards with non-zero money values and a high billing-risk state.

Verified Follow-Ups

  • PF approval self-approval is already fixed and covered by model, service, repository, and handler tests. No code change was made for that item.
  • No production/staging seed migration was added because fixture ownership must be explicit per tenant. Use make seed-release3-control-facts for staging/manual tenants.