# ERPFlow — Module-wise Role-Based QA Checklist

**কীভাবে ব্যবহার করবেন:** প্রতিটি **module** আলাদা section। Module-এর ভিতরে **role অনুযায়ী** permission, view/edit, business rule, আর negative check আছে।  
**Employee detail:** PDF section rules → input behaviour test cases — [`EMPLOYEE_MODULE_QA_CHECKLIST.md`](./EMPLOYEE_MODULE_QA_CHECKLIST.md) (permission = appendix)  
**Personas:** Super Admin · Company Admin · HR Admin · Finance Admin · General Employee · View-only  
**Result mark:** ☐ Not run · ✅ Pass · ❌ Fail · N/A (feature not shipped)

---

## 0. Personas & setup (সব module-এর আগে)

| Persona | Setup | Access model |
|---|---|---|
| **Super Admin** | `admin@erpflow.local` — `user_type=developer` | Permission matrix bypass |
| **Company Admin** | `employee` user + system role `administrator` | Role bypass (সব active permission) |
| **HR Admin** | Custom role — Employee + Attendance ops; **no** `payroll.month-freeze` | Matrix-based |
| **Finance Admin** | Custom role — Payroll ops; **no** `attendance.monthly-approve` | Matrix-based |
| **General Employee** | Seeded role `employee` only (demo user থেকে `configuration-staff` সরিয়ে নিন pure test-এর জন্য) | Self-service keys only |

**Hard rule (SoD):** কোনো role-এ একসাথে `attendance.monthly-approve` + `payroll.month-freeze` থাকবে না।

**Employee self-service keys (seeded):**  
`attendance.menu-view`, `attendance.punch-create`, `attendance.record-view-own`, `attendance.correction-create`, `attendance.leave-apply`, `payroll.menu-view`, `payroll.payslip-view-own`

---

# MODULE 1 — Auth & Onboarding

## 1.1 Screens

| Path | Purpose |
|---|---|
| `/login` | Login (guest only) |
| `/onboarding/mobile-otp` | Mobile OTP |
| `/onboarding/email-otp` | Email OTP |
| `/onboarding/change-password` | Forced password change |
| `/onboarding/documents` | Document upload |
| `/admin/onboarding/policies*` | Policy admin (Platform module) |

## 1.2 Role-wise checklist

### Super Admin
| # | Check | ☐ |
|---|---|---|
| AUTH-SA-01 | Login succeeds; **onboarding skip** (developer hard-skip) | |
| AUTH-SA-02 | Logged-in user visiting `/login` redirects to app | |
| AUTH-SA-03 | Logout → session cleared; protected routes redirect to login | |
| AUTH-SA-04 | Can open Admin › Onboarding Policies (via Platform) | |
| AUTH-SA-05 | Create policy for `user_type=employee` with blocking steps | |
| AUTH-SA-06 | Switch company (if multi-company) works | |

### Company Admin
| # | Check | ☐ |
|---|---|---|
| AUTH-CA-01 | Login as non-developer company admin | |
| AUTH-CA-02 | If policy matches this user → forced onboarding; else free | |
| AUTH-CA-03 | Can manage onboarding policies (`platform.view/create/update`) | |
| AUTH-CA-04 | Cannot skip onboarding unless `user_type=developer` | |

### HR Admin
| # | Check | ☐ |
|---|---|---|
| AUTH-HR-01 | Completes own onboarding if policy applies | |
| AUTH-HR-02 | **Cannot** open `/admin/onboarding/policies` without `platform.view` | |
| AUTH-HR-03 | Wrong OTP / expired OTP rejected | |

### Finance Admin
| # | Check | ☐ |
|---|---|---|
| AUTH-FN-01 | Same as HR for own onboarding | |
| AUTH-FN-02 | No Platform onboarding admin without `platform.*` | |

