# UPLOAD LIST — Detailed Role & Access Control

Access is no longer one tick per module. Every module now opens into two lists —
**what a person may do** and **what a person may see** — and an admin can only ever
hand out what they hold themselves.

**339 permission points across 20 modules: 176 actions and 163 columns.**

Built and tested end to end against a copy of your own database (schema dump +
every existing migration replayed, then `acvmigration.php` and `aclmigration.php`
on top). Role build → staff create → access give and take back → ceiling refusals
→ column hiding were all exercised and pass.

---

## 1. NEW FILES — upload these

| File | What it is |
|---|---|
| `aclmigration.php` | **root** — the database migration. Run once, then delete. |
| `includes/acl.php` | The access engine: the module / action / column registry, the effective-access resolver, the ceiling, the staff levels, the form-field builder and the access log. |
| `admin/staff_fields.php` | Where a Super Admin designs the Add Admin / Add Employee form. |

## 2. MODIFIED FILES — replace these

| File | What changed |
|---|---|
| `admin/roles.php` | Rewritten. A role now opens into modules; inside each one you tick actions and columns separately. Level, description, on/off, duplicate, delete, search, and a change history. Anything above your own access shows locked. |
| `admin/staff.php` | Rewritten. Create an Admin or an Employee by the rules below, on a form you designed yourself. Per-person access: give a little more, take a little back. Reset password, block, remove — each with its own history line. |
| `admin/user_list.php` | Every one of its 14 columns is now gated. The friendly access screen replaces the old one-line "Access Denied". |
| `admin/product_management.php` | Same for both product pages — all columns gated, including each share column on its own, and the friendly access screen. |
| `includes/functions.php` | `hasPermission()` now asks the ACL, translating the old single keys. **Every page written before this carries on unchanged.** Falls back to the original code if `includes/acl.php` is missing. |
| `includes/rbac.php` | `rbac_my_permissions()` works the old-style list out from the new detailed one, so the sidebar and every existing guard follow the new rules automatically. |
| `admin/includes/sidebar.php` | New entry: **Staff Form Fields**, under User Management. |

**Nothing else was touched.** All the other admin pages keep their existing guards
and now obey the new access rules through `hasPermission()` — no edit needed.

---

## 3. HOW TO DEPLOY

1. Upload the three new files and the seven modified ones (keep the folder structure).
2. Open **`https://wbmcz.online/aclmigration.php`** — it should say
   *Migration completed successfully. Nobody lost any access.*
   `includes/acl.php` must already be up, or the page will tell you to upload it first.
3. **Delete `aclmigration.php` from the server.**
4. Hard refresh the admin panel (Ctrl + Shift + R).

Safe to run more than once — every step checks before it changes anything.

---

## 4. DATABASE CHANGES

### New tables

| Table | Holds |
|---|---|
| `staff_form_fields` | The boxes on the Add Admin / Add Employee form: label, kind, whether it is required, which form it belongs on, order. |
| `staff_field_values` | What was filled into the extra boxes for each staff account. |
| `staff_access_log` | Every grant and every take-back: who, to whom, exactly which permissions, the reason, the time and the IP. |

### Changed tables

| Table | Columns added |
|---|---|
| `roles` | `level` (superadmin / admin / employee), `perms_json` (the detailed set), `description`, `status`, `created_by`, `created_by_name`, `updated_at` |
| `users` | `staff_level`, `perm_extra` (given to this person alone), `perm_denied` (taken back from this person alone) |

### What the migration does to your existing data

* **Your six roles are expanded, never trimmed.** Every module a role already held is
  opened completely — every action and every column inside it — so nobody loses a
  single thing the day this goes live. On the test run:
  Super Admin kept full access, ADMIN got 231 points, Support Executive 74,
  Co Founder / Chief Administrator / H. R 261 each.
* **Every staff account is stamped with a level.** No role, or a role holding
  everything → Super Admin. A role that could reach site settings or staff & roles
  → Admin. Everyone else → Employee. You can change any of them afterwards.
* The old-style `roles.permissions` list is kept in step whenever you save a role,
  so any page still reading it keeps working.

---

## 5. WHO MAY CREATE WHOM

| | Super Admin | Admin | Employee |
|---|---|---|---|
| Create an **Admin** | yes, with any access | **no** | no |
| Create an **Employee** | yes, with any access | yes, **at most their own access** | no |
| Build a role | for Admin or Employee | for Employee only | no |
| Change someone's access | anyone | employees only | no |
| See in the staff list | everyone | employees + themselves | — |

