# Resident Import — Validation errors & failed rows

Tài liệu này mô tả định dạng response khi upload file Excel import `residents` (API: `POST /api/v1/residents/import`) và cách FE nên xử lý các lỗi validate / các hàng bị thất bại.

**Ghi chú chung**
- Yêu cầu: `multipart/form-data` với field `file` chứa file `.xlsx`.
- API yêu cầu quyền admin (middleware `requireAdmin`).
- Số hàng (`row`) trong response tương ứng với số hàng Excel thực tế (header = 1, dữ liệu bắt đầu từ hàng 2).

---

## Những case response chính

1) Tất cả thành công (HTTP 200)

Response (envelope chuẩn):

```json
{
  "success": true,
  "trace_id": null,
  "timestamp": "2026-05-26T12:00:00.000Z",
  "errors": null,
  "data": {
    "successful": [ { /* resident created */ }, { /* ... */ } ]
  }
}
```

2) Tất cả thất bại (HTTP 422)

Response:

```json
{
  "success": false,
  "errors": [
    {
      "row": 2,
      "error": [
        { "field": "email", "code": "DUPLICATE_EMAIL_DB", "message": { "vi": "Email đã tồn tại", "en": "Email already exists" } },
        { "field": "phone", "code": "DUPLICATE_PHONE_DB", "message": { "vi": "Số điện thoại đã tồn tại", "en": "Phone already exists" } }
      ]
    },
    {
      "row": 3,
      "error": [ /* ... */ ]
    }
  ]
}
```

3) Mixed — có hàng thành công và có hàng thất bại (HTTP 207)

Response:

```json
{
  "success": true,
  "data": {
    "successful": [ { /* resident objects */ } ],
    "failed": [
      { "row": 2, "error": [ { "field": "email", "code": "DUPLICATE_EMAIL_DB", "message": { "vi":"...","en":"..." } } ] }
    ]
  }
}
```

---

## Cấu trúc `error` (mỗi phần tử trong `error` array)

- `field` (string): tên trường tương ứng trong file import. Các giá trị thường gặp: `bed_codes`, `full_name`, `email`, `phone`, `id_card`, `date_of_birth`, `nationality`, `job`, `emergency_contact_name`, `emergency_contact_phone`, `emergency_contact_relationship`, `status`, `move_in_at`. Nếu `field` là `_` => lỗi chung của hàng (không liên quan đến một cột cụ thể).
- `code` (string): mã lỗi (dùng để mapping sang message/UX). Ví dụ ở mục dưới.
- `message` (object): văn bản lỗi theo `vi` / `en`.

FE chỉ nhận 3 trường trên cho mỗi lỗi (không còn `value`, `otherRows`, `conflictWith` trong payload hiện tại).

## Danh sách mã lỗi (thường gặp)

- `DUPLICATE_EMAIL_DB`: Email đã tồn tại trong hệ thống.
- `DUPLICATE_PHONE_DB`: SĐT đã tồn tại trong hệ thống.
- `DUPLICATE_ID_CARD_DB`: CCCD/CMND đã tồn tại trong hệ thống.
- `DUPLICATE_EMAIL_FILE`: Email trùng lặp trong cùng file upload.
- `DUPLICATE_PHONE_FILE`: SĐT trùng lặp trong cùng file upload.
- `DUPLICATE_ID_CARD_FILE`: CCCD/CMND trùng lặp trong cùng file upload.
- `MISSING_BED_CODE`: Thiếu trường mã giường cho hàng này.
- `BED_NOT_FOUND`: Mã giường không tìm thấy trong hệ thống.
- `IMPORT_ERROR`: Lỗi chung khi tạo đối tượng (hệ thống trả về message chung).
- `INVALID_SHEET`, `EMPTY_FILE`, `MISSING_FILE`: lỗi ở cấp file (format / không có sheet / không upload file).

---

## Hướng dẫn FE xử lý

- Kiểm tra `HTTP status` trước:
  - Nếu `422` — lấy `body.errors` và render danh sách lỗi cho người dùng (highlight hàng và cột tương ứng).
  - Nếu `207` — lấy `body.data.failed` để hiển thị lỗi hàng, và `body.data.successful` để hiển thị preview các bản ghi đã import thành công.
  - Nếu `200` (`success: true`) — import toàn bộ thành công, `body.data.successful` chứa danh sách cư dân mới.
- Dùng `row` để xác định hàng Excel cần highlight (header = 1). FE nên hi-light ngay ô tương ứng với `field` (nếu `field` tồn tại), hoặc cả hàng nếu `field === '_'`.
- Dùng `message.vi` / `message.en` để hiển thị lỗi tuỳ locale người dùng.
- Không dựa vào các trường khác (ví dụ `value`, `otherRows`, `conflictWith`) — backend hiện trả gói lỗi ngắn gọn `{ field, code, message }`.

### Ví dụ xử lý nhanh (pseudo-code)

```javascript
const body = await res.json();
if (!body.success) {
  // HTTP 422
  renderFileErrors(body.errors);
} else if (body.data && Array.isArray(body.data.failed) && body.data.failed.length > 0) {
  // Partial success (207)
  renderSuccessList(body.data.successful || []);
  renderFileErrors(body.data.failed);
} else {
  // All success
  renderSuccessList(body.data?.successful || []);
}

function renderFileErrors(errors) {
  // errors: [{ row: number, error: [{ field, code, message }] }]
  for (const rowErr of errors) {
    const row = rowErr.row; // Excel row number
    for (const e of rowErr.error) {
      highlightCell(row, e.field);
      showMessage(row, e.field, e.message.vi); // or e.message.en
    }
  }
}
```

---

## Mapping trường import (tham chiếu tên `field`)

- `bed_codes` / `bed_code` — Mã giường (có thể chứa nhiều mã phân cách bằng `,` hoặc `;`).
- `full_name` — Họ và tên.
- `email` — Email.
- `phone` — Số điện thoại.
- `id_card` — Số CCCD/CMND.
- `date_of_birth` — Ngày sinh.
- `nationality` — Quốc tịch.
- `job` — Nghề nghiệp.
- `emergency_contact_name` — Người liên hệ khẩn cấp.
- `emergency_contact_phone` — SĐT người liên hệ khẩn cấp.
- `emergency_contact_relationship` — Mối quan hệ.
- `status` — Trạng thái.
- `move_in_at` — Ngày vào ở.

---

Nếu cần, mình có thể bổ sung:
- Bản mẫu JSON chi tiết hơn cho từng mã lỗi.
- Hướng dẫn UI mockups (highlight/cell tooltip).
- Endpoint contract cập nhật (OpenAPI) tương thích lỗi này.

File: [docs/frontend/residents-import-errors.md](docs/frontend/residents-import-errors.md)