### General Employee
| # | Check | ☐ |
|---|---|---|
| AUTH-EMP-01 | Seeded employee policy: mobile OTP → email OTP → password → documents | |
| AUTH-EMP-02 | Blocking step incomplete → main app inaccessible | |
| AUTH-EMP-03 | Document step (non-blocking) may allow limited access with deadline | |
| AUTH-EMP-04 | `/admin/*` সব deny | |
| AUTH-EMP-05 | Invalid credentials / inactive user blocked | |

## 1.3 Negative / security
| # | Check | ☐ |
|---|---|---|
| AUTH-NEG-01 | Unauthenticated API calls → 401 | |
| AUTH-NEG-02 | Token refresh / logout invalidates access | |
| AUTH-NEG-03 | Onboarding APIs do not require `platform.*` but require auth | |

---

# MODULE 2 — Platform (Core Admin)

## 2.1 Permission catalog

| Key | Use |
|---|---|
| `platform.view` | List modules, roles, users, workflows, settings, requests, logs, policies |
| `platform.create` | Create module/action/role/user/workflow/policy |
| `platform.update` | Edit role, permission matrix, user roles/overrides, settings, workflows |
| `platform.delete` | Delete role/module/workflow/policy |
| `platform.approve` | Approve/reject/cancel/delegate/comment on approval requests |
| `platform.export` | Activity log export |

**Nav:** সব Core items → `platform.view`

## 2.2 Screens

| Path | Guard (FE) |
|---|---|
| `/admin/modules`, create, edit | view / create / update |
| `/admin/actions`, create | view / create |
| `/admin/roles`, create, edit, permissions | view / create / update |
| `/admin/users`, create, roles, permission-overrides | view / create / update |
| `/admin/approval-workflows*` | view / create / update |
| `/admin/approval-settings` | view (update via API) |
| `/admin/approval-requests`, `:uuid` | view (actions need approve) |
| `/admin/activity-logs`, `/admin/otp-logs` | view |
| `/admin/onboarding/policies*` | view / create / update |

## 2.3 Role-wise checklist

### Super Admin / Company Admin
| # | Check | ☐ |
|---|---|---|
| PL-ADM-01 | Core section পুরো visible | |
| PL-ADM-02 | Module Registry: list / create / edit | |
| PL-ADM-03 | Actions: list / sync / create | |
| PL-ADM-04 | Roles: create custom role; sync permissions matrix | |
| PL-ADM-05 | System role `administrator` behaves as full access | |
| PL-ADM-06 | Users: create `developer` (no company) vs `employee` (company required) | |
| PL-ADM-07 | Assign roles to user; permission overrides work | |
| PL-ADM-08 | Approval workflow CRUD + steps | |
| PL-ADM-09 | Approval Settings: toggle enable; bind workflow per action | |
| PL-ADM-10 | Attendance/Payroll approvable actions matrix-এ দেখা যায় (correction, leave, monthly, run, advance) | |
| PL-ADM-11 | Approval Requests inbox: approve/reject/delegate/comment | |
| PL-ADM-12 | Activity log list + export (`platform.export`) | |
| PL-ADM-13 | OTP log readable | |
| PL-ADM-14 | Onboarding policy CRUD + applies_to (user_type/role/dept/…) | |

### HR Admin / Finance Admin (without `platform.*`)
| # | Check | ☐ |
|---|---|---|
| PL-OPS-01 | Core nav **hidden** | |
| PL-OPS-02 | Direct `/admin/roles` → redirect/deny + API 403 | |
| PL-OPS-03 | Cannot escalate own permissions via API | |
| PL-OPS-04 | If only `platform.view` (no approve): can open request list but action buttons fail/403 | |

### General Employee
| # | Check | ☐ |
|---|---|---|
| PL-EMP-01 | No Core menu | |
| PL-EMP-02 | All `/admin/*` denied | |
| PL-EMP-03 | `/me/permissions` returns only granted self-service keys | |

