# APIs báo cáo — Phòng khám Luca

Tài liệu này mô tả toàn bộ API báo cáo nội bộ của hệ thống CRM Phòng khám Luca (`pkluca.flyspa.vn`).
Đối tượng đọc: trợ lý AI (openclaw) — dùng tài liệu này để tự chọn endpoint, tự dựng tham số
và tự diễn giải số liệu trả về khi người dùng hỏi bằng ngôn ngữ tự nhiên.

---

## 1. CẤU HÌNH (sửa ở đây khi đổi domain hoặc key)

```
BASE_URL         = https://pkluca.flyspa.vn
API_PREFIX       = /api/internal
INTERNAL_API_KEY = g9H6KQncrY0czTqocZCA
```

Mọi request bắt buộc gửi header:

```
x-api-key: {INTERNAL_API_KEY}
```

URL đầy đủ = `{BASE_URL}{API_PREFIX}/{đường-dẫn-endpoint}`

Ví dụ: `{BASE_URL}/api/internal/reports/sales`

> **Khi đổi domain hoặc key, chỉ cần sửa khối cấu hình phía trên.**
> Toàn bộ ví dụ trong tài liệu đều dùng `{BASE_URL}` và `{INTERNAL_API_KEY}` như biến thay thế.

Mã lỗi xác thực:

| Mã | Ý nghĩa | Cách xử lý |
|---|---|---|
| `401` | Thiếu hoặc sai `x-api-key` | Kiểm tra lại key trong khối cấu hình |
| `500` + `"Internal API key is not configured"` | Server chưa cấu hình key | Báo người dùng liên hệ quản trị hệ thống |

Tất cả endpoint đều là **GET** và chỉ đọc dữ liệu — không có endpoint nào ghi/sửa/xoá.

---

## 2. CÁCH ĐỌC KẾT QUẢ TRẢ VỀ

Mọi endpoint trả về cùng một khung JSON:

```json
{
  "code": 200,
  "messages": "SUCCESS",
  "data": [ ... ],
  "meta": {
    "start_date": "01-08-2026",
    "end_date": "31-08-2026",
    "branch_id": null,
    "location_id": null,
    "total_gross_revenue": 504550000,
    "generated_at": "2026-08-29 10:00:00"
  }
}
```

| Khoá | Ý nghĩa |
|---|---|
| `code` | 200 = thành công |
| `messages` | `"SUCCESS"` khi thành công, hoặc nội dung lỗi |
| `data` | Dữ liệu báo cáo. Thường là **mảng các dòng**; riêng `/reports/overview` là **object nhiều khối** |
| `meta` | Bộ lọc đã áp dụng + các số tổng của toàn báo cáo (`total_*`) + thời điểm sinh dữ liệu |

**Quan trọng khi trả lời người dùng:**

- `meta.start_date` / `meta.end_date` là khoảng thời gian **thực tế** đã áp dụng (định dạng `dd-mm-yyyy`).
  Luôn nhắc lại khoảng này trong câu trả lời để người dùng biết số liệu thuộc kỳ nào.
- `meta.branch_id = null` nghĩa là **toàn hệ thống, tất cả chi nhánh**.
- Các khoá `total_*` trong `meta` là tổng của **toàn bộ** báo cáo, không bị ảnh hưởng bởi `limit`.
  Khi người dùng hỏi "tổng doanh thu là bao nhiêu", hãy lấy từ `meta`, đừng tự cộng các dòng đã bị cắt bởi `limit`.
- `data` là mảng rỗng `[]` không phải lỗi — nghĩa là kỳ đó không có dữ liệu thoả điều kiện.
- Số tiền trả về là **VNĐ**, dạng số nguyên, không có dấu phân cách. Khi hiển thị cho người dùng
  nên format lại (ví dụ `504550000` → `504.550.000 đ`).
- Một số trường số trả về dạng **chuỗi** thay vì số (ví dụ `"186000000"` trong `revenue_by_source`
  và `revenue_by_locale`, hay các trường đếm của `/reports/cskh` như `task_done`, `order_new`,
  `all_payment`) do đặc thù truy vấn tổng hợp. **Luôn ép về số trước khi cộng/so sánh**, nếu không
  sẽ bị nối chuỗi thay vì cộng.

---

## 3. THAM SỐ DÙNG CHUNG

Áp dụng cho mọi endpoint trong nhóm `/reports/*`.

| Tham số | Kiểu | Mặc định | Mô tả |
|---|---|---|---|
| `start_date` | `YYYY-MM-DD` hoặc `DD-MM-YYYY` | 7 ngày trước | Ngày bắt đầu kỳ báo cáo |
| `end_date` | `YYYY-MM-DD` hoặc `DD-MM-YYYY` | hôm nay | Ngày kết thúc kỳ báo cáo (bao gồm cả ngày này) |
| `branch_id` | số nguyên | không lọc = **toàn hệ thống** | Chỉ lấy dữ liệu của 1 chi nhánh. Lấy id từ `/meta/branches` |
| `location_id` | số nguyên | – | Lọc theo **khu vực** (gồm nhiều chi nhánh). Lấy id từ `/meta/locations` |
| `limit` | số nguyên | không giới hạn | Cắt lấy N dòng đầu **sau khi đã sắp xếp**. Dùng khi người dùng hỏi "top 5", "top 10" |
| `team_id` | số nguyên | – | Chỉ lọc nhóm nhân viên cụ thể. Chỉ có tác dụng với báo cáo telesale/marketing. Lấy id từ `/meta/teams` |
| `data_time` | enum | – | Cách chọn kỳ nhanh: `TODAY`, `YESTERDAY`, `THIS_WEEK`, `LAST_WEEK`, `THIS_MONTH`, `LAST_MONTH` |
| `locale_id` | số nguyên | – | Lọc theo khu vực khách hàng. Chỉ có ở báo cáo telesale. Lấy id từ `/meta/locales` |
| `source_id` | số nguyên | – | Lọc theo nguồn khách. Có ở báo cáo telesale. Lấy id từ `/meta/sources` |

