# Phân quyền theo vai — màn Kho tài liệu

> Tài liệu con của [`KHO-TAI-LIEU.md`](./KHO-TAI-LIEU.md).
>
> 📌 File này chứa **logic ẩn/hiện & chặn truy cập theo vai** — thứ không nhìn thấy được khi mở màn hình.
> Các file mô tả màn hình (`KHO-TAI-LIEU.md`, `*.MODAL-*.md`) chỉ ghi **những gì hiển thị trên màn**;
> mọi thứ phụ thuộc vai/quyền thì ghi ở đây, và **test case mới là nơi khẳng định** hành vi đó.
>
> Khảo sát 2026-07-27 bằng 2 tài khoản: `ecm01` (Owner của BHS) và `ecm06` ("Hoàng Văn Mạnh").

> 📌 **Phạm vi tài liệu**: chỉ ghi **hành vi quan sát được trên màn hình theo vai** (element nào
> ẩn/hiện, vai nào mở được màn nào) — tức vẫn là thông tin mô tả màn hình, chỉ khác là phụ thuộc vai.
> **Không** ghi logic test / kỳ vọng của case / quy tắc nghiệp vụ vào đây; chỗ của chúng là spec
> `tests/<mục>/`, `<mục>.steps.ts` và `<mục>.md`.

---

## 0. Quy ước đọc tiền điều kiện của testcase

Xem thêm quy ước chung toàn dự án ở `CLAUDE.md` (mục "Đọc tiền điều kiện của testcase QA").

- Tiền điều kiện QA ghi vai nào có quyền gì thì **coi như vai đó thực hiện được tới bước "Mong muốn"** —
  viết thẳng case theo mô tả, không tự đặt kỳ vọng "vai này chắc bị chặn".
- Quyền ở **cấp lớn hơn bao trùm cấp nhỏ hơn**: có quyền Cập nhật trên **BHS** thì cũng có quyền
  Cập nhật trên **TM/TL** bên trong nó (và ngược lại thì không).
- Bảng dưới đây là **dữ liệu khảo sát**, dùng để dựng tiền điều kiện cho đúng — không dùng để
  suy diễn rồi đổi kỳ vọng của testcase.

---

## 1. 🚨 Điều kiện tiên quyết: BHS phải ở trạng thái "Hoạt động"

| Trạng thái BHS | Vai              | Kết quả khi `goto(recordUrl)`                                          |
| -------------- | ---------------- | ------------------------------------------------------------------------ |
| `Khai báo`     | Owner            | Mở được modal chi tiết                                                   |
| `Khai báo`     | VIEW cấp BHS     | ❌ **Modal KHÔNG mở** + toast `"Bạn không có quyền xem!"`                |
| `Hoạt động`    | VIEW cấp BHS     | ✅ Mở được modal chi tiết                                                |
| `Hoạt động`    | Không có quyền gì | ❌ **Modal KHÔNG mở** + toast `"Bạn không có quyền xem!"`               |

Đã kiểm chứng trực tiếp: cùng 1 BHS, cùng 1 quyền VIEW cấp BHS cho `ecm06` —
ở `Khai báo` thì bị chặn, sau khi `chuyenHoatDong` thì mở được.

> ⚠️ **Mọi case chạy bằng vai không phải Owner BHS đều phải gọi `chuyenHoatDong` trước.**

**Assert trường hợp bị chặn:**

```ts
// ⚠️ Toast chỉ tồn tại ~5 giây kể từ lúc điều hướng → KHÔNG waitForTimeout dài trước khi assert.
await expect(page.locator(".ant-message-notice")).toContainText(
  "Bạn không có quyền xem!",
  { timeout: TIMEOUT.ACTION_LOADING },
);
await expect(page.locator(".ant-modal-content:visible")).toHaveCount(0);
```

> ✅ Dùng hàm **`expectKhongCoQuyenXem(page)`**.
> Không dùng `.ant-notification-notice-message` — build này dùng toast `.ant-message-notice`.

---

## 2. Element hiển thị theo vai (BHS ở trạng thái "Hoạt động")