## 2.4 View vs Edit rules
| Action | Need | Without it |
|---|---|---|
| Open admin list pages | `platform.view` | Hidden + deny |
| Create buttons | `platform.create` | Create routes blocked |
| Edit matrix / user roles | `platform.update` | Read-only / 403 |
| Delete | `platform.delete` | UI may show button — **API must 403** (known FE gap) |
| Act on approval | `platform.approve` | Detail opens with view; actions fail |

## 2.5 Negative
| # | Check | ☐ |
|---|---|---|
| PL-NEG-01 | `view`-only user: create/update/delete API → 403 | |
| PL-NEG-02 | Inactive user → empty permissions | |
| PL-NEG-03 | Removing role → next `/me/permissions` reflects loss | |

---

# MODULE 3 — Configuration

## 3.1 Permission catalog

| Key | Use |
|---|---|
| `configuration.view` | Configuration Home; some non-system reads |
| `configuration.system-configuration-menu-view` | System Configuration page + list/read of org masters |
| `configuration.system-configuration-company-view` | Catalog-এ আছে; **route-এ এখনো unused** — note as gap |
| `configuration.create` | POST companies, branches, divisions, depts, designations, grades, teams, employment types, id-card settings, document types |
| `configuration.update` | PUT counterparts |
| `configuration.delete` | DELETE counterparts |

**Note:** Salary structure write এখন Payroll (`payroll.structure-manage`)-এ। Config থেকে salary tab redirect করে Payroll-এ।

## 3.2 Screens / tabs

| Path / tab | Content |
|---|---|
| `/configuration` | Home |
| `/configuration/system?tab=company` | Company |
| `…company-branches` | Branches |
| `…divisions` | Divisions |
| `…department` | Departments |
| `…designations` | Designations |
| `…grades` | Grades |
| `…employee` | Employment Types |
| `…teams` | Teams |
| `…id-card-management` | ID Card settings |
| Legacy `?tab=salary` | Redirect → `/payroll/config/salary-structures` |

## 3.3 Role-wise checklist

### Super Admin / Company Admin
| # | Check | ☐ |
|---|---|---|
| CFG-ADM-01 | Configuration + System Configuration both visible | |
| CFG-ADM-02 | প্রতিটি tab list loads | |
| CFG-ADM-03 | Create company / branch / division / dept / designation / grade / team / employment type | |
| CFG-ADM-04 | Update each master | |
| CFG-ADM-05 | Delete each master (or soft constraints if referenced) | |
| CFG-ADM-06 | ID Card setting create/edit | |
| CFG-ADM-07 | Document types CRUD if exposed | |
| CFG-ADM-08 | Salary tab redirect to Payroll structures (no write in Config) | |
| CFG-ADM-09 | If configuration approval enabled: create goes through approval workflow | |

### HR Admin (typical: view + system menu + create/update)
| # | Check | ☐ |
|---|---|---|
| CFG-HR-01 | Can open System Configuration if granted menu-view key | |
| CFG-HR-02 | Can create/update org structure used by employees | |
| CFG-HR-03 | Without `configuration.delete`: delete API 403 | |
| CFG-HR-04 | Without system-configuration-menu-view: System Config hidden | |

### Finance Admin
| # | Check | ☐ |
|---|---|---|
| CFG-FN-01 | Usually no config access unless granted — verify menu hidden | |
| CFG-FN-02 | If only payroll rights: `/configuration/system` deny | |

### General Employee / configuration-staff
| # | Check | ☐ |
|---|---|---|
| CFG-EMP-01 | `configuration.view` only: Configuration Home OK | |
| CFG-EMP-02 | System Configuration **hidden** | |
| CFG-EMP-03 | create/update/delete → 403 | |
| CFG-EMP-04 | Pure `employee` role (no configuration-staff): Configuration section entirely hidden | |

## 3.4 View vs Edit rules
| Capability | Permission | QA expectation |
|---|---|---|
| See Configuration menu | `configuration.view` | Else hidden |
| Open System Config | `system-configuration-menu-view` | Else hidden |
| Add forms / create | `configuration.create` | Company tab hides add without create; **list tabs may still show manage UI** (known FE stub `canManage=true`) — API 403 must hold |
| Edit actions | `configuration.update` | |
| Delete | `configuration.delete` | |

