# Báo Cáo Đơn Hàng Theo Tuyến - sp_GetOrderReportByRoute

## Mô Tả

Stored procedure `sp_GetOrderReportByRoute` tổng hợp báo cáo số lượng đơn hàng và doanh số theo tuyến (từ địa chỉ gửi đến địa chỉ giao) và trạng thái đơn hàng.

## Tham Số Đầu Vào

| Tham số | Kiểu dữ liệu | Mô tả | Mặc định |
|---------|--------------|-------|----------|
| @FromDate | DATETIMEOFFSET | Ngày bắt đầu lọc (theo CreatedTime của Order) | Ngày đầu tháng hiện tại |
| @ToDate | DATETIMEOFFSET | Ngày kết thúc lọc | Ngày cuối tháng hiện tại |

## Dữ Liệu Trả Về

Stored procedure trả về một bảng với các cột sau:

| Cột | Kiểu dữ liệu | Mô tả |
|-----|--------------|-------|
| RouteName | NVARCHAR | Tên tuyến: "Tỉnh/Thành gửi - Tỉnh/Thành nhận" |
| SourceCityName | NVARCHAR | Tên tỉnh/thành phố gửi hàng |
| DestCityName | NVARCHAR | Tên tỉnh/thành phố nhận hàng |
| OrderStatusName | NVARCHAR | Tên trạng thái đơn hàng |
| StatusOrder | INT | Thứ tự sắp xếp trạng thái (1-6) |
| OrderCount | INT | Số lượng đơn hàng |
| TotalRevenue | DECIMAL | Tổng doanh số |
| AvgRevenue | DECIMAL | Doanh số trung bình |
| MinRevenue | DECIMAL | Doanh số thấp nhất |
| MaxRevenue | DECIMAL | Doanh số cao nhất |
| FromDate | DATETIMEOFFSET | Ngày bắt đầu (echo lại tham số) |
| ToDate | DATETIMEOFFSET | Ngày kết thúc (echo lại tham số) |

## Định Nghĩa Tuyến

**Tuyến** = Địa chỉ gửi - Địa chỉ giao

- **Địa chỉ gửi**: `OrderAddress` có `AddressType = 1` (Source)
- **Địa chỉ giao**: `OrderAddress` có `AddressType = 2` (Dest)
- Tuyến được xác định dựa trên **CityId** của địa chỉ gửi và giao
- Thông tin tỉnh/thành phố được lấy từ bảng `Location` với `Level = 1`

## Tính Toán Doanh Số

Doanh số được tính theo công thức:

```
Revenue = Fee + CodFee + PickupSubFee + DeliverySubFee - Discount
```

Với:
- `Fee`: Cước giao hàng chính
- `CodFee`: Phí thu hộ (COD)
- `PickupSubFee`: Phụ phí lấy hàng
- `DeliverySubFee`: Phụ phí giao hàng
- `Discount`: Giảm giá

## Trạng Thái Đơn Hàng

Stored procedure phân loại đơn hàng theo 6 trạng thái chính dựa trên `OrderOps.Status` và `OrderOps.PhysicalStatus`:

| Trạng thái | Status | PhysicalStatus | Mô tả |
|------------|--------|----------------|-------|
| 1. Chờ lấy hàng | 1 | 1 | Đơn hàng mới tạo, chờ lái xe đến lấy |
| 2. Đang lấy hàng | 1 | 12 | Lái xe đang trên đường đến lấy hàng |
| 3. Đã lấy hàng | 3 | 13 | Đã lấy hàng thành công |
| 4. Trung chuyển | 3 | 20, 30 | Đang trong quá trình vận chuyển giữa các chi nhánh |
| 5. Đang giao hàng | 3 | 53 | Lái xe đang trên đường giao hàng |
| 6. Đã giao hàng | 55 | 55 | Giao hàng thành công |

*Các đơn hàng có trạng thái khác sẽ được phân vào nhóm "Trạng thái khác"*

## Lưu Ý

1. **Chỉ tính đơn hàng có đủ địa chỉ**: Đơn hàng phải có cả địa chỉ gửi và địa chỉ giao mới được tính vào báo cáo
2. **Lọc theo IsDeleted**: Chỉ lấy đơn hàng chưa bị xóa (`IsDeleted = 0`)
3. **Loại trừ đơn hủy**: Đơn hàng có `Status = 8` (Cancelled) sẽ bị loại trừ khỏi báo cáo
4. **Lọc theo CreatedTime**: Dữ liệu được lọc theo thời gian tạo đơn hàng, không phải thời gian thay đổi trạng thái
5. **Thời gian mặc định**: Nếu không truyền tham số, sẽ tự động lấy dữ liệu của tháng hiện tại

