> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shdevelopment.org/llms.txt
> Use this file to discover all available pages before exploring further.

# sh-policemdt: Police MDT System for RedM Servers

> Install, configure, and operate sh-policemdt, a VORP/RSG RedM law-enforcement MDT with incidents, people profiles, warrants, fines, jail, dispatch, evidence, and forensics.

sh-policemdt is an in-game Mobile Data Terminal for RedM law-enforcement roleplay. It gives authorized officers a NUI dossier interface for citizen profiles, incident reports, warrants, fines, jail processing, dispatch calls, evidence, and forensic reports.

The resource supports VORP and RSG through a shared framework bridge. It can also integrate with `sh-policejob` for duty-aware access and live duty sync, but the police-job bridge is configurable.

## Overview

| Detail                      | Value                                                      |
| --------------------------- | ---------------------------------------------------------- |
| Current version             | `1.3.2`                                                    |
| Frameworks                  | VORP, RSG, or `auto` detection                             |
| Required dependency         | `oxmysql`                                                  |
| Required framework          | `vorp_core` or `rsg-core`                                  |
| Optional duty bridge        | `sh-policejob`                                             |
| Optional identity providers | `sh-identity`, `ss-identitycard`, `fx-idcard`, `af-idcard` |
| UI                          | Bundled NUI, Vue global build, no frontend build step      |
| Default MDT command         | `/mdt`                                                     |
| Emergency close command     | `/mdtclose`                                                |
| Default MDT item            | `sheriffbook`                                              |

<Warning>
  If `Config.Compatibility.PoliceJob.RequireOnDuty = true`, start `sh-policejob` before `sh-policemdt`. Officers will be denied MDT access when the duty bridge cannot confirm they are on duty.
</Warning>

## Features

<Accordion title="Officer dashboard and live units">
  The dashboard shows recent incidents, recent calls, evidence activity, and live officer state. Officer data is refreshed through the configured polling interval and stale heartbeat settings.
</Accordion>

<Accordion title="Citizen profile lookup">
  Search citizen records through the configured identity provider. Profiles can show notes, active warrants, incident history, fine history, jail history, and linked records.
</Accordion>

<Accordion title="Incident reports">
  Create structured incident reports with status, priority, location, linked people, linked units, charges, penal-code entries, evidence, and dispatch call attachments.
</Accordion>

<Accordion title="Warrants">
  List and manage warrants with active, served, recalled, and expired lifecycle states. Supervisor/boss grades can create, resolve, and delete warrants. Expired warrants are handled by the automatic expiry sweep.
</Accordion>

<Accordion title="Fines">
  Issue fines from MDT records and penal-code workflows. Fines are recorded immediately. Payment can be handled later by your courthouse system or by the optional Pay Fine NPC.
</Accordion>

<Accordion title="Jail and custody">
  Create jail records, transport prisoners directly to Siska or through a manual dock workflow, apply optional prison clothing, enforce escape radius, sync custody on reconnect, show `/jailtime`, and auto-release when the sentence ends.
</Accordion>

<Accordion title="Dispatch calls">
  Citizens can request law assistance with the configured dispatch command. Calls can create local alert blips, be attached to units, moved through statuses, routed with a waypoint, and linked to incidents.
</Accordion>

<Accordion title="Evidence and forensics">
  Officers can collect world shell and blood traces, attach evidence bags to incidents, create configured evidence entries from inventory, compare DNA/fingerprint samples, confirm forensic matches, and link forensic reports to incidents.
</Accordion>

<Accordion title="Localization and logging">
  Locale files are included for English, Spanish, and German. Optional Discord logging can record API calls, failed access attempts, and citizen calls.
</Accordion>

## Requirements

<Steps>
  <Step title="Database">
    Install and ensure `oxmysql`. The resource declares `@oxmysql/lib/MySQL.lua` in `fxmanifest.lua`.
  </Step>

  <Step title="Framework">
    Run either `vorp_core` or `rsg-core`. Leave `Config.Framework = 'auto'` for auto-detection, or set it to `vorp` or `rsg`.
  </Step>

  <Step title="Identity data">
    Choose the profile lookup source with `Config.Compatibility.IDSystem`. Supported presets are `sh-identity`, `ss-identitycard`, `fx-idcard`, and `af-idcard`.
  </Step>

  <Step title="Optional police-job bridge">
    Use `sh-policejob` when you want duty-only access and auto-close on duty end. Set `Config.Compatibility.PoliceJob.Name = 'none'` if you are not using that bridge.
  </Step>