## 3.5 Negative / gaps to file
| # | Check | ☐ |
|---|---|---|
| CFG-NEG-01 | View-only: mutate API 403 even if UI button visible | |
| CFG-NEG-02 | `system-configuration-company-view` unused — confirm no accidental gate | |
| CFG-NEG-03 | Cross-company config data isolation | |

---

# MODULE 4 — Employee

> **Rule-wise test cases (PDF/guide style):** [`EMPLOYEE_MODULE_QA_CHECKLIST.md`](./EMPLOYEE_MODULE_QA_CHECKLIST.md)  
> Format: section → What you can do → **Rules → input behaviour TCs** (permission = appendix only).

## 4.1 Card index

| Card | Guide | Example focus |
|---|---|---|
| 0 | §3, §11 | Masters / user eligibility |
| 4–10 | §7.1–§7.7 | Personal…Reporting (self/circular/Change vs Assign…) |
| 11–13 | §7.8–§7.10 | Bank primary, one Active salary, one tax profile |
| 14–19 | §7.11–§7.16 | Docs versioning, assets, notes visibility, timeline |
| 20–21 | §8, §12, §14 | Workflows + common problems |
| Appendix | §9 | Permission suite (secondary) |

## 4.2 Permission smoke (full = Appendix in detail file)

| Persona | Smoke |
|---|---|
| HR / CA | Full mutate; Transfer not second Create |
| View-only | Read OK; mutate 403 |
| EMP | `update-own-profile` → no status; no others |

---

# MODULE 5 — Attendance

## 5.1 Permission catalog

| Key | Intent | Seeded employee? | Shipped API? |
|---|---|---|---|
| `attendance.menu-view` | Module + config reads | Yes | Yes |
| `attendance.config-manage` | Types/shifts/policies mutate | No | Yes |
| `attendance.assignment-manage` | Assignments CRUD/bulk/end | No | Yes |
| `attendance.assignment-preview` | Resolve diagnostic | No | Yes |
| `attendance.punch-create` | Own punch | Yes | **No** |
| `attendance.punch-create-others` | Punch for others | No | No |
| `attendance.record-view-own/team/all` | Record scope | own=Yes | No |
| `attendance.record-export` | Export | No | No |
| `attendance.record-recalculate` | Recalc | No | **Route today uses assignment-manage** |
| `attendance.correction-create/approve/override-lock` | Corrections | create=Yes | No |
| `attendance.leave-apply/approve/balance-view/adjust` | Leave | apply=Yes | No |
| `attendance.monthly-view/approve/unlock` | Month close | No | No |

**Shipped now:** Home, Config (types, shifts, policies, assignments, preview).  
**Placeholder (`#`):** Punches, Records, Corrections, Leave, Monthly Approval — mark N/A until shipped; still verify menu visibility rules.

## 5.2 Screens (shipped)

| Path | Notes |
|---|---|
| `/attendance` | Home — `menu-view` |
| `/attendance/config` | Types / shifts tabs |
| `/attendance/config/policies`, create/edit | Policies |
| `/attendance/config/assignments*`, bulk, preview | Assignments |

## 5.3 Role-wise checklist — Configuration (shipped)