## Cách Sử Dụng

### Ví dụ 1: Lấy báo cáo tháng hiện tại

```sql
EXEC sp_GetOrderReportByRoute;
```

### Ví dụ 2: Lấy báo cáo theo khoảng thời gian

```sql
DECLARE @FromDate DATETIMEOFFSET = '2025-01-01T00:00:00+07:00';
DECLARE @ToDate DATETIMEOFFSET = '2025-01-31T23:59:59+07:00';
EXEC sp_GetOrderReportByRoute @FromDate, @ToDate;
```

### Ví dụ 3: Lấy báo cáo quý 1

```sql
DECLARE @FromDate DATETIMEOFFSET = '2025-01-01T00:00:00+07:00';
DECLARE @ToDate DATETIMEOFFSET = '2025-03-31T23:59:59+07:00';
EXEC sp_GetOrderReportByRoute @FromDate, @ToDate;
```

### Ví dụ 4: Lấy báo cáo tháng trước

```sql
DECLARE @FromDate DATETIMEOFFSET = DATEADD(MONTH, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, SYSDATETIMEOFFSET()), 0));
DECLARE @ToDate DATETIMEOFFSET = DATEADD(MILLISECOND, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, SYSDATETIMEOFFSET()), 0));
EXEC sp_GetOrderReportByRoute @FromDate, @ToDate;
```

## Ứng Dụng

Stored procedure này có thể được sử dụng cho:

1. **Dashboard báo cáo**: Hiển thị tổng quan doanh số và số lượng đơn theo tuyến
2. **Phân tích tuyến đường**: Xác định các tuyến có lượng đơn hàng nhiều nhất
3. **Theo dõi trạng thái**: Giám sát tỷ lệ đơn hàng ở từng trạng thái
4. **Dự báo**: Dựa vào dữ liệu lịch sử để dự đoán xu hướng
5. **Tối ưu hóa**: Phân bổ tài nguyên (lái xe, xe tải) dựa trên dữ liệu tuyến

## Tích Hợp API

### Controller Endpoint (Gợi ý)

```csharp
[HttpPost("GetOrderReportByRoute")]
[Authorize(Roles = "Report.OrderByRoute")]
public async Task<IActionResult> GetOrderReportByRoute([FromBody] DateRangeModel model)
{
    var result = await _reportService.GetOrderReportByRoute(model.FromDate, model.ToDate);
    return Ok(result);
}
```

### Service Method (Gợi ý)

```csharp
public async Task<List<OrderReportByRouteItem>> GetOrderReportByRoute(DateTimeOffset? fromDate, DateTimeOffset? toDate)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        var parameters = new DynamicParameters();
        parameters.Add("@FromDate", fromDate);
        parameters.Add("@ToDate", toDate);
        
        var result = await connection.QueryAsync<OrderReportByRouteItem>(
            "sp_GetOrderReportByRoute",
            parameters,
            commandType: CommandType.StoredProcedure
        );
        
        return result.ToList();
    }
}
```

### DTO Model (Gợi ý)

```csharp
public class OrderReportByRouteItem
{
    public string RouteName { get; set; }
    public string SourceCityName { get; set; }
    public string DestCityName { get; set; }
    public string OrderStatusName { get; set; }
    public int StatusOrder { get; set; }
    public int OrderCount { get; set; }
    public decimal TotalRevenue { get; set; }
    public decimal AvgRevenue { get; set; }
    public decimal MinRevenue { get; set; }
    public decimal MaxRevenue { get; set; }
    public DateTimeOffset FromDate { get; set; }
    public DateTimeOffset ToDate { get; set; }
}
```

## Performance

- Sử dụng CTE (Common Table Expression) để tối ưu query
- Index đề xuất:
  - `Order.CreatedTime`
  - `Order.IsDeleted`
  - `OrderAddress.OrderId, AddressType`
  - `OrderAddress.CityId`
  - `Location.Id, Level`
  - `OrderOps.Id, Status, PhysicalStatus`
  - `OrderFin.Id`

## Changelog

| Ngày | Phiên bản | Mô tả |
|------|-----------|-------|
| 2025-01-06 | 1.0 | Phiên bản đầu tiên - Sử dụng `CreatedTime`, bảng `Location`, loại trừ đơn hủy (Status = 8) |

## Liên Hệ & Hỗ Trợ

Nếu có câu hỏi hoặc phát hiện lỗi, vui lòng liên hệ team phát triển.