</Steps>

## Installation

<Steps>
  <Step title="Place the resource">
    Put the `sh-policemdt` folder in your RedM server resources directory.
  </Step>

  <Step title="Import the MDT schema">
    Run `install/sql.sql` once against your server database. It is non-destructive and uses `CREATE TABLE IF NOT EXISTS`, so it creates missing tables but does not reshape older custom tables.
  </Step>

  <Step title="Install inventory items">
    For VORP, import `install/items.sql`. For RSG, copy entries from `install/RSG items.txt` into `rsg-core/shared/items.lua`, then copy the matching PNGs from `install/images` into your inventory image directory.
  </Step>

  <Step title="Configure framework and identity">
    Edit `config.lua`, set `Config.Framework`, `Config.Compatibility.IDSystem`, and your LEO job mappings.
  </Step>

  <Step title="Configure duty access">
    If using `sh-policejob`, ensure it before the MDT. If not, set `Config.Compatibility.PoliceJob.Name = 'none'` or disable the on-duty requirement.
  </Step>

  <Step title="Start in server.cfg">
    Ensure dependencies first, then start the MDT.
  </Step>
</Steps>

```text server.cfg theme={null}
ensure oxmysql
ensure vorp_core # or ensure rsg-core
ensure sh-policejob # optional, but needed when RequireOnDuty is true
ensure sh-policemdt
```

<Note>
  The SQL install includes the normalized MDT tables for people, incidents, charges, calls, fines, jail records, warrants, evidence, world evidence, officer stats, forensic samples, forensic matches, forensic reports, and related join tables.
</Note>

## Inventory Items

The included item files define the default MDT and forensics items:

| Item                | Purpose                                             |
| ------------------- | --------------------------------------------------- |
| `sheriffbook`       | Opens the MDT when item opening is enabled          |
| `investigation_kit` | Toggles CSI mode for authorized forensics users     |
| `evidence_bag`      | Required by default when linking collected evidence |
| `ink_pad`           | Fingerprint workflow item                           |
| `fingerprint_card`  | Fingerprint record item                             |
| `dna_sampler`       | Collects known DNA samples                          |
| `dna_sample`        | DNA sample output item                              |
| `cleaning_kit`      | Cleans nearby forensic traces                       |
| `pocket_watch`      | Example custom evidence catalog item                |
| `forged_documents`  | Example custom evidence catalog item                |

## Configuration

```lua config.lua theme={null}
Config.Locale = 'en' -- 'en', 'es', or 'de'
Config.Debug = true
Config.Framework = 'auto' -- 'auto' | 'vorp' | 'rsg'

Config.Compatibility = {
    IDSystem = 'sh-identity', -- 'sh-identity' | 'ss-identitycard' | 'fx-idcard' | 'af-idcard'
    PoliceJob = {
        Name = 'sh-policejob', -- 'sh-policejob' | 'none'
        RequireOnDuty = true,
        AutoCloseOnDutyEnd = true,
    },
    Courthouse = {
        Enabled = true,
        ApprovalForWarrants = true,
        ManageFinePayments = true,
    },
}

Config.VorpLEOJobs = {
    marshal = true,
    sheriff = true,
    police = true,
}

Config.RsgAllowedJobType = 'leo'
Config.RsgLEOJobs = {
    vallaw = true,
}

Config.MDT = {
    OpenCommand = 'mdt',
    MinRank = 0,
    RequestTimeoutMs = 8000,
    Item = {
        Enabled = true,
        Name = 'sheriffbook',
    },
}

Config.Supervisorgrade = 6
Config.Bossgrade = 8
```

### Important Config Areas

