# API báo cáo nội bộ — nhánh feature/tintam (AICRM TECH)

Ghi chú triển khai. Tài liệu dành cho openclaw nằm ở [AICRM-TECH-API-bao-cao.md](AICRM-TECH-API-bao-cao.md).

## 1. Phạm vi

15 màn hình báo cáo của giao diện web được mở thành API nội bộ read-only, cộng thêm nhóm
endpoint danh mục để bên phân tích map id sang tên.

| Màn hình web | Endpoint internal | Nguồn logic |
|---|---|---|
| `/statistics` | `/reports/overview` | `BE\StatisticController@index` |
| `/chart-revenue` | `/reports/branch-revenue` | `BE\ChartController@index` |
| `/statistics-task` | `/reports/task-schedules` | `BE\StatisticController@taskSchedules` |
| `/report/branchs` | `/reports/branches` | `BE\Branch\BranchController@index` |
| `/report/branch-sources` | `/reports/branch-sources` | `BE\Branch\BranchController@source` |
| `/report/sales` | `/reports/sales` | `BE\SalesController@index` |
| `/report/sale-ranking` | `/reports/sale-ranking` | `BE\SalesController@ranking` |
| `/report/group-sale` | `/reports/group-sale` | `BE\SalesController@indexGroupCategory` |
| `/marketing/leader` | `/reports/marketing-leader` | `BE\Marketing\MarketingController@index` |
| `/marketing/ranking` | `/reports/marketing-ranking` | `BE\Marketing\MarketingController@ranking` |
| `/marketing/carepage` | `/reports/carepage` | `BE\Marketing\CarepageController@index` |
| `/marketing/carepage-ranking` | `/reports/carepage-ranking` | `BE\Marketing\CarepageController@ranking` |
| `/report/commission` | `/reports/commission` | `BE\CommissionController@statistical` |
| `/report/hoa-hong` | `/reports/hoa-hong` | `BE\CommissionController@statisticalRose` |
| `/report/cskh` | `/reports/cskh` | `BE\Cskh\CskhController@ranking` |
| `/report/tasks` | `/reports/tasks` | `BE\TaskController@statistical` |

## 2. Quyết định thiết kế

| Vấn đề | Phương án | Lý do |
|---|---|---|
| Phân quyền dữ liệu | Chạy **toàn quyền**, phạm vi do tham số quyết định | Màn hình BE giới hạn dữ liệu theo `Auth::user()`; internal API không có phiên đăng nhập nên bỏ ràng buộc đó. Muốn thu hẹp thì truyền `branch_id`, `location_id`, `team_id`. |
| Tái sử dụng code | **Copy logic sang `Controllers/Internal`**, không sửa `Controllers/BE` | Tránh rủi ro hồi quy trên màn hình đang chạy. Đổi lại logic bị nhân đôi. |
| Danh mục | Nhóm `/meta/*` | Báo cáo trả id; bên phân tích cần tên. |
| Phân trang | Trả đầy đủ, có `?limit` | Báo cáo tổng hợp vài chục–vài trăm dòng; AI cần dữ liệu trọn vẹn. Các số `total_*` trong `meta` luôn tính trên **toàn bộ** báo cáo, không phụ thuộc `limit`. |

## 3. Hạ tầng đã thêm

- `app/Http/Middleware/VerifyInternalApiKey.php` — kiểm tra header `x-api-key`
- `app/Http/Kernel.php` — đăng ký alias `internal.api.key`
- `app/Providers/RouteServiceProvider.php` — `mapInternalRoutes()`, prefix `api/internal`
- `config/services.php` — `internal_api_key`
- `.env.example` — `INTERNAL_API_KEY`
- `routes/internal.php` — 28 route

## 4. Khác biệt so với bản triển khai trên nhánh feature/gtg_version (AICRM PRO)

Hai hệ thống chạy mã nguồn khác nhau, nên code internal ở nhánh này được viết lại cho khớp
logic BE của chính nhánh này, không phải copy nguyên:

| Điểm | Nhánh này (TECH) | Nhánh gtg_version (PRO) |
|---|---|---|
| Lọc doanh thu sale/mkt | Không có cờ `revenue_sale` / `revenue_marketing` | Có, và chỉ tính đơn được đánh dấu |
| `sale-ranking`, `marketing-ranking` | Duyệt user + `PaymentHistory::search`, chỉ trả `gross_revenue` | Join `orders`, trả thêm `orders`, `branch_name` |
| `marketing-leader` | `PaymentHistory::search` + `whereHas(is_upsale=0)` | `PaymentHistory::searchNewVersion` (nhánh này không có method đó) |
| `sales` | Có `duplicate`, `call_center`; lọc thêm `locale_id`, `source_id`; lịch hẹn theo `date` | Có `order_hot`, `schedules_hot*`, `answer_time`, `call_2_minute`; lịch hẹn theo `created_at` |
| `carepage` | `Customer::searchApi` + `Order::searchAll`, trả `orders`/`payment`/`avg` | Truy vấn join, trả `contact_with_orders`/`schedules_done` |
| `hoa-hong` | `CommissionEmployee` + `SupportOrder`, chia theo ca (`count`, `price_rose`) | `Commission` join `orders`, chia theo đơn (`earn`, `payment`) |
| `overview` | Có `revenue_by_locale`, `customers_by_status`; không có `top_trademarks` (BE đã tắt) | Có `top_trademarks` |
| `branch-sources` | Tham số `parent_id`, trả hết mọi nguồn | Tham số `parent_source_id`, lọc bỏ nguồn rỗng |
| `CskhService::transformData` | 6 tham số (không nhận `$input`) | 7 tham số |
| Danh mục | Có thêm `/meta/locales` | Không có |

## 5. Kiểm thử

**Local** (`http://127.0.0.1:8000`, DB `crm-spa-all`, dữ liệu tháng 7–8/2026): 26/26 endpoint `200`.
Đối chiếu trực tiếp với controller BE (chạy dưới tài khoản admin, cùng bộ lọc) — khớp 1:1 ở
`/report/branchs`, `/report/branch-sources`, `/report/sales`, `/report/sale-ranking`,
`/marketing/carepage`, `/marketing/leader`, `/report/hoa-hong`.

**Production** (`https://pkluca.flyspa.vn`, tháng 8/2026): 26/26 endpoint `200`, phản hồi
0,06–0,4s (riêng `/reports/hoa-hong` 2,8s). Xác thực đúng: thiếu/sai key trả `401`.
Số liệu tổng quan: doanh số 2.747.350.000 đ · thực thu 2.317.650.000 đ · 284 đơn · 2.525 khách mới.
Tính nhất quán chéo đã kiểm: `branches.total_all_payment` = `overview.summary.payment` = 2.317.650.000 đ.

Khác biệt duy nhất so với web, **đúng thiết kế**: màn hình web ép chi nhánh mặc định khi người
dùng không chọn (`/statistics` ép `branch_id = user->branch_id ?? 1`; `/report/sales` ép
`branch_id = 1`), còn internal API mặc định trả toàn hệ thống. Truyền `branch_id` thì hai bên khớp.

Rỗng/bằng 0 do dữ liệu, không phải lỗi (màn hình web cũng vậy):

- `/reports/hoa-hong` — toàn bộ `price_rose = 0`. Hệ thống này không có nhân sự phòng Bác sĩ (10)
  hay Tư vấn viên (7), và bảng `support_orders` / `commission_employees` chưa được dùng.
- `/reports/commission` — có `days` nhưng `earn` và `gross_revenue` = 0 (chưa cấu hình hoa hồng).
- `/reports/cskh` — chỉ 1 nhân sự CSKH, chưa phát sinh số liệu.
- `/reports/tasks` — bảng `tasks` rỗng.

Lưu ý nhỏ: `branches.total_customer_new` (2.524) lệch 1 so với `overview.summary.customers`
(2.525) — có 1 khách gắn chi nhánh không nằm trong danh mục chi nhánh hiện có.

## 6. Việc còn lại

1. `/reports/hoa-hong` chạy 1 truy vấn cho mỗi nhân sự (49 người → 49 truy vấn), mất ~2,8s.
   Nếu dùng thường xuyên nên gom thành một truy vấn nhóm theo `support_orders`.
2. Logic bị nhân đôi so với `Controllers/BE` — sửa công thức ở web thì phải sửa cả bản internal.
3. Các báo cáo lặp query theo từng chi nhánh/nhân viên (giữ cách làm của BE); kỳ dài + nhiều
   chi nhánh sẽ chậm, cân nhắc cache nếu gọi thường xuyên.
