---
feature: Reports and statistics
slug: reports
surface: admin
consumer: react
version: 1.0.0
status: ready
brd_version: "1.3"
brd_ids: [ADMIN-022, ADMIN-002]
business_rules: [BR-21, BR-24, BR-25, BR-29]
decisions: [DEC-20, TD-160, TD-161]
schema_tables: [customers, providers, drivers, orders, order_items, trips, payments, refunds, wallets, wallet_transactions, earnings, payouts, provider_subscriptions, packages, services]
related_docs: [dashboard.md, commissions.md, payouts.md, ratings.md]
last_updated: 2026-11-02
---

# Reports and statistics — React Dashboard Integration Guide

> **Base URL:** `{HOST}/api/v1/admin` · Shared rules (headers, envelope, errors,
> pagination, axios setup, TS types): [API_CONVENTIONS.md](../API_CONVENTIONS.md)

## Version history

Newest first. Never edit an old row.

| Version | Date | Type | Summary | Dashboard action required |
|---------|------|------|---------|---------------------------|
| 1.0.0 | 2026-11-02 | Initial | Summary figures over a date window and the rankings. | Build the Reports page. |

## Changelog

### 1.0.0 — 2026-11-02

- **Added:** `GET /reports/summary`, `GET /reports/rankings`.
- **Breaking:** No (new). **BRD / decisions:** ADMIN-022, DEC-20 (KPI list = the BRD summaries, owner decision TD-161).

## 1. Business summary

The reports give the admin the platform's figures over a chosen window, provider and driver money kept apart. The figure list is the BRD's ADMIN-022 summaries (owner decision on DEC-20, TD-161). The main [dashboard](dashboard.md) shows the same figures over all time, from the same code, so the two always reconcile.

**Flows** (orders, trips, payments, refunds, commissions, transport fees, wallet movements, new accounts, transfers, featured revenue) are counted inside the window. **Standing** figures (live orders and trips, amounts still due, wallet balances, active and expired subscriptions, approved accounts, totals of accounts) are as of now, whatever the window.

## 2. Pages and permissions

| Page / component | Purpose | Endpoint(s) | RBAC permission (route name) | Section · action |
|------------------|---------|-------------|------------------------------|------------------|
| ReportsSummary | All figures for a window | `GET /reports/summary` | `admin.reports.summary` | reports · view |
| ReportsRankings | Top services, packages, providers, drivers | `GET /reports/rankings` | `admin.reports.rankings` | reports · view |

## 3. List / table

### 3.1 `GET /reports/summary`

Query: `from`, `to` (`YYYY-MM-DD`, both optional and inclusive; `to` not before `from` — Postman: "422 Report Window Reversed"). No window = all time (identical to `GET /dashboard`).

| Block | Keys | Window |
|-------|------|--------|
| `accounts.customers` | `total`, `new` | `new` on `created_at`; `total` now |
| `accounts.providers`, `accounts.drivers` | `total`, `new`, `approved` | as above; `approved` now |
| `orders` | `total`, `completed`, `cancelled`, `rejected`, `expired`, `instant`, `scheduled`, `by_kind {service, package_purchase, package_session}` | placed in the window (`created_at`) |
| `orders.delayed` | count | delay alert sent in the window (BR-21) |
| `orders.current` | count of `pending_provider` … `in_progress` | now |
| `trips` | `total`, `completed`, `no_driver`, `cancelled` | requested in the window |
| `trips.current` | `searching` + driver-live statuses | now |
| `payments` | `count`, `total_amount`, `orders_amount`, `featured_amount`, `gateway_fees` | captured (`paid`, `refunded`, `partially_refunded`) on `paid_at` |
| `refunds` | `count`, `amount` | completed, on `processed_at` |
| `wallet` | `balances_total` (now), `transactions_count`, `credits`, `debits` | movements on `created_at` |
| `commissions` | `provider`, `driver`, `total` | earnings written in the window, not reversed (BR-24) |
| `transportation_fees` | `driver`, `provider_self_travel`, `total` | as above (BR-25) |
| `amounts_due.provider`, `amounts_due.driver` | `due` (pending + available + in payout, now), `transferred` (payouts `paid` in the window) | mixed, as stated |
| `featured` | `active`, `expired` (now), `revenue` (captured in the window) | mixed |

Postman: `admin.reports.summary` › "200 Report Summary For A Month":

```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "period": {
            "from": "2026-10-01",
            "to": "2026-10-31"
        },
        "accounts": {
            "customers": {
                "total": 2,
                "new": 1
            },
            "providers": {
                "total": 1,
                "new": 0,
                "approved": 1
            },
            "drivers": {
                "total": 1,
                "new": 1,
                "approved": 1
            }
        },
        "orders": {
            "total": 3,
            "completed": 1,
            "cancelled": 1,
            "rejected": 0,
            "expired": 0,
            "instant": 1,
            "scheduled": 2,
            "by_kind": {
                "service": 3,
                "package_purchase": 0,
                "package_session": 0
            },
            "delayed": 1,
            "current": 1
        },
        "trips": {
            "total": 2,
            "completed": 1,
            "no_driver": 0,
            "cancelled": 0,
            "current": 1
        },
        "payments": {
            "count": 2,
            "total_amount": "345.00",
            "orders_amount": "115.00",
            "featured_amount": "230.00",
            "gateway_fees": "2.30"
        },
        "refunds": {
            "count": 1,
            "amount": "40.00"
        },
        "wallet": {
            "balances_total": "40.00",
            "transactions_count": 1,
            "credits": "40.00",
            "debits": "0.00"
        },
        "commissions": {
            "provider": "15.00",
            "driver": "2.50",
            "total": "17.50"
        },
        "transportation_fees": {
            "driver": "25.00",
            "provider_self_travel": "0.00",
            "total": "25.00"
        },
        "amounts_due": {
            "provider": {
                "due": "85.00",
                "transferred": "0.00"
            },
            "driver": {
                "due": "0.00",
                "transferred": "22.50"
            }
        },
        "featured": {
            "active": 1,
            "expired": 1,
            "revenue": "230.00"
        }
    }
}
```