### Super Admin / Company Admin / HR Admin
| # | Check | ☐ |
|---|---|---|
| ATT-CFG-01 | Attendance menu + Home + health | |
| ATT-CFG-02 | Open Configuration | |
| ATT-CFG-03 | **Types:** list; create custom; edit name/color | |
| ATT-CFG-04 | System types: badge; **cannot** change/set `system_code`; **cannot** delete (409) — deactivate OK | |
| ATT-CFG-05 | Duplicate `code` rejected | |
| ATT-CFG-06 | **Shifts:** CRUD; overnight end&lt;start accepted; status activate/deactivate | |
| ATT-CFG-07 | **Policies:** leave + holiday; `policy_type` immutable after create | |
| ATT-CFG-08 | Leave validation: entitlement &gt;0, accrual, carry-forward, notice/backdate/document days | |
| ATT-CFG-09 | Holiday: no duplicate dates | |
| ATT-CFG-10 | Archive / activate / deactivate status flows | |
| ATT-CFG-11 | **Assignments:** create by scope (company/branch/division/dept/team/employee) | |
| ATT-CFG-12 | Overlap date range → conflict / 409 | |
| ATT-CFG-13 | End assignment; bulk assign + partial success UI | |
| ATT-CFG-14 | Assignment preview (`assignment-preview`): resolved shift/policy/scope; unassigned → 422 | |
| ATT-CFG-15 | Without config-manage: mutate buttons hidden/disabled + API 403 | |
| ATT-CFG-16 | Without assignment-manage: no create/bulk; list still if menu-view | |

### Finance Admin
| # | Check | ☐ |
|---|---|---|
| ATT-CFG-FN-01 | Typically no config-manage — config mutate deny | |
| ATT-CFG-FN-02 | May have menu-view + record-view-all later for readiness — config write still deny | |

### General Employee
| # | Check | ☐ |
|---|---|---|
| ATT-CFG-EMP-01 | Menu visible (`menu-view`) | |
| ATT-CFG-EMP-02 | Can open Home; may open config **read** URLs (coarse FE route) | |
| ATT-CFG-EMP-03 | Create/edit/delete type/shift/policy → **403** | |
| ATT-CFG-EMP-04 | Assignment create/bulk/preview mutate → 403 | |
| ATT-CFG-EMP-05 | Placeholder cards (punches/records/…) non-navigating | |

## 5.4 Role-wise checklist — Runtime (when shipped; else N/A)

### Punches
| # | Role | Check | ☐ |
|---|---|---|---|
| ATT-PCH-01 | Employee | Punch in/out self (`punch-create`) | |
| ATT-PCH-02 | HR | Punch for other (`punch-create-others`) | |
| ATT-PCH-03 | Employee | Cannot punch for another (API 403) | |
| ATT-PCH-04 | Any | Punches immutable; corrections supersede | |

### Records
| # | Role | Check | ☐ |
|---|---|---|---|
| ATT-REC-01 | Employee | Own records only (`record-view-own`) | |
| ATT-REC-02 | HR | Team / all per grant | |
| ATT-REC-03 | Employee | Other employee_id → 403/empty | |
| ATT-REC-04 | HR | Export with `record-export` | |
| ATT-REC-05 | HR | Recalculate; locked row blocked | |
| ATT-REC-06 | QA | Recalc permission: assignment-manage vs `record-recalculate` mismatch | |

### Corrections
| # | Role | Check | ☐ |
|---|---|---|---|
| ATT-COR-01 | Employee | Submit correction | |
| ATT-COR-02 | HR | Approve/reject via platform approval | |
| ATT-COR-03 | HR | Locked period needs `correction-override-lock` | |
| ATT-COR-04 | Employee | No approve | |

### Leave
| # | Role | Check | ☐ |
|---|---|---|---|
| ATT-LEV-01 | Employee | Apply leave; balance check; notice/backdate rules | |
| ATT-LEV-02 | HR | Pending queue approve/reject | |
| ATT-LEV-03 | HR | View/adjust balances | |
| ATT-LEV-04 | Employee | Own balance only; no adjust | |

### Monthly approval (HR — not Finance freeze)
| # | Role | Check | ☐ |
|---|---|---|---|
| ATT-MON-01 | HR | Monthly grid (`monthly-view`) | |
| ATT-MON-02 | HR | Approve / bulk approve (`monthly-approve`) → locks days, `ready_for_payroll` | |
| ATT-MON-03 | HR | Unresolved month needs override + audit | |
| ATT-MON-04 | HR | Unlock (`monthly-unlock`) unless payroll frozen | |
| ATT-MON-05 | Finance | **Cannot** monthly-approve | |
| ATT-MON-06 | Employee | No monthly UI | |