### Quy tắc chọn khoảng thời gian từ câu hỏi tự nhiên

Ưu tiên **tự tính `start_date` / `end_date`** thay vì dùng `data_time`, vì `data_time` chỉ được
một số báo cáo hỗ trợ đầy đủ. Cách quy đổi (giả sử hôm nay là `2026-08-29`):

| Người dùng nói | start_date | end_date |
|---|---|---|
| "hôm nay" | 2026-08-29 | 2026-08-29 |
| "hôm qua" | 2026-08-28 | 2026-08-28 |
| "7 ngày qua" / "tuần vừa rồi" | 2026-08-23 | 2026-08-29 |
| "tháng này" | 2026-08-01 | 2026-08-31 |
| **"tháng trước"** | **2026-07-01** | **2026-07-31** |
| "quý này" | 2026-07-01 | 2026-09-30 |
| "năm nay" | 2026-01-01 | 2026-12-31 |
| "tháng 6" (không nói năm) | 2026-06-01 | 2026-06-30 |

Nếu người dùng không nói gì về thời gian, **hỏi lại** hoặc mặc định lấy tháng hiện tại và nói rõ
trong câu trả lời rằng đang lấy tháng hiện tại.

---

## 4. TỪ ĐIỂN CHỈ SỐ — ĐỌC KỸ TRƯỚC KHI DIỄN GIẢI SỐ LIỆU

Đây là phần quan trọng nhất. Nhiều tên trường gần giống nhau nhưng ý nghĩa khác hẳn.

### 4.1 Nhóm tiền bạc

| Trường | Tên tiếng Việt | Ý nghĩa chính xác |
|---|---|---|
| `all_total` | **Doanh số** | Tổng giá trị đơn hàng đã chốt trong kỳ. Đây là tiền **ghi trên đơn**, khách có thể chưa trả hết |
| `revenue_total` | **Doanh số** | Như `all_total`, dùng trong báo cáo chi nhánh/nguồn |
| `gross_revenue` | **Doanh thu ghi nhận** | Phần doanh số được ghi nhận là doanh thu trên đơn hàng |
| `payment` | **Tiền thực thu** | Tiền khách đã thanh toán thực tế trong kỳ |
| `all_payment` | **Tổng tiền thực thu** | Tổng tiền đã thu được trong kỳ (dùng ở báo cáo chi nhánh/nguồn/CSKH) |
| `detail_new` | **Tiền thực thu từ khách mới** | Phần tiền thu được từ đơn của khách mới |
| `payment_new` | Tiền thu / doanh thu khách mới | Ở báo cáo chi nhánh: doanh thu ghi nhận của khách mới. Ở báo cáo telesale: tiền thực thu đã trừ nợ |
| `payment_old` | Doanh thu khách cũ | Doanh thu ghi nhận từ khách cũ (upsale) |
| `is_debt` | **Công nợ** | Phần tiền được ghi nhận nhưng khách còn nợ, chưa trả |
| `payment_used` | Thanh toán bằng điểm/ví | Phần thanh toán không phải tiền mặt thật, trừ từ ví/điểm tích luỹ của khách |
| `earn` | **Hoa hồng** | Số tiền hoa hồng nhân viên được chia |
| `price` | Tiền tip | Trong báo cáo công tua: tổng tiền tip kỹ thuật viên nhận được |

> **Phân biệt then chốt:** `all_total` (doanh số — đã bán được bao nhiêu) ≠ `payment`/`all_payment`
> (thực thu — đã lấy được bao nhiêu tiền về). Khi người dùng hỏi "doanh thu", cần xác định họ muốn
> doanh số hay tiền thực thu; nếu không rõ, hãy đưa **cả hai** và giải thích ngắn gọn sự khác nhau.

### 4.2 Nhóm khách hàng và đơn hàng

| Trường | Ý nghĩa |
|---|---|
| `customers` / `customer_new` | Số khách hàng **mới** phát sinh trong kỳ |
| `contact` | Số khách hàng (data) mà nhân viên marketing/carepage đã tiếp nhận trong kỳ |
| `orders` / `order_new` | Số đơn hàng. `order_new` = đơn của **khách mới** |
| `order_old` | Số đơn của **khách cũ** (đơn upsale — bán thêm cho khách đã từng mua) |
| `order_single` | Số đơn giá trị **dưới 1 triệu** |
| `order_multiple` | Số đơn giá trị **từ 1 triệu trở lên** |
| `duplicate` | Số data **trùng lặp** trong tổng data telesale nhận được (khách đã có trong hệ thống) |

**Quy ước khách mới / khách cũ:** trường `is_upsale` trong hệ thống có `0` = khách mới,
`1` = khách cũ (upsale). Vì vậy mọi trường có hậu tố `_new` là của khách mới, `_old` là của khách cũ.

### 4.3 Nhóm lịch hẹn

