PerPro API Documentation

Quy trình Phê duyệt Tờ trình & Mobile Gateway

Tài liệu hướng dẫn tích hợp các API quy trình phê duyệt thông qua Mobile Gateway Transaction Codes (MBGW TranCode) dành cho Website Client và ứng dụng di động.

6 MBGW APIs PP_PROPOSAL_* TranCodes
2 Nhóm API Danh sách Phê duyệt & Review
8 Actions Thao tác Phê duyệt & Review
PER_PRO MBGW Integration System

1. Danh sách Phê duyệt & Review

POST /api/v1/private/proposal-review-inbox/list TranCode: PP_PROPOSAL_CHECK_LIST — Lấy danh sách tờ trình phê duyệt/review PerPro theo 5 tab nghiệp vụ
Inbox
Lấy danh sách các tờ trình trong hộp thư phê duyệt/thẩm định của user hiện tại, phân chia theo 5 tab nghiệp vụ (PENDING_APPROVAL, PENDING_REVIEW, DELEGATED, DELEGATED_BY_ME, PROCESSED).
Mặc định (khi không truyền hoặc truyền ALL), hệ thống sẽ lấy hợp nhất 4 nhóm công việc cần xử lý / theo dõikhông bao gồm nhóm đã xử lý (PROCESSED).

Quy tắc kiểm tra & Ràng buộc nghiệp vụ (Validation Rules)

1. Phân quyền & Xác thực truy cập
  • Bắt buộc truyền Access Token qua Header (Authorization: Bearer <token>).
  • Quyền IAM yêu cầu: User phải có ít nhất 1 trong 3 quyền: PROPOSAL.APPROVE, PROPOSAL.REVIEW hoặc PROPOSAL.CREATE.
  • Người thực hiện (Actor) được trích xuất tự động từ Access Token của phiên đăng nhập (Client không cần truyền thêm thông tin user định danh trong payload).
2. Ý nghĩa của 5 Filter Tab và ALL
  • PENDING_APPROVAL (Chờ tôi phê duyệt): Các tờ trình có bước Phê duyệt (APPROVE) được gán trực tiếp cho bạn và chưa hoàn tất. Nếu bạn đã ủy quyền bước này cho người khác (và ủy quyền còn hiệu lực), tờ trình sẽ tự động chuyển sang tab "Tôi đã ủy quyền".
  • PENDING_REVIEW (Chờ tôi thẩm định): Các tờ trình có bước Thẩm định (REVIEW) được gán trực tiếp cho bạn và chưa hoàn tất.
  • DELEGATED (Được ủy quyền duyệt thay): Các tờ trình mà bạn được người khác ủy quyền xử lý thay, giấy ủy quyền đang còn hiệu lực và bước đó chưa hoàn tất.
  • DELEGATED_BY_ME (Tôi đã ủy quyền): Danh sách các tờ trình do bạn trực tiếp phụ trách nhưng bạn đã bàn giao ủy quyền cho người khác xử lý (dùng để theo dõi tiến độ công việc đã ủy quyền).
  • PROCESSED (Đã xử lý xong): Danh sách các tờ trình mà bạn đã trực tiếp tham gia xử lý hoàn tất (hoặc nhận ủy quyền và đã xử lý xong). Mỗi tờ trình chỉ hiển thị 1 dòng duy nhất.
    * Lưu ý: Tab này hoạt động độc lập và KHÔNG nằm trong bộ lọc ALL. Client bắt buộc phải truyền rõ ràng filter: ["PROCESSED"] khi muốn xem danh sách này.
  • ALL (hoặc mảng rỗng [] / không truyền): Tự động lấy hợp nhất 4 nhóm công việc cần xử lý / theo dõi (PENDING_APPROVAL, PENDING_REVIEW, DELEGATED, DELEGATED_BY_ME) trong một query tối ưu. ALL tuyệt đối không bao gồm PROCESSED.
  • Kết hợp nhiều Filter: Client có thể truyền nhiều filter cùng lúc trong mảng (VD: ["PENDING_APPROVAL", "PENDING_REVIEW"]), hệ thống sẽ tự động gộp và loại bỏ các bước/tờ trình trùng lặp.
  • Trường sourceFilter (Nguồn gốc Filter của bản ghi): Trả về mã tab/bộ lọc nguồn mà bản ghi này xuất phát từ đó (PENDING_APPROVAL, PENDING_REVIEW, DELEGATED, DELEGATED_BY_ME, hoặc PROCESSED). Trường này giúp Client/Frontend dễ dàng phân loại, hiển thị badge/tag nguồn hoặc xử lý điều hướng khi người dùng lọc nhiều tab cùng lúc hoặc dùng chế độ tổng hợp ALL.
  • Phân định Người được giao & Người nhận ủy quyền:
    • assigneeEmailassigneeName: Luôn là snapshot người được giao trực tiếp cho bước duyệt (không bị ghi đè khi có ủy quyền).
    • delegatedApproverEmaildelegatedApproverName: Trả về thông tin của người được ủy quyền khi bước có giấy ủy quyền còn hiệu lực; nếu không có ủy quyền thì trả về null.
    • Lưu ý Frontend: Khi isDelegated = true, UI ưu tiên hiển thị tên người được ủy quyền để người dùng nắm rõ ai đang xử lý thay.
  • Trường canProcess (Quyền thao tác): Trả về true (1) khi bước công việc đang thực sự đến lượt xử lý (status = PROCESSING). Ở nhóm "Tôi đã ủy quyền" và "Đã xử lý", giá trị này luôn trả về false (0).
3. Thông tin người tạo tờ trình (Maker Profile Snapshot)
  • Mỗi bản ghi trong items trả về đầy đủ bộ thông tin định danh snapshot của người tạo tờ trình:
    • makerEmail (String): Email người tạo tờ trình.
    • makerName (String): Họ và tên người tạo tờ trình.
    • makerDepartment (String / null): Phòng ban của người tạo tờ trình.
    • makerTitle (String / null): Chức danh của người tạo tờ trình.
    • makerCompany (String / null): Đơn vị / Công ty của người tạo tờ trình.

Mô tả Payload (Request)

