Trạng thái: Pending / Approved / Rejected

0) Thông tin bài học
0.1. Đối tượng
- Người mới học Power Apps (đã biết thao tác máy tính cơ bản)
- Nhân sự / IT / Vận hành muốn số hóa quy trình OT
0.2. Thời lượng gợi ý
- 2.5 – 3 giờ (thực hành đầy đủ)
0.3. Mục tiêu sau buổi học
Học viên có thể:
- Thiết kế dữ liệu cho quy trình OT (SharePoint List)
- Tạo Canvas App từ đầu
- Tạo màn hình:
- Nhân viên tạo đơn OT
- Nhân viên xem lịch sử OT
- Quản lý duyệt đơn OT (Approve/Reject)
- Thiết lập trạng thái và màu hiển thị theo trạng thái
- Áp dụng các công thức quan trọng:
SubmitForm(),Patch(),Filter(),SortByColumns(),Switch(),If()
- Biết các lỗi hay gặp và cách sửa
1) Business Flow (Luồng nghiệp vụ)
1.1. Vai trò
- Employee (Nhân viên): tạo đơn OT, xem trạng thái
- Manager (Quản lý): duyệt đơn (Approve/Reject), ghi chú
1.2. Luồng xử lý chuẩn
- Nhân viên mở App → tạo đơn OT
- Khi bấm Submit → hệ thống lưu đơn với trạng thái mặc định Pending
- Quản lý mở App → xem danh sách đơn Pending
- Quản lý bấm:
- Approve → Status = Approved
- Reject → Status = Rejected (thường kèm lý do)
- Nhân viên xem lại đơn → thấy trạng thái cập nhật
Ghi chú triển khai thực tế: Có thể mở rộng thêm gửi email/Teams bằng Power Automate, hoặc phân quyền theo phòng ban.
2) Thiết kế dữ liệu (SharePoint List)
Mục tiêu phần này: Tạo đúng cấu trúc dữ liệu để Power Apps hoạt động mượt, dễ lọc và dễ báo cáo.
2.1. Tạo SharePoint List
- Tên List: OvertimeRequests
Cách tạo (từng bước)
- Vào SharePoint Site của bạn
- Chọn New → List
- Chọn Blank list
- Đặt tên:
OvertimeRequests - Create
2.2. Cấu trúc cột (Columns)
SharePoint luôn có sẵn cột Title. Ta tận dụng Title làm Mã đơn.
| Tên cột | Kiểu dữ liệu | Bắt buộc | Ý nghĩa | Ví dụ |
|---|---|---|---|---|
| Title | Single line (mặc định) | Có | Mã đơn OT | OT-001 |
| EmployeeEmail | Single line | Có | Email nhân viên (để lọc “đơn của tôi”) | a@cty.com |
| EmployeeName | Single line | Có | Họ tên nhân viên | Nguyễn Văn A |
| Department | Choice | Có | Phòng ban | IT / HR / Sales |
| OvertimeDate | Date only | Có | Ngày làm thêm | 2026-02-01 |
| StartTime | Date & Time | Có | Giờ bắt đầu | 2026-02-01 18:00 |
| EndTime | Date & Time | Có | Giờ kết thúc | 2026-02-01 21:00 |
| Reason | Multiple lines | Có | Lý do OT | Deploy hệ thống |
| Status | Choice | Có | Trạng thái | Pending/Approved/Rejected |
| ManagerComment | Multiple lines | Không | Ghi chú quản lý khi duyệt | Thiếu người nên OK |
| CreatedBy | Person (optional) | Không | Người tạo (nếu muốn) | (tự động) |
2.2.1. Tạo cột Choice “Department”
- List
OvertimeRequests→ Add column - Chọn Choice
- Name:
Department - Nhập choices:
- IT
- HR
- Sales
- Marketing
- Finance
- Save
2.2.2. Tạo cột Choice “Status”
- Add column → Choice
- Name:
Status - Choices:
- Pending
- Approved
- Rejected
- Default value (nếu có): Pending
- Save
Nếu SharePoint cho chọn Default value thì đặt luôn Pending để giảm lỗi. Nếu không, ta sẽ set Pending trong Power Apps.
2.3. Dữ liệu mẫu (15 dòng)
Mục tiêu: có dữ liệu để test lọc, duyệt, hiển thị màu.
Bạn có thể nhập thủ công 5 dòng, còn lại copy theo mẫu bên dưới.
| Title | EmployeeEmail | EmployeeName | Department | OvertimeDate | StartTime | EndTime | Reason | Status | ManagerComment |
|---|---|---|---|---|---|---|---|---|---|
| OT-001 | a@cty.com | Nguyễn Văn A | IT | 2026-02-01 | 2026-02-01 18:00 | 2026-02-01 21:00 | Deploy hệ thống | Pending | |
| OT-002 | b@cty.com | Trần Thị B | HR | 2026-02-02 | 2026-02-02 17:30 | 2026-02-02 20:00 | Tuyển dụng gấp | Approved | OK |
| OT-003 | c@cty.com | Lê Minh C | Sales | 2026-02-03 | 2026-02-03 18:00 | 2026-02-03 22:00 | Chạy KPI cuối tháng | Rejected | Không đủ lý do |
| OT-004 | d@cty.com | Phạm H D | IT | 2026-02-04 | 2026-02-04 18:00 | 2026-02-04 21:30 | Fix bug production | Pending | |
| OT-005 | e@cty.com | Võ Thị E | Marketing | 2026-02-05 | 2026-02-05 17:00 | 2026-02-05 20:00 | Chạy quảng cáo | Approved | |
| OT-006 | f@cty.com | Hoàng F | IT | 2026-02-06 | 2026-02-06 19:00 | 2026-02-06 23:00 | Backup server | Pending | |
| OT-007 | g@cty.com | Nguyễn G | Sales | 2026-02-07 | 2026-02-07 18:00 | 2026-02-07 21:00 | Gặp khách hàng | Rejected | Không có plan |
| OT-008 | h@cty.com | Trần H | HR | 2026-02-08 | 2026-02-08 17:30 | 2026-02-08 19:30 | Tính lương | Approved | |
| OT-009 | i@cty.com | Lê I | IT | 2026-02-09 | 2026-02-09 18:00 | 2026-02-09 20:30 | Update hệ thống | Pending | |
| OT-010 | j@cty.com | Phạm J | Marketing | 2026-02-10 | 2026-02-10 17:00 | 2026-02-10 19:00 | Event gấp | Pending | |
| OT-011 | k@cty.com | Võ K | IT | 2026-02-11 | 2026-02-11 18:00 | 2026-02-11 22:00 | Testing | Approved | |
| OT-012 | l@cty.com | Hoàng L | Sales | 2026-02-12 | 2026-02-12 18:00 | 2026-02-12 21:00 | Báo cáo | Pending | |
| OT-013 | m@cty.com | Nguyễn M | HR | 2026-02-13 | 2026-02-13 17:30 | 2026-02-13 20:00 | Phỏng vấn | Rejected | Thiếu nhân sự duyệt |
| OT-014 | n@cty.com | Trần N | IT | 2026-02-14 | 2026-02-14 18:00 | 2026-02-14 21:00 | Triển khai | Pending | |
| OT-015 | o@cty.com | Lê O | Marketing | 2026-02-15 | 2026-02-15 17:00 | 2026-02-15 19:30 | Content gấp | Approved | OK |
3) Tạo Canvas App
3.1. Tạo App
- Vào
make.powerapps.com - Chọn Create
- Chọn Canvas app from blank
- Name:
OT Request App - Format: Tablet (khuyến nghị để demo rõ)
- Create
3.2. Kết nối dữ liệu SharePoint
- Mở tab Data (biểu tượng hình database)
- Add data
- Chọn SharePoint
- Chọn Connect (nếu lần đầu)
- Chọn đúng Site
- Chọn List: OvertimeRequests
- Add
Kiểm tra: Trong Data panel phải thấy
OvertimeRequests.
4) Thiết kế giao diện tổng thể (3 màn hình)
Bạn sẽ tạo 3 màn hình chuẩn:
- scr_Request: Nhân viên tạo đơn OT
- scr_MyRequests: Nhân viên xem lịch sử & trạng thái
- scr_Manager: Quản lý duyệt đơn
5) Màn hình 1 — scr_Request (Tạo đơn OT)
5.1. Tạo màn hình
- Tree view → New screen
- Chọn Blank
- Đổi tên Screen:
scr_Request
5.2. Thêm tiêu đề
- Insert → Label
- Text:
Đăng ký làm thêm giờ (OT) - FontSize: 24
- Align: Center
- Width: bằng màn hình
5.3. Thêm Edit Form
- Insert → Forms → Edit form
- Đổi tên form:
frmOT - Ở panel bên phải:
- DataSource:
OvertimeRequests - DefaultMode:
FormMode.New
- DataSource:
5.3.1. Chọn field hiển thị trong Form
- Chọn frmOT → Edit fields
- Add các field:
- Title
- EmployeeEmail
- EmployeeName
- Department
- OvertimeDate
- StartTime
- EndTime
- Reason
- Status (có thể ẩn khỏi người dùng)
- Sắp xếp theo thứ tự hợp lý: thông tin nhân viên → thời gian → lý do
5.4. Tự động set dữ liệu nhân viên
Mục tiêu: tự điền email, tên để giảm sai.
5.4.1. EmployeeEmail Default
- Click DataCard
EmployeeEmail - Unlock (nếu bị khóa)
- Chọn TextInput bên trong (thường tên:
DataCardValue...) - Thuộc tính Default:
User().Email
5.4.2. EmployeeName Default
- Default:
User().FullName
Nếu môi trường không trả FullName, có thể dùng
User().Emailtạm, hoặc lấy từ danh bạ (phần nâng cao).
5.5. Sinh mã đơn (Title) tự động
Cách demo đơn giản: tạo Title dựa trên thời gian.
- Chọn DataCard
Title→ Unlock - Chọn TextInput trong Title → Default:
"OT-" & Text(Now(), "yyyymmdd-hhnnss")
Lưu ý: Đây là mã duy nhất theo thời gian, đủ cho demo. Nếu cần dạng OT-0001 chạy số, sẽ làm ở phần nâng cao.
5.6. Đặt Status mặc định = Pending và không cho sửa
5.6.1. Default của Status
- Chọn DataCard
Status→ Unlock - Chọn ComboBox/Dropdown trong DataCard (thường là ComboBox)
- Thuộc tính DefaultSelectedItems (với Choice):
[{Value:"Pending"}]
5.6.2. Ẩn Status khỏi nhân viên
Chọn DataCard Status → thuộc tính Visible:
false
5.7. Nút Submit (Gửi đơn)
- Insert → Button
- Text:
Gửi đơn OT - Đổi tên:
btnSubmit
5.7.1. OnSelect của btnSubmit
SubmitForm(frmOT)
5.7.2. frmOT – OnSuccess (sau khi lưu thành công)
Chọn frmOT → thuộc tính OnSuccess:
Notify("Gửi đơn OT thành công! Trạng thái: Pending", NotificationType.Success);
NewForm(frmOT)
5.7.3. frmOT – OnFailure (nếu lưu lỗi)
frmOT → OnFailure:
Notify("Gửi đơn thất bại. Vui lòng kiểm tra dữ liệu hoặc kết nối!", NotificationType.Error)
6) Màn hình 2 — scr_MyRequests (Lịch sử đơn của tôi)
6.1. Tạo màn hình
New screen → Blank → đổi tên: scr_MyRequests
6.2. Thêm tiêu đề
Label Text: Đơn OT của tôi
6.3. Thêm Dropdown lọc trạng thái
Insert → Dropdown → đổi tên drpStatus
- Items:
["All","Pending","Approved","Rejected"]
- Default:
"All"
6.4. Thêm Gallery hiển thị đơn
Insert → Vertical gallery → đổi tên galMyOT
6.4.1. Items của galMyOT (lọc theo người dùng + trạng thái)
With(
{ _email: User().Email },
If(
drpStatus.Selected.Value = "All",
SortByColumns(
Filter(OvertimeRequests, EmployeeEmail = _email),
"Created",
SortOrder.Descending
),
SortByColumns(
Filter(
OvertimeRequests,
EmployeeEmail = _email && Status.Value = drpStatus.Selected.Value
),
"Created",
SortOrder.Descending
)
)
)
6.4.2. Hiển thị thông tin trong Gallery
Trong template gallery, thêm các label:
lblEmpName: ThisItem.EmployeeNamelblDate: Text(ThisItem.OvertimeDate, “dd/mm/yyyy”)lblTime: Text(ThisItem.StartTime, “hh:mm”) & ” – ” & Text(ThisItem.EndTime, “hh:mm”)lblStatus: ThisItem.Status.Value
6.4.3. Màu trạng thái
Chọn label Status → thuộc tính Color:
Switch(
ThisItem.Status.Value,
"Pending", ColorValue("#F59E0B"), // cam
"Approved", ColorValue("#10B981"), // xanh lá
"Rejected", ColorValue("#EF4444"), // đỏ
Black
)
7) Màn hình 3 — scr_Manager (Duyệt OT)
7.1. Tạo màn hình
New screen → Blank → đổi tên scr_Manager
7.2. Tiêu đề
Label Text: Duyệt đơn OT
7.3. Dropdown lọc nhanh
Insert → Dropdown → drpManagerFilter
- Items:
["Pending","Approved","Rejected","All"]
- Default:
"Pending"
7.4. Gallery danh sách đơn để duyệt
Insert → Vertical gallery → galApprove
7.4.1. Items
If(
drpManagerFilter.Selected.Value = "All",
SortByColumns(OvertimeRequests, "Created", SortOrder.Descending),
SortByColumns(
Filter(OvertimeRequests, Status.Value = drpManagerFilter.Selected.Value),
"Created",
SortOrder.Descending
)
)
7.5. Chọn 1 đơn → hiển thị chi tiết
Cách dễ dạy: dùng Display Form bên cạnh gallery.
- Insert → Forms → Display form
- Đổi tên:
frmDetail - DataSource:
OvertimeRequests - Item:
galApprove.Selected
- Fields: EmployeeName, Department, OvertimeDate, StartTime, EndTime, Reason, Status, ManagerComment
7.6. Ô nhập “ManagerComment” khi Reject
Cách nhanh: đặt 1 TextInput riêng (không bind trực tiếp form).
- Insert → Text input (Multiline) →
txtComment - HintText:
Nhập lý do/ghi chú của quản lý...
7.7. Nút Approve / Reject
7.7.1. Nút Approve
Insert → Button → Text: Approve
OnSelect:
Patch(
OvertimeRequests,
galApprove.Selected,
{
Status: {Value:"Approved"},
ManagerComment: txtComment.Text
}
);
Notify("Đã duyệt đơn!", NotificationType.Success);
Reset(txtComment)
7.7.2. Nút Reject (bắt buộc nhập lý do)
Insert → Button → Text: Reject
OnSelect:
If(
IsBlank(Trim(txtComment.Text)),
Notify("Vui lòng nhập lý do từ chối (ManagerComment)!", NotificationType.Warning),
Patch(
OvertimeRequests,
galApprove.Selected,
{
Status: {Value:"Rejected"},
ManagerComment: txtComment.Text
}
);
Notify("Đã từ chối đơn!", NotificationType.Error);
Reset(txtComment)
)
8) Điều hướng (Menu) giữa các màn hình
8.1. Thêm 3 nút ở đầu App (hoặc dùng icon)
- Button 1: “Tạo đơn” → OnSelect:
Navigate(scr_Request, ScreenTransition.Fade)
- Button 2: “Đơn của tôi” → OnSelect:
Navigate(scr_MyRequests, ScreenTransition.Fade)
- Button 3: “Duyệt (Manager)” → OnSelect:
Navigate(scr_Manager, ScreenTransition.Fade)
Thực tế cần phân quyền (chỉ Manager mới thấy). Phần này sẽ làm ở nâng cao.
9) Kiểm thử theo kịch bản (Checklist cho học viên)
9.1. Test tạo đơn
- Mở scr_Request
- Nhập Department, OvertimeDate, StartTime, EndTime, Reason
- Bấm Submit
✅ Kỳ vọng:
- Lưu được record trong SharePoint
- Status = Pending
- App báo “Gửi đơn thành công”
9.2. Test xem lịch sử
- Mở scr_MyRequests
- Chọn filter All/Pending/Approved/Rejected
✅ Kỳ vọng:
- Chỉ thấy đơn của email hiện tại
- Lọc đúng theo trạng thái
9.3. Test duyệt
- Mở scr_Manager
- Chọn 1 record Pending
- Nhập comment → Approve
✅ Kỳ vọng:
- Status đổi Approved
- Comment được lưu
- Chọn 1 record Pending → Reject (không nhập comment)
✅ Kỳ vọng:
- Bị cảnh báo yêu cầu nhập lý do
10) Lỗi thường gặp & cách xử lý (rất quan trọng)
10.1. Lỗi “Status không cập nhật”
Hiện tượng: bấm Approve nhưng Status không đổi
Nguyên nhân: Cột Status là Choice → phải patch theo dạng record {Value:"..."}
✅ Cách sửa:
Status: {Value:"Approved"}
10.2. Lỗi “Gallery không lọc được theo Status”
Nguyên nhân: So sánh sai kiểu. Status là Choice → phải dùng Status.Value
✅ Cách sửa:
Filter(OvertimeRequests, Status.Value = "Pending")
10.3. Lỗi SubmitForm không lưu
Nguyên nhân thường gặp:
- Form chưa ở chế độ New
- Thiếu field bắt buộc
✅ Cách sửa: - frmOT.DefaultMode = FormMode.New
- Kiểm tra Required field trong SharePoint
10.4. Lỗi thời gian Start/End sai ngày
Nguyên nhân: người dùng chọn giờ nhưng date bị lệch
✅ Cách giảm lỗi:
- Dùng DatePicker cho ngày + Dropdown giờ riêng (nâng cao)
- Hoặc hướng dẫn nhập đúng ngày cho Start/End