| Trường | Ý nghĩa |
|---|---|
| `schedules` / `all_schedules` / `schedules_new` | Tổng số lịch hẹn trong kỳ |
| `schedules_den` / `become` | Số lịch khách **đã đến** (gồm cả đến rồi mua và đến nhưng chưa mua) |
| `schedules_buy` / `become_buy` | Số lịch khách **đến và có mua** |
| `schedules_notbuy` / `not_buy` | Số lịch khách **đến nhưng không mua** |
| `schedules_cancel` | Số lịch bị **huỷ** |

Mã trạng thái lịch hẹn (tra thêm ở `/meta/enums`): `2` đặt lịch · `3` đến mua · `4` đến chưa mua ·
`5` huỷ · `6` quá hạn.

### 4.4 Nhóm công việc và tổng đài

| Trường | Ý nghĩa |
|---|---|
| `task_todo` | Số việc đang chờ làm |
| `task_done` / `success` | Số việc đã hoàn thành |
| `task_failed` | Số việc thất bại |
| `total` (báo cáo tasks) | Tổng số việc được giao |
| `new` (báo cáo tasks) | Số việc ở trạng thái mới |
| `call_center` | Số cuộc gọi **được nghe máy** (trạng thái ANSWERED) |
| `call` | Ở báo cáo CSKH: tổng số cuộc gọi |
| `phoneNew` | Số data khách mới CSKH tiếp nhận |
| `phoneReceive` | Số data khách CSKH được bàn giao |

### 4.5 Nhóm hiệu suất

| Trường | Ý nghĩa |
|---|---|
| `percent_schedule` (marketing) / `percent_schedules` (carepage) | Tỉ lệ % số data ra được lịch hẹn |
| `percent_order` | Tỉ lệ % số data chốt được đơn |
| `avg` | Giá trị đơn trung bình (doanh thu ÷ số đơn) |
| `days` | Số **công chính** của kỹ thuật viên (trực tiếp làm dịch vụ) |
| `days_phu` | Số **công phụ** (hỗ trợ người khác làm) |
| `count` | Ở báo cáo cơ sở: số ca làm mà nhân sự được chia hoa hồng |
| `price_rose` | Ở báo cáo cơ sở: tổng tiền hoa hồng được chia theo ca |
| `percent_rose` | % hoa hồng cấu hình sẵn cho nhân sự đó |

### 4.6 Phương thức thanh toán

Object `payment_methods` có 3 khoá:

| Khoá | Ý nghĩa |
|---|---|
| `money` | Tiền mặt |
| `card` | Quẹt thẻ |
| `CK` | Chuyển khoản |

### 4.7 Cảnh báo về mốc thời gian

Các chỉ số trong cùng một báo cáo **không cùng một mốc thời gian** (điều này giữ nguyên theo
nghiệp vụ đang chạy trên giao diện web):

- Đơn hàng, doanh số → lọc theo **ngày tạo đơn**
- Tiền thực thu → lọc theo **ngày thanh toán**
- Lịch hẹn → tuỳ báo cáo, lọc theo **ngày hẹn** hoặc **ngày tạo lịch**

Hệ quả: tiền thu trong tháng 8 có thể đến từ đơn chốt tháng 7. Vì vậy **không nên** kết luận kiểu
"tỉ lệ thu hồi = payment ÷ all_total" trong cùng một kỳ ngắn. Nếu người dùng hỏi về tỉ lệ này, hãy
nêu rõ giới hạn trên.

---

## 5. DANH SÁCH ENDPOINT BÁO CÁO

### 5.1 Bảng tra nhanh — hỏi thế nào thì gọi cái nào

| Người dùng hỏi về... | Gọi endpoint |
|---|---|
| Tổng quan doanh thu, bức tranh chung, "tình hình kinh doanh" | `/reports/overview` |
| So sánh doanh thu **giữa các chi nhánh** | `/reports/branch-revenue` hoặc `/reports/branches` |
| Nguồn khách nào hiệu quả (Facebook, Google, giới thiệu...) | `/reports/branch-sources` |
| Dịch vụ / sản phẩm nào bán chạy | `/reports/group-sale` |
| **Báo cáo telesale, hiệu suất nhân viên sale** | `/reports/sales` |
| **Xếp hạng telesale, ai bán giỏi nhất** | `/reports/sale-ranking` |
| Hiệu quả marketing, chi phí quảng cáo ra đơn | `/reports/marketing-leader` |
| Chi phí ads thực tế / doanh thu, ROAS theo nhân viên | `/meta/ads-accounts` + Meta Graph API + `/reports/marketing-leader` — xem Ví dụ 6 |
| Xếp hạng nhân viên marketing | `/reports/marketing-ranking` |
| Nhân viên trực page, chăm sóc tin nhắn | `/reports/carepage` · `/reports/carepage-ranking` |
| Lịch hẹn của từng nhân viên, tỉ lệ khách đến | `/reports/task-schedules` |
| Hoa hồng chia theo ca làm dịch vụ | `/reports/hoa-hong` |
| Công tua kỹ thuật viên, số công, tiền tip | `/reports/commission` |
| Chăm sóc khách hàng sau bán (CSKH) | `/reports/cskh` |
| Hiệu quả xử lý công việc, task | `/reports/tasks` |

### 5.2 Chi tiết từng endpoint

---

#### `GET /reports/overview` — Thống kê tổng quan toàn hệ thống

Bức tranh tổng thể của một kỳ. Dùng khi người dùng hỏi chung chung: "tình hình kinh doanh tháng
trước thế nào", "tổng doanh thu bao nhiêu".

`data` là **object gồm nhiều khối** (không phải mảng):