Field Type Description
filter Array<String> Optional Một hoặc nhiều mã: PENDING_APPROVAL, PENDING_REVIEW, DELEGATED, DELEGATED_BY_ME, PROCESSED, ALL. Mặc định (hoặc khi chọn ALL), hệ thống lấy 4 nhóm công việc cần xử lý (không bao gồm PROCESSED). Chuỗi đơn vẫn được nhận tạm thời để tương thích client cũ.
keyword String Optional Từ khóa tìm kiếm không phân biệt hoa thường trên Số tờ trình (number) và Trích yếu (subjectMatter).
currentPage Integer Optional Trang hiện tại (mặc định: 1, số bản ghi mỗi trang cố định perPage = 10).

Request Sample

JSON - Request Body
{
    "filter": ["PENDING_APPROVAL"],
    "keyword": "0045/2026",
    "currentPage": 1
}

Response Sample (Thành công 200 OK)

JSON - Response 200 OK
{
    "success": true,
    "code": "200",
    "message": "Lấy danh sách phê duyệt/review thành công.",
    "data": {
        "items": [
            {
                "proposalId": 45,
                "stepId": 120,
                "sourceFilter": "DELEGATED",
                "number": "0045/2026/TT",
                "subjectMatter": "Rà soát chi phí hạ tầng máy chủ",
                "proposalStatus": "UNDER_APPROVAL",
                "makerEmail": "creator@example.com",
                "makerName": "Nguyễn Văn Maker",
                "makerDepartment": "Khối Công nghệ Thông tin",
                "makerTitle": "Chuyên viên Cao cấp",
                "makerCompany": "Công ty Cổ phần PER",
                "stepType": "APPROVE",
                "stepStatus": "PROCESSING",
                "stepResult": null,
                "assigneeEmail": "checker@example.com",
                "assigneeName": "Người duyệt gốc",
                "delegatedApproverEmail": "delegate@example.com",
                "delegatedApproverName": "Người được ủy quyền",
                "isDelegated": true,
                "canProcess": true,
                "updatedAt": "2026-08-24T08:30:00+07:00"
            }
        ],
        "pagination": {
            "currentPage": 1,
            "perPage": 10,
            "totalItems": 1,
            "totalPages": 1,
            "hasNext": false,
            "hasPrev": false
        }
    }
}

2. Quy trình Phê duyệt & Thao tác

POST /api/v1/private/proposal-approvals/available-actions TranCode: PP_PROPOSAL_APPROVALS_AVAILABLE_ACTIONS — Lấy quy trình và thao tác phê duyệt khả dụng PerPro
Core
Lấy thông tin toàn bộ quy trình phê duyệt của tờ trình (dạng cây gồm các bước gốc steps và bước con childSteps), danh sách các bước đang chờ xử lý (currentSteps) và các hành động mà người dùng hiện tại được phép bấm (currentUserAllowedActions).

Quy tắc kiểm tra & Ràng buộc nghiệp vụ (Validation Rules)

1. Xác thực người gọi (Actor & Resource-Level Policy)
  • Bắt buộc truyền Access Token qua Header (Authorization: Bearer <token>).
  • Kiểm tra quyền truy cập: User đang đăng nhập phải có liên quan tới tờ trình mới được phép xem:
    • Là người tạo tờ trình (createdBy) hoặc người lập tờ trình (makerEmail).
    • Là người đồng trình trong danh sách (coRequesters).
    • Là người được phân công xử lý (assignee) của ít nhất một bước active trong quy trình.
    • Là người nhận ủy quyền còn hiệu lực (delegatedApprover) của ít nhất một bước trong quy trình.
Nếu user không thuộc bất kỳ vai trò nào trên, hệ thống sẽ trả về mã lỗi 403 Forbidden.
2. Cấu trúc Linked List & Kiểm tra xung đột đa bước
  • steps chỉ chứa các bước gốc theo đúng thứ tự prevStepId / nextStepId. Mỗi bước gốc có mảng childSteps chứa các bước review con.
  • currentSteps luôn là danh sách toàn bộ các bước PROCESSING. Nhiều bước processing chỉ hợp lệ khi tất cả là REVIEW và có cùng parentStepId.
  • Mỗi step trả thêm assignedByEmail. Field này chứa email của actor đã tạo step qua need_review/need_checker; các step thông thường trả null.
  • Mỗi step trả boolean approvalFlowLocked. Field là snapshot tại lần submit/rework-submit: true cho suffix root step tương ứng với perpro_template.approval_flow bị khóa; user step và child step trả false.
  • Mỗi step trả canRevokeRequest. Field chỉ là true khi actor trùng assignedByEmail, có PROPOSAL.APPROVE và step phát sinh chưa PROCESSED.
  • Nếu cùng một User (hoặc người được ủy quyền) được giao xử lý nhiều hơn 1 bước PROCESSING cùng lúc trong cùng tờ trình, API trả về lỗi 409 Conflict (do payload perform-action không truyền stepId).
3. Định dạng trường ghi chú (note) cho các bước được giao việc
  • Bước do người khác chỉ định (assignedByEmail != null): Đối với các bước phát sinh từ hành động giao việc (need_review hoặc need_checker), trường note trả về là một chuỗi JSON (JSON string) chứa đầy đủ thông tin chỉ đạo và phản hồi gồm:
    • assigner_note (chuỗi/null): Ý kiến chỉ đạo / ghi chú của người giao việc.
    • assigned_at (chuỗi): Thời điểm giao việc (định dạng YYYY-MM-DD HH:mm:ss theo GMT+7).
    • assignee_note (chuỗi/null): Ý kiến phản hồi / nhận xét của người được giao (sau khi họ thao tác xử lý xong).
    • processed_at (chuỗi/null): Thời điểm người được giao hoàn tất xử lý (định dạng YYYY-MM-DD HH:mm:ss theo GMT+7).
  • Bước thông thường (assignedByEmail == null): Trường note là chuỗi văn bản thông thường (plaintext) hoặc null.
