# UC29 — Màn hình "Kho tài liệu" / Tạo mới Hồ sơ (Managed Records)

> **Phần dùng chung cả dự án** (fixtures/accounts, TIMEOUT, helper `PW`, import chuẩn,
> selectors AntD, pattern khai báo test) nằm ở: **[../README.md](../README.md)** — đọc file đó trước.
>
> File này chỉ mô tả phần **đặc thù UC29**: URL, các trường của form, button, 2 pop-up liên quan,
> pattern thao tác riêng và danh mục test case.

---

## 1. Thông tin riêng UC29

| Mục              | Giá trị                                       |
| ---------------- | --------------------------------------------- |
| URL màn hình     | `/managed-records` (hằng `uc29DefaultUrl`)    |
| Dữ liệu mẫu      | `uc29DefaultData`                             |
| File data        | [uc29.default.data.ts](uc29.default.data.ts)  |
| Tiền tố tên test | `"UC29 1.1.2.<nhóm>.<stt> - <mô tả> - <vai>"` |

---

## 2. Form "Tạo mới Hồ sơ" — danh sách input (`uc29DefaultData`)

Thứ tự field theo [uc29.default.data.ts](uc29.default.data.ts). `batchInput()` tự route theo prefix testId
(xem bảng prefix ở [../README.md](../README.md#4b-quy-ước-prefix-testid--control-dùng-bởi-batchinput)).

| #   | testId                        | Loại                   | required | Giá trị mẫu                       | Ghi chú                                                                       |
| --- | ----------------------------- | ---------------------- | :------: | --------------------------------- | ----------------------------------------------------------------------------- |
| 1   | `txt-tenHoSo`                 | text                   |    ✔     | `"Hồ sơ tự động " + timestamp`    | Tên hồ sơ. Override khi cần tên unique                                        |
| 2   | `tree-sel-mucLuuTru`          | tree-select            |    ✔     | (đầu tiên)                        | Mục lưu trữ                                                                   |
| 3   | `sel-companyInvestor`         | select                 |    ✔     | (đầu tiên)                        | Công ty / Chủ đầu tư                                                          |
| 4   | `tree-sel-owner-department`   | tree-select            |    ✔     | (đầu tiên)                        | Đơn vị sở hữu                                                                 |
| 5   | `sel-loaiBoHoSo`              | select                 |    ✔     | (đầu tiên)                        | Loại hồ sơ. Nếu loại "dự án" → hiện thêm `sel-duAn` (dùng `checkBranch=true`) |
| 6   | `sel-cauTrucHoSo`             | select                 |          | (đầu tiên)                        | Cấu trúc hồ sơ                                                                |
| 7   | `sel-securityLevel`           | select                 |          | (đầu tiên)                        | Độ mật                                                                        |
| 8   | `txt-soHieuHoSo`              | text                   |          | `"123456"`                        | Số hiệu hồ sơ                                                                 |
| 9   | `sel-trangThai`               | select                 |          | (đầu tiên)                        | Trạng thái hiển thị. Vd `"Private"` → KHÔNG xuất hiện trong search liên quan  |
| 10  | `sel-storageTerm`             | select                 |          | (đầu tiên)                        | Thời hạn lưu trữ                                                              |
| 11  | `sel-hardCopyStatus`          | select                 |    ✔     | (đầu tiên)                        | Trạng thái bản cứng                                                           |
| 12  | `sel-tags-tuKhoa`             | tags (multi)           |          | `"từ khóa 1,từ khóa 2,từ khóa 3"` | Từ khóa                                                                       |
| 13  | `txa-ghiChu`                  | textarea               |          | `"Ghi chú tự động"`               | Ghi chú                                                                       |
| 14  | `pp-multi-usersRightOwner`    | people-picker          |          | `"ecm05,nhóm"`                    | Quyền Owner. **Thường tách riêng** (xem mục 5)                                |
| 15  | `pp-multi-usersRightAdd`      | people-picker          |          | `"ecm06,nhóm"`                    | Quyền Thêm                                                                    |
| 16  | `pp-multi-usersRightEdit`     | people-picker          |          | `"ecm07,nhóm"`                    | Quyền Sửa                                                                     |
| 17  | `pp-multi-usersRightDownload` | people-picker          |          | `"ecm08,nhóm"`                    | Quyền Tải                                                                     |
| 18  | `pp-multi-usersRightViewers`  | people-picker          |          | `"ecm09,nhóm"`                    | Quyền Xem                                                                     |
| 19  | `btn-add-related-ecm`         | nút mở popup liên quan |          | —                                 | Xem mục 4. Trong `batchInput` sẽ gọi `inputRelatedECM` (chọn ecm đầu tiên)    |

---

## 3. Button / element riêng của màn UC29

| testId / locator                               | Mô tả                                                                       |
| ---------------------------------------------- | --------------------------------------------------------------------------- |
| `btn-create-hstl`                              | Nút **Tạo mới** trên màn danh sách                                          |
| `btn-save`                                     | Nút **Lưu** trong form tạo/cập nhật                                         |
| `btn-close-modal`                              | Nút đóng modal form                                                         |
| `btn-add-related-ecm`                          | Nút **+ Thêm thông tin liên quan** (dropdown: Hồ sơ / Tài liệu)             |
| `btn-add-related`                              | Nút **Thêm** trong pop-up tìm kiếm liên quan (chỉ hiện khi đã chọn ≥1 dòng) |
| `lbl-tab-thongTin`                             | Tab "Thông tin" ở màn chi tiết / cập nhật hồ sơ                             |
| `related-item-table`                           | Bảng HS/TL liên quan đã thêm vào form                                       |
| `.cssMaHSTLChild` (trong `related-item-table`) | Cell mã HS/TL của từng dòng đã liên kết                                     |

---

## 4. Hai pop-up "Thông tin liên quan" (điểm dễ nhầm nhất)

Nút `btn-add-related-ecm` là **dropdown** 2 nhánh: **Hồ sơ liên quan** và **Tài liệu liên quan**.
Tiêu đề, search placeholder và cột bảng khác nhau giữa 2 loại.

### 4a. Pop-up "Tìm **hồ sơ** liên quan" — mở bằng **click trực tiếp**

```ts
await pw.clickButton("btn-add-related-ecm");
await page
  .getByText("Tìm hồ sơ liên quan")
  .waitFor({ timeout: TIMEOUT.ACTION_LOADING });
```

- Search placeholder: `Nhập tên hồ sơ, số hồ sơ`
- Cột bảng: `Mã hồ sơ`, `Tên hồ sơ`, `Độ mật`, `Thư mục lưu trữ`, `Dự án`

### 4b. Pop-up "Tìm **tài liệu** liên quan" — mở bằng **hover dropdown → click**

```ts
const parent = page
  .locator('[data-testid="btn-add-related-ecm"]')
  .locator("..");
await parent.locator(".ant-dropdown-trigger").hover();
await page.getByText("Tài liệu liên quan").click();
await expect(page.getByText("Tìm tài liệu liên quan")).toBeVisible({
  timeout: TIMEOUT.DATA_LOADING,
});
```

- Search placeholder: `Nhập tên tài liệu, mã tài liệu`
- Cột bảng: `Mã tài liệu`, `Tên tài liệu`, `Độ mật`, `Loại tài liệu`, `Đơn vị sở hữu`

### Selectors / hành vi dùng chung trong cả 2 pop-up

- Modal/row/checkbox/pagination dùng selector AntD chung — xem [../README.md mục 5](../README.md#5-selectors-antd-dùng-chung).
- Nút xác nhận chọn: `getByTestId("btn-add-related")` — **chỉ visible khi đã chọn ≥1 dòng** (dùng cho case "không cho thêm khi chưa chọn").
- Mặc định 10 items/trang.

### Pattern chọn 1 dòng trong pop-up

```ts
const modalContent = page.locator(".ant-modal-content:visible").last();
await modalContent
  .locator(".ant-table-row.ant-table-row-level-0")
  .first()
  .hover();
await modalContent
  .locator(".ant-table-row.ant-table-row-level-0 .ant-checkbox-wrapper")
  .first()
  .click();
await expect(page.getByTestId("btn-add-related")).toBeVisible({
  timeout: TIMEOUT.CONTROL_LOADING,
});
await pw.clickButton("btn-add-related");
await pw.isVisible("related-item-table");
```

### Pattern tìm kiếm trong pop-up

```ts
//Pop-up Hồ sơ liên quan
const searchInput = modalContent.locator(
  'input[placeholder="Nhập tên hồ sơ, số hồ sơ"]',
);
//Pop-up Tài liệu liên quan
const searchInput = modalContent.locator(
  'input[placeholder="Nhập tên tài liệu, mã tài liệu"]',
);
await searchInput.fill(keyword);
await searchInput.press("Enter");
await page.waitForTimeout(10000); // kết quả load chậm
```

### Pattern chọn 1 hồ sơ/tài liệu **cụ thể** làm liên quan (search theo tên → tick)

> ⚠️ **Bẫy quan trọng:** `inputRelatedECM(testId, order?)` và `btn-add-related-ecm` khi đi qua `batchInput`
> **chỉ tick dòng ĐẦU tiên** trong pop-up (không tìm theo tên). Muốn liên kết tới **một phiếu xác định**
> thì phải **lọc `btn-add-related-ecm` ra khỏi `batchInput`** rồi mở pop-up và search thủ công như dưới đây.
> (Cũng nhờ vậy, nếu muốn phiếu được tạo **không có HS/TL liên quan nào**, chỉ cần lọc `btn-add-related-ecm`.)

```ts
await pw.clickButton("btn-add-related-ecm");
await page
  .getByText("Tìm hồ sơ liên quan")
  .waitFor({ timeout: TIMEOUT.ACTION_LOADING });

const modalContent = page.locator(".ant-modal-content:visible").last();
const searchInput = modalContent.locator(
  'input[placeholder="Nhập tên hồ sơ, số hồ sơ"]',
);
await searchInput.fill(targetName);
await searchInput.press("Enter");
await page.waitForTimeout(10000);

const firstRow = modalContent
  .locator(".ant-table-row.ant-table-row-level-0")
  .first();
await expect(firstRow).toContainText(targetName, { timeout: TIMEOUT.DATA_LOADING }); // đúng phiếu cần
await firstRow.hover();
await modalContent
  .locator(".ant-table-row.ant-table-row-level-0 .ant-checkbox-wrapper")
  .first()
  .click();
await expect(page.getByTestId("btn-add-related")).toBeVisible({
  timeout: TIMEOUT.CONTROL_LOADING,
});
await pw.clickButton("btn-add-related");
await pw.isVisible("related-item-table");
```

### Pattern đọc / đếm dòng trong `related-item-table` (verify đã liên kết)

```ts
const relatedRows = page
  .getByTestId("related-item-table")
  .locator(".cssMaHSTLChild"); // mỗi dòng = 1 cell "Mã HS/TL"
await expect(relatedRows).toHaveCount(1); // số dòng = số lần đã thêm
const code = ((await relatedRows.first().textContent()) || "").trim(); // mã HS/TL đã link
```

---

## 5. Pattern "Tạo mới 1 bộ hồ sơ hoàn chỉnh"

```ts
await page.goto(`${BASE_URL}${uc29DefaultUrl}`);
await pw.isVisible("btn-create-hstl", TIMEOUT.PAGE_LOADING);
await pw.wait(TIMEOUT.HARD_WAITING);
await pw.clickButton("btn-create-hstl");

// Lọc Owner (xử lý riêng); lọc txt-tenHoSo nếu cần tên unique
await pw.batchInput(
  uc29DefaultData.filter(
    (o) =>
      o.testId !== "pp-multi-usersRightOwner" && o.testId !== "txt-tenHoSo",
  ),
  true, // checkBranch: xử lý sel-duAn khi loại hồ sơ là dự án
);
await pw.inputText("txt-tenHoSo", uniqueName);
await pw.inputPeoplePicker("pp-multi-usersRightOwner", "ecm05");
await pw.clickButton("btn-save");
await expect(page.locator(".ant-message-success")).toBeVisible({
  timeout: TIMEOUT.ACTION_LOADING,
});
```

- Tên unique: ``const uniqueName = `AT-HSTL-UNIQUE-${Date.now()}`;`` (biến thể ký tự đặc biệt: `` `AT-HSTL-@#${Date.now()}_!$` ``).
- Sau khi lưu, form chuyển sang **màn Cập nhật**. Có thể đóng bằng `btn-close-modal` rồi mở lại `btn-create-hstl`,
  hoặc lấy `page.url()` để mở lại bằng tài khoản khác (kiểm tra quyền — vào tab `lbl-tab-thongTin` trước).
- **`pp-multi-usersRightOwner`** nên lọc khỏi `batchInput` và gọi riêng `inputPeoplePicker(...)` cho ổn định.
  Owner truyền chuỗi `"ecm05"` là đủ (dùng được với fixture `librarian`/`admin`, **không cần** inject fixture `ecm05`).

### Lưu URL phiếu vừa tạo & mở lại sau đó

```ts
// ngay sau khi .ant-message-success hiện (form đã ở màn Cập nhật):
await pw.wait(TIMEOUT.ACTION_LOADING);
const recordUrl = page.url(); // URL phiếu vừa tạo
await pw.clickButton("btn-close-modal");
await expect(page.getByTestId("btn-close-modal")).not.toBeVisible({
  timeout: TIMEOUT.ACTION_LOADING,
});

// ...mở lại phiếu (cùng hoặc khác tài khoản):
await page.goto(recordUrl);
await pw.wait(TIMEOUT.HARD_WAITING);
await pw.clickButton("lbl-tab-thongTin"); // vào tab Thông tin trước khi thao tác/đọc related-item-table
```

## 6. Lưu ý nhanh đặc thù UC29

1. **Phân biệt 2 pop-up** (mục 4): hồ sơ = click trực tiếp; tài liệu = hover dropdown → "Tài liệu liên quan". Placeholder & cột bảng khác nhau.
2. Nút `btn-add-related` chỉ visible khi đã chọn ≥1 dòng → dùng làm assert cho case "không cho thêm khi chưa chọn".
3. Hồ sơ trạng thái `Private` không xuất hiện trong kết quả tìm liên quan.
4. `pp-multi-usersRightOwner` nên tách khỏi `batchInput`.
5. (Các lưu ý chung: hard wait, hover trước khi tick, wait 10s sau search… xem [../README.md mục 6](../README.md#6-lưu-ý-chung-khi-viết-case).)
