# Portfolio Engine

A lightweight portfolio and financial data backend written in Go. Tracks companies, currencies, and revenue reports across configurable time periods — replacing spreadsheets with a proper database, REST API, and interactive shell.

## Features

- SQLite database with foreign key enforcement
- REST API on port `8080`
- Interactive shell for managing data without an HTTP client
- Revenue tracking by period (quarterly, half-year, full year)
- Revenue broken down by custom categories (product, location, segment...)
- Health endpoint with DB latency, memory, and uptime stats

## Project Structure

```
.
├── main.go                      # Entry point — HTTP routes + shell loop
├── app.db                       # SQLite database (auto-created on first run)
├── build.sh                     # Build script
├── go.mod / go.sum
└── internal/
    ├── database/
    │   └── main.go              # Schema init (InitDB) — all CREATE TABLE statements
    ├── handlers/
    │   ├── main.go              # HealthHandler
    │   ├── currency.go          # AddCurrencyHandler, GetCurrenciesHandler
    │   └── revenue.go           # AddRevenueEntryHandler, GetRevenueReportHandler, GetRevenueSumHandler
    ├── model/
    │   ├── company.go           # Company struct + SQL (InsertCompany, GetAllCompanies)
    │   ├── currency.go          # Currency struct + SQL (InsertCurrency, GetAllCurrencies)
    │   ├── periode.go           # Period struct + helpers (QuarterPeriod, HalfYearPeriod, FullYearPeriod)
    │   └── revenue.go           # Revenue, RevenueReport, RevSum structs + SQL
    ├── service/
    │   ├── main.go              # Service wiring
    │   ├── company.go           # Company business logic
    │   ├── currency.go          # Currency business logic
    │   └── revenue.go           # Revenue aggregation logic
    └── shell/
        ├── company.go           # add-company, list-companies
        ├── currency.go          # add-currency, list-currency
        └── revenue.go           # add-revenue, list-revenue, sum-revenue
```

## Getting Started

### Prerequisites

- Go 1.21+
- GCC (required for SQLite CGO bindings)
  - Ubuntu/Debian: `sudo apt install gcc`
  - macOS: comes with Xcode Command Line Tools

### Install & Run

```bash
git clone git@git.samantha42.xyz:samantha/Portifolio-Engine.git
cd Portifolio-Engine
go mod download
go run main.go
```

Or build first:

```bash
chmod +x build.sh
./build.sh
./Portifolio
```

On startup:

```
Connected to SQLite database
Tables ready
Server running on :8080

Shell ready. Type 'help' for commands.
>
```

The HTTP server and shell run concurrently — the API is live while you type shell commands.

---

## REST API

### `GET /health`

Returns server status, DB connection info, memory usage, and uptime.

```bash
curl http://localhost:8080/health
```

```json
{
  "status": "ok",
  "uptime": "4m32s",
  "database": {
    "status": "ok",
    "latency": "121µs",
    "open_connections": 1,
    "in_use": 0,
    "idle": 1
  },
  "memory": {
    "alloc_mb": 2.31,
    "sys_mb": 9.44,
    "num_gc": 3
  },
  "go_version": "go1.22.0",
  "goroutines": 4
}
```

Returns `503` with `"status": "degraded"` if the database is unreachable.

---

### `POST /add/company`

```bash
curl -X POST http://localhost:8080/add/company \
  -H "Content-Type: application/json" \
  -d '{"name":"Novo Nordisk","shares_outstanding":4442064180,"price":251.00,"currency_id":1}'
```

---

### `GET /companies`

```bash
curl http://localhost:8080/companies
```

---

### `POST /add/currency`

```bash
curl -X POST http://localhost:8080/add/currency \
  -H "Content-Type: application/json" \
  -d '{"code":"DKK","name":"Danish Krone"}'
```

---

### `GET /currencies`

```bash
curl http://localhost:8080/currencies
```

---

### `POST /add/revenue/entry`

Add a single revenue line to a report. The period is created automatically if it doesn't exist.

```bash
curl -X POST http://localhost:8080/add/revenue/entry \
  -H "Content-Type: application/json" \
  -d '{
    "company_id":  1,
    "currency_id": 1,
    "period_type": "Q",
    "year":        2025,
    "index":       1,
    "category":    "product",
    "label":       "iPhone",
    "value":       69143
  }'
```

`period_type` is one of:

| Value | Meaning   | `index` range |
|-------|-----------|---------------|
| `Q`   | Quarter   | 1–4           |
| `H`   | Half-year | 1–2           |
| `Y`   | Full year | 1             |

