feat(finance): public-cloud actuals reporting prototype - #7883
Conversation
Ingest provider actuals for forecast-vs-actual views, store Azure subscription IDs like LZA accounts, and persist month-end BoC FX rates.
Airflow DAGs under helm/tools/dags are Python and should not trigger full-chart kube-linter; include finance ingest DAGs in the prototype.
There was a problem hiding this comment.
Pull request overview
Introduces a prototype “Public Cloud Finance” reporting surface that ingests provider actuals, persists month-end FX rates, and exposes snapshot/rankings/coverage/anomalies/unmatched/export views under /public-cloud/finance, plus per-product Actual/Variance and FOIPPA variance notes.
Changes:
- Adds finance ingest pipeline (simulated + opt-in live adapters), storage models (ActualSpend, rollups, flags, FX, notes), and anomaly evaluation.
- Adds finance UI pages + export helpers, and wires product forecast UI to optionally display actuals/variance + variance notes.
- Extends product metadata for Azure subscription identifiers and billing join links; updates provision callback and local seeding.
Reviewed changes
Copilot reviewed 65 out of 69 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| sandbox/nats-provision/main.ts | Adds mock Azure subscription payload support in provision sandbox. |
| pnpm-lock.yaml | Locks new AWS SDK dependencies and transitive updates. |
| docs/business-logic/public-cloud/finance-reporting.md | Documents join keys, FX behavior, live ingest, and FOIPPA notes. |
| app/validation-schemas/cloud-cost.ts | Adds finance query/body schemas (provider filters, export, anomalies, ingest, notes). |
| app/types/next-auth.d.ts | Adds publicCloudFinance preview flag typing. |
| app/services/public-cloud-finance/monthly-fx-rate.ts | Persists month-end USD/CAD FX rates with fallback behavior. |
| app/services/public-cloud-finance/ingest/types.ts | Defines normalized billing line types and ingest scope. |
| app/services/public-cloud-finance/ingest/simulated-source.ts | Implements simulated billing source for local/dev demos. |
| app/services/public-cloud-finance/ingest/run-ingest.ts | Implements ingest run: match/unmatched, supersede, rollups, flag evaluation. |
| app/services/public-cloud-finance/ingest/real-sources.ts | Implements opt-in live AWS Cost Explorer + Azure Cost Management adapters and JSON export fallback. |
| app/services/public-cloud-finance/ingest/real-sources.live.test.ts | Adds local-only live integration tests for real billing adapters. |
| app/services/public-cloud-finance/ingest/evaluate-flags.ts | Creates anomaly flags (MoM increase, over-forecast, new service line). |
| app/services/public-cloud-finance/finance-measure-utils.test.ts | Adds unit tests for variance/formatting and billing link normalization. |
| app/services/public-cloud-finance/constants.ts | Adds finance defaults, thresholds, preview gating helpers. |
| app/services/public-cloud-finance/billing-account-links.ts | Adds preferred join logic via billingAccountLinks with provider fallbacks. |
| app/services/public-cloud-finance/active-spend.ts | Adds Prisma filter to identify “active” (not superseded) spend lines in MongoDB. |
| app/services/exchange-rates/index.ts | Adds shared exchange-rate exports and basic currency conversion helper. |
| app/services/db/public-cloud-finance.ts | Adds DB access layer for snapshot, rankings, coverage chase list, anomalies, unmatched, notes. |
| app/services/bank-of-canada/usd-cad-rate.ts | Adds Bank of Canada Valet client with caching + month-end parsing. |
| app/services/bank-of-canada/usd-cad-rate.test.ts | Adds unit tests for BoC parsing and fetch URL construction/caching. |
| app/services/backend/public-cloud/finance.ts | Adds frontend API client for finance endpoints + export download. |
| app/services/azure/subscriptions.ts | Adds Azure subscription normalization/merge + demo subscription inventing. |
| app/services/azure/subscriptions.test.ts | Adds tests for Azure subscription normalization and merge behavior. |
| app/seed/seed-finance-local.ts | Adds seed script to load simulated actuals through ingest pipeline. |
| app/seed/seed-demo-products.ts | Seeds demo billingAccountLinks + Azure subscriptions into demo products. |
| app/seed/seed-all-local.ts | Integrates finance seeding into all-local seed flow. |
| app/seed/finance-ingest-live.ts | Adds opt-in local CLI to ingest live billing for allowlisted plates. |
| app/prisma/schema.prisma | Adds finance models/enums and product metadata fields (azureSubscriptions, billingAccountLinks). |
| app/package.json | Adds scripts for finance seeds/live ingest + adds AWS SDK deps. |
| app/jest.setup.ts | Cleans up finance collections in Jest test teardown. |
| app/jest.config.mjs | Excludes *.live.test.* from default Jest runs (CI-safe). |
| app/helpers/pdfs/emou/types.ts | Excludes new product metadata fields from EMOU product type. |
| app/helpers/mock-resources/public-cloud-request.ts | Adds new metadata fields to sample public cloud request mock. |
| app/helpers/mock-resources/public-cloud-product.ts | Adds new metadata fields to sample public cloud product mock. |
| app/helpers/finance-export.ts | Adds XLSX/CSV export builders and content-disposition helper. |
| app/core/auth-options.ts | Adds publicCloudFinance preview flag to session generation. |
| app/constants/public-cloud.ts | Adds Azure subscription naming + portal URL helpers. |
| app/components/public-cloud/sections/PublicCloudForecastSection.tsx | Conditionally fetches/threads finance actuals into forecast panel under finance preview. |
| app/components/public-cloud/forecast/ProjectBudgetForecastPanel.tsx | Adds “Actual” + “Variance” rows and variance notes UI under finance preview. |
| app/components/public-cloud/finance/ProductVarianceNotes.tsx | Adds FOIPPA-gated variance note entry and list UI. |
| app/components/public-cloud/finance/FinanceNav.tsx | Adds navigation for finance prototype pages. |
| app/components/public-cloud/finance/finance-measure-utils.ts | Adds shared finance measure helpers (variance, coverage, FY helpers, formatting). |
| app/components/layouts/UserMenu.tsx | Adds “Public Cloud Finance” nav entry when finance preview enabled. |
| app/components/form/AccountEnvironmentsPublic.tsx | Adds Azure portal links when azureSubscriptions are present. |
| app/app/public-cloud/products/(product)/[licencePlate]/edit/page.tsx | Passes normalized Azure subscriptions into edit form. |
| app/app/public-cloud/finance/page.tsx | Adds finance snapshot page UI. |
| app/app/public-cloud/finance/rankings/page.tsx | Adds rankings page UI. |
| app/app/public-cloud/finance/coverage/page.tsx | Adds coverage chase list UI. |
| app/app/public-cloud/finance/anomalies/page.tsx | Adds anomaly review queue UI. |
| app/app/public-cloud/finance/unmatched/page.tsx | Adds unmatched billing UI + resolve action. |
| app/app/public-cloud/finance/export/page.tsx | Adds export UI for XLSX/CSV datasets. |
| app/app/api/v1/public-cloud/products/[idOrLicencePlate]/provision/route.ts | Persists Azure subscription IDs on provision callback (merge behavior). |
| app/app/api/public-cloud/products/[licencePlate]/finance/route.ts | Adds product finance detail endpoint (forecast/actuals/variance notes). |
| app/app/api/public-cloud/products/[licencePlate]/finance/variance-notes/route.ts | Adds endpoint to create variance notes. |
| app/app/api/public-cloud/products/_operations/delete.ts | Prevents copying new product-only metadata into delete request data. |
| app/app/api/public-cloud/finance/snapshot/route.ts | Adds finance snapshot API endpoint. |
| app/app/api/public-cloud/finance/rankings/route.ts | Adds rankings API endpoint. |
| app/app/api/public-cloud/finance/coverage/route.ts | Adds coverage API endpoint. |
| app/app/api/public-cloud/finance/anomalies/route.ts | Adds anomalies queue API endpoint. |
| app/app/api/public-cloud/finance/anomalies/[id]/review/route.ts | Adds anomaly review API endpoint. |
| app/app/api/public-cloud/finance/unmatched/route.ts | Adds unmatched billing API endpoint. |
| app/app/api/public-cloud/finance/unmatched/[id]/resolve/route.ts | Adds resolve-unmatched API endpoint. |
| app/app/api/public-cloud/finance/ingest/route.ts | Adds internal ingest trigger API endpoint. |
| app/app/api/public-cloud/finance/export/route.ts | Adds finance export API endpoint (XLSX/CSV). |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (1)
app/services/public-cloud-finance/monthly-fx-rate.ts:82
- Same concurrency issue in the FINANCE_USD_CAD_RATE fallback path: concurrent ingests for the same month can hit unique constraint errors on
monthlyFxRate.create. Preferupsertto keep the operation idempotent.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Reduce cognitive complexity with helpers, replace utcnow and role=status, and clean up nested ternaries plus minor lint findings.
Enforce product forecast permissions on product finance APIs, make FX rate writes race-safe, omit FX fields for CAD, and speed up rollups/chart.
Split product/link/service line builders so Sonar S3776 stays under the limit.
Capture Test/Prod service principal and Vault wiring for Cost Explorer and Cost Management, separate from Airflow ingest auth.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 65 out of 69 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (1)
helm/tools/dags/_finance_ingest.py:9
- The Airflow helper only triggers ingest for AWS_LZA and AZURE, but the registry ingest API and validation schema also support provider "AWS". Unless classic AWS is intentionally out of scope, this will leave AWS products perpetually un-ingested in test/prod runs (and provider=AWS filters will show no data).
Use AWS default credential chain and Azure DefaultAzureCredential for Test/Prod, with SSO profile / az login still supported for local live tests.
Skip rounding in convertCurrencyAmount for same-currency and zero paths so CAD→CAD / USD→USD no longer drop cents.
Use client_credentials for Airflow ingest like the provisioner. Include INACTIVE products in finance and forecast rollups so totals stay complete.
Extract helpers so Sonar cognitive complexity stays within the limit.
Wire spend rollups into the platform forecast page and export when finance preview is on, and keep aggregators client-safe so Prisma stays off the browser.
Compare FYTD actual to forecast for the same elapsed months, add an FYTD forecast card, and keep full-year forecast as planning context only.
|



