# esx_addoninventory (/docs/addons/esx_addoninventory)

esx_addoninventory is a server-only library resource that stores item counts outside of a player's own inventory. Use it for society storage rooms, property stashes and similar containers. Other resources such as [esx_society](/docs/addons/esx_society) and [esx_property](/docs/addons/esx_property) read and write these inventories.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_addoninventory` (version `1.0`) |
| Side | Server only |
| Dependencies | `esx_lib`, `es_extended`, `oxmysql` |
| SQL file | `esx_addoninventory.sql` |
| Exports | `GetSharedInventory`, `AddSharedInventory` |

Like [esx_addonaccount](/docs/addons/esx_addonaccount), inventories are either **shared** (`shared = 1`, one inventory without an owner) or **personal** (`shared = 0`, one inventory per player identifier, created when the player loads and stored as `xPlayer.get('addonInventories')`).

## Installation [#installation]

<Steps>
<Step>
### Import the SQL [#import-the-sql]

Run `esx_addoninventory.sql`. It creates `addon_inventory` (definitions) and `addon_inventory_items` (item rows). The script uses plain `CREATE TABLE`, so it fails if the tables already exist. That is expected on an existing database.
</Step>
<Step>
### Ensure the resource [#ensure-the-resource]

```bash title="server.cfg"
ensure es_extended
ensure esx_addoninventory
ensure esx_society
```
</Step>
<Step>
### Define inventories [#define-inventories]

```sql
INSERT INTO `addon_inventory` (`name`, `label`, `shared`) VALUES
  ('society_mechanic', 'Mechanic', 1);
```
</Step>
</Steps>

## Configuration [#configuration]

There is no `config.lua`. The `addon_inventory` table is the configuration.

| Column | Type | Description |
| --- | --- | --- |
| `name` | `VARCHAR(60)` | Unique inventory name, e.g. `society_police` |
| `label` | `VARCHAR(100)` | Display label (used as stash label with ox_inventory) |
| `shared` | `INT` | `1` = shared, `0` = one inventory per player |

Item labels come from the ESX `items` table and are loaded on startup.

## ox_inventory mode [#ox_inventory-mode]

If ox_inventory is running (ESX sets `Config.OxInventory` automatically), the resource does **not** load its own inventories. Instead it registers each `addon_inventory` row as an ox_inventory stash:

| Stash setting | Value |
| --- | --- |
| Stash id | `name` |
| Label | `label` |
| Slots | `100` |
| Max weight | `200000` |
| Owner | `true` for `shared = 0` (one stash per player), otherwise shared |
| Groups | Job name for names containing `society`: the text after `society_` |

In this mode the exports and the getter events are not registered. Use `exports.ox_inventory` functions on the stash id instead.

## Server exports [#server-exports]

### GetSharedInventory [#getsharedinventory]

```lua
exports.esx_addoninventory:GetSharedInventory(name)
```

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | Inventory name from `addon_inventory` |

Returns the [inventory object](#inventory-object) or `nil`.

```lua title="server/main.lua"
local inventory = exports.esx_addoninventory:GetSharedInventory('society_mechanic')

if inventory then
    inventory.addItem('fixkit', 5)
end
```

### AddSharedInventory [#addsharedinventory]

```lua
exports.esx_addoninventory:AddSharedInventory(society)
```

| Name | Type | Description |
| --- | --- | --- |
| `society` | `table` | `{ name = string, label = string }` |

Inserts a shared inventory row and creates an empty inventory in memory. It does not check whether the inventory already exists: calling it twice replaces the in-memory inventory with an empty one (the database insert fails on the primary key). Check with `GetSharedInventory` first.

```lua title="server/main.lua"
if not exports.esx_addoninventory:GetSharedInventory('society_bahama') then
    exports.esx_addoninventory:AddSharedInventory({ name = 'society_bahama', label = 'Bahama Mamas' })
end
```

## Inventory object [#inventory-object]

| Field / method | Description |
| --- | --- |
| `name`, `owner` | Inventory name and owner identifier (`nil` when shared) |
| `items` | Array of `{ name, count, label }` |
| `getItem(name)` | Returns the item entry. Creates a row with count `0` if it does not exist |
| `addItem(name, count)` | Adds `count` and saves |
| `removeItem(name, count)` | Removes `count` (only when `count > 0`) and saves |
| `setItem(name, count)` | Sets the count and saves |

<Callout type="warn" title="No validation">
The inventory methods do not check item names, negative counts or whether enough items exist. `removeItem` happily goes below zero. Always validate on your side before you call them, especially when the request comes from a client.
</Callout>

```lua title="server/main.lua"
RegisterNetEvent('myresource:takeFromStorage', function(itemName, count)
    local xPlayer = ESX.GetPlayerFromId(source)
    count = math.floor(tonumber(count) or 0)
    if not xPlayer or xPlayer.job.name ~= 'mechanic' or count < 1 then return end

    local inventory = exports.esx_addoninventory:GetSharedInventory('society_mechanic')
    local item = inventory and inventory.getItem(itemName)
    if not item or item.count < count or not xPlayer.canCarryItem(itemName, count) then return end

    inventory.removeItem(itemName, count)
    xPlayer.addInventoryItem(itemName, count)
end)
```

## Events [#events]

Both events are local server events (`AddEventHandler`). Clients cannot trigger them.

| Event | Arguments | Description |
| --- | --- | --- |
| `esx_addoninventory:getInventory` | `name, owner, cb` | `cb(inventory)` for a personal inventory |
| `esx_addoninventory:getSharedInventory` | `name, cb` | `cb(inventory)` for a shared inventory |

```lua
TriggerEvent('esx_addoninventory:getInventory', 'property', xPlayer.identifier, function(inventory)
    if inventory then inventory.addItem('bread', 1) end
end)
```

## Integration tips [#integration-tips]

- Use the same `society_<job>` name in [esx_addonaccount](/docs/addons/esx_addonaccount), esx_addoninventory and [esx_datastore](/docs/addons/esx_datastore) so job resources find all three.
- Shared inventories are also mirrored to `GlobalState.SharedInventories`.
- New `addon_inventory` rows are loaded without a restart when ESX fires `esx:sqlCatalogReady` for `addon_inventory` or `items`.

## Common issues [#common-issues]

- **`No such export GetSharedInventory`**: ox_inventory is running, so the resource is in stash mode. Use ox_inventory stashes.
- **Item shows without a label**: the item is missing from the `items` table.
- **SQL import error "table already exists"**: the tables are already there. Skip the import.

## Related [#related]

- [esx_addonaccount](/docs/addons/esx_addonaccount)
- [esx_society](/docs/addons/esx_society)
- [ox_inventory with ESX](/docs/guides/ox-inventory)
- [Add items](/docs/guides/add-items)
