# API Documentation: Thống Kê Đơn Hàng và Doanh Thu Theo Ngày

## Endpoint Information

**URL:** `POST /api/RevenueReport/OrderRevenueByDate`

**Authorization:** Required - Role: `Report.OrderProductionWithRevenue`

**Description:** Lấy thống kê đơn hàng và doanh thu theo ngày tạo đơn, bao gồm các trạng thái triển khai và thông tin tài chính.

---

## Request

### Headers
```
Authorization: Bearer {token}
Content-Type: application/json
```

### Body (SmartTableParam)

```json
{
  "Search": {
    "PredicateObject": {
      "StartDate": "2026-01-01T00:00:00Z",
      "EndDate": "2026-01-31T23:59:59Z",
      "PickupStartDate": "2026-01-01T00:00:00Z",
      "PickupEndDate": "2026-01-31T23:59:59Z",
      "DeliveryStartDate": "2026-01-01T00:00:00Z",
      "DeliveryEndDate": "2026-01-31T23:59:59Z",
      "SaleId": 123
    }
  }
}
```

### Request Parameters

| Tham số | Kiểu | Bắt buộc | Mô tả |
|---------|------|----------|-------|
| `StartDate` | DateTimeOffset | Có điều kiện* | Ngày bắt đầu lọc theo thời gian tạo đơn |
| `EndDate` | DateTimeOffset | Có điều kiện* | Ngày kết thúc lọc theo thời gian tạo đơn |
| `PickupStartDate` | DateTimeOffset | Có điều kiện* | Ngày bắt đầu lọc theo thời gian lấy hàng thành công |
| `PickupEndDate` | DateTimeOffset | Có điều kiện* | Ngày kết thúc lọc theo thời gian lấy hàng thành công |
| `DeliveryStartDate` | DateTimeOffset | Có điều kiện* | **[MỚI]** Ngày bắt đầu lọc theo thời gian giao thành công |
| `DeliveryEndDate` | DateTimeOffset | Có điều kiện* | **[MỚI]** Ngày kết thúc lọc theo thời gian giao thành công |
| `SaleId` | int | Không | ID của nhân viên sale. Nếu user không có role "Founder", tự động lọc theo userId |

> **Lưu ý:** 
> - **(*) Yêu cầu bắt buộc:** Phải cung cấp ít nhất một trong ba bộ lọc sau:
>   - Bộ lọc ngày tạo đơn: `StartDate` **VÀ** `EndDate`
>   - Bộ lọc ngày lấy hàng: `PickupStartDate` **VÀ** `PickupEndDate`
>   - Bộ lọc ngày giao thành công: `DeliveryStartDate` **VÀ** `DeliveryEndDate`
> - Nếu không có bộ lọc nào, API sẽ trả về lỗi: `"Vui lòng chọn ít nhất một bộ lọc: Ngày tạo đơn, Ngày lấy hàng hoặc Ngày giao thành công"`
> - Tất cả các ngày giờ sẽ được chuyển từ UTC sang local time (GMT+7)
> - `StartDate` và `EndDate` lọc theo thời gian **tạo đơn** (`CreatedTime`)
> - `PickupStartDate` và `PickupEndDate` lọc theo thời gian **lấy hàng thành công** (`PickupTime` trong `OrderTimeTrack`)
> - `DeliveryStartDate` và `DeliveryEndDate` lọc theo thời gian **giao thành công** (`DeliveryDate` trong `OrderTimeTrack`)
> - Có thể sử dụng kết hợp nhiều bộ lọc đồng thời

---

## Response

### Success Response (200 OK)

```json
{
  "Result": 0,
  "Message": null,
  "Items": [
    {
      "OrderDate": "2026-01-15T00:00:00",
      "OrderDateStr": "15/01/2026",
      "TotalOrders": 150,
      "TotalRevenue": 45000000,
      "TotalCollectAmount": 30000000,
      
      "WaitPickup": 10,
      "WaitPickupRevenue": 3000000,
      "WaitPickupCollectAmount": 2000000,
      
      "Picking": 15,
      "PickingRevenue": 4500000,
      "PickingCollectAmount": 3000000,
      
      "Picked": 20,
      "PickedRevenue": 6000000,
      "PickedCollectAmount": 4000000,
      
      "InWarehouse": 25,
      "InWarehouseRevenue": 7500000,
      "InWarehouseCollectAmount": 5000000,
      
      "OutWarehouse": 30,
      "OutWarehouseRevenue": 9000000,
      "OutWarehouseCollectAmount": 6000000,
      
      "Delivering": 20,
      "DeliveringRevenue": 6000000,
      "DeliveringCollectAmount": 4000000,
      
      "Delivered": 25,
      "DeliveredRevenue": 7500000,
      "DeliveredCollectAmount": 5000000,
      
      "Cancelled": 5,
      "CancelledRevenue": 1500000,
      "CancelledCollectAmount": 1000000,
      
      "IsTotalRow": false,
      "TotalInProgress": 120,
      "TotalFailed": 0,
      "TotalCompleted": 25,
      "SuccessRate": 16.67,
      "TotalInProgressRevenue": 36000000,
      "TotalFailedRevenue": 0,
      "TotalCompletedRevenue": 7500000,
      "TotalInProgressCollectAmount": 24000000,
      "TotalFailedCollectAmount": 0,
      "TotalCompletedCollectAmount": 5000000
    }
  ],
  "TotalRecord": 1
}
```