Summary
/public-cloud/finance(snapshot, rankings, coverage, anomalies, unmatched, export), product-level Actual/Variance + FOIPPA variance notes, and Actual/Variance rows on the platform/public-cloud/forecastrollup when finance preview is onazureSubscriptions+ provision merge/UI portal links); finance joins preferbillingAccountLinks, then native LZA/Azure fields (classic AWS has no native account field and is out of scope for scheduled ingest)MonthlyFxRate) for historical invoice conversion; keep archived (INACTIVE) products in finance/forecast historical rollups and ingest matching; coverage chase list stays ACTIVE-onlyclient_credentials(*_FINANCE_SA_ID/*_FINANCE_SA_SECRET), same pattern as the provisionerFINANCE_AWS_PROFILE/az login) andpnpm test:finance-live(*.live.test.tsexcluded from CI)helm/tools/dagsdo not trigger full-chart lintTest plan
pnpm --dir app prisma-pushthenpnpm --dir app seed-all-local -- --reset/public-cloud/financewithpublicCloudFinancepreview on; check snapshot / rankings / unmatched / anomalies / export/public-cloud/forecastand confirm Actual/Variance totals (and per-product rows when expanded)azureSubscriptionsare present; provision callback merges subscription IDspnpm --dir app test:finance-livewith AWS SSO /az loginandFINANCE_LIVE_*envExplicit non-goals