4. Ý nghĩa & Nhãn hiển thị (Display Text) của proposalStatus, status và result
A. Trường proposalStatus (Trạng thái tổng thể của tờ trình):
Mã Status (API) Hiển thị trên UI Mô tả chi tiết & Vòng đời
DRAFT Nháp Tờ trình mới tạo, người lập đang soạn thảo và chưa gửi duyệt.
UNDER_APPROVAL Đang duyệt Tờ trình đã gửi đi và đang nằm trong luồng phê duyệt (bước Approve đang hoạt động).
PENDING_REVIEW Chờ đánh giá Người duyệt yêu cầu thêm thẩm định (need_review), tờ trình tạm chuyển sang luồng thẩm định chuyên môn trước khi quay lại người duyệt.
REVISION_REQUIRED Cần sửa lại Người duyệt yêu cầu làm lại (need_rework), người tạo có thể sửa và gửi lại qua action rework-submit.
PENDING_DOCUMENTS Chờ bổ sung chứng từ Người xử lý bước phê duyệt hoặc thẩm định yêu cầu hồ sơ (documents_request), người tạo có thể tải lên chứng từ và gửi qua action additional-documents-submit.
REJECTED Bị từ chối Người duyệt từ chối tờ trình (reject), quy trình kết thúc tại đây và tờ trình không còn hiệu lực.
APPROVED Đã duyệt Tất cả các bước duyệt trong quy trình đã được thông qua thành công (approve), tờ trình có hiệu lực chính thức.
LOCKED Khóa cấu hình Tờ trình bị khóa, không cho phép chỉnh sửa hoặc thực hiện thao tác quy trình.
B. Trường status (Trạng thái của từng bước duyệt trong steps):
Mã Status (API) Hiển thị trên UI Mô tả chi tiết
DRAFT Bản nháp Bước duyệt đang ở dạng nháp (khi tờ trình chưa submit).
LOCKED Chưa đến lượt Bước duyệt đang bị khóa, cần chờ các bước trước đó xử lý xong.
PROCESSING Đang xử lý Bước duyệt đang mở và đến lượt người được phân công xử lý (duyệt/thẩm định/uỷ quyền).
PROCESSED Đã xử lý Bước duyệt đã hoàn tất xử lý và có kết quả tương ứng trong trường result.
C. Trường result (Kết quả xử lý của bước duyệt khi đã PROCESSED):
Mã Result (API) Hiển thị trên UI Hành động tạo ra (Action) & Ý nghĩa
null (Chưa có) Bước chưa hoàn tất xử lý (khi statusPROCESSING, LOCKED hoặc DRAFT).
APPROVED Đồng ý / Đã phê duyệt Tạo bởi action approve: Người duyệt chấp thuận nội dung tờ trình.
REJECTED Từ chối Tạo bởi action reject: Người duyệt từ chối tờ trình.
REWORK Yêu cầu điều chỉnh Tạo bởi action need_rework: Người duyệt yêu cầu người tạo chỉnh sửa và gửi lại tờ trình.
REVIEWED Hoàn tất review / Đã thẩm định Tạo bởi action reviewed: Người thẩm định đã hoàn thành việc xem xét, đánh giá.
REQUEST_DOCUMENTS Yêu cầu bổ sung chứng từ Tạo bởi action documents_request: Người phê duyệt hoặc thẩm định yêu cầu người tạo bổ sung hồ sơ chứng từ.

Mô tả Payload (Request)

Field Type Description
proposalId Integer Required ID của tờ trình cần lấy thông tin quy trình. Phải là số nguyên dương (>= 1).

Request Sample

JSON - Request Body
{
    "proposalId": 123
}

Response Sample (Thành công 200 OK)

JSON - Response 200 OK
{
    "success": true,
    "code": "200",
    "message": "Lấy thông tin quy trình và hành động được phép thành công.",
    "data": {
        "proposalId": 123,
        "proposalStatus": "UNDER_APPROVAL",
        "workflowCompleted": false,
        "requesterPermissions": [
            { "permission": "PROPOSAL.CREATE", "granted": true },
            { "permission": "PROPOSAL.APPROVE", "granted": true },
            { "permission": "PROPOSAL.REVIEW", "granted": false }
        ],
        "steps": [
            {
                "stepId": 1,
                "proposalId": 123,
                "nextStepId": 2,
                "prevStepId": null,
                "parentStepId": null,
                "stepType": "APPROVE",
                "assignedByEmail": null,
                "approvalFlowLocked": false,
                "status": "PROCESSED",
                "result": "APPROVED",
                "note": "Đồng ý phê duyệt",
                "assignee": {
                    "email": "user1@example.com",
                    "name": "Nguyễn Văn A",
                    "title": "Trưởng phòng",
                    "department": "Phòng IT",
                    "company": "Công ty ABC"
                },
                "delegatedApprover": null,
                "isCurrent": false,
                "isAssignedToRequester": false,
                "createdAt": "2026-08-24T08:00:00+07:00",
                "createdBy": "creator@example.com",
                "updatedAt": "2026-08-24T09:00:00+07:00",
                "updatedBy": "user1@example.com",
                "childSteps": []
            },
            {
                "stepId": 2,
                "proposalId": 123,
                "nextStepId": null,
                "prevStepId": 1,
                "parentStepId": null,
                "stepType": "APPROVE",
                "assignedByEmail": "user1@example.com",
                "approvalFlowLocked": true,
                "status": "PROCESSING",
                "result": null,
                "note": "{\"assigner_note\":\"Nhờ anh duyệt gấp giúp em\",\"assigned_at\":\"2026-08-26 22:45:15\",\"assignee_note\":null,\"processed_at\":null}",
                "assignee": {
                    "email": "user2@example.com",
                    "name": "Trần Thị B",
                    "title": "Phó Giám đốc",
                    "department": "Khối Vận hành",
                    "company": "Công ty ABC"
                },
                "delegatedApprover": null,
                "isCurrent": true,
                "isAssignedToRequester": true,
                "createdAt": "2026-08-24T08:00:00+07:00",
                "createdBy": "creator@example.com",
                "updatedAt": "2026-08-24T09:00:00+07:00",
                "updatedBy": "user1@example.com",
                "childSteps": []
            }
        ],
        "currentSteps": [
            {
                "stepId": 2,
                "proposalId": 123,
                "stepType": "APPROVE",
                "assignedByEmail": "user1@example.com",
                "approvalFlowLocked": true,
                "status": "PROCESSING",
                "result": null,
                "note": "{\"assigner_note\":\"Nhờ anh duyệt gấp giúp em\",\"assigned_at\":\"2026-08-26 22:45:15\",\"assignee_note\":null,\"processed_at\":null}",
                "assignee": {
                    "email": "user2@example.com",
                    "name": "Trần Thị B",
                    "title": "Phó Giám đốc",
                    "department": "Khối Vận hành",
                    "company": "Công ty ABC"
                },
                "delegatedApprover": null,
                "isCurrent": true,
                "isAssignedToRequester": true,
                "canRevokeRequest": false,
                "requiredPermission": "PROPOSAL.APPROVE",
                "currentUserAllowedActions": [
                    "approve",
                    "reject",
                    "need_rework",
                    "need_review",
                    "need_checker",
                    "delegate_approver"
                ],
                "createdAt": "2026-08-24T08:00:00+07:00",
                "createdBy": "creator@example.com",
                "updatedAt": "2026-08-24T09:00:00+07:00",
                "updatedBy": "user1@example.com"
            }
        ]
    }
}
POST /api/v1/private/proposal-approvals/eligible-reviewers TranCode: PP_PROPOSAL_APPROVALS_ELIGIBLE_REVIEWERS — Lấy danh sách reviewer hợp lệ PerPro từ IAM
Core
Lấy danh sách tất cả các tài khoản có quyền PROPOSAL.REVIEW trên hệ thống IAM để phục vụ chức năng yêu cầu thêm người thẩm định (action need_review) trên màn hình phê duyệt.

