# API: Tạo Phiếu Đối Soát Khách Hàng

## Thông Tin Cơ Bản

**Endpoint:** `POST /api/CustomerReconciliationSlip/Create`

**Mô tả:** API này tạo phiếu đối soát cho khách hàng dựa trên danh sách đơn hàng được chọn. Phiếu đối soát được tạo sẽ có trạng thái ban đầu là "Chờ duyệt" (PendingApproval) và cần được kế toán phê duyệt trước khi hoàn tất.

**Yêu cầu xác thực:** Có (Bearer Token)

**Quyền cần thiết:** Một trong các quyền sau:
- `CustomerReconciliationSlip.Create`
- `Founder`

---

## Request

### Headers
```json
{
  "Authorization": "Bearer {access_token}",
  "Content-Type": "application/json"
}
```

### Body Parameters

```json
{
  "CustomerId": 125,
  "OrderIds": [10523, 10524, 10530, 10531],
  "InternalNote": "Đối soát tháng 11/2024 - Đợt 1"
}
```

#### Chi tiết các tham số:

| Tham số | Kiểu | Bắt buộc | Mô tả |
|---------|------|----------|-------|
| `CustomerId` | `int` | Có | ID của khách hàng cần tạo phiếu đối soát |
| `OrderIds` | `int[]` | Có | Danh sách ID các đơn hàng được chọn để đối soát (tối thiểu 1 đơn) |
| `InternalNote` | `string` | Không | Ghi chú nội bộ về phiếu đối soát (mặc định: chuỗi rỗng) |

---

## Response

### Success Response (200 OK)

```json
{
  "Id": 1234,
  "Message": "Tạo phiếu thành công"
}
```

### Response Fields

| Trường | Kiểu | Mô tả |
|--------|------|-------|
| `Id` | `int` | ID của phiếu đối soát vừa được tạo |
| `Message` | `string` | Thông báo thành công |

---

## Error Response

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

**Nguyên nhân:** Token không hợp lệ hoặc đã hết hạn

---

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

**Nguyên nhân:** User không có quyền `CustomerReconciliationSlip.Create` hoặc `Founder`

---

### 400 Bad Request - Validation Error
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "CustomerId": [
      "The CustomerId field is required."
    ],
    "OrderIds": [
      "The OrderIds field is required."
    ]
  },
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** Thiếu các trường bắt buộc hoặc dữ liệu không hợp lệ

---

### 500 Internal Server Error - Khách hàng không tồn tại
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "detail": "Khách hàng không tồn tại",
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** `CustomerId` không tồn tại trong hệ thống

---

### 500 Internal Server Error - Không có quyền tạo phiếu
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "detail": "Bạn chỉ được tạo phiếu cho khách hàng mình quản lý",
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** User không phải Founder và không phải là Manager của khách hàng này

---

### 500 Internal Server Error - Đơn hàng không tồn tại
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "detail": "Một số đơn hàng không tồn tại",
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** Một hoặc nhiều `OrderId` trong danh sách không tồn tại

---

### 500 Internal Server Error - Đơn hàng không thuộc khách hàng
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "detail": "Đơn hàng không thuộc khách hàng này",
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** Có đơn hàng trong danh sách không thuộc về khách hàng được chỉ định

---

### 500 Internal Server Error - Đơn hàng đã bị khóa
```json
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "detail": "Đơn hàng 10523, 10524 đã nằm trong phiếu đối soát khác",
  "traceId": "00-xxxxx"
}
```

**Nguyên nhân:** Một hoặc nhiều đơn hàng đã nằm trong phiếu đối soát khác (trừ phiếu bị từ chối)

---

## Business Logic

### 1. Validation và Phân Quyền

#### Kiểm tra khách hàng:
- Khách hàng phải tồn tại trong hệ thống
- User phải có quyền tạo phiếu cho khách hàng này:
  - **Founder**: Có quyền tạo phiếu cho tất cả khách hàng
  - **Sale/Manager**: Chỉ được tạo phiếu cho khách hàng mình quản lý (`customer.ManagerId == currentUser.UserId`)