| Element                          | Owner BHS | VIEW cấp BHS | Chỉ có quyền ở 1 thư mục con |
| -------------------------------- | :-------: | :----------: | :--------------------------: |
| Mở được modal chi tiết           |     ✔     |      ✔       |              ✔               |
| `btn-save`                       |     ✔     |      ❌      |              ❌              |
| `btn-chuyen-luu-tru`             |     ✔     |      ❌      |              ❌              |
| `btn-chuyen-hoat-dong`           |    (✔)    |      ❌      |              ❌              |
| **`btn-more`** (menu "...")      |     ✔     |    **❌**    |            **❌**            |
| → `btn-kiem-tra-phan-quyen`      |     ✔     |      ❌      |              ❌              |
| Tab `lbl-tab-thongTin`           |     ✔     |      ✔       |              ✔               |
| Tab `lbl-tab-cauTrucHoSo`        |     ✔     |      ✔       |              ✔               |
| **Tab `lbl-tab-luuTru`**         |     ✔     |    **❌**    |            **❌**            |
| Tab `tab-lich-su`                |     ✔     |      ✔       |              ✔               |
| `btn-tao-moi` (tab Cấu trúc)     |     ✔     |      ❌      |              ❌              |
| `btn-more` (tab Cấu trúc)        |     ✔     |      ❌      |              ❌              |
| `txt-search-ho-so-tai-lieu`, `sel-phan-loai`, `btn-hien-thi-bo-loc`, `btn-xoa-bo-loc`, `btn-dong-bo-loc` | ✔ | ✔ | ✔ |

> Vì `btn-more` **không tồn tại** với vai VIEW, modal Phân quyền nâng cao và tab "Theo người dùng"
> cũng không truy cập được. Assert theo hướng
> `await expect(modal.getByTestId("btn-more")).toHaveCount(0)`.

---

## 3. Lọc dữ liệu theo quyền trong tab "Cấu trúc hồ sơ"

Vai chỉ có quyền trên **một** thư mục con → bảng Cấu trúc hồ sơ **chỉ hiện đúng nhánh đó**.

Kiểm chứng: BHS có 3 item gốc (`A. thư mục`, `B. tài liệu`, `C. thư mục`); `ecm06` chỉ có `VIEW`
trên `C` → chỉ thấy đúng 1 dòng `C. AT-TM-DOCLAP-7830`.

```ts
const names = await getActiveTabPane(ecm06Page)
  .locator('[data-testid^="lnk-ten-ho-so-tai-lieu-"]')
  .allInnerTexts();
expect(names).toEqual(["C. AT-TM-DOCLAP-7830"]);
```

---

## 4. Dựng tiền điều kiện "vai X có quyền Y tại TM/TL"

Cấp quyền trực tiếp lên 1 TM/TL **buộc phải ngắt kế thừa trước** (modal Phân quyền nâng cao:
item đang kế thừa chỉ có nút "Ngắt kế thừa", chưa có nút "Thêm"). Vì vậy:

| Cần                                                   | Cách dựng                                                                                      |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Vai X có quyền tại TM, TM **vẫn đang kế thừa**        | Cấp quyền ở **cấp cha** rồi để TM kế thừa xuống: cấp ở BHS lúc `createBoHoSo`, hoặc tạo TM cha `quyenDocLap` + `perm` rồi tạo TM con với `parentName` |
| Vai X có quyền tại TM, TM **đã có quyền riêng**       | `openPhanQuyenNangCao` → `ngatKeThua(code)` → `themQuyenChoNguoiDung(code, account, quyen)`      |
| Vai X là **nhóm người dùng**                          | Điền **tên nhóm** vào people-picker y hệt khi điền user (gõ tên → Enter, tìm kiếm nhanh)         |

---

## 5. ⚠️ Chưa khảo sát

- Vai **ADD / EDIT / DOWNLOAD** cấp BHS: chưa thử → chưa rõ `btn-save` / `btn-more` có hiện không.
- Vai **Admin (`ecm09`)** và **Thủ thư kho khác**: chưa thử.
- Menu hành động của **dòng** trong bảng Cấu trúc hồ sơ (`mni-cap-nhat`, `mni-xoa`…) với vai
  không phải Owner BHS: chưa thử.
- Tab **"Theo người dùng"** của modal Phân quyền nâng cao: mới xác nhận Owner BHS thì có;
  chưa có vai nào khác mở được modal để đối chứng.