### Response Fields

#### Thông tin cơ bản
| Trường | Kiểu | Mô tả |
|--------|------|-------|
| `OrderDate` | DateTime | Ngày tạo đơn |
| `OrderDateStr` | string | Chuỗi hiển thị ngày (dd/MM/yyyy) hoặc "TỔNG CỘNG" |
| `TotalOrders` | int | Tổng số đơn hàng (không bao gồm đơn hủy) |
| `TotalRevenue` | decimal | Tổng doanh số (không bao gồm đơn hủy) |
| `TotalCollectAmount` | decimal | Tổng tiền thu hộ (không bao gồm đơn hủy) |

#### Trạng thái đơn hàng

| Trạng thái | Trường Count | Trường Revenue | Trường CollectAmount | Điều kiện | Mô tả |
|------------|--------------|----------------|----------------------|-----------|-------|
| **Chờ lấy hàng** | `WaitPickup` | `WaitPickupRevenue` | `WaitPickupCollectAmount` | Status=1, PhysicalStatus=1 | Đơn đang chờ được lấy |
| **Đang lấy hàng** | `Picking` | `PickingRevenue` | `PickingCollectAmount` | Status=1, PhysicalStatus IN (11,12) | Đơn đang trong quá trình lấy |
| **Đã lấy** ⭐ | `Picked` | `PickedRevenue` | `PickedCollectAmount` | Status=3, PhysicalStatus=13 | Đã lấy hàng thành công |
| **Nhập kho** ⭐ | `InWarehouse` | `InWarehouseRevenue` | `InWarehouseCollectAmount` | Status=3, PhysicalStatus=20 | Đã nhập kho |
| **Xuất kho** ⭐ | `OutWarehouse` | `OutWarehouseRevenue` | `OutWarehouseCollectAmount` | Status=3, PhysicalStatus=30 | Đã xuất kho |
| **Đang giao** | `Delivering` | `DeliveringRevenue` | `DeliveringCollectAmount` | Status=3, PhysicalStatus IN (51,53) | Đang giao hàng |
| **Đã giao** | `Delivered` | `DeliveredRevenue` | `DeliveredCollectAmount` | Status=55, PhysicalStatus=55 | Giao hàng thành công |
| **Đã hủy** | `Cancelled` | `CancelledRevenue` | `CancelledCollectAmount` | Status=8 | Đơn đã bị hủy |

> ⭐ **Trường mới được thêm trong phiên bản này**

#### Các trường tính toán

| Trường | Kiểu | Mô tả | Công thức |
|--------|------|-------|-----------|
| `IsTotalRow` | bool | Kiểm tra có phải dòng tổng cộng không | OrderDate == DateTime.MinValue |
| `TotalInProgress` | int | Tổng số đơn đang xử lý | WaitPickup + Picking + Picked + InWarehouse + OutWarehouse + Delivering |
| `TotalFailed` | int | Tổng số đơn thất bại | 0 (không còn trạng thái thất bại) |
| `TotalCompleted` | int | Tổng số đơn hoàn thành | Delivered |
| `SuccessRate` | decimal | Tỷ lệ thành công (%) | (TotalCompleted / TotalOrders) * 100 |
| `TotalInProgressRevenue` | decimal | Tổng doanh số đơn đang xử lý | Tổng revenue của các đơn đang xử lý |
| `TotalFailedRevenue` | decimal | Tổng doanh số đơn thất bại | 0 |
| `TotalCompletedRevenue` | decimal | Tổng doanh số đơn hoàn thành | DeliveredRevenue |
| `TotalInProgressCollectAmount` | decimal | Tổng tiền thu hộ đơn đang xử lý | Tổng collect amount của các đơn đang xử lý |
| `TotalFailedCollectAmount` | decimal | Tổng tiền thu hộ đơn thất bại | 0 |
| `TotalCompletedCollectAmount` | decimal | Tổng tiền thu hộ đơn hoàn thành | DeliveredCollectAmount |

---

## Thay Đổi So Với Phiên Bản Trước

### ✅ Thêm mới

1. **Bộ lọc thời gian lấy hàng:**
   - `PickupStartDate`: Lọc theo thời gian lấy hàng thành công (bắt đầu)
   - `PickupEndDate`: Lọc theo thời gian lấy hàng thành công (kết thúc)

2. **Bộ lọc thời gian giao thành công:** ⭐ **MỚI**
   - `DeliveryStartDate`: Lọc theo thời gian giao thành công (bắt đầu)
   - `DeliveryEndDate`: Lọc theo thời gian giao thành công (kết thúc)