| Khối | Nội dung |
|---|---|
| `summary` | `all_total` doanh số · `gross_revenue` doanh thu ghi nhận · `payment` tiền thực thu · `is_debt` công nợ · `orders` số đơn · `customers` số khách mới · `order_single` đơn dưới 1 triệu · `order_multiple` đơn từ 1 triệu |
| `products` | Doanh số/doanh thu/số đơn của riêng **sản phẩm** |
| `services` | Doanh số/doanh thu/số đơn của riêng **dịch vụ**; thêm `combo_total`, `combo_gross` cho gói combo |
| `schedules` | `all_schedules` tổng lịch hẹn · `become` số lịch khách đã đến |
| `wallets` | Ví/điểm tích luỹ của khách: `revenue` nạp vào · `payment` thanh toán bằng ví · `used` đã tiêu · `orders` số giao dịch |
| `payment_methods` | Tiền thực thu chia theo `money` / `card` / `CK` |
| `revenue_by_customer_type` | `revenue_new` tiền thu từ khách mới · `revenue_old` từ khách cũ |
| `revenue_by_gender` | Mảng `{name, all_total}` — doanh thu theo giới tính khách |
| `revenue_by_month` | Mảng `{month, all_total}` — tiền thu theo **tháng trong năm hiện tại** (luôn là năm nay, không phụ thuộc kỳ lọc) |
| `revenue_by_day` | Mảng theo từng ngày: `payment_date` ngày · `payment_revenue` tiền thu · `order_month` doanh số đơn trong ngày · `wallet_month`, `payment_wallet_month` phần ví |
| `revenue_by_source` | Mảng `{name, revenue}` — tiền thu theo nguồn khách |
| `revenue_by_locale` | Top 5 khu vực khách hàng theo doanh thu: `{name, total}` |
| `customers_by_status` | Số khách theo trạng thái quan hệ: `{name, total}` |
| `top_services` | Top 5 dịch vụ doanh thu cao nhất: `{name, total}` |

---

#### `GET /reports/branch-revenue` — Doanh thu theo chi nhánh (dạng biểu đồ)

Dùng để **so sánh nhanh** giữa các chi nhánh. Mỗi dòng:

| Trường | Ý nghĩa |
|---|---|
| `branch_id`, `branch_name` | Chi nhánh |
| `all_total` | Doanh số (đã cộng phần nạp ví) |
| `gross_revenue` | Doanh thu ghi nhận |
| `payment` | Tiền thực thu (đã cộng ví nạp, trừ ví tiêu) |
| `wallet_revenue`, `wallet_used` | Tiền nạp ví / tiền tiêu từ ví |
| `payment_methods` | Chia theo tiền mặt, thẻ, chuyển khoản |

`meta` có `total_all_total`, `total_gross_revenue`, `total_payment`.

---

#### `GET /reports/branches` — Báo cáo chi tiết theo chi nhánh

Chi tiết hơn `/reports/branch-revenue`: có thêm khách mới, lịch hẹn, tách khách mới/cũ.
Chỉ trả về chi nhánh **có phát sinh tiền thu**, sắp xếp giảm dần theo `all_payment`.

Trường: `branch_name` · `customer_new` khách mới · `schedules_new` lịch hẹn khách mới ·
`schedules_den` lịch khách đã đến · `order_new`/`order_old` số đơn khách mới/cũ ·
`revenue_new`/`revenue_old`/`revenue_total` doanh số · `payment_revenue`/`payment_new`/`payment_old`
doanh thu ghi nhận · `detail_new` tiền thu từ khách mới · `all_payment` tổng thực thu ·
`payment_wallet` thu qua ví · `payment_used` thanh toán bằng điểm.

`meta`: `total_all_payment`, `total_revenue_total`, `total_customer_new`.

---

#### `GET /reports/branch-sources` — Doanh thu theo nguồn khách hàng

Trả lời câu hỏi "nguồn nào mang lại doanh thu tốt nhất". Cấu trúc dòng giống `/reports/branches`
nhưng nhóm theo nguồn: `source_id`, `source_name` thay cho chi nhánh.

Tham số riêng: `parent_id` — chỉ lấy các nguồn con trực tiếp của nguồn cha này (lấy id từ
`/meta/sources`, nguồn cha là bản ghi có `parent_id = null`).

Trả về **tất cả** nguồn, kể cả nguồn không phát sinh số liệu trong kỳ.

---

#### `GET /reports/group-sale` — Doanh thu theo nhóm dịch vụ / sản phẩm

Tham số riêng: `type` — `1` = **dịch vụ** (mặc định), `2` = **sản phẩm**.

Trường: `category_id`, `category_name` nhóm · `customer_new` khách mới của nhóm ·
`schedules_new` lịch hẹn · `become` lịch khách đến · `order_new` số đơn khách mới ·
`revenue_new` doanh số khách mới · `revenue_total` tổng doanh số.

Sắp xếp giảm dần theo `revenue_total`. `meta`: `total_revenue_total`, `total_revenue_new`.

---

#### `GET /reports/sales` — Báo cáo doanh thu telesale ⭐

Báo cáo hiệu suất chi tiết của từng nhân viên telesale. Đây là endpoint dùng khi người dùng hỏi
**"báo cáo telesale"**.