**The ceiling.** Nobody can hand out access they do not hold. When an Admin builds a
role or ticks boxes for an employee, the form only offers what that Admin has —
everything else is drawn locked with a padlock — and the server intersects the
request with the ceiling *again* before saving. Editing the page in a browser
changes nothing: in testing, an Admin who asked for 8 permissions but held only 3 of
them had exactly 3 saved, and was told so.

**What an Admin cannot take away either.** If a Super Admin gave an employee something
the Admin does not hold — say wallet credit — that permission is left exactly as it
is when the Admin edits that employee. They can neither give it nor remove it, and the
screen says how many points were left untouched.

---

## 6. HOW IT WORKS DAY TO DAY

### Custom Roles — `Admin → User Management → Custom Roles`

A role opens into modules, grouped as Overview · Management · Service · Money · Site.
Inside each module:

* **What they may do** — view, create, edit, delete, export, approve, price, share,
  assign, hand over, refund … 176 of them across the panel.
* **Which columns they may see** — 163 of them, listed one by one.

Search across every module, tick a whole module with **All**, or take the lot with
**Tick everything I can give**. Each role carries a level, a description, an on/off
switch, a **Make a copy** button, and a history showing every permission ever added
or removed with the reason and the person.

A role that is switched off takes access away from everyone on it until it is switched
back on. A role someone is using cannot be deleted — the screen says how many people
are on it.

### Staff Accounts — `Admin → User Management → Staff Accounts`

**Add Staff** asks what kind of account (only the kinds you are allowed to create),
fills the boxes you designed, lets you start from a role, and then shows the whole
permission grid so you can add or drop anything for that one person before saving.

On the list, each staff row shows the level, the role, and a summary like
*User Management 6/28 · Product Management 4/31*, plus **+3 extra / −1 removed** when
that person differs from their role. The buttons are:

* **Access** — the same grid again. Tick to give, untick to take back. A *Why is this
  changing?* box goes into the history. **Back to exactly what the role gives** undoes
  any per-person tweaks in one click.
* **Edit** — the details, on the same form you designed.
* **Password** — set a new one.
* **Block** — they can no longer sign in; their history stays.
* **Remove** — the account goes; the activity trail stays.

Every one of those writes a line to the access history with the name, level, time and
IP, and also into the main Activity Log.

### Staff Form Fields — `Admin → User Management → Staff Form Fields`

You decide what the Add Admin and Add Employee forms ask for. Each box has a label, a
kind (text, long text, number, phone, email, date, dropdown, link), a hint, a note, and
three settings: **which form it belongs on** (Admin only / Employee only / both),
**whether it must be filled in**, and **whether it shows at all**. Change the numbers in
the Order column and press *Save the order* to move boxes around.

It starts with nine boxes: Full Name, Mobile Number, Email Address, Login Password,
Full Address, WhatsApp Number, Gender, Joining Date, Designation. The first four are
marked **SYSTEM** — rename and re-order them freely, but they cannot be hidden or
deleted, because an account cannot be made without them. Everything else is yours to
keep, rename, hide or throw away, and you can add as many of your own as you like
(*Aadhaar Number*, *Branch*, *Reporting To*, whatever you need).

Boxes that match a real user column — mobile, email, WhatsApp, gender — are written
straight into that column. Everything else is stored beside the account, so your live
`users` table is never altered to add a field.

---

## 7. WHAT TO CHECK AFTER UPLOADING

1. **Custom Roles** — open any existing role. It now shows every action and every
   column its old module tick covered, all ticked. Nothing was lost.
2. Create a role called *Front Desk* for Employee: give it User Management → *See the
   user list*, *Write notes*, and only the **Name, user ID, mobile, email** column.
   Save it.
3. **Staff Accounts → Add Staff** → Employee, start from *Front Desk*, fill the form,
   create.
4. Sign in as that employee and open **User Management**: the table shows the tick box
   and that one column — no balance, no transfer, no KYC. Product Management shows a
   polite *You do not have access here* screen.
5. Back as yourself, press **Access** on that employee, tick *KYC Status* as a column,
   write a reason, save. Sign in as them again — the column is there. The history
   underneath shows exactly what you gave and why.
6. **Staff Form Fields** — add a box called *Aadhaar Number*, set it to Employee only
   and required. Open Add Staff → Employee; it is there, and the Admin form does not
   have it.

---

## 8. TWO THINGS WORTH ADJUSTING AFTERWARDS

* The migration reads a role's level from what it could already reach. Your **ADMIN**
  role came out as *Employee level* because it never held site settings or staff &
  roles. If it is meant to be an admin role, open it and change **This role is for**
  to *Admin*.
* Admins can only create employees. If you want a particular admin to be able to create
  other admins, they need to be a Super Admin instead — that is deliberate, so the
  chain of who-can-create-whom stays clear.
