> For the complete documentation index, see [llms.txt](https://docs.gkshop.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gkshop.org/tablet/configuration/custom-inventory.md).

# Custom Inventory

### Overview

GKS Tablet uses an **adapter pattern** for inventory integration. Adapter files live in:

| Side   | Path                           |
| ------ | ------------------------------ |
| Server | `gks-tablet/server/inventory/` |
| Client | `gks-tablet/client/inventory/` |

These folders are **editable** in escrow. Files are loaded automatically via `fxmanifest.lua` (`client/inventory/*.lua`, `server/inventory/*.lua`).

When `Config.TabletItemRequire = true`, the tablet verifies the player owns the configured item before opening. When `false`, anyone can open the tablet via command/keybind (item use is optional).

### 1. Configuration

Open `gks-tablet/config/config.lua` and set your inventory adapter name:

```lua
Config.InventoryScript = "custom"   -- must match your adapter file guard
Config.TabletItemRequire = true     -- require tablet item to open
Config.TabletItemName = "tablet"    -- item name in your inventory
```

#### InventoryScript options

| Value            | Behavior                                                                                         |
| ---------------- | ------------------------------------------------------------------------------------------------ |
| `"auto"`         | Uses `ox_inventory` if started, otherwise `"none"`                                               |
| `"ox_inventory"` | Built-in ox\_inventory adapter (no custom files needed)                                          |
| `"none"`         | No item check adapter — **blocks open** when `TabletItemRequire` is `true`                       |
| `"custom"`       | Your adapter in `server/inventory/custom.lua` + `client/inventory/custom.lua`                    |
| `"my-inventory"` | Any string — guard your files with `if Config.InventoryScript ~= "my-inventory" then return end` |

#### MDT case evidence stash (optional)

If your inventory supports stashes, configure case evidence storage:

```lua
Config.CaseStash = {
    slots = 50,
    maxWeight = 100000,
    prefix = "gks_mdt_case_",
}
```

Without `RegisterStash` / `OpenStash`, MDT case evidence stash returns *"not supported"* — the rest of the tablet still works.

***

### 2. Server-Side Integration

Create `gks-tablet/server/inventory/custom.lua`:

```lua
if Config.InventoryScript ~= "custom" then return end

local itemName = Config.TabletItemName or "tablet"

print("^2[GKS TABLET]^7 Custom inventory server adapter loaded")
```

#### Required function: HasTabletItem

Called when a player requests to open the tablet (`gkstablet:server:requestOpenTablet`).

```lua
--- Check if the player has the tablet item.
--- @param source number Player server ID
--- @return boolean
function HasTabletItem(source)
    -- Your custom inventory logic
    -- Example:
    -- local count = exports['my-inventory']:GetItemCount(source, itemName)
    -- return count > 0
    return false
end
```

#### Optional functions: RegisterStash / OpenStash

Required only for **MDT case evidence stash** (`gkstablet:server:mdt:openCaseStash`).

```lua
local registeredStashes = {}

--- Register a stash with your inventory system (idempotent).
--- @param stashId string Unique stash ID (e.g. gks_mdt_case_42)
--- @param label string Display label
--- @return boolean
function RegisterStash(stashId, label)
    if registeredStashes[stashId] then return true end

    Config.CaseStash = Config.CaseStash or {}
    local slots = Config.CaseStash.slots or 50
    local maxWeight = Config.CaseStash.maxWeight or 100000

    -- Example:
    -- exports['my-inventory']:RegisterStash(stashId, label, slots, maxWeight)
    registeredStashes[stashId] = true
    return true
end

--- Open a stash for the player (closes tablet UI first on client).
--- @param source number
--- @param stashId string
--- @param label string
--- @return boolean
function OpenStash(source, stashId, label)
    if not RegisterStash(stashId, label) then return false end

    -- Tell client to open stash UI
    TriggerClientEvent("gkstablet:client:inventory:openStash", source, stashId)
    return true
end
```

#### Register usable item (framework fallback)

If your inventory does **not** use a client-side `UseTabletItem` export, register the item as usable via ESX/QBCore. This is handled automatically by `server/inventory/framework_usable.lua` when:

* `Config.TabletItemRequire = true`
* `Config.Framework` is `esx`, `qb`, or `qbx`

For fully custom frameworks, register item use yourself:

```lua
-- Example: trigger tablet open from server on item use
RegisterUsableItem(itemName, function(source)
    TriggerClientEvent('gkstablet:client:useTabletItem', source)
end)
```

***

### 3. Client-Side Integration

Create `gks-tablet/client/inventory/custom.lua`:

```lua
if Config.InventoryScript ~= "custom" then return end

local itemName = Config.TabletItemName or "tablet"

print("^2[GKS TABLET]^7 Custom inventory client adapter loaded")
```

#### Required function: HasTabletItem

Used by `CanOpenTablet()` for local item checks (command/keybind path).

```lua
--- Client-side item check (no source parameter).
--- @return boolean
function HasTabletItem()
    -- Example:
    -- return exports['my-inventory']:Search('count', itemName) > 0
    return false
end
```

#### Item use export (recommended for export-based inventories)

If your inventory calls a client export on item use (like ox\_inventory):

```lua
exports("UseTabletItem", function(data, itemData)
    TriggerEvent("gkstablet:client:useTabletItem")
end)
```

Register in your inventory item definition:

```lua
-- Example item definition (structure varies by inventory)
client = {
    export = 'gks-tablet.UseTabletItem'
}
```

`UseTabletItem` toggles the tablet UI via internal event `gkstablet:client:useTabletItem`.

#### Handle item removal

If the tablet item is removed while the UI is open, close the tablet:

```lua
RegisterNetEvent('my-inventory:client:ItemRemoved', function(removedItem, count)
    if removedItem ~= itemName then return end
    if not Config.TabletItemRequire then return end
    if not IsTabletOpen then return end
    if not HasTabletItem() then
        ItemTabletDeleted()  -- closes tablet UI
    end
end)
```

`ItemTabletDeleted()` is defined in `client/tablet.lua` — do not redefine it.

#### Handle stash open (optional)

If you implemented server `OpenStash`, handle the client event:

```lua
RegisterNetEvent('gkstablet:client:inventory:openStash', function(stashId)
    if not stashId or stashId == '' then return end

    if ToggleTablet then
        ToggleTablet(false)  -- close tablet before opening stash
    end

    Wait(150)

    -- Open your inventory stash UI
    -- exports['my-inventory']:openStash(stashId)
end)
```

***

### 4. Full Reference Example

Based on the built-in `ox_inventory` adapter.

#### Server (`server/inventory/custom.lua`)

```lua
if Config.InventoryScript ~= "custom" then return end

local itemName = Config.TabletItemName or "tablet"
local registeredStashes = {}

function HasTabletItem(source)
    local count = exports.my_inventory:GetItemCount(source, itemName)
    return count and count > 0
end

function RegisterStash(stashId, label)
    if registeredStashes[stashId] then return true end

    Config.CaseStash = Config.CaseStash or {}
    local ok = pcall(function()
        exports.my_inventory:RegisterStash(
            stashId,
            label,
            Config.CaseStash.slots or 50,
            Config.CaseStash.maxWeight or 100000
        )
    end)

    if ok then registeredStashes[stashId] = true end
    return ok
end

function OpenStash(source, stashId, label)
    if not RegisterStash(stashId, label) then return false end
    TriggerClientEvent("gkstablet:client:inventory:openStash", source, stashId)
    return true
end

print("^2[GKS TABLET]^7 Custom inventory server adapter loaded")
```

#### Client (`client/inventory/custom.lua`)

```lua
if Config.InventoryScript ~= "custom" then return end

local itemName = Config.TabletItemName or "tablet"

function HasTabletItem()
    local count = exports.my_inventory:Search('count', itemName)
    return count > 0
end

exports("UseTabletItem", function(data, itemData)
    TriggerEvent("gkstablet:client:useTabletItem")
end)

RegisterNetEvent("my_inventory:client:updateInventory", function()
    if not Config.TabletItemRequire then return end
    if not IsTabletOpen then return end
    if not HasTabletItem() then
        ItemTabletDeleted()
    end
end)

RegisterNetEvent('gkstablet:client:inventory:openStash', function(stashId)
    if not stashId or stashId == '' then return end
    if ToggleTablet then ToggleTablet(false) end
    Wait(150)
    exports.my_inventory:openStash(stashId)
end)

print("^2[GKS TABLET]^7 Custom inventory client adapter loaded")
```

***