3. **Trạng thái mới:**
   - `Picked` (Đã lấy): Status=3, PhysicalStatus=13
   - `InWarehouse` (Nhập kho): Status=3, PhysicalStatus=20
   - `OutWarehouse` (Xuất kho): Status=3, PhysicalStatus=30

### ❌ Đã xóa

Các trạng thái sau đã bị xóa khỏi response:
- `PickupFailed` (Lấy hàng thất bại)
- `InTransit` (Đang trung chuyển)
- `DeliveryFailed` (Giao hàng thất bại)
- `WaitReturn` (Chờ trả về)
- `Returning` (Đang trả hàng)
- `Returned` (Đã trả về)
- `ReturnFailed` (Trả về thất bại)
- `LostOrDamaged` (Thất lạc/hỏng)
- `InStorage` (Lưu kho)

### 🔄 Thay đổi công thức tính toán

- `TotalInProgress`: Bây giờ bao gồm Picked, InWarehouse, OutWarehouse
- `TotalFailed`: Luôn trả về 0 (không còn trạng thái thất bại)
- `TotalCompleted`: Chỉ bao gồm Delivered (không còn Returned)

---

## Error Responses

### 400 Bad Request - Missing Date Filter
```json
{
  "Result": 1,
  "Message": "Vui lòng chọn ít nhất một bộ lọc: Ngày tạo đơn, Ngày lấy hàng hoặc Ngày giao thành công",
  "Items": [],
  "TotalRecord": 0
}
```

**Nguyên nhân:** Không cung cấp ít nhất một trong ba bộ lọc:
- Bộ lọc ngày tạo đơn (`StartDate` và `EndDate`)
- Bộ lọc ngày lấy hàng (`PickupStartDate` và `PickupEndDate`)
- Bộ lọc ngày giao thành công (`DeliveryStartDate` và `DeliveryEndDate`)

### 401 Unauthorized
```json
{
  "type": "https://tools.ietf.org/html/rfc7235#section-3.1",
  "title": "Unauthorized",
  "status": 401
}
```

### 403 Forbidden
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.3",
  "title": "Forbidden",
  "status": 403
}
```

### 500 Internal Server Error
```json
{
  "Result": 2,
  "Message": "Chi tiết lỗi hệ thống",
  "Items": [],
  "TotalRecord": 0
}
```

---

## Ví Dụ Sử Dụng

### Ví dụ 1: Lấy thống kê theo tháng 1/2026

```bash
curl -X POST "https://api.example.com/api/RevenueReport/OrderRevenueByDate" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "Search": {
      "PredicateObject": {
        "StartDate": "2026-01-01T00:00:00Z",
        "EndDate": "2026-01-31T23:59:59Z"
      }
    }
  }'
```

### Ví dụ 2: Lọc theo thời gian lấy hàng thành công

```bash
curl -X POST "https://api.example.com/api/RevenueReport/OrderRevenueByDate" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "Search": {
      "PredicateObject": {
        "PickupStartDate": "2026-01-05T00:00:00Z",
        "PickupEndDate": "2026-01-15T23:59:59Z"
      }
    }
  }'
```

### Ví dụ 3: Lọc theo thời gian giao thành công

```bash
curl -X POST "https://api.example.com/api/RevenueReport/OrderRevenueByDate" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "Search": {
      "PredicateObject": {
        "DeliveryStartDate": "2026-01-10T00:00:00Z",
        "DeliveryEndDate": "2026-01-20T23:59:59Z"
      }
    }
  }'
```

### Ví dụ 4: Lọc theo Sale cụ thể

```bash
curl -X POST "https://api.example.com/api/RevenueReport/OrderRevenueByDate" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "Search": {
      "PredicateObject": {
        "StartDate": "2026-01-01T00:00:00Z",
        "EndDate": "2026-01-31T23:59:59Z",
        "SaleId": 123
      }
    }
  }'
```

### Ví dụ 5: Kết hợp nhiều bộ lọc

```bash
curl -X POST "https://api.example.com/api/RevenueReport/OrderRevenueByDate" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "Search": {
      "PredicateObject": {
        "StartDate": "2026-01-01T00:00:00Z",
        "EndDate": "2026-01-31T23:59:59Z",
        "PickupStartDate": "2026-01-05T00:00:00Z",
        "PickupEndDate": "2026-01-25T23:59:59Z",
        "DeliveryStartDate": "2026-01-10T00:00:00Z",
        "DeliveryEndDate": "2026-01-30T23:59:59Z"
      }
    }
  }'
```

---

## Ghi Chú Kỹ Thuật

1. **Timezone:** Tất cả các ngày giờ đều được chuyển từ UTC sang GMT+7 (local time)
2. **Phân quyền:** User không có role "Founder" sẽ tự động chỉ xem được đơn của mình
3. **Doanh số:** Được tính bằng: `Fee + CodFee + PickupSubFee + DeliverySubFee - Discount`
4. **Đơn hủy:** Không được tính vào `TotalRevenue` và `TotalCollectAmount`, nhưng có số liệu riêng
5. **Stored Procedure:** Sử dụng `sp_GetOrderRevenueStatisticsByDate` với các tham số mới