Quy tắc kiểm tra & Ràng buộc nghiệp vụ

1. Quyền IAM yêu cầu & Xác thực
  • Bắt buộc truyền Access Token qua Header (Authorization: Bearer <token>).
  • Quyền IAM yêu cầu: User gọi API bắt buộc phải có quyền PERMISSION:PROPOSAL:APPROVE. Nếu thiếu quyền sẽ bị từ chối với mã lỗi 403 Forbidden.
2. Cơ chế lọc & Sắp xếp danh sách
  • Hệ thống sẽ lấy danh sách các user có quyền REVIEW đối với module phê duyệt tờ trình.
  • Loại bỏ chính người gọi: Tự động lọc bỏ email của User đang đăng nhập khỏi danh sách kết quả.
  • Sắp xếp: Danh sách trả về được sắp xếp theo thứ tự bảng chữ cái dựa trên Tên người dùng (hoặc Email).

Mô tả Payload (Request)

API này không yêu cầu tham số payload trong body (truyền JSON rỗng {} hoặc body trống).

Request Sample

JSON - Request Body
{}

Response Sample (Thành công 200 OK)

JSON - Response 200 OK
{
    "success": true,
    "code": "200",
    "message": "Lấy danh sách reviewer hợp lệ thành công.",
    "data": {
        "items": [
            {
                "email": "reviewer1@example.com",
                "name": "Đặng Thị Thảo",
                "title": "Chuyên viên Thẩm định Rủi ro",
                "department": "Ban Thẩm định",
                "company": "Công ty ABC"
            },
            {
                "email": "reviewer2@example.com",
                "name": "Nguyễn Văn Hùng",
                "title": "Trưởng nhóm Pháp chế",
                "department": "Phòng Pháp chế",
                "company": "Công ty ABC"
            }
        ]
    }
}
POST /api/v1/private/proposal-approvals/perform-action TranCode: PP_PROPOSAL_APPROVALS_PERFORM_ACTION — Thực hiện thao tác phê duyệt/review PerPro (10 actions + note)
Core
Thực hiện hành động phê duyệt trên workflow của tờ trình. Hỗ trợ 10 hành động: approve (Phê duyệt), reject (Từ chối), need_rework (Yêu cầu làm lại), need_review (Yêu cầu thêm thẩm định), need_checker (Yêu cầu thêm người duyệt), reviewed (Đã thẩm định xong), documents_request (Yêu cầu bổ sung hồ sơ), delegate_approver (Ủy quyền xử lý), revoke_delegate_approver (Thu hồi uỷ quyền), revoke_added_step (Thu hồi yêu cầu bổ sung bước).
Các hành động nghiệp vụ có ghi chú sử dụng trường chuẩn data.note; revoke_delegate_approver không yêu cầu data.

Quy tắc kiểm tra & Ràng buộc nghiệp vụ (Validation Rules)

1. Quy tắc chung & Cơ chế Khóa đồng thời (Pessimistic Lock)
  • Kiểm tra bước xử lý: Bước phê duyệt/thẩm định hiện tại phải ở trạng thái PROCESSING và chưa có kết quả (result == null). User phải là effective assignee (hoặc người nhận ủy quyền hợp lệ).
  • Cơ chế chống xung đột: Hệ thống đảm bảo tính đồng bộ dữ liệu. Nếu bước phê duyệt đã bị một người dùng khác xử lý trước đó, API sẽ trả về lỗi 409 Conflict.
2. Cơ chế ghi chú công việc (Giao việc & Phản hồi)
  • Khi giao thêm việc (need_review / need_checker): Lời nhắn trong data.note của người giao việc sẽ được hệ thống lưu lại thành ý kiến chỉ đạo kèm thời gian giao.
  • Khi phản hồi / hoàn tất công việc (reviewed / documents_request...): Nhận xét trong data.note của người được giao sẽ được lưu lại thành ý kiến phản hồi kèm thời gian hoàn tất.
  • Quy chuẩn gửi dữ liệu: Client chỉ cần gửi nội dung vào trường data.note cho mọi thao tác; hệ thống sẽ tự động xử lý và lưu trữ theo đúng luồng giao việc hay phản hồi.
3. Quy tắc cho các hành động bước APPROVE (approve, reject, need_rework, documents_request)
  • Quyền IAM: User bắt buộc phải có quyền PERMISSION:PROPOSAL:APPROVE.
  • Trạng thái tờ trình: Bắt buộc phải là UNDER_APPROVAL.
  • Yêu cầu bổ sung chứng từ: Action documents_request dùng cùng validation actor và quyền PROPOSAL.APPROVE của bước hiện tại; step giữ PROCESSING với result REQUEST_DOCUMENTS và proposal chuyển sang PENDING_DOCUMENTS.
  • Kiểm tra người nhận ủy quyền: Khi người nhận ủy quyền thực hiện phê duyệt, hệ thống sẽ kiểm tra lại hiệu lực của việc ủy quyền. Nếu quyền đã bị thu hồi hoặc xóa, request sẽ bị từ chối với lỗi 403.
