# Recurring Expenses — Frontend Integration Guide

This describes the **new** way recurring payments work. The model changed: a recurring
payment is now created **through the expense (transaction) flow**, and recurring payments /
their logs are **read-only views**. Read this fully before building the screens.

All endpoints are under `/api/v1`, require `Authorization: Bearer <token>`, and (when an
advisor/agency acts on behalf of a client) the `X-Tenant-Id: <clientId>` header. Every
response uses the standard envelope:

```json
{ "status": 200, "message": "...", "data": <payload>, "errors": [] }
```

---

## 1. Mental model (read this first)

- A **recurring payment** is a *schedule*: `start_date → end_date` repeating at a `frequency`.
  It is **not** an expense by itself and stores **no money** on its own.
- The schedule's **occurrences** (the individual due dates) are *projected* from
  `start_date + frequency` up to `end_date`. They are not stored until paid.
- When an occurrence is **marked paid**, the system creates **one expense transaction**
  for it. Unmarking it deletes that expense. So: **one paid occurrence ⇄ exactly one expense.**
- Every such expense carries `is_recurring_payment: true` and `recurring_payment_id`
  (the schedule it belongs to), so you can show/group/filter them.

> There is **no separate "first expense"**. Creating the schedule posts nothing. If the user
> has already paid some occurrences, send them in `paid_occurrences` at creation time (below).

---

## 2. Creating a recurring expense — `POST /api/v1/transactions`

It's the **same endpoint** as a normal expense. Set `is_recurring_payment: true` and add the
schedule fields. Optionally include `paid_occurrences` to immediately log already-paid periods.

### Request body

```jsonc
{
  "type": "expense",                 // REQUIRED — must be "expense" when recurring
  "is_recurring_payment": true,      // REQUIRED to enter recurring mode

  "label": "Apartment Rent",         // REQUIRED for recurring (schedule name)
  "category_id": 12,                 // REQUIRED for recurring
  "currency_id": 1,                  // REQUIRED
  "currency_rate_to_client_real_currency": 1, // optional, defaults to 1
  "amount": 5000,                    // REQUIRED — per-occurrence amount
  "account_id": 3,                   // optional — payment account; stored on the schedule
                                     // and copied onto every occurrence's expense

  "date": "2026-01-01",              // REQUIRED — this is the schedule START date
  "end_date": "2026-12-31",          // REQUIRED for recurring — schedule END date
  "frequency": "monthly",            // REQUIRED — daily|weekly|monthly|quarterly|yearly

  // OPTIONAL — occurrences the user has already paid. Each becomes one expense.
  "paid_occurrences": [
    { "due_date": "2026-01-01", "paid_at": "2026-01-03", "notes": "Jan rent" },
    { "due_date": "2026-02-01" },
    { "due_date": "2026-03-01" }
  ]
}
```

### Rules to enforce in the form

- `is_recurring_payment` is only valid when `type = expense`.
- When recurring, `label`, `category_id`, `frequency`, and `end_date` are **all required**.
- `end_date` must be **on or after** `date` (the start).
- **Each `paid_occurrences[].due_date` must be a real occurrence of the schedule** — i.e. it
  must equal `start_date + n × frequency` and fall within `start_date … end_date`. Sending a
  date that doesn't land on the cadence is rejected (`422`). No duplicate due dates.
- `paid_occurrences` is rejected entirely if `is_recurring_payment` is not true.

### Cadence rule (generate the dates client-side — must match the backend exactly)

Start at `date`, step forward until past `end_date`:

| frequency   | step       |
|-------------|------------|
| `daily`     | + 1 day    |
| `weekly`    | + 1 week   |
| `monthly`   | + 1 month  |
| `quarterly` | + 3 months |
| `yearly`    | + 1 year   |

Generate this set yourself to drive the "which periods are already paid?" UI (section 5), so
the dates you submit are guaranteed valid.

### Response

A recurring create returns the **schedule** (not a transaction). The message reports how many
occurrences were logged:

```json
{
  "status": 201,
  "message": "Recurring payment schedule created successfully (3 occurrence(s) logged as paid)",
  "data": {
    "id": 7, "label": "Apartment Rent", "amount": 5000,
    "start_date": "2026-01-01", "end_date": "2026-12-31", "frequency": "monthly",
    "currency_id": 1, "category_id": 12,
    "payment_account_id": 3,
    "payment_account": { "id": 3, "name": "ADCB Current" },
    "transactions_count": 3
  },
  "errors": []
}
```

> A **normal** (non-recurring) expense to the same endpoint returns the transaction as before.
> Branch on the response: recurring → schedule object; otherwise → transaction.

---

## 3. Viewing schedules (read-only)

- `GET /api/v1/recurring-payments` — paginated list of schedules. Supports `?export=csv|xlsx`.
- `GET /api/v1/recurring-payments/{id}` — one schedule.
- `GET /api/v1/recurring-payments/insights` — dashboard metrics (monthly run-rate, annual
  commitment, next charge, run-rate by cadence).

Each schedule (index items and show) eager-loads its relations, including the **payment
account** it is paid from:

```jsonc
{
  "id": 7, "label": "Apartment Rent", "amount": 5000,
  "start_date": "2026-01-01", "end_date": "2026-12-31", "frequency": "monthly",
  "currency_id": 1, "category_id": 12,
  "currency": { "id": 1, "code": "AED", "symbol": "د.إ" },
  "category": { "id": 12, "name": "Rent", "icon": "..." },
  "payment_account_id": 3,
  "payment_account": { "id": 3, "name": "ADCB Current" }   // null if none set
}
```
- `DELETE /api/v1/recurring-payments/{id}` — remove a schedule. **This also deletes every
  expense generated from its paid occurrences** (and the occurrence logs). Warn the user that
  the related expenses will disappear from transactions.

> There is **no** `POST`/`PUT` on recurring-payments. Create via transactions (section 2).
> "Editing" a schedule = delete + recreate.

Permission: these routes require the `feature.recurring.calendar` entitlement.

---

## 4. Viewing & paying occurrences (logs)

### List occurrences — `GET /api/v1/recurring-payments/{id}/logs`

Returns projected occurrences with paid status. Query params: `from`, `to` (defaults:
`from = start_date`, `to = today` — **pass a future `to` to see upcoming occurrences**),
`per_page`, `page`.

```jsonc
// data: paginated items
{
  "due_date": "2026-03-01",
  "is_paid": true,
  "paid_at": "2026-03-02T00:00:00Z",
  "notes": "Mar rent",
  "receipt_url": "https://.../storage/...",
  "log_id": 42
}
```

### Mark / unmark an occurrence — `PUT /api/v1/recurring-payments/{id}/logs/{due_date}`

`{due_date}` is `YYYY-MM-DD`. Multipart (for the optional receipt) or JSON.

```jsonc
{
  "is_paid": true,            // REQUIRED
  "paid_at": "2026-03-02",    // optional, defaults to now() when paid
  "notes": "Mar rent",        // optional
  "receipt": <file>           // optional (max 10 MB)
}
```

- `is_paid: true` → creates/updates the log **and posts the expense transaction**.
- `is_paid: false` → marks unpaid and **deletes the mirrored expense**.

Use this endpoint for ongoing months after setup, and for attaching receipts (receipts are
**not** accepted in the bulk `paid_occurrences` create — attach them here per occurrence).

---

## 5. Linking expenses back to a schedule

Every recurring-origin expense has `is_recurring_payment: true` and `recurring_payment_id`,
and the transactions list eager-loads a compact `recurring_payment` object
(`id, label, frequency, start_date, end_date`).

- **All expenses for a schedule:** `GET /api/v1/transactions?recurring_payment_id=7`
- In the transactions list, badge rows where `is_recurring_payment === true` and link to the
  schedule via `recurring_payment.id`.

---

## 6. UX requirements (please make this genuinely easy)

The whole point of this redesign is a smooth "make it recurring + log what's paid" experience.
The design and UX must be very well thought out:

### Making an expense recurring
- On the **Add Expense** form, a clear **"Repeat this expense"** toggle. When on, reveal the
  schedule fields inline (no separate screen): **Frequency**, **Start date** (prefill with the
  expense `date`), **End date**.
- Show a plain-language summary as they type, e.g. _"AED 5,000 every month from Jan 1, 2026 to
  Dec 31, 2026 — 12 payments."_ Compute the count from the cadence rule (section 2).
- Validate inline using the same rules the API enforces (required fields, end ≥ start).

### Logging payments based on the frequency (the key flow)
- Once frequency + start + end are set, **generate the occurrence dates** (cadence rule) and
  render them as a **checklist / calendar of periods** right in the form:
  _"Which of these have you already paid?"_ — each row a date + amount + a "Paid" checkbox
  (optionally a paid-on date and note).
- The checked rows become `paid_occurrences` in the create request. Because you generated the
  dates from the same rule, they're guaranteed to pass validation.
- Default nothing checked (a forward-looking schedule). Make "select all past-due" a one-tap
  helper for someone backfilling history.
- After creation, the schedule detail view shows the same occurrence list (from the logs
  endpoint) with paid/unpaid state, where each unpaid row has a one-tap **"Mark paid"**
  (the `PUT .../logs/{due_date}` call) — and that instantly produces the expense.

### Consistency
- Make paid/unpaid state and the resulting expense feel like the **same object** across the
  recurring view and the transactions list (same label, amount, category, badge). The user
  should never wonder "did my payment get recorded?" — marking paid must visibly add the
  expense, unmarking must visibly remove it.

---

## 7. Quick reference

| Action | Method & path |
|---|---|
| Create recurring schedule (+ optional paid occurrences) | `POST /transactions` (`is_recurring_payment: true`) |
| Create normal expense/income | `POST /transactions` |
| List schedules | `GET /recurring-payments` |
| Schedule detail | `GET /recurring-payments/{id}` |
| Schedule insights | `GET /recurring-payments/insights` |
| Delete schedule | `DELETE /recurring-payments/{id}` |
| List occurrences (paid/unpaid) | `GET /recurring-payments/{id}/logs?from=&to=` |
| Mark / unmark occurrence paid | `PUT /recurring-payments/{id}/logs/{YYYY-MM-DD}` |
| All expenses for a schedule | `GET /transactions?recurring_payment_id={id}` |
