# API Documentation - VAT Invoice Management

## Mục lục
1. [Quản lý Hóa đơn VAT](#1-quản-lý-hóa-đơn-vat)
2. [Đồng bộ Hóa đơn](#2-đồng-bộ-hóa-đơn)
3. [Models & Enums](#3-models--enums)

---

## 1. Quản lý Hóa đơn VAT

### 1.1. Tìm kiếm hóa đơn VAT

**Endpoint:** `POST /api/VatInvoice/Search`

**Authorization:** `VatInvoice.Search`

**Request Body:**
```json
{
  "Pagination": {
    "PageIndex": 1,
    "PageSize": 15
  },
  "Search": {
    "PredicateObject": {
      "Keyword": "CÔNG TY",
      "FromDate": "2026-01-01T00:00:00+07:00",
      "ToDate": "2026-02-16T23:59:59+07:00",
      "SellerTaxCode": "0100109106",
      "BuyerTaxCode": "0312345678",
      "Status": 1,
      "InvoiceDirection": 2,
      "InvoiceCheckResult": 5
    }
  },
  "Sort": {
    "Predicate": "SyncTime",
    "Reverse": true
  }
}
```

**Search Parameters:**
- `Keyword` (string, optional): Tìm kiếm theo số hóa đơn, tên/MST người bán, tên/MST người mua
- `FromDate` (DateTimeOffset, optional): Lọc hóa đơn từ ngày lập
- `ToDate` (DateTimeOffset, optional): Lọc hóa đơn đến ngày lập
- `SellerTaxCode` (string, optional): Lọc theo MST người bán (exact match)
- `BuyerTaxCode` (string, optional): Lọc theo MST người mua (exact match)
- `Status` (int, optional): Lọc theo tình trạng hóa đơn
- `InvoiceDirection` (int, optional): Lọc theo chiều hóa đơn (1=Bán ra, 2=Mua vào)
- `InvoiceCheckResult` (int, optional): Lọc theo kết quả kiểm tra hóa đơn (5, 6, 8). Chỉ áp dụng cho hóa đơn mua vào

> [!NOTE]
> **Lọc theo thời gian**
> - `FromDate` và `ToDate` phải được truyền cùng nhau
> - Nếu không truyền, hệ thống sẽ trả về tất cả hóa đơn (không filter theo ngày)

**Response:**
```json
{
  "Items": [
    {
      "Id": 1,
      "TemplateCode": 1,
      "InvoiceSymbol": "K23TAA",
      "InvoiceNumber": "00012345",
      "IssueDate": "2026-02-10T00:00:00+07:00",
      "SellerTaxCode": "0100109106",
      "SellerName": "CÔNG TY XĂNG DẦU ABC",
      "BuyerTaxCode": "0312345678",
      "BuyerName": "CÔNG TY VẬN TẢI XYZ",
      "AmountBeforeTax": 5000000,
      "TaxAmount": 400000,
      "TotalAmount": 5400000,
      "Currency": "VND",
      "Status": 1,
      "StatusName": "Hóa đơn mới",
      "Note": "",
      "SyncTime": "2026-02-13T03:00:15+07:00",
      "InvoiceDirection": 2,
      "InvoiceDirectionName": "Mua vào",
      "InvoiceCheckResult": 5,
      "InvoiceCheckResultName": "Đã cấp mã hóa đơn"
    }
  ],
  "TotalRecord": 1,
  "NumberOfPages": 1
}
```

**Sort Options:**
- `SyncTime` (default) - Theo thời gian đồng bộ
- `IssueDate` - Theo ngày lập hóa đơn
- `InvoiceNumber` - Theo số hóa đơn
- `TotalAmount` - Theo tổng thanh toán
- `SellerName` - Theo tên người bán
- `BuyerName` - Theo tên người mua

**Response Fields:**

| Field | Type | Mô tả |
|-------|------|-------|
| `Id` | int | ID bản ghi |
| `TemplateCode` | int | Ký hiệu mẫu số (khmshdon) |
| `InvoiceSymbol` | string | Ký hiệu hóa đơn (khhdon) |
| `InvoiceNumber` | string | Số hóa đơn (shdon) |
| `IssueDate` | DateTimeOffset | Ngày lập (tdlap) |
| `SellerTaxCode` | string | MST người bán (nbmst) |
| `SellerName` | string | Tên người bán (nbten) |
| `BuyerTaxCode` | string | MST người mua (nmmst) |
| `BuyerName` | string | Tên người mua (nmten) |
| `AmountBeforeTax` | decimal | Tổng tiền hàng chưa thuế (tgtcthue) |
| `TaxAmount` | decimal | Tổng tiền thuế (tgtthue) |
| `TotalAmount` | decimal | Tổng thanh toán (tgtttbso) |
| `Currency` | string | Đơn vị tiền tệ (dvtte) |
| `Status` | int | Tình trạng hóa đơn (tthai) |
| `StatusName` | string | Tên trạng thái: "Hóa đơn mới", "Hóa đơn thay thế", "Hóa đơn điều chỉnh"... |
| `Note` | string | Ghi chú (gchu) |
| `SyncTime` | DateTimeOffset | Thời gian đồng bộ từ API |
| `InvoiceDirection` | int | Chiều hóa đơn: 1=Bán ra, 2=Mua vào |
| `InvoiceDirectionName` | string | Tên hiển thị: "Bán ra" hoặc "Mua vào" |
| `InvoiceCheckResult` | int | Kết quả kiểm tra hóa đơn (ttxly) |
| `InvoiceCheckResultName` | string | Tên kết quả kiểm tra |

> [!TIP]
> **Hiển thị danh sách theo cấu trúc bảng Excel**
> Các cột hiển thị theo thứ tự: STT, Chiều HĐ, Ký hiệu mẫu số, Ký hiệu HĐ, Số HĐ, Ngày lập, MST người bán, Tên người bán, MST người mua, Tên người mua, Tổng tiền hàng, Tổng tiền thuế, Tổng thanh toán, Đơn vị tiền, Tình trạng, Ghi chú

---

## 2. Đồng bộ Hóa đơn

### 2.1. Đồng bộ thủ công từ API Tổng cục Thuế

**Endpoint:** `POST /api/VatInvoice/SyncManual`

**Authorization:** `VatInvoice.Sync`

**Mô tả:** Đồng bộ hóa đơn VAT từ API Tổng cục Thuế theo khoảng thời gian chỉ định. Hỗ trợ cả hóa đơn **bán ra** và **mua vào**.

**Request Body (Hóa đơn bán ra - mặc định):**
```json
{
  "FromDate": "2026-02-01T00:00:00+07:00",
  "ToDate": "2026-02-16T23:59:59+07:00"
}
```

**Request Body (Hóa đơn mua vào):**
```json
{
  "FromDate": "2026-02-01T00:00:00+07:00",
  "ToDate": "2026-02-16T23:59:59+07:00",
  "InvoiceDirection": 2
}
```

**Request Parameters:**
- `FromDate` (DateTimeOffset, required): Ngày bắt đầu khoảng thời gian cần đồng bộ
- `ToDate` (DateTimeOffset, required): Ngày kết thúc khoảng thời gian cần đồng bộ
- `InvoiceDirection` (int, optional): Chiều hóa đơn. `1` = Bán ra (mặc định), `2` = Mua vào

> [!IMPORTANT]
> **Validation**
> - `FromDate` phải nhỏ hơn hoặc bằng `ToDate`
> - Khoảng thời gian tối đa là **30 ngày** (`ToDate - FromDate <= 30 ngày`)
> - Nếu vi phạm, API trả về lỗi 400 Bad Request

> [!NOTE]
> **Cơ chế đồng bộ theo chiều hóa đơn**
> - `InvoiceDirection = 1` (Bán ra): Gọi API `/query/invoices/sold` (1 lần)
> - `InvoiceDirection = 2` (Mua vào): Gọi API `/query/invoices/purchase` **3 lần** với các filter:
>   - `ttxly==5` (Đã cấp mã hóa đơn)
>   - `ttxly==6` (Cục Thuế đã nhận không mã)
>   - `ttxly==8` (Cục Thuế đã nhận HĐ có mã khởi tạo từ máy tính tiền)
> - Mỗi lần gọi đều hỗ trợ **phân trang tự động** (cursor-based pagination dùng `state` token), lấy hết toàn bộ dữ liệu
> - Hóa đơn được phân biệt bằng `ExternalId + InvoiceDirection`, nên một `ExternalId` có thể tồn tại ở cả 2 chiều
> - Giá trị `ttxly` được lưu vào trường `InvoiceCheckResult` để phục vụ lọc

**Response (Success):**
```json
{
  "Result": 1,
  "Message": "Đồng bộ thành công 5 hóa đơn mới và cập nhật 3 hóa đơn"
}
```

**Response (No Data):**
```json
{
  "Result": 1,
  "Message": "Không có hóa đơn nào trong khoảng 2026-02-01 đến 2026-02-16"
}
```

**Response (Error - Token):**
```json
{
  "Result": 2,
  "Message": "Chưa cấu hình Bearer Token cho VAT API. Vui lòng kiểm tra AppConfig với key 'VatApi.BearerToken'"
}
```

**Response (Error - API):**
```json
{
  "Result": 2,
  "Message": "API call failed with status code: Unauthorized"
}
```

> [!NOTE]
> **Đồng bộ tự động**
> - Hệ thống tự động đồng bộ **cả hóa đơn bán ra và mua vào** của ngày hôm qua vào **lúc 3:00 AM hàng ngày** qua Hangfire job
> - Job bán ra: `SyncYesterdayInvoices()` 
> - Job mua vào: `SyncYesterdayPurchaseInvoices()`
> - Có thể kiểm tra trạng thái job trên Hangfire Dashboard (`/hangfire`)

> [!IMPORTANT]
> **Lịch sử đồng bộ**
> - Mỗi lần đồng bộ (cả thủ công và tự động) đều được lưu lại lịch sử
> - Lịch sử bao gồm: thời gian, số lượng, trạng thái, lỗi (nếu có), chiều hóa đơn
> - Sử dụng API `GetLastSyncInfo` để lấy thông tin đồng bộ gần nhất

---

### 2.2. Lấy thông tin đồng bộ gần nhất

**Endpoint:** `GET /api/VatInvoice/GetLastSyncInfo`

**Authorization:** `VatInvoice.Search`

**Mô tả:** Lấy thông tin lần đồng bộ gần nhất. Frontend dùng API này để hiển thị trạng thái sync trên giao diện.

**Response (Có lịch sử):**
```json
{
  "HasSyncHistory": true,
  "LastSyncTime": "2026-02-16T03:00:15+07:00",
  "SyncDateRange": "15/02/2026 - 16/02/2026",
  "Status": "Success",
  "InsertedCount": 5,
  "UpdatedCount": 3,
  "TotalCount": 8,
  "DurationSeconds": 2.5,
  "SyncType": "Auto",
  "ErrorMessage": null,
  "Message": "Lần đồng bộ gần nhất: 16/02/2026 03:00:15 (Auto)"
}
```

**Response (Chưa có lịch sử):**
```json
{
  "HasSyncHistory": false,
  "LastSyncTime": null,
  "SyncDateRange": null,
  "Status": null,
  "InsertedCount": 0,
  "UpdatedCount": 0,
  "TotalCount": 0,
  "DurationSeconds": 0,
  "SyncType": null,
  "ErrorMessage": null,
  "Message": "Chưa có lịch sử đồng bộ nào"
}
```

**Response (Lần sync gần nhất thất bại):**
```json
{
  "HasSyncHistory": true,
  "LastSyncTime": "2026-02-16T03:00:15+07:00",
  "SyncDateRange": "15/02/2026 - 16/02/2026",
  "Status": "Failed",
  "InsertedCount": 0,
  "UpdatedCount": 0,
  "TotalCount": 0,
  "DurationSeconds": 1.2,
  "SyncType": "Auto",
  "ErrorMessage": "API call failed with status code: Unauthorized",
  "Message": "Lần đồng bộ gần nhất thất bại: 16/02/2026 03:00:15"
}
```

**Response Fields:**

| Field | Type | Mô tả |
|-------|------|-------|
| `HasSyncHistory` | bool | `true` nếu đã có ít nhất 1 lần đồng bộ |
| `LastSyncTime` | DateTimeOffset? | Thời gian đồng bộ gần nhất |
| `SyncDateRange` | string | Khoảng ngày đã đồng bộ (dd/MM/yyyy - dd/MM/yyyy) |
| `Status` | string | Trạng thái: `Success`, `Failed`, `InProgress` |
| `InsertedCount` | int | Số hóa đơn mới được thêm |
| `UpdatedCount` | int | Số hóa đơn được cập nhật |
| `TotalCount` | int | Tổng số hóa đơn xử lý (InsertedCount + UpdatedCount) |
| `DurationSeconds` | double | Thời gian thực hiện (giây) |
| `SyncType` | string | Loại đồng bộ: `Auto` (Hangfire) hoặc `Manual` (User) |
| `ErrorMessage` | string? | Thông báo lỗi chi tiết (null nếu thành công) |
| `Message` | string | Thông báo ngắn gọn cho người dùng |

> [!TIP]
> **Gợi ý hiển thị trên UI**
> - Kiểm tra `HasSyncHistory` trước khi hiển thị thông tin
> - Dùng `Status` để hiển thị icon/badge trạng thái (✅ Success, ❌ Failed, ⏳ InProgress)
> - Hiển thị `Message` cho người dùng
> - Nếu `Status == "Failed"`, hiển thị `ErrorMessage` để admin khắc phục

---

## 3. Models & Enums

### 3.1. Chiều hóa đơn (InvoiceDirection)

| Giá trị | Mô tả | API Endpoint | Filter bổ sung |
|---------|-------|-------------|----------------|
| `1` | Bán ra (Sold) | `/query/invoices/sold` | Không |
| `2` | Mua vào (Purchase) | `/query/invoices/purchase` | `ttxly==5`, `ttxly==6`, `ttxly==8` (gọi 3 lần) |

> [!NOTE]
> **Ý nghĩa**
> - **Bán ra**: Hóa đơn mà công ty xuất cho khách hàng. MST của công ty nằm ở trường `SellerTaxCode` (nbmst).
> - **Mua vào**: Hóa đơn mà đối tác/nhà cung cấp xuất cho công ty. MST của công ty nằm ở trường `BuyerTaxCode` (nmmst).

### 3.2. Kết quả kiểm tra hóa đơn (InvoiceCheckResult / ttxly)

**Hóa đơn bán ra (InvoiceDirection = 1):**

| Giá trị | Mô tả |
|---------|-------|
| `0` | Cục Thuế đã nhận |
| `1` | Đang tiến hành kiểm tra điều kiện cấp mã |
| `2` | CQT từ chối hóa đơn theo từng lần phát sinh |
| `3` | Hóa đơn đủ điều kiện cấp mã |
| `4` | Hóa đơn không đủ điều kiện cấp mã |
| `5` | Đã cấp mã hóa đơn |
| `6` | Cục Thuế đã nhận không mã |
| `7` | Đã kiểm tra định kỳ HĐĐT không có mã |
| `8` | Cục Thuế đã nhận HĐ có mã khởi tạo từ máy tính tiền |

**Hóa đơn mua vào (InvoiceDirection = 2):**

| Giá trị | Mô tả |
|---------|-------|
| `5` | Đã cấp mã hóa đơn |
| `6` | Cục Thuế đã nhận không mã |
| `8` | Cục Thuế đã nhận HĐ có mã khởi tạo từ máy tính tiền |

> [!NOTE]
> **Giá trị `InvoiceCheckResult`** tương ứng với tham số `ttxly` trong API GDT. Giá trị được lấy trực tiếp từ dữ liệu mỗi hóa đơn khi đồng bộ.

### 3.3. Tình trạng hóa đơn (Status / tthai)

| Giá trị | Mô tả |
|---------|-------|
| `1` | Hóa đơn mới |
| `2` | Hóa đơn thay thế |
| `3` | Hóa đơn điều chỉnh |
| `4` | Hóa đơn đã bị thay thế |
| `5` | Hóa đơn đã bị điều chỉnh |
| `6` | Hóa đơn đã bị hủy |

> [!NOTE]
> **Giá trị Status** được lấy từ API Tổng cục Thuế (trường `tthai`). Giá trị cụ thể do TCT quy định.

### 3.4. Loại đồng bộ (SyncType)

| Giá trị | Mô tả |
|---------|-------|
| `Auto` | Đồng bộ tự động bởi Hangfire (3:00 AM hàng ngày) |
| `Manual` | Đồng bộ thủ công bởi người dùng |

### 3.5. Trạng thái đồng bộ (Sync Status)

| Giá trị | Mô tả |
|---------|-------|
| `InProgress` | Đang đồng bộ |
| `Success` | Đồng bộ thành công |
| `Failed` | Đồng bộ thất bại |

### 3.6. Authorization Roles

| Role | Mô tả | Dùng cho |
|------|-------|----------|
| `VatInvoice.Search` | Tìm kiếm hóa đơn VAT | Search, GetLastSyncInfo |
| `VatInvoice.Sync` | Đồng bộ hóa đơn VAT | SyncManual |

---

## 4. Gợi ý UI/UX

### 4.1. Màn hình danh sách hóa đơn VAT

```
┌─────────────────────────────────────────────────────────┐
│  🧾 Hóa đơn VAT điện tử                                │
│                                                         │
│  🟢 Lần đồng bộ gần nhất: 16/02/2026 03:00 (Auto)     │
│  [🔄 Đồng bộ bán ra]  [🔄 Đồng bộ mua vào]            │
│                                                         │
│  ┌─── Bộ lọc ──────────────────────────────────────┐   │
│  │ Từ khóa: [___________]  Từ ngày: [__/__/____]   │   │
│  │ MST NB:  [___________]  Đến ngày: [__/__/____]  │   │
│  │ MST NM:  [___________]  Tình trạng: [Tất cả ▾]  │   │
│  │ Chiều HĐ: [Tất cả ▾]       [🔍 Tìm kiếm]      │   │
│  └──────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─── Bảng dữ liệu ───────────────────────────────┐   │
│  │ STT │ Chiều │ Mẫu số │ Ký hiệu │ Số HĐ │ ...  │   │
│  │  1  │Bán ra │   1    │ K23TAA  │ 12345 │ ...   │   │
│  │  2  │Mua vào│   1    │ C26TSH  │   13  │ ...   │   │
│  └──────────────────────────────────────────────────┘   │
│                                                         │
│  Trang 1/5  [◀] [1] [2] [3] ... [▶]                   │
└─────────────────────────────────────────────────────────┘
```

### 4.2. Dialog đồng bộ thủ công

```
┌─── Đồng bộ hóa đơn ──────────────────┐
│                                        │
│  Chiều hóa đơn: [Bán ra ▾]           │
│  Từ ngày:       [01/02/2026]          │
│  Đến ngày:      [16/02/2026]          │
│                                        │
│           [Hủy]  [Đồng bộ]            │
└────────────────────────────────────────┘
```

### 4.3. Flow đồng bộ

```
1. User nhấn "Đồng bộ bán ra" hoặc "Đồng bộ mua vào"
2. Hiển thị dialog chọn khoảng ngày (chiều HĐ đã chọn sẵn)
3. Gọi POST /api/VatInvoice/SyncManual với InvoiceDirection tương ứng
4. Hiển thị loading/spinner
5. Khi nhận response:
   - Result = 1: Hiển thị toast success + reload danh sách
   - Result = 2: Hiển thị toast error với Message
6. Gọi GET /api/VatInvoice/GetLastSyncInfo để cập nhật trạng thái
```