Quy tắc chống tự phê duyệt (Chặn tuyệt đối): Người tạo tờ trình (createdBy) KHÔNG ĐƯỢC PHÉP thực hiện phê duyệt (approve) tờ trình của chính mình. Vi phạm sẽ bị trả về mã lỗi 01.
4. Quy tắc cho hành động "need_review" & "need_checker"
  • need_review: Chèn 1 root step REVIEW mới ngay TRƯỚC bước Approve hiện tại, chuyển step Review sang PROCESSING, khóa step Approve sang LOCKED và đưa proposal về PENDING_REVIEW. Reviewer phải có quyền PROPOSAL.REVIEW trong IAM.
  • need_checker: Chèn 1 root step APPROVE mới ngay TRƯỚC bước hiện tại, chuyển step mới sang PROCESSING, step hiện tại sang LOCKED; proposal giữ nguyên UNDER_APPROVAL. Checker phải có quyền PROPOSAL.APPROVE trong IAM và KHÔNG ĐƯỢC là người tạo tờ trình.
  • Với cả hai action, backend lấy email của actor đang đăng nhập, lưu vào perpro_approval_step.assigned_by_email của step mới và trả về field assignedByEmail.
5. Quy tắc cho hành động "delegate_approver" (Ủy quyền)
  • Chỉ assignee gốc của bước APPROVE/PROCESSING mới được ủy quyền. Người nhận ủy quyền không được ủy quyền tiếp.
  • Người nhận ủy quyền phải nằm trong danh bạ perpro_delegation_recipient của actor, có quyền PROPOSAL.APPROVE trong IAM và KHÔNG ĐƯỢC là người tạo tờ trình.
  • Thiết lập ủy quyền thành công sẽ không làm thay đổi trạng thái của bước phê duyệt hiện tại.
6. Quy tắc cho hành động "revoke_delegate_approver" (Thu hồi uỷ quyền)
  • Chỉ assignee gốc của bước APPROVE/PROCESSING có mapping uỷ quyền đang hiệu lực và có quyền PROPOSAL.APPROVE được thu hồi. Người nhận uỷ quyền không được tự thu hồi.
  • Mapping chuyển từ ACTIVE sang REVOKED và ghi audit thu hồi; step tiếp tục PROCESSING, proposal giữ UNDER_APPROVAL.
  • Sau khi thu hồi, quyền xử lý bước được trả lại assignee gốc.
7. Quy tắc cho hành động "revoke_added_step" (Thu hồi yêu cầu)
  • Bắt buộc data.stepId. Chỉ actor trùng assignedByEmail của root step phát sinh, có PROPOSAL.APPROVE, được thu hồi.
  • Cho phép khi status khác PROCESSED; backend soft-delete step, thu hồi delegation ACTIVE và nối lại prevStepId/nextStepId.
  • Nếu step đang PROCESSING, step kế tiếp được kích hoạt và proposal chuyển theo loại step kế tiếp. Nếu step chưa tới lượt, proposal/current step giữ nguyên.

Mô tả Payload (Request)

Field Type Description
proposalId Integer Required ID của tờ trình. Phải là số nguyên dương.
action String Required Loại hành động: approve, reject, need_rework, need_review, need_checker, reviewed, documents_request, delegate_approver, revoke_delegate_approver, revoke_added_step.
data Object Conditional Đối tượng dữ liệu đính kèm. Bắt buộc truyền khi action là need_review, need_checker, delegate_approver hoặc revoke_added_step. Riêng action thu hồi step bắt buộc data.stepId. Tùy chọn truyền khi muốn gửi note cho các action khác.

Request Sample 1: Action approve (Phê duyệt)

JSON - Request Body
{
    "proposalId": 123,
    "action": "approve",
    "data": {
        "note": "Đồng ý phê duyệt phần hạn mức bổ sung theo biên bản họp."
    }
}

Request Sample 2: Action reject (Từ chối)

JSON - Request Body
{
    "proposalId": 123,
    "action": "reject",
    "data": {
        "note": "Không đồng ý phê duyệt do rủi ro tài chính cao."
    }
}

Request Sample 3: Action need_rework (Yêu cầu làm lại)

JSON - Request Body
{
    "proposalId": 123,
    "action": "need_rework",
    "data": {
        "note": "Đề nghị bổ sung thêm tài liệu đánh giá rủi ro."
    }
}

Request Sample 4: Action need_review (Yêu cầu thêm người thẩm định)

JSON - Request Body
{
    "proposalId": 123,
    "action": "need_review",
    "data": {
        "reviewers": [
            { "email": "reviewer@example.com" }
        ],
        "note": "Nhờ anh xem giúp phần đánh giá rủi ro và các cam kết tài chính."
    }
}

Request Sample 5: Action need_checker (Yêu cầu thêm người phê duyệt)

JSON - Request Body
{
    "proposalId": 123,
    "action": "need_checker",
    "data": {
        "checkers": [
            { "email": "checker@example.com" }
        ],
        "note": "Nhờ anh duyệt bổ sung phần hạn mức tín dụng vượt thẩm quyền."
    }
}

Request Sample 6: Action reviewed (Đã thẩm định xong)

JSON - Request Body
{
    "proposalId": 123,
    "action": "reviewed",
    "data": {
        "note": "Đã xem xét và đánh giá hồ sơ đầy đủ."
    }
}

Request Sample 7: Action documents_request (Yêu cầu bổ sung hồ sơ)

JSON - Request Body
{
    "proposalId": 123,
    "action": "documents_request",
    "data": {
        "note": "Bổ sung thêm bản sao kê tài khoản ngân hàng."
    }
}

Request Sample 8: Action delegate_approver (Ủy quyền bước phê duyệt)

JSON - Request Body
{
    "proposalId": 123,
    "action": "delegate_approver",
    "data": {
        "delegatedApprover": {
            "email": "delegate@example.com"
        },
        "validTo": "2026-12-31T23:59:59+07:00",
        "note": "Đồng ý ủy quyền xử lý bước phê duyệt hiện tại."
    }
}

Request Sample 9: Action revoke_delegate_approver (Thu hồi uỷ quyền bước phê duyệt)

