# FP&A Budgeting Engine

A REST API for corporate budget management, actuals ingestion, and variance reporting. Built to replace the manual Excel workflows FP&A teams run every month.

---

## The Problem

Every month, finance teams export actuals from their ERP, paste them into spreadsheets, and manually calculate budget vs. actual variances by department and GL account. It is slow, error-prone, and breaks the moment someone edits the wrong cell.

This engine moves that logic into a versioned, auditable API. Budget data lives in a structured schema. Actuals are ingested on demand. Variance reports — with favourability, percentage, and absolute figures — are available instantly for any department, period, or budget version.

---

## Features

- **Budget CRUD** — create and update budgets by department, GL account, fiscal period, and version (original, forecast 1–3)
- **Actuals ingestion** — upsert actuals from any source via JSON; designed to accept ERP export feeds
- **Variance analysis** — budget vs. actual with correct favourability logic (revenue accounts favour positive variance; cost accounts favour negative)
- **Alert endpoint** — returns all GL lines where absolute variance exceeds a configurable threshold
- **Zero infrastructure** — SQLite database, single binary, runs anywhere with no external dependencies

---

## Stack

| Layer | Choice | Why |
|---|---|---|
| Language | Go | Fast, statically typed, single binary deploy |
| Routing | `net/http` (stdlib) | No dependencies; method+path routing since Go 1.22 |
| Database | SQLite via `modernc.org/sqlite` | Zero infrastructure, file-based, ships with the binary |
| Schema | Auto-migrated on startup | No migration tool needed; `CREATE TABLE IF NOT EXISTS` is idempotent |

---

## Project Structure

```
fpa-budgeting-engine/
├── cmd/server/main.go        # Entry point, wiring, graceful shutdown
├── internal/
│   ├── handler/              # HTTP layer — decode request, call service, encode response
│   │   ├── budget.go
│   │   ├── actuals.go
│   │   └── variance.go
│   ├── model/                # Domain types
│   │   ├── budget.go
│   │   ├── actuals.go
│   │   └── variance.go
│   ├── repository/           # Database queries
│   │   ├── budget_repo.go
│   │   └── actuals_repo.go
│   └── service/              # Business logic
│       ├── budget_service.go
│       └── variance_service.go
├── migrations/               # Reference SQL (schema is also auto-applied on start)
├── scripts/seed_demo.sql     # Realistic demo data for Engineering, Sales, Marketing
├── docs/openapi.yaml         # API specification
└── .env.example
```

---

## Getting Started

```bash
git clone https://gitea.yoursite.com/yourname/fpa-budgeting-engine
cd fpa-budgeting-engine

cp .env.example .env

go mod tidy
go run ./cmd/server
# → API on http://localhost:8080
# → fpa.db created automatically on first run
```

To load demo data (Engineering, Sales, Marketing for FY2024 P09):

```bash
# Demo data loads via the ingest endpoint — see scripts/seed_demo.sh
# or POST directly to /api/v1/actuals/ingest
```

---

## API

### Budgets

```
POST   /api/v1/budgets            Create a budget line
PUT    /api/v1/budgets/{id}       Update amount or notes
DELETE /api/v1/budgets/{id}       Remove a budget line
```

### Actuals

```
POST   /api/v1/actuals/ingest     Upsert an actual (idempotent by period + dept + GL)
```

### Variance

```
GET    /api/v1/variance           Full variance report
GET    /api/v1/variance/alerts    Lines exceeding threshold (default 10%)
```

Query parameters for variance endpoints:

| Param | Example | Description |
|---|---|---|
| `year` | `2024` | Fiscal year |
| `period` | `9` | Fiscal period (1–12) |
| `dept` | `ENG` | Department code (omit for all) |
| `version` | `original` | Budget version |
| `threshold` | `15` | Alert threshold % (alerts endpoint only) |

### Health

```
GET    /api/v1/health             Returns 200 if DB is reachable
```

---

## Example: Variance Report

```
GET /api/v1/variance?year=2024&period=9&dept=ENG
```

```json
{
  "department": "ENG",
  "fiscal_year": 2024,
  "fiscal_period": 9,
  "version": "original",
  "currency": "DKK",
  "total_budget": 6740000,
  "total_actual": 7074600,
  "total_variance": -334600,
  "total_variance_pct": -4.97,
  "lines": [
    {
      "gl_code": "6100",
      "gl_description": "Salaries & Wages",
      "gl_type": "headcount",
      "budget": 4200000,
      "actual": 4380000,
      "variance_abs": -180000,
      "variance_pct": -4.29,
      "status": "unfavourable",
      "currency": "DKK"
    },
    {
      "gl_code": "6500",
      "gl_description": "Consulting & Contractors",
      "gl_type": "opex",
      "budget": 450000,
      "actual": 680000,
      "variance_abs": -230000,
      "variance_pct": -51.11,
      "status": "unfavourable",
      "currency": "DKK"
    },
    {
      "gl_code": "5000",
      "gl_description": "Cloud Infrastructure",
      "gl_type": "cogs",
      "budget": 850000,
      "actual": 791000,
      "variance_abs": 59000,
      "variance_pct": 6.94,
      "status": "favourable",
      "currency": "DKK"
    }
  ]
}
```

---

## Finance Concepts

**Favourability convention** — whether a variance is good or bad depends on the account type. Revenue accounts are favourable when actuals exceed budget. Cost accounts are favourable when actuals come in under budget. The engine handles both correctly using a `favour_high` flag on each GL account.

**Budget versioning** — original budget and up to three forecast revisions are stored separately, enabling budget vs. forecast vs. actual three-way comparison without overwriting history.

**GL account types** — accounts are typed as `revenue`, `cogs`, `opex`, `capex`, or `headcount`. This drives both the P&L rollup structure and the favourability logic.

**Fiscal periods** — stored as integers 1–12, decoupled from calendar months so the schema supports fiscal years that start in any month.

---

## Configuration

```bash
# .env.example
DB_PATH=fpa.db     # Path to SQLite file. Use :memory: for tests.
PORT=8080
```

---

## Why Go for a Finance API

Most finance tooling is Python notebooks or Excel macros — useful for analysis, fragile in production. Go compiles to a single binary with no runtime dependencies, handles concurrency safely, and makes every error path explicit. The result is an API that behaves predictably under load and is straightforward to deploy on any server or container without a setup checklist.