#### Kiểm tra đơn hàng:
- Tất cả đơn hàng trong `OrderIds` phải tồn tại
- Tất cả đơn hàng phải thuộc về khách hàng được chỉ định (`order.CustomerId == model.CustomerId`)

#### Kiểm tra locking (đơn hàng đã bị khóa):
- Đơn hàng không được nằm trong bất kỳ phiếu đối soát nào có trạng thái khác `Rejected`
- Nếu đơn hàng nằm trong phiếu bị từ chối (`Status = Rejected`), đơn hàng đó được "giải phóng" và có thể tạo phiếu mới

### 2. Tính Toán Tự Động

Hệ thống tự động tính toán các giá trị sau:

| Trường | Công thức | Mô tả |
|--------|-----------|-------|
| `TotalCodAmount` | `SUM(OrderFin.CollectAmount)` | Tổng tiền COD của tất cả đơn hàng |
| `TotalFeeAmount` | `SUM(OrderFin.TotalFee)` | Tổng phí vận chuyển của tất cả đơn hàng |
| `FinalAmount` | `ABS(TotalCodAmount - TotalFeeAmount)` | Số tiền cuối cùng (giá trị tuyệt đối) |
| `Type` | `TotalCodAmount >= TotalFeeAmount ? Receivable : Payable` | Loại phiếu |
| `OrderCount` | `COUNT(OrderIds)` | Số lượng đơn hàng trong phiếu |

#### Loại phiếu (Type):
- **Receivable** (Phải thu): Khi `TotalCodAmount >= TotalFeeAmount` → Công ty nợ khách hàng
- **Payable** (Phải trả): Khi `TotalCodAmount < TotalFeeAmount` → Khách hàng nợ công ty

### 3. Tạo Phiếu Đối Soát

#### Thông tin phiếu chính (CustomerReconciliationSlip):
- **Code**: Tự động sinh theo format `CR-YYMMDD-XXXX` (VD: `CR-241208-A3F2`)
  - `CR`: Customer Reconciliation
  - `YYMMDD`: Ngày tạo
  - `XXXX`: 4 ký tự ngẫu nhiên (uppercase)
- **Status**: `PendingApproval` (Chờ duyệt)
- **CreatedBy**: ID của user tạo phiếu
- **CreatedByUserName**: Username của user tạo phiếu
- **CreatedTime**: Thời điểm tạo phiếu

#### Chi tiết đơn hàng (CustomerReconciliationSlipDetail):
Với mỗi đơn hàng trong `OrderIds`, hệ thống tạo một bản ghi chi tiết bao gồm:

| Trường | Nguồn dữ liệu | Mô tả |
|--------|---------------|-------|
| `OrderId` | `order.Id` | ID đơn hàng |
| `OrderCode` | `order.Code` | Mã đơn hàng |
| `PartnerCode` | `order.PartnerCode` | Mã vận đơn đối tác |
| `AppliedCodAmount` | `order.OrderFin.CollectAmount` | Tiền COD áp dụng |
| `AppliedFeeAmount` | `order.OrderFin.TotalFee` | Phí vận chuyển áp dụng |
| `NetAmount` | `CollectAmount - TotalFee` | Số tiền ròng |
| `ChargeWeight` | `order.OrderFin.ChargeWeight` | Trọng lượng tính cước |
| `ShippingFee` | `order.OrderFin.Fee` | Phí vận chuyển cơ bản |
| `DeliveryAddress` | `order.Addresses[Dest].Address1` | Địa chỉ giao hàng |
| `ReceiverName` | `order.Addresses[Dest].Name` | Tên người nhận |
| `ReceiverPhone` | `order.Addresses[Dest].PhoneNumber1` | SĐT người nhận |

### 4. Ghi Log

