# Bill API Documentation

Dành cho Frontend Team - Tài liệu API Bill (Hóa đơn)

---

## Table of Contents

- [Overview](#overview)
- [Authentication](#authentication)
- [Response Format](#response-format)
- [Bill APIs](#bill-apis)
- [Bill Schema](#bill-schema)
- [Bill Item Schema](#bill-item-schema)
- [Bill Code Format](#bill-code-format)
- [Workflows](#workflows)
- [Error Handling](#error-handling)

---

## Overview

Bill API quản lý thông tin hóa đơn thanh toán của cư dân, bao gồm:

- Thông tin hóa đơn (mã hóa đơn, kỳ thanh toán, hạn thanh toán, trạng thái)
- Chi tiết các khoản phí trong hóa đơn (bill items)
- Thông tin cư dân liên quan (full_name, email, phone)
- Thông tin phòng/căn hộ/tòa nhà (room_name, apartment_name, building_name)
- Trạng thái thanh toán (draft, pending, overdue, paid)

**Base URL**: `https://api.example.com/api/v1`

---

## Authentication

Tất cả API đều yêu cầu Bearer token trong header:

```http
Authorization: Bearer <access_token>
```

**Roles**:

- `admin`: Quản trị viên - full access
- `system_admin`: Quản trị viên hệ thống - full access
- User thường: Chỉ xem thông tin của chính mình (nếu có)

---

## Response Format

### Success Response

```json
{
  "success": true,
  "trace_id": null,
  "timestamp": "2024-01-15T08:30:00.000Z",
  "errors": null,
  "data": { ... }
}
```

### Error Response

```json
{
	"error": {
		"code": "VALIDATION_ERROR",
		"message": "Validation failed",
		"details": [
			{
				"field": "billing_period",
				"code": "INVALID_FORMAT",
				"messages": {
					"vi": "Kỳ thanh toán phải có định dạng yyyy-mm",
					"en": "Billing period must be in yyyy-mm format"
				}
			}
		]
	}
}
```

---

## Bill APIs

### 1. Get Bills List

Lấy danh sách hóa đơn với thông tin chi tiết (kể cả thông tin resident và room/apartment/building)

```http
GET /api/v1/bills
```

**Middleware**: `verify`, `requireAdmin`, `validateQueryZod(QueryParamsSchema)`, `queryModifier`

**Query Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | number | No | Số trang (mặc định: 1) |
| pageSize | number | No | Số bản ghi/trang (mặc định: 20, tối đa: 100) |
| sortField | string | No | Trường để sắp xếp (ví dụ: created_at, due_date) |
| sortOrder | string | No | Thứ tự sắp xếp (ASC hoặc DESC, mặc định: DESC) |
| filters | object | No | Bộ lọc tùy chỉnh |

**Example Request**:

```http
GET /api/v1/bills?page=1&pageSize=20&sortField=created_at&sortOrder=DESC
```

**Success Response**:

```json
{
  "success": true,
  "data": {
    "count": 150,
    "rows": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "code": "HD-202401-0001",
        "room_id": "550e8400-e29b-41d4-a716-446655440001",
        "resident_id": "550e8400-e29b-41d4-a716-446655440002",
        "billing_period": "2024-01",
        "due_date": "2024-01-31",
        "payment_method": "bank_transfer",
        "notes": "Thanh toán tiền phòng tháng 1",
        "total_amount": 5000000,
        "status": "pending",
        "created_by": "550e8400-e29b-41d4-a716-446655440003",
        "created_at": "2024-01-15T08:30:00.000Z",
        "updated_at": "2024-01-15T08:30:00.000Z",
        "deleted_by": null,
        "deleted_at": null,
        "room_name": "101",
        "apartment_name": "Tầng 1",
        "building_name": "Tòa A",
        "resident_full_name": "Nguyễn Văn A",
        "resident_email": "nguyenvana@example.com",
        "resident_phone": "0912345678"
      }
    ],
    "page": 1,
    "pageSize": 20
  }
}
```

---

### 2. Get Bill by ID

Lấy chi tiết hóa đơn theo ID, bao gồm danh sách các khoản phí (bill items)

```http
GET /api/v1/bills/{id}
```

**Middleware**: `verify`, `validateId`

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | string (UUID) | Yes | ID của hóa đơn |

**Example Request**:

```http
GET /api/v1/bills/550e8400-e29b-41d4-a716-446655440000
```

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "code": "HD-202401-0001",
    "room_id": "550e8400-e29b-41d4-a716-446655440001",
    "resident_id": "550e8400-e29b-41d4-a716-446655440002",
    "billing_period": "2024-01",
    "due_date": "2024-01-31",
    "payment_method": "bank_transfer",
    "notes": "Thanh toán tiền phòng tháng 1",
    "total_amount": 5000000,
    "status": "pending",
    "created_by": "550e8400-e29b-41d4-a716-446655440003",
    "created_at": "2024-01-15T08:30:00.000Z",
    "updated_at": "2024-01-15T08:30:00.000Z",
    "deleted_by": null,
    "deleted_at": null,
    "room_name": "101",
    "apartment_name": "Tầng 1",
    "building_name": "Tòa A",
    "resident_full_name": "Nguyễn Văn A",
    "resident_email": "nguyenvana@example.com",
    "resident_phone": "0912345678",
    "bill_items": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440010",
        "bill_id": "550e8400-e29b-41d4-a716-446655440000",
        "fee_name": "Tiền phòng",
        "quantity": 1,
        "unit_price": 4000000,
        "created_at": "2024-01-15T08:30:00.000Z"
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440011",
        "bill_id": "550e8400-e29b-41d4-a716-446655440000",
        "fee_name": "Điện",
        "quantity": 1,
        "unit_price": 500000,
        "created_at": "2024-01-15T08:30:00.000Z"
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440012",
        "bill_id": "550e8400-e29b-41d4-a716-446655440000",
        "fee_name": "Nước",
        "quantity": 1,
        "unit_price": 500000,
        "created_at": "2024-01-15T08:30:00.000Z"
      }
    ]
  }
}
```

---

### 3. Create Bill

Tạo hóa đơn mới với danh sách các khoản phí (bill items)

```http
POST /api/v1/bills
```

**Middleware**: `verify`, `requireAdmin`, `validateBodyZod(BillCreateBodySchema)`

**Request Body**:

```json
{
  "room_id": "550e8400-e29b-41d4-a716-446655440001",
  "resident_id": "550e8400-e29b-41d4-a716-446655440002",
  "billing_period": "2024-02",
  "due_date": "2024-02-29",
  "payment_method": "cash",
  "notes": "Thanh toán tiền phòng tháng 2",
  "total_amount": 5000000,
  "status": "pending",
  "bill_items": [
    {
      "fee_name": "Tiền phòng",
      "quantity": 1,
      "unit_price": 4000000
    },
    {
      "fee_name": "Điện",
      "quantity": 1,
      "unit_price": 500000
    },
    {
      "fee_name": "Nước",
      "quantity": 1,
      "unit_price": 500000
    }
  ]
}
```

**Field Validation**:

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| room_id | string (UUID) | Yes | Phải là UUID hợp lệ |
| resident_id | string (UUID) | Yes | Phải là UUID hợp lệ |
| billing_period | string | Yes | Phải theo format yyyy-mm (ví dụ: 2024-01) |
| due_date | string | Yes | Phải là định dạng DATE |
| payment_method | string | No | Tối đa 50 ký tự |
| notes | string | No | Optional |
| total_amount | number | No | Phải >= 0 |
| status | enum | No | draft, paid, pending, overdue (mặc định: pending) |
| bill_items | array | Yes | Phải có ít nhất 1 item |

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440020",
    "code": "HD-202402-0001",
    "room_id": "550e8400-e29b-41d4-a716-446655440001",
    "resident_id": "550e8400-e29b-41d4-a716-446655440002",
    "billing_period": "2024-02",
    "due_date": "2024-02-29",
    "payment_method": "cash",
    "notes": "Thanh toán tiền phòng tháng 2",
    "total_amount": 5000000,
    "status": "pending",
    "created_by": "550e8400-e29b-41d4-a716-446655440003",
    "created_at": "2024-01-20T10:00:00.000Z",
    "updated_at": "2024-01-20T10:00:00.000Z",
    "deleted_by": null,
    "deleted_at": null
  }
}
```

**Note**: Mã hóa đơn (`code`) sẽ được tự động sinh theo format `HD-YYYYMM-XXXX` (ví dụ: HD-202402-0001). Không cần truyền `code` trong request body.

---

### 4. Update Bill

Cập nhật thông tin hóa đơn

```http
PUT /api/v1/bills/{id}
```

**Middleware**: `verify`, `requireAdmin`, `validateId`, `validateBodyZod(BillUpdateBodySchema)`

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | string (UUID) | Yes | ID của hóa đơn |

**Request Body** (tất cả fields đều optional):

```json
{
  "room_id": "550e8400-e29b-41d4-a716-446655440001",
  "resident_id": "550e8400-e29b-41d4-a716-446655440002",
  "billing_period": "2024-02",
  "due_date": "2024-03-31",
  "payment_method": "bank_transfer",
  "notes": "Cập nhật ghi chú",
  "total_amount": 5500000,
  "status": "paid"
}
```

**Field Validation**:

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| room_id | string (UUID) | No | Phải là UUID hợp lệ |
| resident_id | string (UUID) | No | Phải là UUID hợp lệ |
| billing_period | string | No | Phải theo format yyyy-mm |
| due_date | string | No | Phải là định dạng DATE |
| payment_method | string | No | Tối đa 50 ký tự |
| notes | string | No | Optional |
| total_amount | number | No | Phải >= 0 |
| status | enum | No | draft, paid, pending, overdue |

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440020",
    "code": "HD-202402-0001",
    "room_id": "550e8400-e29b-41d4-a716-446655440001",
    "resident_id": "550e8400-e29b-41d4-a716-446655440002",
    "billing_period": "2024-02",
    "due_date": "2024-03-31",
    "payment_method": "bank_transfer",
    "notes": "Cập nhật ghi chú",
    "total_amount": 5500000,
    "status": "paid",
    "created_by": "550e8400-e29b-41d4-a716-446655440003",
    "created_at": "2024-01-20T10:00:00.000Z",
    "updated_at": "2024-01-25T14:00:00.000Z",
    "deleted_by": null,
    "deleted_at": null
  }
}
```

---

### 5. Delete Bill (Soft Delete)

Xóa hóa đơn (soft delete - đánh dấu là đã xóa nhưng không xóa khỏi database)

```http
DELETE /api/v1/bills/{id}
```

**Middleware**: `verify`, `requireAdmin`, `validateId`

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| id | string (UUID) | Yes | ID của hóa đơn |

**Example Request**:

```http
DELETE /api/v1/bills/550e8400-e29b-41d4-a716-446655440020
```

**Success Response**:

```json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440020",
    "code": "HD-202402-0001",
    "deleted_at": "2024-01-30T09:00:00.000Z",
    "deleted_by": "550e8400-e29b-41d4-a716-446655440003"
  }
}
```

**Note**: Đây là soft delete, hóa đơn sẽ không hiển thị trong API GET nhưng vẫn tồn tại trong database với `deleted_at` và `deleted_by` được set.

---

## Bill Schema

### Bill Object

| Field | Type | Nullable | Description |
|-------|------|----------|-------------|
| id | string (UUID) | No | ID của hóa đơn |
| code | string | No | Mã hóa đơn (format: HD-YYYYMM-XXXX) |
| room_id | string (UUID) | No | ID của phòng |
| resident_id | string (UUID) | No | ID của cư dân |
| billing_period | string | No | Kỳ thanh toán (format: yyyy-mm) |
| due_date | string (DATE) | No | Hạn thanh toán |
| payment_method | string | Yes | Phương thức thanh toán |
| notes | string | Yes | Ghi chú |
| total_amount | number | Yes | Tổng số tiền |
| status | enum | No | Trạng thái: draft, paid, pending, overdue |
| created_by | string (UUID) | Yes | ID user tạo hóa đơn |
| created_at | string (datetime) | No | Thời gian tạo |
| updated_at | string (datetime) | Yes | Thời gian cập nhật gần nhất |
| deleted_by | string (UUID) | Yes | ID user xóa hóa đơn |
| deleted_at | string (datetime) | Yes | Thời gian xóa (soft delete) |
| room_name | string | Yes | Tên phòng (từ join) |
| apartment_name | string | Yes | Tên căn hộ/tầng (từ join) |
| building_name | string | Yes | Tên tòa nhà (từ join) |
| resident_full_name | string | Yes | Họ tên cư dân (từ join) |
| resident_email | string | Yes | Email cư dân (từ join) |
| resident_phone | string | Yes | Số điện thoại cư dân (từ join) |

### Status Values

| Value | Description |
|-------|-------------|
| draft | Hóa đơn nháp |
| pending | Chờ thanh toán |
| overdue | Quá hạn thanh toán |
| paid | Đã thanh toán |

**Note**: Status sẽ tự động chuyển từ `pending` sang `overdue` khi `due_date` đã qua. Việc chuyển đổi được thực hiện bởi scheduled job chạy lúc 1:00 AM mỗi ngày.

---

## Bill Item Schema

### Bill Item Object

| Field | Type | Nullable | Description |
|-------|------|----------|-------------|
| id | string (UUID) | No | ID của bill item |
| bill_id | string (UUID) | No | ID của hóa đơn cha |
| fee_name | string | No | Tên khoản phí |
| quantity | number | No | Số lượng (mặc định: 1) |
| unit_price | number | No | Đơn giá |
| created_at | string (datetime) | No | Thời gian tạo |

**Note**: Bill items chỉ được trả về khi gọi API `GET /api/v1/bills/{id}` (chi tiết hóa đơn), không được trả về trong list bills.

---

## Bill Code Format

### Format

```
HD-YYYYMM-XXXX
```

**Ví dụ**: `HD-202401-0001`

- `HD`: Prefix cố định
- `YYYYMM`: Năm và tháng của kỳ thanh toán
- `XXXX`: Số thứ tự (0001 - 9999)

### Tự động sinh mã

Mã hóa đơn được tự động sinh khi tạo mới hóa đơn. Hệ thống sẽ:

1. Lấy billing_period từ request (ví dụ: 2024-01)
2. Query database để tìm mã hóa đơn lớn nhất trong cùng billing_period
3. Tăng sequence lên 1 và sinh mã mới

**Ví dụ**:
- Nếu hóa đơn cuối cùng trong tháng 1/2024 là `HD-202401-0050`
- Hóa đơn mới sẽ có mã `HD-202401-0051`

### Không cần truyền code

Khi tạo hóa đơn, **không cần truyền** trường `code` trong request body. Hệ thống sẽ tự động sinh.

---

## Workflows

### Workflow 1: Tạo hóa đơn mới

1. FE thu thập thông tin hóa đơn (room, resident, billing_period, due_date, payment_method, notes)
2. FE thu thập danh sách các khoản phí (bill_items)
3. FE gọi `POST /api/v1/bills` với request body đầy đủ
4. Backend tự động sinh mã hóa đơn
5. Backend trả về thông tin hóa đơn đã tạo (bao gồm mã hóa đơn)

### Workflow 2: Xem danh sách hóa đơn

1. FE gọi `GET /api/v1/bills` với query parameters (page, pageSize, sortField, sortOrder, filters)
2. Backend trả về danh sách hóa đơn với thông tin resident và room/apartment/building
3. FE hiển thị danh sách hóa đơn

### Workflow 3: Xem chi tiết hóa đơn

1. User click vào hóa đơn trong danh sách
2. FE gọi `GET /api/v1/bills/{id}`
3. Backend trả về chi tiết hóa đơn bao gồm danh sách bill_items
4. FE hiển thị chi tiết hóa đơn và các khoản phí

### Workflow 4: Cập nhật trạng thái thanh toán

1. User thanh toán hóa đơn
2. FE gọi `PUT /api/v1/bills/{id}` với `status: "paid"`
3. Backend cập nhật trạng thái hóa đơn
4. FE hiển thị thông báo thanh toán thành công

### Workflow 5: Auto chuyển sang overdue

1. Scheduled job chạy lúc 1:00 AM mỗi ngày
2. Job tìm tất cả hóa đơn có status = `pending` và due_date < hôm nay
3. Job cập nhật status sang `overdue`
4. FE có thể refresh danh sách để thấy trạng thái đã cập nhật

---

## Error Handling

### Common Error Codes

| Code | Description | Solution |
|------|-------------|----------|
| VALIDATION_ERROR | Dữ liệu không hợp lệ | Kiểm tra validation rules |
| NOT_FOUND | Hóa đơn không tồn tại | Kiểm tra ID hóa đơn |
| FORBIDDEN | Không có quyền truy cập | Kiểm tra role của user |
| INVALID_UUID | UUID không hợp lệ | Kiểm tra format UUID |
| INVALID_FORMAT | Format không đúng (ví dụ: billing_period) | Đảm bảo format yyyy-mm |

### Validation Error Example

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "billing_period",
        "code": "INVALID_FORMAT",
        "messages": {
          "vi": "Kỳ thanh toán phải có định dạng yyyy-mm",
          "en": "Billing period must be in yyyy-mm format"
        }
      },
      {
        "field": "bill_items",
        "code": "ARRAY_MIN",
        "messages": {
          "vi": "Phải có ít nhất 1 khoản phí",
          "en": "At least 1 bill item is required"
        }
      }
    ]
  }
}
```

---

## Tips cho Frontend

1. **Không cần sinh mã hóa đơn**: Backend tự động sinh, không cần tự implement logic sinh mã
2. **Billing period format**: Luôn dùng format yyyy-mm (ví dụ: 2024-01)
3. **Soft delete**: Khi xóa hóa đơn, backend không xóa hẳn mà chỉ set deleted_at, nên không cần refresh toàn bộ list
4. **Bill items chỉ trong detail**: Bill items chỉ có trong response của `GET /api/v1/bills/{id}`, không có trong list
5. **Resident info từ join**: Thông tin resident (full_name, email, phone) được lấy từ join, không cần gọi API riêng
6. **Auto overdue**: Status sẽ tự động chuyển sang overdue, không cần FE xử lý logic này
7. **Pagination**: Luôn dùng pagination để tránh load quá nhiều dữ liệu
8. **Sorting**: Có thể sort theo các field như created_at, due_date, total_amount, status

---

## Changelog

### 2024-01-27
- Thêm thông tin resident (resident_full_name, resident_email, resident_phone) vào response
- Thêm thông tin room/apartment/building (room_name, apartment_name, building_name) vào response
- Thêm bill_items vào response của GET /api/v1/bills/{id}
- Implement soft delete cho bills
- Implement auto chuyển status sang overdue bằng scheduled job