## 5.5 View vs Edit (config)
| Surface | View | Edit |
|---|---|---|
| Types/shifts/policies list | `menu-view` | `config-manage` |
| Assignments list | `menu-view` | `assignment-manage` |
| Preview page | needs `assignment-preview` (deep-link with only menu-view → fallback warning) | N/A |

## 5.6 Negative
| # | Check | ☐ |
|---|---|---|
| ATT-NEG-01 | Employee mutate config → 403 | |
| ATT-NEG-02 | Finance with freeze cannot approve month | |
| ATT-NEG-03 | No role holds monthly-approve + month-freeze together | |

---

# MODULE 6 — Payroll

## 6.1 Permission catalog

| Key | Intent | Seeded employee? | Shipped? |
|---|---|---|---|
| `payroll.menu-view` | Module shell | Yes | Yes |
| `payroll.settings-manage` | Company settings | No | **API yes / FE no** |
| `payroll.structure-manage` | Salary structures | No | Yes |
| `payroll.deduction-manage` | Loans/fines | No | No |
| `payroll.advance-manage` / `advance-approve` | Advances | No | No |
| `payroll.payment-mode-manage` | Payment split | No | No |
| `payroll.run-create` / `run-override-readiness` / `run-approve` | Runs | No | No |
| `payroll.month-freeze` / `month-unfreeze-paid` | Finance lock | No | No |
| `payroll.disburse` | Disbursement | No | No |
| `payroll.payslip-view-own/all` | Payslips | own=Yes | No |

**Shipped:** Home, Salary Structures (list/detail/form).  
**Coming soon / `#`:** Settings UI, Tax, Deductions, Runs, Payslips, Disbursements, Advances.

## 6.2 Screens

| Path | Guard |
|---|---|
| `/payroll` | `menu-view` |
| `/payroll/config` | `menu-view` (redirects / stub) |
| `/payroll/config/salary-structures*` | **`structure-manage`** (stricter than attendance) |

## 6.3 Role-wise checklist — Salary structures (shipped)

### Super Admin / Company Admin / Finance Admin (or HR if granted structure-manage)
| # | Check | ☐ |
|---|---|---|
| PAY-STR-01 | Payroll menu + Home + health | |
| PAY-STR-02 | Open salary structures list | |
| PAY-STR-03 | Create structure (unique `code`) | |
| PAY-STR-04 | Edit structure fields | |
| PAY-STR-05 | ReadinessBadge reflects missing components / basic | |
| PAY-STR-06 | Activate blocked when `!ready_to_activate` (e.g. `requires_basic` without basic component — 7.2 pending) | |
| PAY-STR-07 | Activate when ready; status only via activate/deactivate endpoints | |
| PAY-STR-08 | Deactivate Active structure (blocks new assignments; keeps existing) | |
| PAY-STR-09 | Legacy `/payroll/salary-structures` redirects to config path | |
| PAY-STR-10 | Config module salary picker remains read-only | |

### HR Admin (without structure-manage)
| # | Check | ☐ |
|---|---|---|
| PAY-STR-HR-01 | May see Payroll home with menu-view | |
| PAY-STR-HR-02 | `/payroll/config/salary-structures` **denied** (route guard) | |
| PAY-STR-HR-03 | Structure API → 403 | |

### General Employee
| # | Check | ☐ |
|---|---|---|
| PAY-STR-EMP-01 | Menu visible; Home OK | |
| PAY-STR-EMP-02 | Structures routes/API 403 | |
| PAY-STR-EMP-03 | Placeholder cards non-navigating | |

## 6.4 Role-wise checklist — Settings API (BE only until FE)
| # | Role | Check | ☐ |
|---|---|---|---|
| PAY-SET-01 | Finance | GET settings auto-creates defaults | |
| PAY-SET-02 | Finance | PUT settings (`settings-manage`) | |
| PAY-SET-03 | Employee/HR without key | 403 | |
| PAY-SET-04 | FE | Settings tab “Coming soon” / disabled | |