Sau khi tạo phiếu thành công, hệ thống tự động ghi log:
- **ActionType**: `Create`
- **Description**: "Tạo mới phiếu đối soát"
- **UserId**: ID của user thực hiện
- **UserName**: Username của user thực hiện
- **Timestamp**: Thời điểm thực hiện

---

## Ví dụ sử dụng

### Example 1: Tạo phiếu đối soát cơ bản

**Request:**
```bash
curl -X POST "https://api.yourdomain.com/api/CustomerReconciliationSlip/Create" \
  -H "Authorization: Bearer {your_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "CustomerId": 125,
    "OrderIds": [10523, 10524, 10530],
    "InternalNote": "Đối soát tháng 11/2024"
  }'
```

**Response:**
```json
{
  "Id": 1234,
  "Message": "Tạo phiếu thành công"
}
```

---

### Example 2: Tạo phiếu không có ghi chú

**Request:**
```bash
curl -X POST "https://api.yourdomain.com/api/CustomerReconciliationSlip/Create" \
  -H "Authorization: Bearer {your_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "CustomerId": 256,
    "OrderIds": [10601, 10602, 10603, 10604, 10605]
  }'
```

**Response:**
```json
{
  "Id": 1235,
  "Message": "Tạo phiếu thành công"
}
```

---

### Example 3: Frontend integration (JavaScript)

```javascript
// Service function để tạo phiếu đối soát
async function createReconciliationSlip(customerId, orderIds, internalNote = '') {
  try {
    const response = await fetch(
      '/api/CustomerReconciliationSlip/Create',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${getAccessToken()}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          CustomerId: customerId,
          OrderIds: orderIds,
          InternalNote: internalNote
        })
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.detail || 'Tạo phiếu thất bại');
    }

    const result = await response.json();
    console.log(`Tạo phiếu thành công! ID: ${result.Id}`);
    
    return result;
  } catch (error) {
    console.error('Failed to create reconciliation slip:', error);
    throw error;
  }
}

// Sử dụng
const selectedOrderIds = [10523, 10524, 10530];
const result = await createReconciliationSlip(
  125, 
  selectedOrderIds, 
  'Đối soát tháng 11/2024 - Đợt 1'
);

// Chuyển hướng đến trang chi tiết phiếu
window.location.href = `/reconciliation/detail/${result.Id}`;
```

---

### Example 4: Frontend integration với validation

```javascript
// Component Vue.js
async function handleCreateReconciliation() {
  // Validation
  if (!selectedCustomerId) {
    this.$message.error('Vui lòng chọn khách hàng');
    return;
  }

  if (selectedOrderIds.length === 0) {
    this.$message.error('Vui lòng chọn ít nhất 1 đơn hàng');
    return;
  }

  // Confirm
  const confirmed = await this.$confirm(
    `Bạn có chắc muốn tạo phiếu đối soát cho ${selectedOrderIds.length} đơn hàng?`,
    'Xác nhận',
    {
      confirmButtonText: 'Tạo phiếu',
      cancelButtonText: 'Hủy',
      type: 'warning'
    }
  ).catch(() => false);

  if (!confirmed) return;

  // Loading
  this.loading = true;

  try {
    const result = await createReconciliationSlip(
      selectedCustomerId,
      selectedOrderIds,
      internalNote
    );

    this.$message.success('Tạo phiếu đối soát thành công!');
    
    // Redirect to detail page
    this.$router.push({
      name: 'CustomerReconciliationDetail',
      params: { id: result.Id }
    });
  } catch (error) {
    this.$message.error(error.message || 'Tạo phiếu thất bại');
  } finally {
    this.loading = false;
  }
}
```

---

## TypeScript Interface