| Trường | Ý nghĩa |
|---|---|
| `user_id`, `full_name` | Nhân viên telesale |
| `caller_number` | Số máy lẻ tổng đài |
| `customer_new` | Số data khách mới được giao trong kỳ |
| `duplicate` | Trong số data đó, bao nhiêu data bị **trùng** (khách đã có sẵn trong hệ thống) |
| `order_new` | Số đơn chốt được từ khách mới |
| `revenue_new` | Doanh số từ khách mới |
| `detail_new` | **Tiền thực thu** từ đơn khách mới — chỉ số xếp hạng chính |
| `is_debt` | Phần công nợ trong số đã ghi nhận |
| `payment_new` | Tiền thực thu đã trừ nợ (`detail_new` − `is_debt`) |
| `schedules_new` | Số lịch hẹn đã đặt cho khách mới |
| `schedules_den` | Số lịch khách mới đã đến |
| `become_buy` | Số lịch khách đến **và mua** |
| `not_buy` | Số lịch khách đến **nhưng không mua** |
| `call_center` | Số cuộc gọi **được nghe máy** trong kỳ |

Tham số riêng: `locale_id` (khu vực khách hàng), `source_id` (nguồn khách).

Sắp xếp giảm dần theo `detail_new`. `meta`: `total_revenue_new`, `total_payment_new`.

**Gợi ý phân tích:** tỉ lệ chốt = `become_buy` ÷ `schedules_den`; tỉ lệ ra lịch =
`schedules_new` ÷ `customer_new`; tỉ lệ data sạch = 1 − (`duplicate` ÷ `customer_new`).

---

#### `GET /reports/sale-ranking` — Bảng xếp hạng telesale

Bảng xếp hạng gọn, dùng khi người dùng hỏi "ai bán giỏi nhất", "top 5 sale".

Trường: `user_id` · `full_name` · `avatar` · `gross_revenue` = **tiền thực thu từ đơn khách mới**.

Đã sắp xếp sẵn giảm dần theo `gross_revenue` — dùng `limit` để lấy top N.
`meta`: `total_gross_revenue`.

---

#### `GET /reports/marketing-leader` — Báo cáo doanh thu marketing

Hiệu quả của từng nhân viên marketing.

Trường: `user_id`, `full_name`, `avatar` · `contact` số data mang về · `orders` số đơn ·
`schedules` số lịch hẹn · `all_total` doanh số · `gross_revenue` tiền thực thu ·
`percent_schedule` % data ra lịch · `percent_order` % data ra đơn · `avg` giá trị đơn trung bình.

`meta`: `total_contact`, `total_orders`, `total_gross_revenue`.

---

#### `GET /reports/marketing-ranking` — Bảng xếp hạng marketing

Cấu trúc giống `/reports/sale-ranking`: `user_id`, `full_name`, `avatar`, `gross_revenue`
(tiền thực thu từ đơn khách mới gắn với nhân viên marketing đó).

---

#### `GET /reports/carepage` — Báo cáo carepage (nhân viên trực page)

| Trường | Ý nghĩa |
|---|---|
| `user_id`, `full_name`, `avatar` | Nhân viên carepage |
| `contact` | Số data khách tiếp nhận trong kỳ |
| `schedules` | Số lịch hẹn của nhóm khách đó |
| `orders` | Số đơn phát sinh |
| `all_total` | Doanh số các đơn |
| `gross_revenue` | Doanh thu ghi nhận trên đơn |
| `payment` | **Tiền thực thu** từ đơn khách mới — dùng để xếp hạng |
| `percent_order` | % data ra đơn |
| `percent_schedules` | % data ra lịch hẹn |
| `avg` | Tiền thu trung bình mỗi đơn |

Sắp xếp giảm dần theo `payment`. `meta`: `total_contact`, `total_schedules`, `total_payment`.

---

#### `GET /reports/carepage-ranking` — Xếp hạng carepage

Trường: `user_id`, `full_name`, `avatar`, `gross_revenue` (doanh thu ghi nhận từ đơn khách mới).

---

#### `GET /reports/task-schedules` — Thống kê lịch hẹn theo nhân viên

Dùng khi người dùng hỏi về lịch hẹn: ai đặt được nhiều lịch, tỉ lệ khách đến bao nhiêu.
Chỉ trả về nhân viên **có ít nhất 1 lịch hẹn** trong kỳ.

Trường: `user_id`, `full_name`, `phone` · `schedules` tổng lịch · `schedules_buy` khách đến mua ·
`schedules_notbuy` khách đến không mua · `schedules_cancel` lịch huỷ.

`meta`: `total_schedules`, `total_schedules_buy`, `total_schedules_notbuy`, `total_schedules_cancel`.

---

#### `GET /reports/hoa-hong` — Báo cáo cơ sở (hoa hồng chia theo ca làm)

Tính phần hoa hồng mà từng nhân sự nhận được từ các ca làm dịch vụ. Một ca gồm nhiều vai trò:
bác sĩ, y tá 1, y tá 2, phụ 1, phụ 2 — mỗi vai trò có mức chia riêng.

| Trường | Ý nghĩa |
|---|---|
| `user_id`, `full_name`, `avatar` | Nhân sự |
| `department_id` | Phòng ban (10 bác sĩ · 7 tư vấn viên · 6 kỹ thuật viên · 4 lễ tân) |
| `branch_id` | Chi nhánh |
| `percent_rose` | % hoa hồng được cấu hình cho nhân sự |
| `count` | Số ca làm được chia hoa hồng trong kỳ |
| `price_rose` | **Tổng tiền hoa hồng** nhận được |

Tham số riêng: `department_id` — chỉ xem một nhóm nhân sự (ví dụ chỉ kỹ thuật viên: `6`).

Sắp xếp giảm dần theo `price_rose`. `meta`: `total_price_rose`, `total_count`.

---

#### `GET /reports/commission` — Báo cáo công tua kỹ thuật viên

