---
title: "Connecteurs OCS Inventory"
module: store
type: functional
status: active
updated: 2026-08-13
---

# Connecteurs OCS Inventory

**Mise à jour :** 2026-05-29 (v0.7 — exclusions durables champ/poste, refonte onglet Ignorés ; v0.6 — matching utilisateur intelligent, auto-résolution entité/adresse, fallback Office)

---

## Deux variantes

| | OCS Isi-Inventaire | OCS Inventory — Custom |
|--|-------------------|----------------------|
| **Code** | `ocs_isi_inventaire` | `ocs_custom` |
| **Type connexion** | Database (MySQL direct) | Database (MySQL direct) |
| **Managé** | Oui — config et mapping read-only | Non — tout configurable |
| **Abonnement requis** | `it-ocs` | `it-ocs` |
| **Disponible** | Oui | Non (badge "Bientôt disponible") |
| **Cas d'usage** | Client abonné ISI, serveur OCS mutualisé ISI | Client avec son propre serveur OCS OnPremise |
| **Installation** | 1-clic, auto-provisioning, actif immédiatement | Manuelle (credentials + mapping + flux) |

> `ocs_custom` était initialement nommé `ocs_inventory`. Renommé par migration `2026_04_03_000002` pour refléter son usage OnPremise configurable. Les instances existantes (FK sur `store_connector_id`) ne sont pas affectées.

---

## OCS Isi-Inventaire (`ocs_isi_inventaire`) — Connecteur managé

### Installation en 1-clic

```
POST /store/connecteur/ocs_isi_inventaire/install
    └── StoreController::install()
            ├── isInstallableBy($_id)  → vérifie abonnement it-ocs
            └── installManaged()
                    ├── StoreInstance::create([..., is_active: true, auth_params: $connector->default_auth_params])
                    └── provisionManagedFlows()
                            ├── MappingConfigService::initializeHardwareFluxPair()  → flows: hardware + hardware_vm
                            └── MappingConfigService::initializeFluxFromDefaults()  → flow: software
```

L'instance est créée **active immédiatement** (`is_active = true`).

### Credentials

Les credentials du serveur OCS mutualisé ISI sont stockés dans `StoreConnector.default_auth_params` (chiffrés via `Crypt::encryptString`). Ils sont initialisés à la création du connecteur depuis `config('database.connections.ocs.*')`.

À l'installation, ils sont copiés **sans décryptage** dans `StoreInstance.auth_params` — aucune exposition en mémoire ou logs.

Déchiffrement à l'usage : `StoreInstance::getDecryptedParams()` → `['db_host', 'db_port', 'db_database', 'db_username', 'db_password']`.

### Flows provisionnés automatiquement

Définis dans `StoreConnector.default_flow_config` :

```json
{
    "flows": [
        {"code": "hardware"},
        {"code": "software", "sort_order": 3}
    ]
}
```

`hardware` déclenche `initializeHardwareFluxPair()` qui crée **deux** flows (sort_order 1 et 2) :
- `hardware` — matériels physiques → `dsi_hw`
- `hardware_vm` — machines virtuelles → `dsi_hwvm`

`software` crée le flow logiciels (sort_order 3).

Les `StoreMappingField` sont lus depuis les fichiers JSON de configuration : `config/store/mappings/ocs_isi_inventaire/{hardware,hardware_vm,software}.json`.

### Ce que l'utilisateur peut faire

| Action | Autorisé |
|--------|---------|
| Tester la connexion (InstancePing) | Oui |
| Lancer une extraction | Oui |
| Consulter les logs | Oui |
| Consulter le mapping (lecture) | Oui |
| Consulter le staging et appliquer les changements | Oui |
| **Modifier la config (host, credentials)** | **Non** |
| **Modifier le mapping** | **Non** |
| **Supprimer l'instance** | **Non** |
| **Réinitialiser le mapping** | **Non** |

### Guards read-only

- **UI** : `<fieldset disabled>` sur le formulaire de config, `MappingEditor` en mode lecture, bouton Supprimer masqué, bouton Réinitialiser masqué
- **Serveur** : `InstanceController::update()`, `::destroy()`, `MappingController::reset()`, `MappingEditor::save()` retournent une erreur si `$instance->connector->is_managed`

---

## OCS Inventory — Custom (`ocs_custom`) — Connecteur configurable

> **Non disponible à l'installation** (`is_available = false`). Les instances existantes (migrées depuis `ocs_inventory`) continuent de fonctionner normalement.

Ce connecteur est destiné aux clients possédant leur propre serveur OCS Inventory OnPremise. La configuration complète est à la charge du client :
- Saisie des credentials BDD (host, port, database, username, password)
- Sélection du flux de données (hardware, software…)
- Configuration du mapping champ à champ via `MappingEditor`

---

## Pipeline technique OCS (commun aux deux variantes)

### Services