## 6.5 Runtime features (when shipped; else N/A)

### Advances
| # | Role | Check | ☐ |
|---|---|---|---|
| PAY-ADV-01 | HR | Create/edit/cancel (`advance-manage`) | |
| PAY-ADV-02 | Finance/HR | Approve (`advance-approve`) or auto if setting off | |
| PAY-ADV-03 | HR | Record payment (offline handover) → `paid` | |
| PAY-ADV-04 | System | Only `paid` nets on payslip; unpaid approved ignored | |
| PAY-ADV-05 | System | Company `advance_enabled=false` → 409 + UI hidden | |
| PAY-ADV-06 | System | Monthly ceiling % / amount; `basis_gross` frozen | |
| PAY-ADV-07 | Employee | No advance manage APIs | |
| PAY-ADV-08 | QA | Advance ≠ loan (`employee_deductions.type=advance`) | |

### Runs / payslips / disburse
| # | Role | Check | ☐ |
|---|---|---|---|
| PAY-RUN-01 | Finance | Create run; override readiness | |
| PAY-RUN-02 | Finance | Approve run | |
| PAY-RUN-03 | Finance | Payslip view-all | |
| PAY-RUN-04 | Employee | Own payslip only; other ID 403 | |
| PAY-RUN-05 | Finance | Disburse / retry | |
| PAY-RUN-06 | HR | No disburse / no run-create unless granted | |

### Month freeze (Finance SoD)
| # | Role | Check | ☐ |
|---|---|---|---|
| PAY-FRZ-01 | Finance | Freeze after attendance monthly approved; else 409 | |
| PAY-FRZ-02 | Finance | Unfreeze; paid run needs `month-unfreeze-paid` + reason | |
| PAY-FRZ-03 | HR | Freeze **hidden** + API 403 | |
| PAY-FRZ-04 | UI | Freeze under Payroll menu, not beside Monthly Approval | |
| PAY-FRZ-05 | States | Pending → Approved → Frozen → Paid | |

## 6.6 View vs Edit
| Surface | View | Edit |
|---|---|---|
| Payroll home | `menu-view` | — |
| Salary structures | `structure-manage` (list included) | same key for mutate |
| Settings | API: `settings-manage` | same |
| Own payslip | `payslip-view-own` | — |
| All payslips | `payslip-view-all` | — |

## 6.7 Negative
| # | Check | ☐ |
|---|---|---|
| PAY-NEG-01 | menu-view alone cannot open structures | |
| PAY-NEG-02 | HR monthly-approve holder cannot freeze | |
| PAY-NEG-03 | Employee cannot view others’ payslips | |
| PAY-NEG-04 | Activate structure without required basic → blocked | |

---

# MODULE 7 — Approvals (cross-cutting, Platform-hosted)

## 7.1 Approvable actions (Attendance / Payroll / Config)

| Module | Action | Permission / setting |
|---|---|---|
| configuration | create/update/delete | `requires_approval` in actions.php |
| attendance | correction-approve, leave-approve, monthly-approve | platform gateway |
| payroll | run-approve, advance-approve | platform gateway; advance may short-circuit via company setting |

## 7.2 Role-wise checklist

### Super Admin / Company Admin
| # | Check | ☐ |
|---|---|---|
| APR-ADM-01 | Wire each action to a workflow in Approval Settings | |
| APR-ADM-02 | Enable/disable approval per action | |
| APR-ADM-03 | Inbox + All scopes | |
| APR-ADM-04 | Approve executes domain side-effect | |
| APR-ADM-05 | Reject leaves entity rejected; no side-effect | |
| APR-ADM-06 | Delegate / comment / cancel | |

### HR Admin (as approver for leave/correction/monthly)
| # | Check | ☐ |
|---|---|---|
| APR-HR-01 | Sees pending leave/correction/monthly in inbox when resolver matches | |
| APR-HR-02 | Without `platform.approve`: cannot act even if can open list | |
| APR-HR-03 | Employee submitter cannot approve own request (unless resolver allows — verify policy) | |