### 3.2 `GET /reports/rankings`

Query: `from`, `to` as above, `limit` (1–50, default 10; Postman: "422 Report Rankings Limit Too High"). Lists:

| List | Row | Counted |
|------|-----|---------|
| `services` | `{id, name, count}` | orders placed in the window containing the service (service and package-session lines) |
| `packages` | `{id, name, count}` | package purchases placed in the window |
| `providers_by_orders` | `{id, name, count}` | orders completed in the window |
| `providers_by_rating` | `{id, name, rating_avg, rating_count}` | saved averages now (hidden ratings excluded, TD-160), at least one rating |
| `drivers_by_trips` | `{id, name, count}` | trips completed in the window |

Postman: `admin.reports.rankings` › "200 Report Rankings For A Month":

```json
{
    "status": "success",
    "message": "Data retrieved successfully",
    "data": {
        "period": {
            "from": "2026-10-01",
            "to": "2026-10-31"
        },
        "services": [
            {
                "id": "01a19bed-350d-70e8-97ba-a4d788bc59e5",
                "name": "Sample-xyay22554184",
                "count": 3
            }
        ],
        "packages": [],
        "providers_by_orders": [
            {
                "id": "01a19bed-34fc-725e-8cdd-ad16698e948e",
                "name": "Louisa Cremin",
                "count": 1
            }
        ],
        "providers_by_rating": [
            {
                "id": "01a19bed-34fc-725e-8cdd-ad16698e948e",
                "name": "Louisa Cremin",
                "rating_avg": "4.50",
                "rating_count": 2
            }
        ],
        "drivers_by_trips": [
            {
                "id": "01a19bed-3500-707a-a92e-e46653e84746",
                "name": "Miss Aditya Gorczany Jr.",
                "count": 1
            }
        ]
    }
}
```

## 4. Details view

N/A.

## 5. Forms (create / edit)

N/A — read-only.

## 6. Actions (non-CRUD)

N/A. Export to PDF / Excel is not built.

## 7. Statuses and enums

N/A — counts and SAR amounts only (2-decimal strings).

## Data model

Reads only. Order outcomes use `orders.status`; payments count when captured; commissions and transport fees come from `earnings` (never reversed rows); transfers from `payouts.paid_at`.

## 8. TypeScript types

```ts
type Money = string; // "1234.50" SAR
interface ReportSummary {
  period: { from: string | null; to: string | null };
  accounts: { customers: { total: number; new: number }; providers: { total: number; new: number; approved: number }; drivers: { total: number; new: number; approved: number } };
  orders: { total: number; completed: number; cancelled: number; rejected: number; expired: number; instant: number; scheduled: number;
            by_kind: { service: number; package_purchase: number; package_session: number }; delayed: number; current: number };
  trips: { total: number; completed: number; no_driver: number; cancelled: number; current: number };
  payments: { count: number; total_amount: Money; orders_amount: Money; featured_amount: Money; gateway_fees: Money };
  refunds: { count: number; amount: Money };
  wallet: { balances_total: Money; transactions_count: number; credits: Money; debits: Money };
  commissions: { provider: Money; driver: Money; total: Money };
  transportation_fees: { driver: Money; provider_self_travel: Money; total: Money };
  amounts_due: { provider: { due: Money; transferred: Money }; driver: { due: Money; transferred: Money } };
  featured: { active: number; expired: number; revenue: Money };
}
interface RankRow { id: string; name: string | null; count: number }
interface ReportRankings {
  period: ReportSummary['period'];
  services: RankRow[]; packages: RankRow[]; providers_by_orders: RankRow[]; drivers_by_trips: RankRow[];
  providers_by_rating: { id: string; name: string; rating_avg: string; rating_count: number }[];
}
```

## 9. Business rules enforced by the backend

- Provider and driver commissions, transport fees and amounts due are separate figures (ADMIN-017, BR-24, BR-25).
- Money sums are exact (decimal), never float.

## 10. Notifications triggered

None.

## 11. Integration checklist

- [ ] Date-range picker sending `from` / `to`; label standing figures "as of now".
- [ ] Format money from the string; never sum it in JavaScript floats.

## 12. Acceptance scenarios

AC-ADMIN-022: `ReportsTest` reconciles a known month — flows inside the window, records from 2025 left out, standing figures as of now, provider vs driver money apart — and the rankings. AC-ADMIN-002: the dashboard equals the all-time summary.

## 13. Open decisions and known limitations

| ID | Impact | Current behavior |
|----|--------|------------------|
| DEC-20 | Performance targets / NFR KPIs | Not defined; the figures are the BRD summaries (TD-161) |
| Export | PDF / Excel | Not built |