| Service | Rôle |
|---------|------|
| `OcsConnectionService` | Crée une connexion MySQL dynamique nommée `store_ocs_{instanceId}` à partir des `auth_params` déchiffrés |
| `OcsSyncService` | Extraction des données OCS, enrichissement (détection VM, normalisation CPU/OS), transformation via les `StoreMappingField`, écriture dans `store_staging` |
| `OcsCompareService` | Compare le staging contre `dsi_hw` + `dsi_hwvm`, détermine `tpaction` = `create` / `update` / `delete` / `unchanged` |
| `OcsIntegrationService` | Applique les changements sur `dsi_hw` (physiques) ou `dsi_hwvm` (VMs), enregistre dans `store_correspondence_ids` et `store_logs` |

### Job asynchrone

`OcsExtractJob` — dispatché sur la queue `store`, timeout 10 minutes. Déclenché via `POST /store/instances/{id}/extract`.

### Tables OCS source (connexion dynamique)

Lues depuis la base OCS distante : `hardware`, `bios`, `softwares`, `drives`, `storages`, `memories`, `monitors`, `networks`, `videos`, `sounds`, `printers`, `slots`.

### Déduplication

Par numéro de série BIOS (`BIOS_SSN`). Un doublon (même serial + même tenant) est ignoré — comptabilisé dans `duplicates_ignored` affiché dans `ExtractStatus`.

### Détection VM / Physique

`OcsSyncService` détecte les VMs via la valeur `STYPE` du champ `hardware`. Les VMs sont routées vers `dsi_hwvm`, les physiques vers `dsi_hw`.

### Matching utilisateur intelligent (v0.6)

Le pipeline V3 rapproche automatiquement le champ `USERID` OCS (login Windows) avec un utilisateur ISI-APP du tenant.

**Sur la card staging**, l'opérateur voit l'un des 4 cas suivants pour le champ "Utilisateur" :

| Cas | Affichage | Action |
|---|---|---|
| **Proposition unique** | `Ancien → Nouveau` (avec badge bleu `manuel` si choix manuel) | Bouton croix rouge pour refuser ; popover (i) info déplacement vers les utilisateurs additionnels si applicable |
| **Plusieurs candidats** | Liste déroulante avec les candidats + option "— Faire le choix plus tard —" | Sélection libre ou report de décision |
| **Aucun candidat** | Sélecteur Tom Select cherche-friendly sur tous les users du tenant | Choix manuel libre |
| **Refusé** (transitoire) | "Proposition refusée" puis bascule auto en sélecteur manuel pour permettre un nouveau choix | — |

**Sémantique de la croix rouge "refuser"** :
- En **création** : annule la sélection courante, sans rien écrire en BDD. L'utilisateur peut choisir un autre user. Pas de boucle infinie.
- En **modification** : mémorise le refus dans `store_user_match_rejections` (le matcher ne reproposera plus ce candidat pour ce PC à la prochaine sync) ET bascule vers le sélecteur Tom Select pour choisir immédiatement un autre user.

**Déplacement vers les utilisateurs additionnels** : à l'apply d'une modification, si le PC avait déjà un utilisateur principal différent du nouveau, l'ancien est déplacé dans la table de pivot `dsi_hw_users` (avec `dtvalid = aujourd'hui`). Le nouveau devient principal. Compatible avec le modèle multi-users existant.

**Exclusion des comptes système** : `administrator`, `admin`, `system`, `guest` (insensible à la casse, trim appliqué) sont automatiquement écartés du matching.

**Limites** : le matching reproduit fidèlement la logique V2 (`OcsHelper::prepareUserFromOcs`). Cas non gérés (hérités V2) :
- Logins tout en minuscules (`johndoe`) ou tout en majuscules (`JOHNDOE`) → pas de split prénom/nom
- Logins multi-parties (`JeanPierreDupont`) → pas de split
- Séparateurs autres que CamelCase (`john.doe`, `john_doe`) → pas de split

Dans ces cas, l'opérateur passe par le sélecteur manuel.

### Auto-résolution entité + adresse

À chaque sync, `OcsCompareService::injectIpDetection()` rapproche l'adresse IP de chaque PC du **plan d'adressage IP** (`dsi_planip`) du tenant. Si une plage unique correspond, l'entité (`idc`) et l'adresse (`idaddr`) sont auto-affectées sur la card. Si plusieurs plages se recouvrent, l'opérateur choisit dans un dropdown.

**Préservation DHCP historique** : si un PC a déjà `dsi_hw.idip = 'DHCP'` en BDD (issu d'une décision V2 ou d'un toggle manuel précédent), V3 affiche "DHCP" sur la card et ne réécrit jamais l'IP. Le toggle DHCP ↔ IP classique reste accessible sur la card.

### Détection Office améliorée (v0.6)

V3 cherche Office d'abord dans `officepack`, puis en **fallback** dans la table `software` via les GUID (`O365*`, `O15*`, `O16*`, `Office*`). Aligné sur le comportement V2.

**Préservation Office BDD** : si la sync courante ne retrouve aucune trace d'Office mais que la BDD contient déjà une valeur (`dsi_hw.tpof = O365` par exemple), V3 ne l'écrase pas. Pattern aligné sur l'exclusion existante pour TeamViewer (`iddist`).

### Ignorer durablement une remontée OCS (v0.7)

OCS détecte parfois des changements qu'on ne veut **jamais** appliquer — par exemple un poste renommé manuellement (`1083SYL-POR4770` → `5013AEJPOR4770`) qu'OCS veut systématiquement remettre à son ancien nom, ou un poste transféré dans un établissement non supervisé par OCS qui remonte indéfiniment.

Deux niveaux d'exclusion durable sont disponibles dans l'onglet **Modifications** :

| Action | Où | Effet |
|---|---|---|
| **Ignorer ce champ** (icône œil barré sur une ligne de modification) | Sur chaque champ modifié | Ce champ précis (ex: le nom) ne sera plus jamais synchronisé pour ce poste. La valeur saisie manuellement est conservée. Les autres champs continuent de se synchroniser normalement. |
| **Ne plus synchroniser ce poste** (bouton en bas de la carte) | Sur tout le poste | Le poste disparaît complètement de la synchronisation OCS (aucune création, modification ou suppression proposée). Idéal pour les machines transférées/non supervisées. |

Contrairement à l'ancien "Ignorer" (qui réapparaissait au sync suivant), ces exclusions sont **persistantes** : elles sont consultées à chaque synchronisation, donc la remontée ne réapparaît plus jamais.

**Onglet "Ignorés"** : liste toutes les exclusions actives (postes et champs), avec qui les a posées. Chaque exclusion peut être **levée** via le bouton "Réactiver" — la synchronisation reprend alors au prochain inventaire.

---

## Fichiers de configuration des mappings

```
config/store/mappings/
    ocs_isi_inventaire/
        _flux.json          # Définition des flux disponibles
        hardware.json       # Mapping champs matériels physiques
        hardware_vm.json    # Mapping champs VMs
        software.json       # Mapping champs logiciels
    ocs_custom/
        _flux.json
        hardware.json
        hardware_vm.json
        software.json