JSON - Request Body
{
    "proposalId": 123,
    "action": "revoke_delegate_approver"
}

Request Sample 10: Action revoke_added_step (Thu hồi yêu cầu bổ sung bước)

JSON - Request Body
{
    "proposalId": 123,
    "action": "revoke_added_step",
    "data": {
        "stepId": 456
    }
}

Response Sample

JSON - Response 200 OK
{
    "success": true,
    "code": "200",
    "message": "Phê duyệt bước hiện tại thành công.",
    "data": {
        "proposalId": 123,
        "performedAction": "approve",
        "proposalStatus": "UNDER_APPROVAL",
        "workflowCompleted": false,
        "requesterPermissions": [
            { "permission": "PROPOSAL.CREATE", "granted": true },
            { "permission": "PROPOSAL.APPROVE", "granted": true },
            { "permission": "PROPOSAL.REVIEW", "granted": false }
        ],
        "steps": [ "...danh sách các steps sau khi cập nhật..." ],
        "currentSteps": [ "...danh sách các bước đang chờ xử lý tiếp theo..." ]
    }
}
POST /api/v1/private/proposal-approvals/modify-steps TranCode: PP_PROPOSAL_APPROVALS_MODIFY_STEPS — Chỉnh sửa và lưu danh sách các bước phê duyệt PerPro
Core
Lưu và cập nhật lại toàn bộ danh sách các bước phê duyệt của tờ trình. Hỗ trợ tạo mới, cập nhật, đổi thứ tự và xóa mềm (soft-delete) các bước cũ không nằm trong danh sách truyền lên.
Alias Route: POST /api/v1/private/approval-steps/create (Hỗ trợ định dạng response list phẳng cho các component Portal phiên bản cũ).

Quy tắc kiểm tra & Ràng buộc nghiệp vụ

1. Quyền IAM & Người thực hiện (Actor Restriction)
  • Quyền IAM: Bắt buộc có quyền PERMISSION:PROPOSAL:CREATE.
  • Người thực hiện: CHỈ DUY NHẤT NGƯỜI TẠO TỜ TRÌNH (createdBy) mới được phép chỉnh sửa danh sách bước phê duyệt.
2. Ràng buộc danh sách bước (Steps Validation)
  • Tờ trình bắt buộc đang ở trạng thái DRAFT hoặc LOCKED.
  • steps: Bắt buộc là một mảng JSON danh sách, có ít nhất 1 bước (count >= 1).
  • Bước cuối cùng: Bắt buộc phải có stepType = APPROVE.
  • Chống tự duyệt: Người tạo tờ trình (createdBy) KHÔNG ĐƯỢC gán chính mình vào bất kỳ bước nào có stepType = APPROVE. Vi phạm trả lỗi 01 ngay lập tức.
  • Hệ thống sẽ tự động đối soát quyền với IAM và lưu lại thông tin hồ sơ (tên, chức danh, phòng ban, công ty).
3. Tự động nối quy trình mẫu bị khóa (Template Approval Flow Locked)
  • Khi Template có approval_flow_locked = trueapproval_flow có dữ liệu:
    • Backend so sánh phần đuôi (suffix) của steps gửi lên với approval_flow. Nếu chưa khớp đủ số lượng, đúng thứ tự, stepTypeassigneeEmail, backend sẽ tự động nối toàn bộ approval_flow vào cuối danh sách các bước.
    • Email được so sánh không phân biệt hoa thường; stepId, note và các thông tin snapshot không tham gia so sánh.
    • Không trả lỗi khi client thiếu hoặc thay đổi các bước cấu hình ở cuối; nếu suffix đã khớp đầy đủ thì backend không tạo thêm bước trùng.
    • Áp dụng cho cả tờ trình DRAFTLOCKED.
    • Nếu approval_flow_locked = false hoặc approval_flow rỗng/null, backend không can thiệp tự thêm bước.
  • Kiểm soát thay đổi cấu hình đồng thời: Nếu cấu hình quy trình mặc định của mẫu bị thay đổi trong lúc thao tác, hệ thống trả về lỗi xung đột 409 Conflict ("Cấu hình quy trình phê duyệt mặc định của mẫu đã thay đổi. Vui lòng tải lại dữ liệu.").

Mô tả Payload (Request)

Field Type Description
proposalId Integer Required ID của tờ trình.
steps Array Required Danh sách các bước phê duyệt mới theo đúng thứ tự linked-list. Phải có ít nhất 1 bước.

Request Sample

JSON - Request Body
{
    "proposalId": 20,
    "steps": [
        {
            "stepId": 113,
            "stepType": "REVIEW",
            "assignee": {
                "email": "reviewer@example.com"
            },
            "note": "Thẩm định hồ sơ ban đầu"
        },
        {
            "stepId": null,
            "stepType": "APPROVE",
            "assignee": {
                "email": "approver@example.com"
            },
            "note": "Phê duyệt tờ trình"
        }
    ]
}

Response Sample

JSON - Response 200 OK
{
    "success": true,
    "code": "200",
    "message": "Lưu danh sách quy trình phê duyệt thành công.",
    "data": {
        "proposalId": 20,
        "modifiedStepCount": 2,
        "deletedStepCount": 0,
        "proposalStatus": "DRAFT",
        "workflowCompleted": false,
        "requesterPermissions": [
            { "permission": "PROPOSAL.CREATE", "granted": true },
            { "permission": "PROPOSAL.APPROVE", "granted": true },
            { "permission": "PROPOSAL.REVIEW", "granted": false }
        ],
        "steps": [ "...danh sách steps sau khi lưu..." ],
        "currentSteps": []
    }
}
POST /api/v1/private/proposal-approvals/trigger-step-flow TranCode: PP_PROPOSAL_APPROVALS_TRIGGER_STEP_FLOW — Khởi chạy quy trình phê duyệt PerPro (submit, rework-submit, additional_documents_submit)
Core
Khởi chạy quy trình phê duyệt lần đầu (submit), gửi lại tờ trình sau khi đã chỉnh sửa (rework-submit), hoặc gửi thông báo đã bổ sung tài liệu/chứng từ (additional_documents_submit).

Quy tắc kiểm tra theo từng Action