### Finance Admin (run / advance approve)
| # | Check | ☐ |
|---|---|---|
| APR-FN-01 | Payroll run / advance approvals only | |
| APR-FN-02 | Cannot approve leave/correction unless also HR | |

### General Employee
| # | Check | ☐ |
|---|---|---|
| APR-EMP-01 | Can submit leave/correction (when shipped) | |
| APR-EMP-02 | No approval admin UI | |
| APR-EMP-03 | Bank/identity pending approval shows waiting state | |

## 7.3 Negative
| # | Check | ☐ |
|---|---|---|
| APR-NEG-01 | Wrong role approve API → 403 | |
| APR-NEG-02 | Advance auto-approve when `advance_requires_approval=false` | |

---

# MODULE 8 — Cross-module / Security regression

প্রতিটি release-এ একবার চালান।

| # | Check | Roles | ☐ |
|---|---|---|---|
| X-01 | Sidebar = union of granted menu permissions only | All | |
| X-02 | Direct URL + API both enforced (not UI-only) | All | |
| X-03 | `/me/permissions` matches assigned roles (bypass = all active keys) | SA/CA vs EMP | |
| X-04 | Company data isolation (no other company IDs) | All | |
| X-05 | SoD: no role with monthly-approve + month-freeze | HR vs Finance | |
| X-06 | Demo employee dual roles (`employee` + `configuration-staff`) = union — document for pure employee tests | EMP | |
| X-07 | Dashboard `/` visible to any post-onboarding authenticated user | All | |
| X-08 | Logout / inactive / wrong token | All | |

---

# Appendix A — Recommended HR Admin permission pack

```
employee.menu-view, employee.create, employee.update,
employee.view-bank, employee.view-salary, employee.view-documents, employee.view-restricted-documents,
employee-id-card.history, employee-id-card.reissue, employee-id-card.deactivate,

attendance.menu-view, attendance.config-manage, attendance.assignment-manage, attendance.assignment-preview,
attendance.punch-create-others, attendance.record-view-all, attendance.record-export, attendance.record-recalculate,
attendance.correction-create, attendance.correction-approve, attendance.correction-override-lock,
attendance.leave-apply, attendance.leave-approve, attendance.leave-balance-view, attendance.leave-balance-adjust,
attendance.monthly-view, attendance.monthly-approve, attendance.monthly-unlock,

payroll.menu-view, payroll.advance-manage, payroll.payslip-view-all
(+ payroll.structure-manage / payment-mode-manage if HR owns salary setup)

configuration.view, configuration.system-configuration-menu-view, configuration.create, configuration.update

DO NOT GRANT: payroll.month-freeze, payroll.month-unfreeze-paid, payroll.disburse
```

# Appendix B — Recommended Finance Admin permission pack

```
payroll.menu-view, payroll.settings-manage, payroll.structure-manage, payroll.deduction-manage,
payroll.advance-approve, payroll.payment-mode-manage,
payroll.run-create, payroll.run-override-readiness, payroll.run-approve,
payroll.month-freeze, payroll.month-unfreeze-paid, payroll.disburse,
payroll.payslip-view-all,

employee.menu-view, employee.view-salary, employee.view-bank,

attendance.menu-view, attendance.record-view-all, attendance.monthly-view

DO NOT GRANT: attendance.monthly-approve, attendance.monthly-unlock,
attendance.leave-approve, attendance.correction-approve
```

# Appendix C — Bug report template

```
Persona:
Role slug(s):
Expected permissions:
Actual /me/permissions:
Module / screen / URL:
API method + path + status:
Expected vs Actual:
Screenshot / HAR:
```

---

*Module sections are the source of truth for role QA. Update when new screens ship (replace N/A with real checks). Aligned with `actions.php`, seeders, and current FE/BE guards.*