```

Ces fichiers définissent les champs source (colonnes OCS) et cible (colonnes ISI-APP) disponibles dans `MappingEditor`. Les deux connecteurs partagent le même schéma de mapping.

---

## Ancien système OCS (legacy — coexiste)

L'ancienne interface OCS (`/ocs`) continue de fonctionner en parallèle du Store :

| | Legacy (`/ocs`) | Store (`/ocs-v3`) |
|--|----------------|-----------------|
| **Route** | `/ocs` | `/ocs-v3` + `/store/instances` |
| **Middleware** | `isLocalAdmin`, `hardEnsureHasModule:it-ocs` | `auth`, `sub`, `pswd` |
| **Connexion DB** | Globale (`config/database.php` → connexion `ocs`) | Dynamique par instance |
| **Logique sync** | `OcsHelper` (1 573 lignes) | Services séparés (4 services) |
| **Interface** | jQuery + DataTables | Livewire 4 + Onyx |

**Ne pas toucher au système legacy.** Il coexiste avec le Store et est utilisé par des clients non encore migrés.

---

## Migration des clients existants

### Commande `store:migrate-ocs-clients`

Migre les clients ayant OCS actif (via `DsiParam.lkocs` non null) vers une instance `ocs_isi_inventaire` managée.

```bash
php artisan store:migrate-ocs-clients              # Dry-run
php artisan store:migrate-ocs-clients --execute    # Applique
php artisan store:migrate-ocs-clients --execute --force  # Sans confirmation
```

**Logique :**
1. Lit `DsiParam::whereNotNull('lkocs')->where('lkocs', '!=', '')` pour lister les clients éligibles
2. Vérifie absence d'instance existante (`withTrashed()` pour détecter les soft-deleted)
3. Crée `StoreInstance` avec `_id = $dsiParam->_id`, `auth_params = $connector->default_auth_params`
4. Provisionne flows + mappings automatiquement

### Page OCS v3 (`/ocs-v3`)

Le controller `OcsV3Controller` liste les instances liées aux connecteurs `ocs_inventory`, `ocs_custom`, et `ocs_isi_inventaire` pour le tenant courant. Il route vers la page de synchronisation dédiée `/ocs-v3/{id}`.

**Accès réservé aux administrateurs** : comme le Store, les écrans OCS v3 ne sont ouverts qu'aux
profils administrateur général (100), équipe IT (110), directeur IT (150), comptes internes ISI et
membres Isi-DSI. Lancer une extraction, supprimer des doublons OCS ou arbitrer le staging sont des
actes d'administration : un utilisateur simple (900) ou en lecture seule (120 / 300) ne voit pas
l'entrée de menu et reçoit une erreur 403 s'il ouvre l'URL directement.

---

## Voir aussi

- [`docs/store/README.md`](README.md) — Architecture générale du Store
- [`.claude/technical-docs/store/ocs-v3.md`](../../.claude/technical-docs/store/ocs-v3.md) — Architecture technique OCS v3 (staging, Livewire, services)
- [`.claude/technical-docs/store/ocs-technique.md`](../../.claude/technical-docs/store/ocs-technique.md) — Système OCS legacy (OcsHelper)