Trường: `user_id`, `full_name`, `avatar`, `branch_id`, `branch_name` · `orders` số đơn ·
`all_total` doanh số · `gross_revenue` doanh thu ghi nhận · `days` **số công chính** ·
`days_phu` **số công phụ** · `earn` hoa hồng · `price` **tiền tip**.

Chỉ trả về kỹ thuật viên có công hoặc có tip. `meta`: `total_gross_revenue`, `total_earn`, `total_days`.

---

#### `GET /reports/cskh` — Xếp hạng chăm sóc khách hàng

Trường: `id`, `full_name`, `avatar` · `task_todo`/`task_done`/`task_failed` công việc ·
`call` tổng số cuộc gọi ·
`phoneNew` data mới · `phoneReceive` data được bàn giao ·
`order_new`/`order_upsale` số đơn khách mới/upsale ·
`payment_new`/`payment_upsale` tiền thu tương ứng · `all_payment` tổng tiền thu.

Sắp xếp giảm dần theo `all_payment`. `meta`: `total_all_payment`, `total_task_done`.

> Trường định danh ở đây là `id` (không phải `user_id` như các báo cáo khác).

---

#### `GET /reports/tasks` — Hiệu quả xử lý công việc

Trường: `user_id`, `full_name` · `total` tổng số việc · `new` việc mới chưa xử lý ·
`success` việc đã hoàn thành.

Ngoài `start_date`/`end_date`, endpoint này hỗ trợ tốt `data_time` (`THIS_MONTH`, `LAST_MONTH`...).

`meta`: `total_tasks`, `total_success`, cùng `range_from` / `range_to` là khoảng thời gian thực tế đã dùng.

---

## 6. ENDPOINT DANH MỤC (tra cứu id ↔ tên)

Các báo cáo trả về id. Dùng nhóm này để dịch id sang tên, hoặc để tìm id khi người dùng
nói tên ("chi nhánh Gò Vấp" → cần `branch_id`).

| Endpoint | Trả về | Tham số |
|---|---|---|
| `/meta/branches` | Danh sách chi nhánh: `id`, `name`, `phone`, `address`, `location_id` | – |
| `/meta/locations` | Khu vực chi nhánh: `id`, `name` | – |
| `/meta/locales` | Khu vực khách hàng (dùng cho `locale_id`): `id`, `name` | – |
| `/meta/users` | Nhân viên: `id`, `full_name`, `phone`, `email`, `department_id`, `branch_id`, `role`, `active`, `is_leader` | `department_id`, `branch_id`, `include_inactive` |
| `/meta/ads-accounts` | Nhân viên đã cấu hình Meta Ads, **kèm credential** để gọi Meta Graph API — xem mục 6.1 | `user_id`, `department_id`, `branch_id`, `include_inactive` |
| `/meta/departments` | Phòng ban: `id`, `name`, `parent_id` | – |
| `/meta/teams` | Nhóm: `id`, `name`, `department_id` | – |
| `/meta/sources` | Nguồn khách: `id`, `name`, `parent_id` | – |
| `/meta/categories` | Nhóm dịch vụ/sản phẩm: `id`, `name`, `type` | `type` |
| `/meta/services` | Dịch vụ/sản phẩm: `id`, `name`, `type`, `category_id` | `type`, `category_id` |
| `/meta/enums` | Bảng mã trạng thái lịch hẹn, loại đơn, phương thức thanh toán, khách mới/cũ, trạng thái công việc | – |

Mã phòng ban của hệ thống này (`department_id`):
`1` Ban giám đốc · `2` Telesale · `3` Marketing · `4` CSKH · `5` Lễ Tân · `6` Kỹ thuật viên ·
`7` Tư vấn viên · `8` Kế toán · `9` Care Page · `10` Bác Sĩ · `11` HCNS · `12` Seeding.

Chi nhánh hiện có: `1` Hà Nội · `2` Q10 · `4` Gò Vấp · `6` Luca.
Khu vực chi nhánh (`location_id`): `1` Cụm Hà Nội (gồm Hà Nội, Luca) · `2` Cụm HCM (gồm Q10, Gò Vấp).
`/meta/locales` là danh sách 63 tỉnh/thành — dùng cho tham số `locale_id` (quê quán khách hàng),
đừng nhầm với `location_id`.

---

### 6.1 `GET /meta/ads-accounts` — Credential Meta Ads theo từng nhân viên

Trả về danh sách nhân viên đã cấu hình đủ **cả ba** thông tin Meta trong CRM
(`meta_app_id`, `meta_app_secret`, `meta_access_token`). Ai thiếu một trong ba sẽ không xuất hiện.

Đây là endpoint để trợ lý tự lấy chi phí quảng cáo thực tế: mỗi nhân viên marketing dùng
Meta App và access token riêng, nên phải lấy credential của đúng người rồi mới gọi
Meta Graph API cho người đó.

**Tham số**

| Tham số | Ý nghĩa |
|---|---|
| `user_id` | Chỉ lấy một nhân viên |
| `department_id` | Lọc theo phòng ban (Marketing = `3`) |
| `branch_id` | Lọc theo chi nhánh |
| `include_inactive` | Truyền bất kỳ giá trị nào để lấy cả nhân viên đã nghỉ. Mặc định chỉ lấy `active = 1` |

**Trường trả về**