| Config                  | What it controls                                                                                             |
| ----------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Config.UI`             | Job-specific header titles, date format, and NUI polling intervals                                           |
| `Config.Pagination`     | Default and maximum list sizes                                                                               |
| `Config.LEORankNames`   | Rank labels by framework and job                                                                             |
| `Config.Notifications`  | Optional custom notification message overrides                                                               |
| `Config.DiscordLogging` | Discord webhook logging for API, security, and citizen calls                                                 |
| `Config.PenalCode`      | Penal-code categories, charges, fine amounts, and jail minutes                                               |
| `Config.Incidents`      | Auto-created incident defaults for fine and jail actions                                                     |
| `Config.Warrants`       | Warrant enablement, list limits, and expiry sweep interval                                                   |
| `Config.Fines`          | Fine money type and officer/target notifications                                                             |
| `Config.PayFineNPC`     | Optional in-world fine payment clerk                                                                         |
| `Config.Dispatch`       | Citizen call command, call statuses, alert blips, and routing                                                |
| `Config.Jail`           | Siska/manual transport, prison outfit, custody enforcement, reconnect sync, and release                      |
| `Config.RateLimits`     | API, officer heartbeat, stale officer pruning, and shot evidence throttles                                   |
| `Config.Evidence`       | Shell drops, detector behavior, evidence bag requirements, and custom evidence catalog                       |
| `Config.Forensics`      | CSI access, investigation kit, DNA, fingerprints, trace cleanup, forensic code format, and report visibility |

## Access Rules

MDT access is checked server-side. A player must match the configured framework law job settings and meet `Config.MDT.MinRank`. When `Config.Compatibility.PoliceJob.RequireOnDuty = true`, the player must also be on duty through the police-job bridge.

Privileged moderation actions use the configured grade window:

```lua theme={null}
Config.Supervisorgrade = 6
Config.Bossgrade = 8
```

Grades in that inclusive range are treated as supervisor/boss-level for sensitive actions such as warrant management, record moderation, jail workflows, and forensics access when enabled by config.

## Commands

| Command               | Default                | Description                                                        |
| --------------------- | ---------------------- | ------------------------------------------------------------------ |
| MDT open              | `/mdt`                 | Opens the MDT for authorized LEOs                                  |
| Emergency close       | `/mdtclose`            | Releases NUI focus if the interface gets stuck                     |
| Citizen law alert     | `/alertlaw`            | Creates a citizen dispatch call when dispatch is enabled           |
| Clear dispatch alerts | `/cleardispatchalerts` | Clears local dispatch alert blips                                  |
| Clean evidence        | `/cleanevidence`       | Destroys nearby traces when authorized and carrying a cleaning kit |
| Jail time             | `/jailtime`            | Shows remaining custody time                                       |

<Tip>
  `Config.Dispatch.Enabled` is `false` by default in the provided config. Enable it before expecting `/alertlaw`, MDT call APIs, alert blips, or the Dispatch UI tab to function.
</Tip>

## Common Workflows

<Tabs>
  <Tab title="Open MDT">
    Officers use `/mdt`, the `sheriffbook` item, `sh_mdt:openFromPed`, or the `sh_policejob:client:openMdt` / `sh_policejob:client:openMDT` events. Access is still validated server-side before the UI opens.
  </Tab>

  <Tab title="Create Incident">
    Open the Incidents tab, create a case, set status and priority, add involved people, units, charges, penal-code entries, evidence, and related dispatch calls. Fine and jail actions can also auto-create incidents when enabled.
  </Tab>

  <Tab title="Manage Warrants">
    Supervisors can create active warrants, serve or recall them, and delete records where allowed. Status transitions are limited: active warrants can become `served` or `recalled`; `expired` is system-managed.
  </Tab>

  <Tab title="Issue Fine">
    Create a fine against a person profile or incident context. The fine is recorded as unpaid unless an external courthouse flow or the optional Pay Fine NPC handles payment.
  </Tab>

  <Tab title="Process Jail">
    Create the jail record from the MDT. Use direct Siska teleport or manual dock transport. Custody can enforce escape return, reconnect sync, timed release, and release teleport to Saint Denis docks.
  </Tab>

  <Tab title="Forensics">
    Authorized users with `investigation_kit` can toggle CSI mode, collect traces, create known DNA/fingerprint samples, compare matches, confirm forensic reports, and link reports or evidence to incidents.
  </Tab>
</Tabs>

## Identity Provider Notes

Person search and profile lookup depend on the selected identity preset:

| Provider          | Expected source                            |
| ----------------- | ------------------------------------------ |
| `sh-identity`     | `sh_idcards`                               |
| `ss-identitycard` | `ss_identitycard`                          |
| `fx-idcard`       | `fx_idcard` with `charid` plus JSON `data` |
| `af-idcard`       | `af_idcards`                               |

Search is designed around roleplay names. If person lookup is empty, confirm the provider table exists and that `Config.Compatibility.IDSystem` matches the installed identity resource.

## Localization

Set the active language with:

```lua theme={null}
Config.Locale = 'en'
```

Locale files live in `locales/*.lua`. The package includes `en.lua`, `es.lua`, and `de.lua`. Translations are grouped under server messages, client messages, notification keys, UI tab labels, UI text, and UI literal replacements.

## API Surface

The NUI talks to the server through the unified events `sh_mdt:api:request` and `sh_mdt:api:response`. Responses use a standard envelope:

```lua theme={null}
-- Success
{ ok = true, data = ... }

-- Failure
{ ok = false, error = 'error_code', message = 'Readable message' }
```

Primary MDT actions include:

```text theme={null}
mdt:bootstrap
mdt:officers:list
mdt:incidents:list|get|save|delete|setStatus|assignUnit|linkEvidence
mdt:people:search|getProfile|setNotes|delete
mdt:people:records:deleteIncident|deleteFine|deleteJail
mdt:warrants:list|create|setStatus|delete
mdt:calls:list|attach|setStatus|addNote
mdt:evidence:list|linkIncident
mdt:fines:list|create|void|markPaid
mdt:jail:list|create|release|completeTransport
mdt:forensics:dashboard
mdt:forensics:samples:list|createKnown
mdt:forensics:matches:compare|confirm
mdt:forensics:reports:list|linkIncident
```

## Troubleshooting

<Accordion title="The UI opens blank">
  Confirm `html/assets/vue.global.prod.js` exists, check F8/NUI console for JavaScript errors, then run `/mdtclose` and reopen the MDT.
</Accordion>

<Accordion title="Officers are denied access">
  Check `Config.Framework`, `Config.VorpLEOJobs`, `Config.RsgAllowedJobType`, `Config.RsgLEOJobs`, and `Config.MDT.MinRank`. If `RequireOnDuty` is enabled, confirm `sh-policejob` is started and the officer is clocked in.
</Accordion>

<Accordion title="Citizen search returns no results">
  Confirm the identity provider table exists and matches `Config.Compatibility.IDSystem`. For example, `sh-identity` expects `sh_idcards`.
</Accordion>

<Accordion title="Database writes fail">
  Confirm `oxmysql` is started before the MDT, database credentials are valid, and `install/sql.sql` was imported.
</Accordion>

<Accordion title="Dispatch commands do nothing">
  Set `Config.Dispatch.Enabled = true`. The default config disables dispatch commands, dispatch APIs, alert blips, and the Dispatch UI tab.
</Accordion>

<Accordion title="Fines are recorded but not paid">
  This is expected with the manual payment model. Use a courthouse payment flow or enable and configure `Config.PayFineNPC`.
</Accordion>

<Accordion title="Jail outfit does not apply">
  If using `Mode = 'd_labs_prisonclothes'`, ensure `d_labs_prisonclothes` is started and the configured event names match. For other clothing systems, use `Mode = 'event'` and set `EventName`.
</Accordion>

<Accordion title="Forensics is unavailable">
  Confirm `Config.Forensics.Enabled = true`, the officer is in the supervisor/boss grade window or a configured detective job, and the required items exist in inventory.
</Accordion>

## Upgrade Notes

`install/sql.sql` is safe for a first install and for creating missing tables, but it does not migrate existing table columns because it uses `CREATE TABLE IF NOT EXISTS`. Back up your database before upgrading from an older release, then compare schema changes before applying custom migrations.

<Note>
  Keep `Config.Forensics.ProfileSalt` unique per server. Leaving the default value makes forensic profile hashes predictable across servers.
</Note>