1. Action "submit" (Gửi duyệt lần đầu)
  • Trạng thái tờ trình: Phải là DRAFT.
  • Payload: CẤM truyền field steps trong request.
  • Ghép quy trình mẫu (approval_flow): Khi approval_flow_locked = trueapproval_flow có dữ liệu, backend tự động nối toàn bộ approval_flow vào cuối chuỗi bước duyệt nếu phần đuôi (suffix) hiện tại chưa khớp đủ số lượng, thứ tự, stepTypeassigneeEmail.
  • Tất cả các bước phải đang ở DRAFT và chưa có kết quả. Bước cuối cùng sau khi ghép bắt buộc là APPROVE.
  • Mở bước đầu tiên thành PROCESSING, các bước sau thành LOCKED; proposal chuyển thành UNDER_APPROVAL (hoặc PENDING_REVIEW nếu bước đầu là Review).
2. Action "rework-submit" (Gửi lại sau khi chỉnh sửa)
  • Trạng thái tờ trình: Phải là REVISION_REQUIRED.
  • Payload: BẮT BUỘC truyền field steps chứa danh sách root steps mới/chỉnh sửa (không truyền childSteps).
  • Ghép quy trình mẫu bị khóa (approval_flow_locked = true): Khi approval_flow có dữ liệu và suffix của payload chưa khớp đủ số lượng, thứ tự, stepTypeassigneeEmail, backend tự động nối toàn bộ approval_flow vào cuối thay vì báo lỗi.
  • Nếu approval_flow_locked = false hoặc approval_flow rỗng/null, backend không tự thêm step.
  • Bước cuối cùng của chuỗi hiệu lực sau khi ghép bắt buộc phải là APPROVE.
  • Bảo toàn ghi chú (note): Backend cập nhật note của step theo đúng dữ liệu client truyền lên trong payload (nếu muốn giữ note cũ thì client phải truyền lại).
  • Hệ thống tự động thu hồi (revoke) các ủy quyền cũ, cập nhật danh sách bước, reset kết quả và khởi chạy lại từ bước đầu tiên (PROCESSING).
3. Action "additional_documents_submit" (Gửi bổ sung chứng từ)
  • Trạng thái tờ trình: Phải là PENDING_DOCUMENTS.
  • Payload: CẤM truyền field steps trong request.
  • Tờ trình phải có đúng 1 bước APPROVE hoặc REVIEW đang ở PROCESSING với kết quả REQUEST_DOCUMENTS.
  • Reset kết quả bước đó về null (tiếp tục PROCESSING), chuyển tờ trình về UNDER_APPROVAL nếu là bước APPROVE hoặc PENDING_REVIEW nếu là bước REVIEW.
4. Cơ chế Ghép quy trình mẫu (approval_flow) & Kiểm soát đồng thời
  • Quy tắc so sánh Suffix: So sánh toàn bộ approval_flow của template với phần đuôi (suffix) của danh sách root steps theo stepTypeassigneeEmail (so sánh email không phân biệt hoa thường; stepId, note và snapshot không tham gia so sánh).
  • Khi khớp đủ: Giữ nguyên các step đã có, không tạo thêm và không ghi đè note/snapshot đã cấu hình.
  • Khi không khớp / khớp một phần: Backend nối (append) toàn bộ approval_flow vào cuối và kiểm tra tính liên tục, không cho phép hai bước liên tiếp có cùng vai trò/người xử lý.
  • Snapshot khóa trên từng step: Khi submit hoặc rework-submit thành công, toàn bộ suffix tương ứng với flow bị khóa nhận perpro_approval_step.approval_flow_locked = true, kể cả suffix đã tồn tại; các root step còn lại nhận false. modify-steps chưa cập nhật field này.
  • Kiểm soát thay đổi cấu hình đồng thời: Nếu cấu hình template bị sửa đổi giữa lúc tra cứu IAM và lúc ghi DB, hệ thống trả về lỗi xung đột 409 Conflict ("Cấu hình quy trình phê duyệt mặc định của mẫu đã thay đổi. Vui lòng tải lại dữ liệu.").

Request Sample 1: Action submit (Gửi duyệt lần đầu)

JSON - Request Body
{
    "proposalId": 123,
    "action": "submit"
}

Request Sample 2: Action rework-submit (Gửi lại sau khi chỉnh sửa)

JSON - Request Body
{
    "proposalId": 123,
    "action": "rework-submit",
    "steps": [
        {
            "stepId": 1,
            "stepType": "APPROVE",
            "assignee": { "email": "approver1@example.com" },
            "note": "Phê duyệt bước 1"
        },
        {
            "stepId": null,
            "stepType": "APPROVE",
            "assignee": { "email": "approver2@example.com" },
            "note": "Phê duyệt bước 2"
        }
    ]
}

Request Sample 3: Action additional_documents_submit (Gửi bổ sung chứng từ)

JSON - Request Body
{
    "proposalId": 123,
    "action": "additional_documents_submit"
}

3. Bảng Tra cứu Mã lỗi, Trạng thái & Ma trận Chuyển đổi

TRA CỨU Bảng Mã Lỗi & Trạng Thái Hệ Thống — Danh mục MBGW TranCode, mã lỗi, trạng thái tờ trình, trạng thái bước và ma trận transition
Reference

1. Bảng Tổng hợp Mobile Gateway Transaction Codes (MBGW Mapping)

Danh mục 6 mã giao dịch trên Mobile Gateway ánh xạ tới API PerPro Upstream (cấu hình trong mg_transaction, mg_integrationmg_transaction_component theo migration Version20260825171306.php):

MBGW TranCode (TRANSACTIONS.code) PerPro Upstream Endpoint Tên Transaction & Chức năng nghiệp vụ
PP_PROPOSAL_CHECK_LIST /api/v1/private/proposal-review-inbox/list Lấy danh sách tờ trình phê duyệt/review PerPro
PP_PROPOSAL_APPROVALS_AVAILABLE_ACTIONS /api/v1/private/proposal-approvals/available-actions Lấy quy trình và thao tác phê duyệt khả dụng PerPro
PP_PROPOSAL_APPROVALS_ELIGIBLE_REVIEWERS /api/v1/private/proposal-approvals/eligible-reviewers Lấy danh sách reviewer hợp lệ PerPro
PP_PROPOSAL_APPROVALS_PERFORM_ACTION /api/v1/private/proposal-approvals/perform-action Thực hiện thao tác phê duyệt/review PerPro
PP_PROPOSAL_APPROVALS_MODIFY_STEPS /api/v1/private/proposal-approvals/modify-steps Chỉnh sửa các bước phê duyệt PerPro
PP_PROPOSAL_APPROVALS_TRIGGER_STEP_FLOW /api/v1/private/proposal-approvals/trigger-step-flow Khởi chạy quy trình phê duyệt PerPro