| Trường | Ý nghĩa |
|---|---|
| `user_id`, `full_name`, `email`, `phone` | Định danh nhân viên. `user_id` là **khoá ghép** sang các báo cáo khác |
| `department_id`, `branch_id`, `active` | Phạm vi tổ chức |
| `meta_app_id`, `meta_app_secret`, `meta_access_token` | Credential thật để gọi Meta Graph API |
| `meta_token_mode` | `USER_TOKEN` (hết hạn ~60 ngày) hoặc `SYSTEM_USER_TOKEN` (thường không hết hạn) |
| `meta_graph_version` | Phiên bản Graph API nên dùng, ví dụ `v26.0` |
| `token_status` | `ACTIVE` · `EXPIRING_SOON` (≤7 ngày) · `EXPIRED` · `NO_SCHEDULED_EXPIRY` |
| `token_expires_at` | Unix timestamp; `null` nghĩa là không có lịch hết hạn |
| `token_expires_at_human` | Cùng mốc trên, dạng `Y-m-d H:i:s` cho dễ đọc |
| `token_days_left` | Số ngày còn lại; `null` khi không có lịch hết hạn |
| `token_obtained_at` | Thời điểm token được cấp/gia hạn lần cuối |

`meta` bổ sung: `total`, `expired`, `expiring_soon`, `graph_version`, `required_scope`.

**Ví dụ**

```bash
curl -s -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/meta/ads-accounts?department_id=3"
```

**Cách dùng**

1. Gọi endpoint này trước để biết ai có credential.
2. Bỏ qua bản ghi `token_status = "EXPIRED"` — token đã chết, gọi Meta sẽ lỗi.
   Báo lại để nhân viên vào *Marketing → Tài khoản quảng cáo Meta* lấy token mới.
3. Với mỗi nhân viên còn lại, dùng credential của họ gọi Meta Graph API
   (`/me/adaccounts`, rồi `/{act_id}/insights`) để lấy `spend`.
4. Ghép với `/reports/marketing-leader` bằng `user_id` để ra chi phí trên doanh thu.
   Chi tiết công thức xem `docs/SKILL_ADS.md` mục 14.2.

> **Lưu ý bảo mật:** response chứa App Secret và Access Token thật. Endpoint chỉ chạy nội bộ
> sau header `x-api-key`. Không log, không in ra chat, không lưu xuống file. Không gọi
> endpoint này từ trình duyệt hay bất kỳ nơi nào người dùng cuối nhìn thấy được response.

---

## 7. QUY TRÌNH XỬ LÝ CÂU HỎI CỦA NGƯỜI DÙNG

1. **Xác định chủ đề** → tra bảng ở mục 5.1 để chọn endpoint.
2. **Xác định khoảng thời gian** → quy đổi theo bảng ở mục 3.
3. **Xác định phạm vi** → nếu người dùng nhắc tên chi nhánh/khu vực/nhóm, gọi `/meta/*` tương ứng
   để lấy id trước, rồi truyền vào tham số. Không nhắc gì thì để trống = toàn hệ thống.
4. **Gọi API**, kiểm tra `code == 200`.
5. **Diễn giải**: dùng từ điển ở mục 4 để gọi đúng tên chỉ số, format tiền tệ, và **luôn nêu rõ
   kỳ báo cáo** lấy từ `meta.start_date` / `meta.end_date`.
6. Nếu `data` rỗng, nói rõ "không có dữ liệu trong kỳ này" kèm điều kiện đã lọc — đừng báo lỗi.

---

## 8. VÍ DỤ ĐẦY ĐỦ

### Ví dụ 1 — "Cho tôi thông tin báo cáo telesale tháng trước"

Hôm nay `2026-08-29` → tháng trước là `2026-07-01` đến `2026-07-31`.
Không nhắc chi nhánh → để trống, lấy toàn hệ thống.

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/sales?start_date=2026-07-01&end_date=2026-07-31"
```

Kết quả rút gọn (**số liệu dưới đây chỉ là minh hoạ cấu trúc**, không phải dữ liệu thật):

```json
{
  "code": 200,
  "data": [
    {
      "user_id": 176, "full_name": "Telesales Thu Viễn",
      "caller_number": "00906",
      "customer_new": 168, "duplicate": 12, "order_new": 12,
      "revenue_new": 145000000, "detail_new": 98000000,
      "is_debt": 5000000, "payment_new": 93000000,
      "schedules_new": 32, "schedules_den": 14, "become_buy": 10, "not_buy": 4,
      "call_center": 150
    }
  ],
  "meta": { "start_date": "01-07-2026", "end_date": "31-07-2026",
            "total_revenue_new": 145000000, "total_payment_new": 93000000 }
}
```

Cách trả lời người dùng (bám theo số minh hoạ ở trên):

> **Báo cáo telesale tháng 7/2026** (01/07 – 31/07, toàn hệ thống)
>
> Tổng doanh số từ khách mới: **145.000.000 đ**, tiền thực thu sau khi trừ công nợ: **93.000.000 đ**.
>
> Dẫn đầu là **Telesales Thu Viễn**: nhận 168 data (trong đó 12 data trùng), đặt được 32 lịch hẹn
> (tỉ lệ ra lịch 19%), khách đến 14 lượt và chốt được 10 đơn — tỉ lệ chốt trên khách đến đạt 71%.
> Thực thu 98.000.000 đ, trong đó còn 5.000.000 đ công nợ. Tổng đài ghi nhận 150 cuộc gọi được nghe máy.

### Ví dụ 2 — "Top 5 sale doanh thu cao nhất tháng này"

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/sale-ranking?start_date=2026-08-01&end_date=2026-08-31&limit=5"
```

### Ví dụ 3 — "Doanh thu chi nhánh Gò Vấp quý này"

