> For the complete documentation index, see [llms.txt](https://lunaris-1.gitbook.io/untitled/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lunaris-1.gitbook.io/untitled/script/ls-vipshop/ajouter-un-type-de-produit.md).

# Ajouter un type de produit

Un **type de produit** définit comment un article est validé et livré (item, véhicule, argent, VIP, etc.). Pour en ajouter un nouveau (ex. « pet », « licence »), il faut :

1. **Déclarer le type** dans `Config.ProductTypes` (config.lua).
2. **Créer le module serveur** qui enregistre le type et optionnellement les listes (dropdown admin).

#### 1. Déclarer le type dans config.lua

Dans **config.lua**, ajoute une entrée dans `Config.ProductTypes` :

```lua
Config.ProductTypes = {
  -- ... types existants (item, money, vehicle, weapon, ped, property, vip, etc.) ...
  { key = "pet", label = "Animal", icon = "fa-paw", handler = "pet" },
}
```

* **key** : identifiant du type (stocké en BDD dans le produit).
* **label** : libellé dans l’admin (ex. "Animal").
* **icon** : icône (ex. Font Awesome).
* **handler** : nom du fichier Lua (sans `.lua`) dans `server/types/` ou `server/types_custom/`.

#### 2. Créer le module serveur

* **Option A — Type inclus par défaut**\
  Crée un fichier `server/types/<handler>.lua` (ex. `server/types/pet.lua`).\
  Le script charge tous les `server/types/*.lua` après `00_registry.lua`.
* **Option B — Type personnalisé (recommandé pour ne pas écraser les mises à jour)**\
  Crée un fichier `server/types_custom/<handler>.lua` (ex. `server/types_custom/pet.lua`).\
  Le script charge aussi tous les `server/types_custom/*.lua`.

Dans ce fichier, tu dois **enregistrer le type** avec `OCShop_RegisterType` :

```lua
OCShop_RegisterType({
    key = 'pet',  -- doit correspondre à Config.ProductTypes[].key
    validate = function(reward)
        if type(reward) ~= 'table' then return false, 'reward invalide' end
        local name = tostring(reward.name or '')
        if name == '' then return false, 'nom invalide' end
        local amount = math.floor(tonumber(reward.amount) or 1)
        if amount <= 0 then return false, 'quantité invalide' end
        return true
    end,
    grant = function(src, reward)
        local xPlayer = ESX.GetPlayerFromId(src)
        if not xPlayer then return false, 'player introuvable' end
        local name = tostring(reward.name or '')
        local amount = math.max(1, math.floor(tonumber(reward.amount) or 1))
        -- TODO: donne le pet au joueur (event client, table BDD, etc.)
        return true
    end
})
```

* **validate** : reçoit l’objet `reward` (contenu du produit). Retourne `true` ou `false, "message d'erreur"`.
* **grant** : exécuté à l’achat ; reçoit `src` (player id) et `reward`. Doit retourner `true` en cas de succès, `false, "message"` en cas d’échec.

#### Liste / recherche pour l’admin (optionnel)

Si tu veux un **dropdown avec recherche** dans l’admin (comme pour les véhicules ou les items), enregistre une liste et éventuellement une recherche :

```lua
-- Liste complète (ex. tous les pets disponibles)
OCShop_RegisterList('pets', function()
    return {
        { value = 'dog_retriever', label = 'Golden Retriever' },
        { value = 'cat_persian',   label = 'Chat persan' },
    }
end)

-- Recherche (optionnel, pour autocomplete)
OCShop_RegisterSearchList('pets', function(q, limit)
    local list = OCShop_GetList('pets') or {}
    -- Filtrer selon q, limiter à limit résultats
    return list
end)
```

Côté NUI admin, il faudra utiliser la clé de liste (ex. `pets`) dans le plugin du type pour afficher ce dropdown. Les plugins par défaut sont dans `html/admin_types/` ; les personnalisés dans `html/admin_types_custom/` (voir ton interface admin existante).

***
