> For the complete documentation index, see [llms.txt](https://safky.gitbook.io/product-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://safky.gitbook.io/product-docs/scripts/s_mdt.md).

# MDT

Police and EMS tablet: citizen profiles, incidents with fines and jail, reports, BOLOs, vehicles, live units, duty statistics and logs.

A tablet for police and EMS jobs: citizen profiles, incidents with charges, fines and jail, reports, BOLOs, vehicles, live units, duty hours, prisoners and an activity log.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-de548774271163acf73921c6cc32fc7f6a0b26c0%2Fmdt-dashboard.jpg?alt=media" alt="The dashboard: dispatch calls, warrants, bulletins and the units on duty grouped in squads."><figcaption><p>The dashboard: dispatch calls, warrants, bulletins and the units on duty grouped in squads.</p></figcaption></figure>

***

## ✅ Requirements

|              |                                                                                                                                                    |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Frameworks   | ESX, QBCore, QBX (detected on start)                                                                                                               |
| Dependencies | [ox\_lib](https://github.com/overextended/ox_lib), [oxmysql](https://github.com/overextended/oxmysql)                                              |
| Optional     | [s\_dispatch](/product-docs/scripts/s_dispatch.md) for calls on the dashboard, [s\_radar](/product-docs/scripts/s_radar.md) for BOLOs on the radar |

***

## 📥 Installation

```
ensure ox_lib
ensure oxmysql
ensure s_mdt
```

There is no SQL to import. The tables are created on the first start.

***

## ⚙️ Configuration

Everything is in `config/`. The options not covered here are commented in `config/config.lua`.

### Jobs and permissions

```lua
Jobs = {
    ['police'] = {
        category = 'Police',
        type = 'police',
        color = '#3b82f6',
        logo = '',
        order = 1,
        permissions = {
            dashboard = { view = 0 },
            profiles  = { view = 0, edit = 0, clearRecord = 4 },
            incidents = { view = 0, create = 0, edit = 0, delete = 2 },
            -- reports, bolos, bulletins, vehicles, codes, units, statistics, prisoners, logs
        },
    },
},
```

* Jobs with the same `category` see each other's incidents, reports, BOLOs and bulletins, and share the units panel. Use the same category names in s\_dispatch and s\_radar.
* `type = 'medical'` hides warrants and the criminal record and switches the wording to medics.
* Each number is the minimum job grade. A page missing from `permissions` is hidden for that job; an action missing from a page is not allowed.
* `profiles.clearRecord` is who can clear a criminal record. The citizen has to accept and pays `ClearRecordCost` from the bank.
* `logo` takes any image URL (`https://...` or `nui://your_resource/logo.png`). Empty shows an icon.

{% hint style="warning" %}
With `RequireDuty = false` (the default) everyone with the job opens the MDT, but only players on duty appear in the units panel and count for the statistics. On ESX, which has no duty toggle, everyone with the job counts as on duty unless your ESX sets `job.onDuty`.
{% endhint %}

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-ef9f200240c6480d8c2ad955010968cf07b7db71%2Fmdt-profile.jpg?alt=media" alt="A citizen profile with an active warrant, the licenses, the officers&#x27; notes and a property."><figcaption><p>A citizen profile with an active warrant, the licenses, the officers' notes and a property.</p></figcaption></figure>

### Licenses

```lua
Licenses = {
    {
        key = 'weapon',
        label = 'Weapon License',
        icon = 'gun',
        canGrant = true,
        canRevoke = true,
        grantCost = 5000,
        requireOnline = { grant = true, revoke = false },
        allowedCategories = { 'Police' },
    },
},
```

`key` is the license name in your framework: the key inside `metadata.licences` on QBCore and QBX (`driver`, `weapon`), the `type` in `user_licenses` on ESX (`weapon`, `drive`, `dmv`). A license with a `grantCost` needs the citizen online: they get a request on screen and pay from the bank only if they accept.

### Penal code

```lua
PenalCode = {
    ['Minor Offenses'] = {
        { title = 'Art. 2 - Speeding', fine = 750, months = 0, color = 'green' },
    },
},
```

The incident adds up the fines and months of the charges picked for each person. The fine an officer sends can be lower than that, never higher, and only after the incident is saved with the charges on it. Keep the titles unique: the check reads them back from the saved incident.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-aba9239506e6836d5d64ece221477b7e7b63885d%2Fmdt-incident-edit.jpg?alt=media" alt="An incident in edit mode: two people with their charges, the recommended fine and sentence, and the Fine and Jail buttons."><figcaption><p>An incident in edit mode: two people with their charges, the recommended fine and sentence, and the Fine and Jail buttons.</p></figcaption></figure>

### Jail, prisoners, phones and properties

These come from your own scripts, through `config/sv-functions.lua`. A hook left as `nil` turns its feature off and the MDT hides the button or the page.

| Hook                                         | Default                                                  | Shows                                    |
| -------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- |
| `Hooks.SendFine`                             | Takes the fine from the citizen's bank, citizen online   | The Fine button                          |
| `Hooks.JailPlayer`                           | `nil`                                                    | The Jail button                          |
| `Hooks.GetPrisonLimits`                      | `nil`, uses `Config.Sentence`                            | The allowed months                       |
| `Hooks.GetInmates`                           | `nil`                                                    | The Prisoners page                       |
| `Hooks.SetSentence`, `Hooks.ReleasePrisoner` | `nil`                                                    | Change sentence and Release on that page |
| `Hooks.GetPhoneNumbers`                      | Framework phone (`charinfo.phone`, `users.phone_number`) | The phone on profiles                    |
| `Hooks.GetProperties`                        | `nil`                                                    | The properties on profiles, with GPS     |
| `Hooks.GetVehicleState`                      | The framework garage column                              | Garage, out or impounded                 |
| `Hooks.OnLog`                                | `nil`                                                    | Every log entry, for your own logging    |

The file has examples for qb-policejob, lb-phone and qb-houses. `GetInmates` returns a list of `{ citizenid, name, reason, jailedBy, jailedAt, total, left }`.

### Radio codes

`Codes` is a list of sections, each with a `title`, an optional Font Awesome `icon` and its `codes`. `alert = true` highlights a code.

***

## 💬 Commands

| Command | Who            | What it does  |
| ------- | -------------- | ------------- |
| `/mdt`  | Jobs in `Jobs` | Opens the MDT |

The name is `OpenCommand` in `config/config.lua`.

***

## 🔌 Integration

### Server exports

| Export                                                                          | Returns                                                                                                                            |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `exports.s_mdt:GetOnDutyUnits()`                                                | Table by server id: `source`, `identifier`, `callsign`, `name`, `radio`, `job`, `jobLabel`, `jobCategory`, `jobColor`, `dutyStart` |
| `exports.s_mdt:IsPlayerOnDuty(source)`                                          | `true` if the player is on the duty roster                                                                                         |
| `exports.s_mdt:GetOnDutyByCategory(category)`                                   | The roster filtered by category                                                                                                    |
| `exports.s_mdt:IsPlateFlagged(plate)`                                           | `{ hasBolo, boloCount }`, spaces and case ignored                                                                                  |
| `exports.s_mdt:AddBolo(plate, detail, author, job)`                             | The new BOLO id                                                                                                                    |
| `exports.s_mdt:AddWarrant(identifier, charges, sentence, author, details, job)` | The conviction id and the incident id created behind it                                                                            |

`AddWarrant` takes the character identifier (citizenid on QBCore and QBX, identifier on ESX) and a list of charge titles. It creates an incident so the warrant can be opened and removed from the MDT. `job` is optional; without it the BOLO or warrant goes to the first police job in `Jobs`.

### Events

| Event                                | Side          | Use                                                |
| ------------------------------------ | ------------- | -------------------------------------------------- |
| `s_mdt:server:boloChanged` (plate)   | Server, local | Fired when a BOLO is created, edited or deleted    |
| `s_mdt:client:searchVehicle` (plate) | Client, local | Opens the MDT on the vehicle search for that plate |
| `s_mdt:client:searchBolo` (plate)    | Client, local | Opens the MDT on the BOLOs for that plate          |

### Client hooks (`config/cl-functions.lua`)

| Function                      | What it is for                                            |
| ----------------------------- | --------------------------------------------------------- |
| `Notify(msg, type, duration)` | Every notification the MDT shows                          |
| `CanOpenMDT()`                | Return `false` to block the MDT (dead, cuffed, in a menu) |

### Framework (`config/framework/`)

`sv-framework.lua` holds every query against the framework tables: characters, jobs, licenses and vehicles. A fork with renamed tables or columns changes them there.

***

## 🗄️ Database

| Table                                             | Holds                                                                         |
| ------------------------------------------------- | ----------------------------------------------------------------------------- |
| `s_mdt_accounts`                                  | Callsign and avatar per character                                             |
| `s_mdt_duty_time`                                 | Seconds on duty per character and job                                         |
| `s_mdt_incidents`, `s_mdt_convictions`            | Incidents, and each person on them with charges, fine, sentence and `warrant` |
| `s_mdt_reports`, `s_mdt_bulletins`, `s_mdt_bolos` | Reports, dashboard bulletins, BOLOs                                           |
| `s_mdt_profiles`, `s_mdt_vehicle_info`            | Photo and notes per citizen, notes per plate                                  |
| `s_mdt_logs`                                      | The log shown in the Logs page                                                |

Character columns (`cid`, `identifier`, `citizenid`) hold the character identifier of your framework.