Bước 1 — tìm id chi nhánh:

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" "{BASE_URL}/api/internal/meta/branches"
# → {"id": 4, "name": "Gò Vấp", ...}
```

Bước 2 — gọi báo cáo:

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/branches?start_date=2026-07-01&end_date=2026-09-30&branch_id=4"
```

### Ví dụ 4 — "Nguồn khách nào hiệu quả nhất tháng trước"

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/branch-sources?start_date=2026-07-01&end_date=2026-07-31&limit=10"
```

So sánh `customer_new` (số data) với `all_payment` (tiền thu) để đánh giá nguồn nào cho khách
chất lượng, không chỉ nhìn số lượng data.

### Ví dụ 5 — "So sánh doanh thu tháng này với tháng trước"

Gọi 2 lần cùng một endpoint với 2 khoảng thời gian, rồi so sánh các khoá `total_*` trong `meta`:

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/overview?start_date=2026-07-01&end_date=2026-07-31"
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/overview?start_date=2026-08-01&end_date=2026-08-31"
```

---

### Ví dụ 6 — "Chi phí quảng cáo trên doanh thu của từng nhân viên tháng trước"

Câu hỏi này cần **hai nguồn**: chi phí ads lấy từ Meta, doanh thu lấy từ CRM.

Bước 1 — lấy credential Meta của nhân viên marketing:

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/meta/ads-accounts?department_id=3"
# → [{"user_id": 42, "full_name": "Nguyễn Văn A", "meta_app_id": "…",
#     "meta_access_token": "…", "token_status": "ACTIVE", ...}]
```

Bước 2 — với **từng** `user_id`, dùng credential của chính họ gọi Meta lấy `spend`
(chi tiết trong `docs/SKILL_ADS.md`):

```bash
curl -s -G "https://graph.facebook.com/v26.0/me/adaccounts" \
  --data-urlencode "access_token=${meta_access_token}" ...
curl -s -G "https://graph.facebook.com/v26.0/${act_id}/insights" \
  --data-urlencode "level=campaign" \
  --data-urlencode 'time_range={"since":"2026-08-01","until":"2026-08-31"}' ...
```

Bước 3 — lấy doanh thu CRM cùng kỳ. **Chú ý CRM dùng `DD-MM-YYYY`, Meta dùng `YYYY-MM-DD`**:

```bash
curl -H "x-api-key: {INTERNAL_API_KEY}" \
  "{BASE_URL}/api/internal/reports/marketing-leader?start_date=01-08-2026&end_date=31-08-2026"
# → [{"user_id": 42, "full_name": "Nguyễn Văn A", "contact": 120,
#     "orders": 12, "gross_revenue": 38500000, ...}]
```

Bước 4 — ghép bằng `user_id` rồi tính:

```text
Chi phí/Doanh thu = ad_spend / gross_revenue × 100%
ROAS              = gross_revenue / ad_spend
```

Dòng tổng phải cộng tử số và mẫu số toàn cục rồi mới chia — **không lấy trung bình
tỷ lệ của từng nhân viên**. Nhân viên có chi phí nhưng `gross_revenue = 0` thì ghi
`Không xác định`, không ghi `0%`.

---

## 9. LƯU Ý KHI SỬ DỤNG

- API **không giới hạn phân trang**: mặc định trả toàn bộ dòng của kỳ. Dùng `limit` khi chỉ cần top N.
- Khoảng thời gian càng dài, số chi nhánh/nhân viên càng nhiều thì phản hồi càng chậm. Với báo cáo
  cả năm nên cân nhắc chia nhỏ theo tháng.
- Ngày sai định dạng sẽ **không báo lỗi** mà rơi về giá trị mặc định (7 ngày gần nhất). Luôn kiểm tra
  `meta.start_date` / `meta.end_date` để chắc chắn kỳ báo cáo đúng ý người dùng.
- API chạy với quyền xem **toàn hệ thống** — không giới hạn theo chi nhánh hay phòng ban của bất kỳ
  tài khoản nào. Phạm vi dữ liệu hoàn toàn do tham số quyết định.
- Số liệu trả về khớp chính xác với các màn hình báo cáo trên giao diện web của hệ thống, với một
  khác biệt: giao diện web tự chọn sẵn một chi nhánh mặc định ở màn hình tổng quan và báo cáo
  telesale, còn API mặc định lấy toàn hệ thống. Truyền `branch_id` nếu muốn kết quả giống hệt
  màn hình web.
- Chỉ dùng `https://`. Gọi qua `http://` sẽ bị chuyển hướng 301.

### Các báo cáo hiện chưa có số liệu

Tính đến thời điểm lập tài liệu, bốn báo cáo sau trả về rỗng hoặc bằng 0 vì **nghiệp vụ tương ứng
chưa được sử dụng trên hệ thống**, không phải do lỗi API. Khi người dùng hỏi tới, hãy nói rõ là
chưa có dữ liệu thay vì báo "doanh thu bằng 0":

| Báo cáo | Tình trạng |
|---|---|
| `/reports/hoa-hong` | `price_rose` = 0 cho mọi nhân sự — chưa dùng tính năng chia hoa hồng theo ca |
| `/reports/commission` | Có `days` (số công) nhưng `earn` và `price` = 0 — chưa cấu hình hoa hồng, tiền tip |
| `/reports/cskh` | Chỉ có 1 nhân sự CSKH, chưa phát sinh số liệu |
| `/reports/tasks` | Chưa có công việc nào được tạo |

Các báo cáo còn lại (tổng quan, chi nhánh, nguồn, nhóm dịch vụ, telesale, marketing, carepage,
lịch hẹn) đều có số liệu đầy đủ.