2. Bảng Mã lỗi phản hồi (Error Codes)

Mọi lỗi phát sinh sau khi request vào Controller đều trả HTTP Status 200 kèm mã lỗi tương ứng trong response envelope code để Client dễ dàng xử lý:

Mã lỗi (Code) Tên lỗi Mô tả chi tiết & Trường hợp xảy ra
01 Creator Cannot Approve Người tạo tờ trình vi phạm quy tắc chống tự phê duyệt (cố tình thực hiện duyệt, gán mình làm người duyệt hoặc nhận ủy quyền).
400 Bad Request Payload JSON không hợp lệ, thiếu trường bắt buộc, sai định dạng email/validTo, sai kiểu dữ liệu, hoặc assignee không có quyền IAM tương ứng.
401 Unauthorized Phiên đăng nhập không hợp lệ, thiếu Bearer Token hoặc token đã hết hạn.
403 Forbidden Người dùng không có quyền truy cập tờ trình, không phải người tạo tờ trình khi modify/submit, không được giao xử lý bước hiện tại, hoặc cấu hình ủy quyền trong danh bạ đã bị xóa/thu hồi.
404 Not Found Không tìm thấy tờ trình (Proposal ID không tồn tại hoặc đã bị soft-delete).
409 Conflict Xung đột trạng thái: Bước phê duyệt đã bị người khác xử lý trước đó, trạng thái tờ trình không phù hợp với hành động, user được giao nhiều hơn 1 bước processing, hoặc vi phạm tính liên tục của linked-list.
503 Service Unavailable Dịch vụ IAM tạm thời không khả dụng khi xác thực quyền của assignee/reviewer/checker.
99 System Error Lỗi hệ thống không xác định trong quá trình xử lý.

3. Bảng Ma trận Chuyển đổi Trạng thái khi thực hiện Action (State Transitions)

Action Step Type Trạng thái trước Hành vi & Kết quả sau khi thực hiện
approve APPROVE UNDER_APPROVAL Cập nhật note nếu có data.note; Current step thành PROCESSED/APPROVED. Mở bước kế tiếp thành PROCESSING, hoặc chuyển proposal thành APPROVED nếu là bước cuối cùng (kết thúc workflow).
reject APPROVE UNDER_APPROVAL Cập nhật note nếu có data.note; Tất cả các bước chưa xử lý thành PROCESSED/REJECTED; Proposal chuyển sang REJECTED (kết thúc quy trình).
need_rework APPROVE UNDER_APPROVAL Cập nhật note nếu có data.note; Current step giữ PROCESSING với kết quả result = REWORK; Proposal chuyển sang REVISION_REQUIRED để người tạo sửa lại.
need_review APPROVE UNDER_APPROVAL Lưu data.note dạng JSON trên step mới; Chèn 1 root step REVIEW/PROCESSING ngay trước step Approve hiện tại; Step Approve chuyển sang LOCKED; Proposal chuyển sang PENDING_REVIEW.
need_checker APPROVE UNDER_APPROVAL Lưu data.note dạng JSON trên step mới; Chèn 1 root step APPROVE/PROCESSING ngay trước step Approve hiện tại; Step Approve chuyển sang LOCKED; Proposal giữ nguyên UNDER_APPROVAL.
delegate_approver APPROVE UNDER_APPROVAL Cập nhật note nếu có data.note; Tạo mapping ủy quyền có hiệu lực trong bảng perpro_approval_step_delegated_approver; Giữ nguyên trạng thái step và proposal.
revoke_delegate_approver APPROVE UNDER_APPROVAL Assignee gốc thu hồi mapping uỷ quyền đang hiệu lực; mapping chuyển sang REVOKED, step/proposal giữ nguyên và quyền xử lý trở lại assignee gốc.
revoke_added_step APPROVE hoặc REVIEW phát sinh Trạng thái hiện tại của proposal Actor trùng assignedByEmail soft-delete step chưa PROCESSED, thu hồi delegation và nối lại workflow. Nếu target đang PROCESSING, backend kích hoạt step kế tiếp và cập nhật proposal; nếu target chưa tới lượt thì giữ nguyên proposal/current step.
reviewed REVIEW PENDING_REVIEW Cập nhật assignee_note/processed_at nếu step dùng JSON; Current step thành PROCESSED/REVIEWED; Mở lại bước Approve tiếp theo thành PROCESSING; Proposal chuyển về UNDER_APPROVAL.
documents_request APPROVE hoặc REVIEW UNDER_APPROVAL hoặc PENDING_REVIEW Cập nhật note của current step nếu payload có data.note; Current step giữ PROCESSING với kết quả result = REQUEST_DOCUMENTS; Proposal chuyển sang PENDING_DOCUMENTS để người tạo nộp thêm hồ sơ. Permission được kiểm tra theo loại step: PROPOSAL.APPROVE hoặc PROPOSAL.REVIEW.
submit DRAFT Kích hoạt bước đầu tiên thành PROCESSING, các bước sau LOCKED; Proposal chuyển sang UNDER_APPROVAL (hoặc PENDING_REVIEW nếu bước 1 là Review).
rework-submit REVISION_REQUIRED Thu hồi các ủy quyền cũ, cập nhật lại danh sách steps, reset kết quả; Bước 1 chuyển sang PROCESSING; Proposal chuyển sang UNDER_APPROVAL (hoặc PENDING_REVIEW).
additional_documents_submit PENDING_DOCUMENTS Reset kết quả của bước APPROVE hoặc REVIEW đang request documents về null (tiếp tục PROCESSING); Proposal chuyển về UNDER_APPROVAL hoặc PENDING_REVIEW theo loại step.
Đã sao chép vào bộ nhớ tạm!