```typescript
// Request Model
interface CreateCustomerReconciliationSlipModel {
  CustomerId: number;
  OrderIds: number[];
  InternalNote?: string;
}

// Response Model
interface CreateReconciliationSlipResponse {
  Id: number;
  Message: string;
}

// API Function
async function createReconciliationSlip(
  model: CreateCustomerReconciliationSlipModel
): Promise<CreateReconciliationSlipResponse> {
  const response = await fetch('/api/CustomerReconciliationSlip/Create', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${getAccessToken()}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(model)
  });

  if (!response.ok) {
    throw new Error('Failed to create reconciliation slip');
  }

  return response.json();
}
```

---

## Workflow

API này thường được sử dụng trong quy trình sau:

1. **Bước 1:** Gọi `GetCustomersOverview` để lấy danh sách khách hàng cần đối soát
2. **Bước 2:** User chọn một khách hàng từ danh sách
3. **Bước 3:** Gọi `GetReconciliableOrders` để lấy danh sách đơn hàng có thể đối soát của khách hàng đó
4. **Bước 4:** User chọn các đơn hàng cần đối soát (có thể chọn tất cả hoặc một phần)
5. **Bước 5:** User nhập ghi chú nội bộ (optional)
6. **Bước 6:** Gọi `Create` API này để tạo phiếu đối soát
7. **Bước 7:** Phiếu được tạo với trạng thái "Chờ duyệt", chờ kế toán phê duyệt
8. **Bước 8:** Kế toán gọi `Approve` hoặc `Reject` để xử lý phiếu
9. **Bước 9:** Nếu được duyệt, gọi `Complete` để xác nhận thanh toán

---

## Notes

- **Định dạng mã phiếu:** `CR-YYMMDD-XXXX` (VD: `CR-241208-A3F2`)
- **Trạng thái ban đầu:** Luôn là `PendingApproval` (Chờ duyệt)
- **Locking mechanism:** Đơn hàng trong phiếu sẽ bị "khóa" và không thể tạo phiếu mới cho đến khi phiếu hiện tại bị từ chối
- **Atomic operation:** Toàn bộ quá trình tạo phiếu được thực hiện trong một transaction, đảm bảo tính nhất quán dữ liệu
- **Audit trail:** Mọi thao tác đều được ghi log để truy vết
- **Security:** 
  - API yêu cầu Bearer Token hợp lệ
  - Phân quyền theo role và manager assignment
  - Validate ownership trước khi cho phép thao tác
- **Performance:** 
  - Sử dụng `Include()` để eager load các navigation properties cần thiết
  - Bulk insert cho chi tiết đơn hàng
  - Transaction để đảm bảo data integrity

---

## Validation Rules Summary

| Rule | Error Message |
|------|---------------|
| CustomerId required | "The CustomerId field is required." |
| OrderIds required | "The OrderIds field is required." |
| Customer not found | "Khách hàng không tồn tại" |
| Permission denied | "Bạn chỉ được tạo phiếu cho khách hàng mình quản lý" |
| Order count mismatch | "Một số đơn hàng không tồn tại" |
| Order ownership | "Đơn hàng không thuộc khách hàng này" |
| Order locked | "Đơn hàng {ids} đã nằm trong phiếu đối soát khác" |

---

## Change Log

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2024-12-08 | Development Team | Initial release |

---

## Related APIs

- `POST /api/CustomerReconciliationSlip/GetCustomersOverview` - Lấy danh sách khách hàng cần đối soát
- `GET /api/CustomerReconciliationSlip/GetReconciliableOrders` - Lấy danh sách đơn hàng có thể đối soát
- `GET /api/CustomerReconciliationSlip/GetById/{id}` - Lấy chi tiết phiếu đối soát
- `POST /api/CustomerReconciliationSlip/Search` - Tìm kiếm phiếu đối soát
- `PUT /api/CustomerReconciliationSlip/Approve/{id}` - Duyệt phiếu đối soát
- `POST /api/CustomerReconciliationSlip/Reject` - Từ chối phiếu đối soát
- `POST /api/CustomerReconciliationSlip/Complete` - Hoàn thành phiếu (xác nhận thanh toán)
