# Tax Invoice APIs Documentation

## Overview
This document describes the APIs for Tax Invoice management, including creating, updating status, retrieving, and deleting tax invoices for Customer Reconciliation Slips.

---

## Table of Contents
1. [Create Tax Invoice](#1-create-tax-invoice)
2. [Update Tax Invoice Status](#2-update-tax-invoice-status)
3. [Get Tax Invoice by ID](#3-get-tax-invoice-by-id)
4. [Get Tax Invoice by Slip ID](#4-get-tax-invoice-by-slip-id)
5. [Delete Tax Invoice](#5-delete-tax-invoice)
6. [Data Models](#data-models)
7. [Business Rules](#business-rules)
8. [Error Codes](#error-codes)

---

## 1. Create Tax Invoice

### Endpoint
```
POST /api/TaxInvoice/Create
```

### Description
Tạo hóa đơn thuế mới cho phiếu đối soát khách hàng. Hóa đơn thuế chỉ có thể được tạo cho phiếu đối soát đã được duyệt hoặc hoàn thành.

### Authorization
**Required Roles:** `TaxInvoice.Create` OR `Founder`

### Request Body
```json
{
  "CustomerReconciliationSlipId": 123,
  "VatInfoId": 45,
  "Note": "Hóa đơn thuế cho phiếu đối soát tháng 12/2024"
}
```

### Request Model: `CreateTaxInvoiceModel`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| CustomerReconciliationSlipId | int | Yes | ID của phiếu đối soát |
| VatInfoId | int | Yes | ID thông tin VAT của khách hàng |
| Note | string | No | Ghi chú (tối đa 2048 ký tự) |

### Request Example
```http
POST /api/TaxInvoice/Create
Content-Type: application/json
Authorization: Bearer {token}

{
  "CustomerReconciliationSlipId": 123,
  "VatInfoId": 45,
  "Note": "Xuất hóa đơn cho phiếu CR-241212-A1B2"
}
```

### Response

#### Success Response (200 OK)
```json
{
  "Result": 1,
  "Message": "Tạo hóa đơn thuế thành công",
  "TaxInvoiceId": 67,
  "Status": 1,
  "StatusName": "Chưa xuất"
}
```

#### Error Responses

**400 Bad Request** - Validation errors
```json
{
  "Result": 2,
  "Message": "ID phiếu đối soát không hợp lệ"
}
```

**400 Bad Request** - Phiếu đối soát chưa được duyệt
```json
{
  "Result": 2,
  "Message": "Chỉ có thể tạo hóa đơn thuế cho phiếu đối soát đã được duyệt hoặc hoàn thành"
}
```

**400 Bad Request** - Phiếu đã có hóa đơn thuế
```json
{
  "Result": 2,
  "Message": "Phiếu đối soát này đã có hóa đơn thuế"
}
```

**400 Bad Request** - VatInfo không thuộc khách hàng
```json
{
  "Result": 2,
  "Message": "Thông tin VAT không thuộc khách hàng của phiếu đối soát"
}
```

**401 Unauthorized**
```json
{
  "Message": "Unauthorized"
}
```

**403 Forbidden**
```json
{
  "Message": "Forbidden"
}
```

### Business Logic

1. **Validation:**
   - Phiếu đối soát phải tồn tại
   - Phiếu đối soát phải ở trạng thái `Approved` hoặc `Completed`
   - Phiếu đối soát chưa có hóa đơn thuế (one-to-one relationship)
   - VatInfo phải tồn tại và thuộc về cùng khách hàng với phiếu đối soát

2. **VAT Calculation:**
   - `TotalAmount` = `FinalAmount` của phiếu đối soát
   - `VatRate` = 10% (mặc định)
   - `VatAmount` = `TotalAmount` × `VatRate` / 100
   - `TotalAmountIncludingVat` = `TotalAmount` + `VatAmount`

3. **Process:**
   - Tạo hóa đơn thuế với trạng thái `NotIssued` (Chưa xuất)
   - Lưu thông tin người tạo (CreatedBy, CreatedByUserName, CreatedTime)
   - Cập nhật `TaxInvoiceId` trong phiếu đối soát

4. **Side Effects:**
   - Phiếu đối soát được liên kết với hóa đơn thuế
   - Có thể trigger notification cho kế toán

### Notes
- Mỗi phiếu đối soát chỉ có thể có **một** hóa đơn thuế
- Hóa đơn thuế được tạo với trạng thái `NotIssued` (Chưa xuất)
- Thuế suất VAT mặc định là 10%

---

## 2. Update Tax Invoice Status

### Endpoint
```
POST /api/TaxInvoice/UpdateStatus
```

### Description
Cập nhật trạng thái hóa đơn thuế từ `NotIssued` → `Draft` hoặc `Issued`, hoặc từ `Draft` → `Issued`. Khi cập nhật sang `Draft` hoặc `Issued`, bắt buộc phải upload file PDF hóa đơn.

### Authorization
**Required Roles:** `TaxInvoice.Update` OR `Founder`

### Request Body (multipart/form-data)
```
taxInvoiceId: 67
status: 2
invoiceNumber: INV-2024-12-001
invoiceDate: 2024-12-12T10:30:00Z
files: [PDF file]
```

### Request Model: `UpdateTaxInvoiceStatusModel`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| TaxInvoiceId | int | Yes | ID hóa đơn thuế |
| Status | int | Yes | Trạng thái mới (2=Draft, 3=Issued) |
| InvoiceNumber | string | Yes* | Số hóa đơn (tối đa 50 ký tự) |
| InvoiceDate | DateTimeOffset | Yes* | Ngày xuất hóa đơn |
| Files | List<IFormFile> | Yes* | File PDF hóa đơn (chỉ 1 file) |

*Required khi status = Draft (2) hoặc Issued (3)

### Request Example
```http
POST /api/TaxInvoice/UpdateStatus
Content-Type: multipart/form-data
Authorization: Bearer {token}

--boundary
Content-Disposition: form-data; name="TaxInvoiceId"

67
--boundary
Content-Disposition: form-data; name="Status"

2
--boundary
Content-Disposition: form-data; name="InvoiceNumber"

INV-2024-12-001
--boundary
Content-Disposition: form-data; name="InvoiceDate"

2024-12-12T10:30:00Z
--boundary
Content-Disposition: form-data; name="files"; filename="invoice.pdf"
Content-Type: application/pdf

[PDF file content]
--boundary--
```

### Response

#### Success Response (200 OK)
```json
{
  "Result": 1,
  "Message": "Cập nhật trạng thái hóa đơn thuế thành công",
  "TaxInvoiceId": 67,
  "InvoiceNumber": "INV-2024-12-001",
  "Status": 2,
  "StatusName": "Nháp"
}
```

#### Error Responses

**400 Bad Request** - Validation errors
```json
{
  "Result": 2,
  "Message": "Số hóa đơn là bắt buộc khi cập nhật sang Draft hoặc Issued"
}
```

**400 Bad Request** - Invalid status transition
```json
{
  "Result": 2,
  "Message": "Không thể chuyển từ trạng thái Đã xuất sang Nháp"
}
```

**400 Bad Request** - Missing PDF file
```json
{
  "Result": 2,
  "Message": "Cần upload file PDF hóa đơn"
}
```

**400 Bad Request** - Invalid file format
```json
{
  "Result": 2,
  "Message": "File tải lên phải là định dạng PDF"
}
```

**401 Unauthorized**
```json
{
  "Message": "Unauthorized"
}
```

**403 Forbidden**
```json
{
  "Message": "Forbidden"
}
```

**404 Not Found**
```json
{
  "Result": 2,
  "Message": "Hóa đơn thuế không tồn tại"
}
```

### Business Logic

1. **Validation:**
   - Hóa đơn thuế phải tồn tại
   - Trạng thái chuyển đổi phải hợp lệ (xem Status Transition Rules)
   - Khi status = Draft hoặc Issued: bắt buộc có InvoiceNumber, InvoiceDate, và PDF file
   - File upload phải là định dạng PDF
   - Chỉ cho phép upload 1 file

2. **Status Transition Rules:**
   - `NotIssued` (1) → `Draft` (2) hoặc `Issued` (3) ✅
   - `Draft` (2) → `Issued` (3) ✅
   - `Issued` (3) → Không thể chuyển sang trạng thái khác ❌

3. **File Processing:**
   - Validate file type = `application/pdf`
   - Process file qua `IUnifiedMediaService.ProcessImageAsync()`
   - Tạo Media record với SystemPath, AltText, Caption
   - Lưu MediaId vào TaxInvoice

4. **Process:**
   - Cập nhật Status, InvoiceNumber, InvoiceDate
   - Lưu MediaId (nếu có upload file)
   - Lưu thông tin người cập nhật (UpdatedBy, UpdatedByUserName, UpdatedTime)

### Notes
- File PDF được lưu vào hệ thống Media
- Trạng thái `Issued` là trạng thái cuối cùng, không thể thay đổi
- Mỗi lần cập nhật trạng thái có thể upload file PDF mới

---

## 3. Get Tax Invoice by ID

### Endpoint
```
GET /api/TaxInvoice/{id}
```

### Description
Lấy thông tin chi tiết hóa đơn thuế theo ID, bao gồm thông tin VatInfo và URL file PDF.

### Authorization
**Required Roles:** `TaxInvoice.View` OR `Founder`

### Path Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | int | Yes | ID của hóa đơn thuế |

### Request Example
```http
GET /api/TaxInvoice/67
Authorization: Bearer {token}
```

### Response

#### Success Response (200 OK)
**When tax invoice exists:**
```json
{
  "Id": 67,
  "CustomerReconciliationSlipId": 123,
  "ReconciliationSlipCode": "CR-241212-A1B2",
  "VatInfoId": 45,
  "TaxCode": "0123456789",
  "CompanyName": "CÔNG TY TNHH ABC",
  "Address": "123 Đường XYZ, Quận 1, TP.HCM",
  "InvoiceEmail": "ketoan@abc.com",
  "InvoiceNumber": "INV-2024-12-001",
  "InvoiceDate": "2024-12-12T10:30:00Z",
  "Status": 2,
  "StatusName": "Nháp",
  "TotalAmount": 10000000,
  "VatAmount": 1000000,
  "TotalAmountIncludingVat": 11000000,
  "VatRate": 10,
  "PdfUrl": "/media/documents/invoice_67_20241212.pdf",
  "Note": "Hóa đơn thuế cho phiếu CR-241212-A1B2",
  "CreatedTime": "2024-12-12T09:00:00Z",
  "CreatedBy": 1,
  "CreatedByUserName": "admin",
  "UpdatedTime": "2024-12-12T10:30:00Z",
  "UpdatedBy": 1,
  "UpdatedByUserName": "admin"
}
```

**When tax invoice does not exist:**
```json
null
```

#### Error Responses

**401 Unauthorized**
```json
{
  "Message": "Unauthorized"
}
```

**403 Forbidden**
```json
{
  "Message": "Forbidden"
}
```

### Notes
- Response bao gồm đầy đủ thông tin VatInfo (TaxCode, CompanyName, Address, InvoiceEmail)
- `pdfUrl` chứa đường dẫn để download file PDF hóa đơn
- Tất cả số tiền được trả về dưới dạng decimal
- **Trả về `null` thay vì 404** khi không tìm thấy hóa đơn thuế
- Frontend có thể check `if (response.data === null)` để xử lý trường hợp không tìm thấy

---

## 4. Get Tax Invoice by Slip ID

### Endpoint
```
GET /api/TaxInvoice/BySlip/{slipId}
```

### Description
Lấy thông tin hóa đơn thuế theo ID phiếu đối soát. Hữu ích khi cần kiểm tra xem phiếu đối soát đã có hóa đơn thuế chưa.

### Authorization
**Required Roles:** `TaxInvoice.View` OR `Founder`

### Path Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| slipId | int | Yes | ID của phiếu đối soát |

### Request Example
```http
GET /api/TaxInvoice/BySlip/123
Authorization: Bearer {token}
```

### Response

#### Success Response (200 OK)
**When reconciliation slip has tax invoice:**
```json
{
  "Id": 67,
  "CustomerReconciliationSlipId": 123,
  "ReconciliationSlipCode": "CR-241212-A1B2",
  "VatInfoId": 45,
  "TaxCode": "0123456789",
  "CompanyName": "CÔNG TY TNHH ABC",
  "Address": "123 Đường XYZ, Quận 1, TP.HCM",
  "InvoiceEmail": "ketoan@abc.com",
  "InvoiceNumber": "INV-2024-12-001",
  "InvoiceDate": "2024-12-12T10:30:00Z",
  "Status": 2,
  "StatusName": "Nháp",
  "TotalAmount": 10000000,
  "VatAmount": 1000000,
  "TotalAmountIncludingVat": 11000000,
  "VatRate": 10,
  "PdfUrl": "/media/documents/invoice_67_20241212.pdf",
  "Note": "Hóa đơn thuế cho phiếu CR-241212-A1B2",
  "CreatedTime": "2024-12-12T09:00:00Z",
  "CreatedBy": 1,
  "CreatedByUserName": "admin",
  "UpdatedTime": "2024-12-12T10:30:00Z",
  "UpdatedBy": 1,
  "UpdatedByUserName": "admin"
}
```

**When reconciliation slip does not have tax invoice:**
```json
null
```

#### Error Responses

**401 Unauthorized**
```json
{
  "Message": "Unauthorized"
}
```

**403 Forbidden**
```json
{
  "Message": "Forbidden"
}
```

### Notes
- **Trả về `null` thay vì 404** khi phiếu đối soát chưa có hóa đơn thuế
- Response format giống với Get by ID
- Hữu ích để check xem phiếu đối soát đã có hóa đơn thuế chưa
- Frontend có thể hiển thị nút "Tạo hóa đơn thuế" khi response là `null`

---

## 5. Delete Tax Invoice

### Endpoint
```
DELETE /api/TaxInvoice/{id}
```

### Description
Xóa hóa đơn thuế. Chỉ có thể xóa hóa đơn thuế ở trạng thái `NotIssued` (Chưa xuất).

### Authorization
**Required Roles:** `TaxInvoice.Delete` OR `Founder`

### Path Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | int | Yes | ID của hóa đơn thuế cần xóa |

### Request Example
```http
DELETE /api/TaxInvoice/67
Authorization: Bearer {token}
```

### Response

#### Success Response (200 OK)
```json
{
  "Message": "Xóa hóa đơn thuế thành công"
}
```

#### Error Responses

**400 Bad Request** - Không thể xóa hóa đơn ở trạng thái này
```json
{
  "Message": "Chỉ có thể xóa hóa đơn thuế ở trạng thái Chưa xuất"
}
```

**401 Unauthorized**
```json
{
  "Message": "Unauthorized"
}
```

**403 Forbidden**
```json
{
  "Message": "Forbidden"
}
```

**404 Not Found**
```json
{
  "Message": "Hóa đơn thuế không tồn tại"
}
```

### Business Logic

1. **Validation:**
   - Hóa đơn thuế phải tồn tại
   - Hóa đơn thuế phải ở trạng thái `NotIssued` (1)

2. **Process:**
   - Xóa `TaxInvoiceId` trong phiếu đối soát (set null)
   - Xóa Media record (file PDF) nếu có
   - Xóa TaxInvoice record

3. **Side Effects:**
   - Phiếu đối soát có thể tạo hóa đơn thuế mới
   - File PDF được xóa khỏi hệ thống

### Notes
- **Chỉ** có thể xóa hóa đơn ở trạng thái `NotIssued`
- Hóa đơn ở trạng thái `Draft` hoặc `Issued` không thể xóa
- Sau khi xóa, phiếu đối soát có thể tạo hóa đơn thuế mới

---

## Data Models

### TaxInvoiceStatus (Enum)
```csharp
public enum TaxInvoiceStatus
{
    [Description("Chưa xuất")]
    NotIssued = 1,
    
    [Description("Nháp")]
    Draft = 2,
    
    [Description("Đã xuất")]
    Issued = 3
}
```

### CreateTaxInvoiceModel
```csharp
public class CreateTaxInvoiceModel
{
    [Required(ErrorMessage = "ID phiếu đối soát là bắt buộc")]
    public int CustomerReconciliationSlipId { get; set; }

    [Required(ErrorMessage = "ID thông tin VAT là bắt buộc")]
    public int VatInfoId { get; set; }

    [MaxLength(2048, ErrorMessage = "Ghi chú không được vượt quá 2048 ký tự")]
    public string Note { get; set; } = string.Empty;
}
```

### UpdateTaxInvoiceStatusModel
```csharp
public class UpdateTaxInvoiceStatusModel
{
    [Required(ErrorMessage = "ID hóa đơn thuế là bắt buộc")]
    public int TaxInvoiceId { get; set; }

    [Required(ErrorMessage = "Trạng thái là bắt buộc")]
    [Range(2, 3, ErrorMessage = "Trạng thái phải là Draft (2) hoặc Issued (3)")]
    public int Status { get; set; }

    [MaxLength(50, ErrorMessage = "Số hóa đơn không được vượt quá 50 ký tự")]
    public string InvoiceNumber { get; set; } = string.Empty;

    public DateTimeOffset? InvoiceDate { get; set; }

    public List<IFormFile>? Files { get; set; }
}
```

### TaxInvoiceItem
```csharp
public class TaxInvoiceItem
{
    public int Id { get; set; }
    public int CustomerReconciliationSlipId { get; set; }
    public string ReconciliationSlipCode { get; set; }
    public int VatInfoId { get; set; }
    public string TaxCode { get; set; }
    public string CompanyName { get; set; }
    public string Address { get; set; }
    public string InvoiceEmail { get; set; }
    public string InvoiceNumber { get; set; }
    public DateTimeOffset? InvoiceDate { get; set; }
    public int Status { get; set; }
    public string StatusName { get; set; }
    public decimal TotalAmount { get; set; }
    public decimal VatAmount { get; set; }
    public decimal TotalAmountIncludingVat { get; set; }
    public decimal VatRate { get; set; }
    public string PdfUrl { get; set; }
    public string Note { get; set; }
    // Audit fields...
}
```

---

## Business Rules

### Status Transition Rules

```
NotIssued (1) ──┬──> Draft (2) ──> Issued (3)
                └──> Issued (3)
```

**Valid Transitions:**
- `NotIssued` → `Draft` ✅
- `NotIssued` → `Issued` ✅
- `Draft` → `Issued` ✅
- `Issued` → *Cannot change* ❌

### Relationship Rules

1. **One-to-One with CustomerReconciliationSlip:**
   - Mỗi phiếu đối soát chỉ có **một** hóa đơn thuế
   - Mỗi hóa đơn thuế chỉ thuộc về **một** phiếu đối soát

2. **VatInfo Ownership:**
   - VatInfo phải thuộc về cùng khách hàng với phiếu đối soát
   - Một khách hàng có thể có nhiều VatInfo (công ty, chi nhánh, etc.)

3. **Reconciliation Slip Status:**
   - Chỉ tạo hóa đơn thuế cho phiếu đã `Approved` hoặc `Completed`
   - Không tạo được cho phiếu `Draft`, `PendingApproval`, hoặc `Rejected`

### File Upload Rules

1. **File Format:**
   - Chỉ chấp nhận file PDF (`application/pdf`)
   - Reject các định dạng khác (image, doc, etc.)

2. **File Count:**
   - Chỉ cho phép upload **1 file** mỗi lần
   - Upload nhiều file sẽ bị reject

3. **File Requirement:**
   - Bắt buộc khi status = `Draft` (2) hoặc `Issued` (3)
   - Không bắt buộc khi tạo mới (status = `NotIssued`)

### VAT Calculation Rules

```
TotalAmount = CustomerReconciliationSlip.FinalAmount
VatRate = 10% (default)
VatAmount = TotalAmount × VatRate / 100
TotalAmountIncludingVat = TotalAmount + VatAmount
```

**Example:**
- FinalAmount = 10,000,000 VND
- VatRate = 10%
- VatAmount = 1,000,000 VND
- TotalAmountIncludingVat = 11,000,000 VND

### Deletion Rules

1. **Status Restriction:**
   - Chỉ xóa được hóa đơn ở trạng thái `NotIssued`
   - Hóa đơn `Draft` hoặc `Issued` không thể xóa

2. **Cleanup:**
   - Xóa Media record (file PDF)
   - Xóa TaxInvoiceId trong CustomerReconciliationSlip
   - Xóa TaxInvoice record

---

## Error Codes

### HTTP Status Codes

| Code | Description | When to Use |
|------|-------------|-------------|
| 200 | OK | Request thành công |
| 400 | Bad Request | Validation errors, business rule violations |
| 401 | Unauthorized | Missing hoặc invalid authentication token |
| 403 | Forbidden | User không có quyền thực hiện action |
| 404 | Not Found | Resource không tồn tại |
| 500 | Internal Server Error | Lỗi hệ thống không mong đợi |

### Result Enum Values

| Value | Name | Description |
|-------|------|-------------|
| 1 | Success | Thao tác thành công |
| 2 | Failed | Thao tác thất bại do validation hoặc business rules |
| 3 | SystemError | Lỗi hệ thống |

### Common Error Messages

**Validation Errors:**
- "ID phiếu đối soát không hợp lệ"
- "ID thông tin VAT không hợp lệ"
- "Số hóa đơn là bắt buộc khi cập nhật sang Draft hoặc Issued"
- "Ngày xuất hóa đơn là bắt buộc khi cập nhật sang Draft hoặc Issued"
- "File PDF hóa đơn là bắt buộc khi cập nhật sang Draft hoặc Issued"
- "Chỉ được upload 1 file PDF"

**Business Rule Errors:**
- "Phiếu đối soát không tồn tại"
- "Chỉ có thể tạo hóa đơn thuế cho phiếu đối soát đã được duyệt hoặc hoàn thành"
- "Phiếu đối soát này đã có hóa đơn thuế"
- "Thông tin VAT không tồn tại"
- "Thông tin VAT không thuộc khách hàng của phiếu đối soát"
- "Hóa đơn thuế không tồn tại"
- "Không thể chuyển từ trạng thái {current} sang {new}"
- "File tải lên phải là định dạng PDF"
- "Chỉ có thể xóa hóa đơn thuế ở trạng thái Chưa xuất"

---

## Testing Guide

### Postman Collection

#### 1. Create Tax Invoice
```
POST {{baseUrl}}/api/TaxInvoice/Create
Headers:
  Authorization: Bearer {{token}}
  Content-Type: application/json

Body:
{
  "CustomerReconciliationSlipId": 123,
  "VatInfoId": 45,
  "Note": "Test tax invoice"
}
```

#### 2. Update to Draft
```
POST {{baseUrl}}/api/TaxInvoice/UpdateStatus
Headers:
  Authorization: Bearer {{token}}

Body (form-data):
  taxInvoiceId: 67
  status: 2
  invoiceNumber: INV-2024-12-001
  invoiceDate: 2024-12-12T10:30:00Z
  files: [Select PDF file]
```

#### 3. Get by ID
```
GET {{baseUrl}}/api/TaxInvoice/67
Headers:
  Authorization: Bearer {{token}}
```

#### 4. Get by Slip ID
```
GET {{baseUrl}}/api/TaxInvoice/BySlip/123
Headers:
  Authorization: Bearer {{token}}
```

#### 5. Delete
```
DELETE {{baseUrl}}/api/TaxInvoice/67
Headers:
  Authorization: Bearer {{token}}
```

### Test Scenarios

**Scenario 1: Complete Flow**
1. Create reconciliation slip → Approve → Complete
2. Create tax invoice (status = NotIssued)
3. Update to Draft with PDF upload
4. Update to Issued
5. Verify cannot delete (status = Issued)

**Scenario 2: Validation Tests**
1. Try create tax invoice for Draft slip → Should fail
2. Try create duplicate tax invoice → Should fail
3. Try update without PDF file → Should fail
4. Try upload non-PDF file → Should fail
5. Try invalid status transition (Issued → Draft) → Should fail

**Scenario 3: Deletion Tests**
1. Create tax invoice (NotIssued)
2. Delete successfully
3. Create again → Update to Draft
4. Try delete → Should fail (status != NotIssued)

---

## Integration Notes

### Frontend Integration

**Tax Invoice Management UI:**
```javascript
// Check if slip has tax invoice
const taxInvoice = await getTaxInvoiceBySlipId(slipId);

if (!taxInvoice) {
  // Show "Create Tax Invoice" button
  // Display VatInfo selection dropdown
}

// Create tax invoice
const result = await createTaxInvoice({
  customerReconciliationSlipId: slipId,
  vatInfoId: selectedVatInfoId,
  note: note
});

// Update status with file upload
const formData = new FormData();
formData.append('taxInvoiceId', taxInvoiceId);
formData.append('status', 2); // Draft
formData.append('invoiceNumber', invoiceNumber);
formData.append('invoiceDate', invoiceDate);
formData.append('files', pdfFile);

const updateResult = await updateTaxInvoiceStatus(formData);
```

**Status Display:**
```javascript
const statusBadge = {
  1: { text: 'Chưa xuất', color: 'gray' },
  2: { text: 'Nháp', color: 'blue' },
  3: { text: 'Đã xuất', color: 'green' }
};
```

### Database Schema

**TaxInvoices Table:**
```sql
CREATE TABLE TaxInvoices (
  Id INT PRIMARY KEY IDENTITY,
  CustomerReconciliationSlipId INT NOT NULL UNIQUE,
  VatInfoId INT NOT NULL,
  InvoiceNumber NVARCHAR(50),
  InvoiceDate DATETIMEOFFSET,
  Status INT NOT NULL DEFAULT 1,
  TotalAmount DECIMAL(18,2) NOT NULL,
  VatAmount DECIMAL(18,2) NOT NULL,
  TotalAmountIncludingVat DECIMAL(18,2) NOT NULL,
  VatRate DECIMAL(5,2) NOT NULL DEFAULT 10,
  MediaId UNIQUEIDENTIFIER,
  Note NVARCHAR(2048),
  CreatedBy INT NOT NULL,
  CreatedByUserName NVARCHAR(128) NOT NULL,
  CreatedTime DATETIMEOFFSET NOT NULL,
  UpdatedBy INT,
  UpdatedByUserName NVARCHAR(128),
  UpdatedTime DATETIMEOFFSET,
  
  CONSTRAINT FK_TaxInvoices_CustomerReconciliationSlips 
    FOREIGN KEY (CustomerReconciliationSlipId) 
    REFERENCES CustomerReconciliationSlips(Id),
  CONSTRAINT FK_TaxInvoices_VatInfos 
    FOREIGN KEY (VatInfoId) 
    REFERENCES VatInfos(Id),
  CONSTRAINT FK_TaxInvoices_Medias 
    FOREIGN KEY (MediaId) 
    REFERENCES Medias(Id)
);

CREATE INDEX IX_TaxInvoices_VatInfoId ON TaxInvoices(VatInfoId);
CREATE INDEX IX_TaxInvoices_Status ON TaxInvoices(Status);
CREATE INDEX IX_TaxInvoices_InvoiceNumber ON TaxInvoices(InvoiceNumber);
CREATE INDEX IX_TaxInvoices_CreatedTime ON TaxInvoices(CreatedTime);
```

---

## Changelog

### Version 1.0.0 (2024-12-12)
- Initial release
- Create, Update, Get, Delete tax invoice APIs
- PDF file upload support
- Status management (NotIssued, Draft, Issued)
- VAT calculation (10% default)
- One-to-one relationship with CustomerReconciliationSlip