---

### `GET /revenue/report`

Get all revenue entries for a company and period.

```bash
curl "http://localhost:8080/revenue/report?company_id=1&period_type=Q&year=2025&index=1"
```

---

### `GET /revenue/sum`

Sum all revenue entries for a company across all periods of a given type in a year.

```bash
curl "http://localhost:8080/revenue/sum?company_id=1&period_type=Q&year=2025"
```

---

## Shell Commands

| Command          | Description                                   |
|------------------|-----------------------------------------------|
| `add-company`    | Add a new company interactively               |
| `list-companies` | List all companies with price and share count |
| `add-currency`   | Add a new currency interactively              |
| `list-currency`  | List all currencies with their IDs            |
| `add-revenue`    | Add a revenue entry interactively             |
| `list-revenue`   | List revenue entries for a company/period     |
| `sum-revenue`    | Sum revenue across all periods in a year      |
| `help`           | Show all available commands                   |
| `exit`           | Quit                                          |

### Example session

```
> add-currency
  Code (e.g. DKK): DKK
  Name (e.g. Danish Krone): Danish Krone
  ✓ Currency 'Danish Krone' (DKK) added with ID 1

> add-company
  Name: Novo Nordisk
  Shares outstanding: 4442064180
  Price: 251.00
  Currency ID: 1
  ✓ Company 'Novo Nordisk' added.

> add-revenue
  Company ID: 1
  Currency ID: 1
  Period type (Q/H/Y): Q
  Year: 2025
  Index (Q: 1-4 | H: 1-2 | Y: 1): 1
  Category (product/location/total): product
  Label (e.g. iPhone, Americas): Diabetes Care
  Value: 54200
  ✓ Revenue entry added: product / Diabetes Care = 54200.00 (Q1 2025)

> sum-revenue
  Company ID: 1
  Period type to sum (Q/H/Y): Q
  Year: 2025

  Revenue Sum — FY2025
  Total: 54200.00

  By Category:
    product         54200.00

  By Label:
    Diabetes Care   54200.00

> list-companies
  ID    NAME                 CURRENCY   PRICE           SHARES
  ------------------------------------------------------------
  1     Novo Nordisk         DKK        251.00          4442064180
```

---

## Database Schema

```sql
CREATE TABLE currencies (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    code TEXT    NOT NULL UNIQUE,
    name TEXT    NOT NULL
);

CREATE TABLE companies (
    id                 INTEGER PRIMARY KEY AUTOINCREMENT,
    name               TEXT    NOT NULL UNIQUE,
    shares_outstanding INTEGER NOT NULL,
    price              REAL    NOT NULL,
    currency_id        INTEGER NOT NULL,
    FOREIGN KEY (currency_id) REFERENCES currencies(id)
);

CREATE TABLE periods (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    type       TEXT    NOT NULL CHECK(type IN ('Q', 'H', 'Y')),
    year       INTEGER NOT NULL,
    idx        INTEGER NOT NULL,
    start_date TEXT    NOT NULL,
    end_date   TEXT    NOT NULL,
    UNIQUE(type, year, idx)
);

CREATE TABLE revenue_reports (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    company_id INTEGER NOT NULL,
    period_id  INTEGER NOT NULL,
    FOREIGN KEY (company_id) REFERENCES companies(id),
    FOREIGN KEY (period_id)  REFERENCES periods(id),
    UNIQUE(company_id, period_id)
);

CREATE TABLE revenue_entries (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    report_id   INTEGER NOT NULL,
    currency_id INTEGER NOT NULL,
    category    TEXT    NOT NULL,
    label       TEXT    NOT NULL,
    value       REAL    NOT NULL,
    FOREIGN KEY (report_id)   REFERENCES revenue_reports(id),
    FOREIGN KEY (currency_id) REFERENCES currencies(id)
);

CREATE TABLE category_defs (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    company_id INTEGER NOT NULL,
    name       TEXT    NOT NULL,
    FOREIGN KEY (company_id) REFERENCES companies(id),
    UNIQUE(company_id, name)
);

CREATE TABLE category_labels (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    category_def_id INTEGER NOT NULL,
    label           TEXT    NOT NULL,
    FOREIGN KEY (category_def_id) REFERENCES category_defs(id),
    UNIQUE(category_def_id, label)
);
```

---

## Roadmap

- `GET /company/{id}/revenue` — full revenue history for a company
- Price update endpoint
- Market cap calculation (price × shares outstanding)
- Multi-currency conversion
- Frontend dashboard