ca79b8f8e6
Boite a outils de migration des tiers (clients / fournisseurs / prestataires) depuis l'ancien CRM Mixgraine vers Starseed : - extract_mixgraine.py : extraction + normalisation via l'API Mixgraine (cache disque reprenable, debit ~1 req/s, backoff 429/5xx) -> JSON format Starseed - build_tiers_xlsx.py : Excel de relecture (1 onglet par type + Synthese, colonne 'Site manquant' filtrable) - run.sh : enchaine extraction + Excel - README.md : prerequis, recuperation du token, lancement - mixgraine-migration-analysis.md : analyse + mapping des champs Mixgraine -> Starseed WIP : les commandes d'import Symfony cote Starseed (seed referentiels/sites, import Client/Supplier/Provider, 2e passe distributeur/courtier) restent a faire. Le dossier de sortie mixgraine-export/ (IBAN/BIC + PII reelles) est volontairement .gitignore : reproductible localement via MIXGRAINE_JWT.
307 lines
22 KiB
Markdown
307 lines
22 KiB
Markdown
# Migration Mixgraine → Starseed — Analyse des tiers (clients / fournisseurs)
|
||
|
||
> Sources analysées (exports Mixgraine triés par groupe) :
|
||
> - `client.json.json` — **1023 clients** (`customer=true`)
|
||
> - `fournisseur.json` — **306 fournisseurs** (`supplier=true`)
|
||
> - `fournisseur-client.json` — **106 tiers mixtes** (`customer=true` ET `supplier=true`)
|
||
>
|
||
> Cible : module `Commercial` de Starseed — entités `Client` / `Supplier` et leurs sous-entités `*Contact`, `*Address`, `*Rib`.
|
||
> Date d'analyse : 2026-06-16. (Remplace l'analyse initiale faite sur `customer.json`, 737 tiers, où la classification était absente.)
|
||
>
|
||
> 🔑 **Source de migration recommandée : l'endpoint unitaire, pas l'export en masse.**
|
||
> Les exports CSV/JSON en masse sont **incomplets** : ni RIB/IBAN, ni N° TVA, ni banque de virement, ni rattachement aux sites. Toutes ces données existent dans l'endpoint détail `GET /api/customer/{id}` et surtout dans `PUT /api/customer/{id}` avec `{"__data":true}` (renvoie le **schéma de formulaire complet + les valeurs**). Procédure : paginer `GET /api/customer/?...&limit=...&page=N` pour récupérer les `id`, puis appeler l'endpoint détail pour chaque id. Cf. § 9.
|
||
|
||
---
|
||
|
||
## 1. Synthèse
|
||
|
||
L'export est désormais **trié par groupe** : la distinction client / fournisseur est nette, ce qui lève le principal point bloquant de l'analyse précédente. **1435 tiers distincts** (aucun recoupement d'id entre les trois fichiers).
|
||
|
||
| Groupe source | Tiers | Devient dans Starseed |
|
||
|---|---|---|
|
||
| `client.json.json` (`customer=true`) | 1023 | `Client` |
|
||
| `fournisseur.json` (`supplier=true`) | 306 | `Supplier` |
|
||
| `fournisseur-client.json` (`customer=true` + `supplier=true`) | 106 | **`Client` ET `Supplier`** (cf. § 4) |
|
||
| **prestataires** (`prestataire=true`, vue séparée Mixgraine) | à extraire via l'API (filtre `prestataire:true`) | **`Provider`** (module Technique, M3) |
|
||
|
||
> ✅ Les **prestataires** sont un 4e type de tiers, géré dans une vue dédiée de Mixgraine mais sur le **même endpoint** `/api/customer/` (filtre `{"prestataire":true}`). Côté Starseed, ils ont une **entité dédiée** : `Provider` (module **Technique**, `src/Module/Technique/Domain/Entity/Provider.php`), jumelle du `Supplier`, avec `ProviderContact` / `ProviderAddress` / `ProviderRib`. **Ils ne deviennent donc PAS des `Supplier`.** Particularités vs Supplier : pas d'onglet Information (entité minimale nom + comptabilité) et les **sites sont rattachés directement au prestataire** (M2M `provider_site`, min 1, RG-3.03) — le script agrège donc les sites des adresses au niveau du prestataire. Catégories de type **PRESTATAIRE** exigées (RG-3.09). Le script collecte les prestataires via leur liste dédiée et produit `providers.json` (§ 9).
|
||
|
||
Volumétrie migrable (tous groupes confondus) :
|
||
|
||
| Objet source | Quantité | Cible Starseed |
|
||
|---|---|---|
|
||
| Tiers racine | 1435 | `Client` (1129) + `Supplier` (412) = **1541 entités** créées (les 106 mixtes comptent double) |
|
||
| Adresses | **2282** | `ClientAddress` / `SupplierAddress` |
|
||
| Contacts | **2704** | `ClientContact` / `SupplierContact` |
|
||
| RIB | **0** | *rien à migrer (ni IBAN ni BIC dans l'export)* |
|
||
|
||
Détail par groupe :
|
||
|
||
| Groupe | Tiers | Adresses | Contacts | Avec tél | Avec catégorie | Avec type paiement |
|
||
|---|---|---|---|---|---|---|
|
||
| Clients | 1023 | 1125 | 1279 | 1023 (100 %) | 648 (63 %) | 599 |
|
||
| Fournisseurs | 306 | 585 | 758 | 306 (100 %) | 92 (30 %) | 283 |
|
||
| Mixtes | 106 | 572 | 667 | 106 (100 %) | 37 (35 %) | 95 |
|
||
|
||
Points de décision restants (détaillés plus bas) :
|
||
|
||
1. **Les 106 tiers mixtes** doivent être dédoublés en un `Client` + un `Supplier` (le modèle Starveed sépare les deux entités). § 4.
|
||
2. **Référentiels à compléter** : `paymentDelay` (« 20 jours » absent du seed), `tvaMode` (Intracom/Export/achats), mapping des ~100 catégories. § 5.
|
||
3. **Site obligatoire par adresse** (min 1) — notion toujours absente de l'export. § 6.
|
||
|
||
---
|
||
|
||
## 2. Structure de l'export
|
||
|
||
Chaque ligne = un tiers. Les champs `addresses.*` et `contacts.*` sont des **listes parallèles** (un tiers peut avoir plusieurs adresses / contacts).
|
||
|
||
Qualité des données (tous groupes) :
|
||
|
||
- **Téléphone racine** : rempli sur **100 %** des tiers (1435/1435).
|
||
- **Emails contacts** : 1257 présents, **1256 valides** (1 invalide).
|
||
- **Codes postaux** : 2282 présents, **10 invalides** à nettoyer : `.` (×3), `B.5300`, `?`, `684`, `854300`, etc. (sinon 422 sur la regex 4-5 chiffres RG-1.09/2.05).
|
||
- **Catégories** : 55 % des tiers en ont au moins une (45 % sans) ; **92 % des adresses** ont une catégorie (2104/2282).
|
||
- **Noms de contact** : format « `CIVILITÉ NOM PRÉNOM` » (`M `, `Mme`, `M.`) → à parser pour `lastName` / `firstName`.
|
||
|
||
Pays des adresses : FR (2266), ES (11), BE (2), NL (2), PT (1).
|
||
|
||
---
|
||
|
||
## 3. Mapping champ par champ
|
||
|
||
Légende : ✅ champ existant · ⚠️ migrable avec transformation · ❌ pas de cible (donnée perdue) · 🔒 contrainte de validation à gérer.
|
||
|
||
### 3.1 Niveau Tiers → `Client` / `Supplier`
|
||
|
||
| Champ Mixgraine | Cible Starseed | Statut | Note de transformation |
|
||
|---|---|---|---|
|
||
| `name` / `reference` | `companyName` (≤180) | ✅ | Vérifier l'unicité (index partiel `LOWER(company_name)`). Risque : un même nom présent en client ET fournisseur reste OK (tables distinctes) ; doublons intra-table à contrôler. |
|
||
| `phone` (tél de l'objet de base) | `*Contact.phonePrimary` | ⚠️ | **Migré dans la liste de contacts** (cf. § 3.3bis), jamais au niveau racine. Rempli à 100 %. |
|
||
| `categoriesStr` / `category.name` | `categories` (M2M `Category`) | ⚠️🔒 | **Min 1 obligatoire**. ~100 valeurs distinctes à mapper vers le référentiel `Category` (§ 5.1). Attention `Courtier`/`Distributeur` (cf. broker/distributor). |
|
||
| `liability.name` | `tvaMode` | ⚠️ | `France (ventes)`→`FRANCE_VENTES` (1426), `Intracom (ventes)`→`INTRACOM_VENTES` (6), `Export (ventes)`→`EXPORT_VENTES` (2), `France (achats)`→**pas de mode « achats » au seed** (1 cas). |
|
||
| `paymentDelay.label` | `paymentDelay` | ⚠️🔒 | `15 jours`→`J15` (1376), `30 jours`→`J30` (43), `A réception`→`A_RECEPTION` (1), **`20 jours` (15)→ code `J20` à créer** (absent du seed). |
|
||
| `paymentType.label` | `paymentType` | ⚠️🔒 | `LCR non soumise`→**à trancher** (`LCR` impose ≥1 RIB, or 0 RIB → préférer `NON_SOUMISE`), `Virement`→`VIREMENT` (95, **exige `bank`** absente de la source), `Chèque`→`CHEQUE` (28). |
|
||
| `billingAccount` | `accountNumber` (≤40) | ✅ | Peu rempli. |
|
||
| `distributor.name` | `distributor` (FK auto-réf. `Client`) | ⚠️ | Suppose que le distributeur existe comme `Client`. Pas d'équivalent côté `Supplier`. |
|
||
| `courtier.id` / `courtier.name` | `broker` (FK auto-réf.) | ⚠️ | À vérifier sur les nouveaux exports (vide dans l'ancien). Mutuellement exclusif avec `distributor` (RG-1.03). |
|
||
| `customer` / `supplier` | choix de l'entité cible (`Client` vs `Supplier`) | ⚠️ | **Sert au routage**, pas stocké tel quel. |
|
||
| `tiersIndexable`, `prestataire`, `coreLocked`, `isOgm`, `maxOwed`, `autoGenerateInvoice` | — | ❌ | Flags Mixgraine sans équivalent métier. |
|
||
| `organization1..3` / `organizationsStr` | — | ❌ | Notion d'organisation/agence absente du modèle. |
|
||
| `articlesSold.*` | — | ❌ | Relève du catalogue, hors périmètre Client/Supplier. |
|
||
| `siren`, `nTva`, `bank` | `siren`, `nTva`, `bank` | ❌ | **Non présents dans l'export** → resteront vides. |
|
||
|
||
### 3.2 Niveau Adresse → `ClientAddress` / `SupplierAddress`
|
||
|
||
| Champ Mixgraine | Cible Starseed | Statut | Note |
|
||
|---|---|---|---|
|
||
| `addresses.street1` | `street` (≤255) | ✅ | Obligatoire. |
|
||
| `addresses.street2` | `streetComplement` (≤255) | ✅ | |
|
||
| `addresses.postcode` | `postalCode` (≤20, regex 4-5 chiffres) | ⚠️ | 10 valeurs à nettoyer. |
|
||
| `addresses.city` | `city` (≤120) | ✅ | Obligatoire. |
|
||
| `addresses.country` | `country` (≤80, défaut « France ») | ⚠️ | Convertir code ISO → libellé (`FR`→France, `ES`→Espagne, `BE`→Belgique, `NL`→Pays-Bas, `PT`→Portugal). |
|
||
| `addresses.lat` / `addresses.lng` | — | ❌ | Coordonnées GPS perdues (pas de champ géo). |
|
||
| `addresses.billing` (true) | `isBilling` (Client) | ⚠️🔒 | Si `true`, `billingEmail` **obligatoire** (RG-1.11), absent de la source → soit ne pas marquer facturation, soit collecter l'email. |
|
||
| `addresses.sales` | `isDelivery` (Client) | ⚠️ | |
|
||
| `addresses.purchase` | type adresse (Supplier : `DEPART`/`RENDU`) | ⚠️ | Côté fournisseur, l'adresse a un enum `PROSPECT`/`DEPART`/`RENDU`. |
|
||
| `addresses.salesTrip` | `isProspect` (?) | ⚠️ | Sémantique « tournée commerciale » à confirmer. |
|
||
| `addresses.benneCount` | `bennes` (SupplierAddress) | ⚠️ | Souvent vide. |
|
||
| `addresses.carrierTypeStr`, `addresses.name`, `addresses.distances` | — | ❌ | Sans cible. |
|
||
| `addresses.categories.name` | `categories` (M2M adresse) | ⚠️🔒 | 92 % des adresses en ont une. **Min 1 obligatoire** côté `ClientAddress`. ⚠️ Codes `DISTRIBUTEUR`/`COURTIER` **interdits** au niveau adresse (RG-1.29). |
|
||
| — (aucun) | `sites` (M2M, **min 1**) | 🔒 | **Notion absente de l'export** — bloquant majeur (§ 6). |
|
||
|
||
### 3.3 Niveau Contact → `ClientContact` / `SupplierContact`
|
||
|
||
| Champ Mixgraine | Cible Starseed | Statut | Note |
|
||
|---|---|---|---|
|
||
| `contacts.name` | `lastName` / `firstName` (≤120) | ⚠️ | Parser « CIVILITÉ NOM PRÉNOM » : retirer civilité, 1er token = `lastName`, reste = `firstName`. Au moins l'un des deux requis (RG-1.05/2.04). |
|
||
| `contacts.function` | `jobTitle` (≤120) | ✅ | |
|
||
| `contacts.email` | `email` (≤180) | ✅ | 99,9 % valides. |
|
||
| `contacts.phone` | `phonePrimary` (≤20) | ✅ | |
|
||
| `contacts.mobile` | `phoneSecondary` (≤20) | ✅ | |
|
||
| `contacts.fax` | — | ❌ | Pas de champ fax dans les contacts Starseed. |
|
||
| `contacts.siege` | — | ❌ | Notion « contact siège » non modélisée. |
|
||
| `addresses.contacts.*` | `*Address.contacts` (M2M) | ⚠️ | Rattachement contact↔adresse via les ids `addresses.contacts.id`. |
|
||
|
||
### 3.3bis — Règle : le contact porté par l'objet de base va dans la liste de contacts
|
||
|
||
Starseed n'a **aucun champ contact au niveau racine** de `Client`/`Supplier`. Tout doit atterrir dans la collection `*Contact` :
|
||
|
||
- Le bloc `contacts.*` (nom, fonction, email, tél, mobile) → une entrée `*Contact` par contact.
|
||
- Le `phone` de l'objet de base (rempli à 100 %) → reporté sur un contact :
|
||
- si le numéro figure déjà sur un contact → ne rien créer (déduplication) ;
|
||
- s'il diffère → l'ajouter comme `phoneSecondary` d'un contact existant ;
|
||
- si le tiers **n'a aucun contact** → **créer un contact « Standard »** (`lastName = "Standard"` pour respecter RG-1.05/2.04) portant ce `phonePrimary`.
|
||
|
||
Conséquence : aucun tiers ne perd son téléphone, et rien n'est stocké au niveau racine (conforme au modèle Starseed).
|
||
|
||
### 3.4 Niveau RIB → `ClientRib` / `SupplierRib`
|
||
|
||
**Disponible via l'endpoint unitaire** (champ `banks[]`), absent de l'export en masse. Mapping direct :
|
||
|
||
| Champ Mixgraine (`banks[]`) | Cible Starseed (`*Rib`) | Statut |
|
||
|---|---|---|
|
||
| `banks[].label` | `label` (≤120) | ✅ |
|
||
| `banks[].iban` | `iban` (≤34) | ✅ (validé Assert\Iban) |
|
||
| `banks[].bic` | `bic` (≤20) | ✅ (validé Assert\Bic) |
|
||
|
||
⚠️ Toujours mapper `paymentType="LCR non soumise"` vers `NON_SOUMISE` (et pas `LCR`) : les tiers sans `banks[]` violeraient sinon RG-1.13/2.08 (min 1 RIB).
|
||
|
||
### 3.5 Champs supplémentaires de l'endpoint unitaire (absents de l'export en masse)
|
||
|
||
| Champ Mixgraine (détail / `__data`) | Cible Starseed | Statut | Note |
|
||
|---|---|---|---|
|
||
| `vatNumber` | `nTva` (≤40) | ✅ | N° TVA. |
|
||
| `accountingBank` (1=CIC, 2=SOCIETE GENERALE, 3=CREDIT AGRICOLE) | `bank` (FK) | ✅ | **Correspond au seed Starseed** (CIC/SG/CA). Banque de virement. |
|
||
| `courtier` (FK select, 18 valeurs) | `broker` (FK auto-réf.) | ✅ | Courtier réel (≠ catégorie « Courtier »). Exclusif avec `distributor` (RG-1.03). |
|
||
| `distributor` (FK select, 14 valeurs) | `distributor` (FK auto-réf.) | ✅ | Distributeur réel. |
|
||
| `mails[]` {`mail`, `invoice`} | `ClientAddress.billingEmail` / secondaire | ⚠️ | Emails d'envoi de documents ; `invoice=true` → email de facturation (RG-1.11). |
|
||
| address `carrierType` (1=Rendu, 2=Départ) | `SupplierAddress.addressType` (RENDU/DEPART) | ⚠️ | Radio exclusif côté fournisseur (RG-2.09). |
|
||
| address `organization_1/2/3` | `*Address.sites` (M2M) | ✅ | **Les sites** (cf. § 6). |
|
||
| address `hasBenne` / `benneCount` | `SupplierAddress.bennes` | ⚠️ | Nombre de bennes. |
|
||
| address `validated` | — | ❌ | « Adresse vérifiée », pas de cible. |
|
||
| `isDistributor`, `directBilling`, `distributorDirectOrder`, `newGuarantees`, `commentMail`, `zoneChartering(2)`, `emailDocument*` | — | ❌ | Champs Mixgraine sans équivalent métier. |
|
||
|
||
---
|
||
|
||
## 4. Tiers mixtes (client + fournisseur) — 106 cas
|
||
|
||
Les 106 tiers du fichier `fournisseur-client.json` sont **à la fois client et fournisseur**. Le modèle Starseed sépare strictement `Client` et `Supplier` (deux tables, deux jeux de sous-entités). Deux options :
|
||
|
||
- **(Recommandé)** Créer **deux entités** par tiers mixte : un `Client` et un `Supplier`, chacun avec sa copie des adresses/contacts. C'est cohérent avec le modèle, au prix d'une duplication (à reconcilier plus tard si un lien inter-entités est ajouté).
|
||
- Choisir une seule face (client OU fournisseur) selon l'usage dominant — risque de perte d'information.
|
||
|
||
Impact volumétrie : 1023 + 106 = **1129 `Client`**, 306 + 106 = **412 `Supplier`**.
|
||
|
||
---
|
||
|
||
## 5. Référentiels à préparer avant import
|
||
|
||
### 5.1 Catégories (`Category`)
|
||
|
||
Situation nettement meilleure que l'export initial : **55 % des tiers** et **92 % des adresses** portent une catégorie. L'export contient **~100 valeurs distinctes** (top : Eleveur 392, Semencier 61, Coopérative 53, Courtier 38, Négociant 30, …, puis une longue traîne de catégories « fournisseur » métier : Transports, Informatique, Garagistes, Banques…).
|
||
|
||
À faire :
|
||
- **Construire le référentiel `Category`** depuis ces valeurs (dédoublonner casse/accents, ex. `Courtier`/`COURTIERS`).
|
||
- **Politique pour les 45 % sans catégorie** : attribuer une catégorie par défaut (ex. `À QUALIFIER`) pour satisfaire la contrainte min 1.
|
||
- **Niveau adresse** : exclure `DISTRIBUTEUR`/`COURTIER` (interdits RG-1.29) — les router vers les FK `distributor`/`broker` ou la catégorie tiers.
|
||
- Distinguer catégories **type CLIENT** vs **type FOURNISSEUR** (RG-2.10 : une catégorie fournisseur est exigée sur un `Supplier`).
|
||
|
||
### 5.2 Référentiels comptables
|
||
|
||
| Référentiel | Valeurs export | Action |
|
||
|---|---|---|
|
||
| `paymentDelay` | 15 jours, 30 jours, **20 jours**, A réception | Le seed n'a que `J15`/`J30`/`A_RECEPTION` → **créer `J20`**. |
|
||
| `paymentType` | LCR non soumise, Virement, Chèque | Mapper `LCR non soumise`→`NON_SOUMISE` (éviter la contrainte RIB) ; `Virement`→`VIREMENT` (mais `bank` absente → soit la laisser vide en assouplissant RG-1.12, soit collecter). |
|
||
| `tvaMode` | France (ventes), Intracom (ventes), Export (ventes), **France (achats)** | Mapper sur `FRANCE_VENTES`/`INTRACOM_VENTES`/`EXPORT_VENTES` ; pas de mode « achats » au seed (1 cas) → décision. |
|
||
|
||
---
|
||
|
||
## 6. Sites — résolu via les « organisations » Mixgraine
|
||
|
||
Chaque `ClientAddress`/`SupplierAddress` doit référencer **au moins un `Site`** (RG-1.10/2.06). Cette notion **existe dans Mixgraine** sous le nom d'**organisations**, visible dans l'endpoint unitaire :
|
||
|
||
- au niveau tiers : `organizationsStr` (ex. `"Châtellerault, Saint-Jean, Pommevic"`) ;
|
||
- au niveau adresse : trois booléens `organization_1` / `organization_2` / `organization_3` étiquetés **Châtellerault / Saint-Jean / Pommevic** ;
|
||
- le bloc `addresses.distances` donne même la distance de l'adresse vers chacun de ces 3 sites.
|
||
|
||
**Les 3 sites à créer dans Starseed** : `Châtellerault`, `Saint-Jean`, `Pommevic`. Le rattachement adresse↔site se reconstruit depuis les booléens `organization_n=true`. Pour une adresse sans aucune organisation cochée, prévoir un site par défaut (à décider).
|
||
|
||
⚠️ Ce rattachement n'est lisible que via l'endpoint unitaire — raison de plus pour migrer depuis le détail (cf. § 9).
|
||
|
||
---
|
||
|
||
## 7. Synthèse des pertes de données
|
||
|
||
Perdus si non traités en amont : coordonnées GPS (`lat`/`lng`), fax des contacts, contact siège, articles vendus, « adresse vérifiée », et divers flags Mixgraine (`isOgm`, `maxOwed`, `directBilling`, `zoneChartering`, `commentMail`).
|
||
|
||
Champs cibles qui resteront vides (vraiment absents de la source, même au détail) : `siren` (le `vatNumber` alimente `nTva`, mais pas de SIREN distinct).
|
||
|
||
> Note : `nTva`, `bank` et les `*Rib` ne sont **plus** des pertes — ils sont disponibles via l'endpoint unitaire (§ 3.4 / § 3.5). Ils n'étaient absents que de l'export en masse.
|
||
|
||
---
|
||
|
||
## 7bis. Excel de relecture (un onglet par type)
|
||
|
||
Beaucoup d'adresses n'ont **aucun site** rattaché (les organisations ne sont pas cochées dans Mixgraine), et d'autres trous existent (CP invalide, facturation sans email…). Avant tout import, on produit un classeur via `build_tiers_xlsx.py` : **`mixgraine-tiers.xlsx`**.
|
||
|
||
- Un onglet **Synthèse** (compteurs) + un onglet par type : **Clients**, **Fournisseurs**, **Prestataires**.
|
||
- Chaque onglet liste toutes les données, **une ligne par adresse** (colonnes du tiers répétées).
|
||
- Colonne **Site manquant** (OUI/vide) + **filtre automatique** → un clic pour isoler les adresses sans site. Colonne **Problèmes** pour le reste. Lignes à problème surlignées.
|
||
|
||
Chiffres réels (extraction 2026-06-16, 1442 tiers) :
|
||
|
||
| Type | Tiers | Lignes (adresses) | Adresses sans site | Sans catégorie |
|
||
|---|---|---|---|---|
|
||
| Clients | 1129 | 1697 | 1111 | 444 (39 %) |
|
||
| Fournisseurs | 412 | 1157 | 612 | 283 (68 %) |
|
||
| Prestataires | 300 | 333 | 0 | 8 (2 %) |
|
||
| **Total** | **1841\*** | **3187** | **1723** | **735** |
|
||
|
||
\* mixtes comptés en Client + Fournisseur. RIB extraits : 90. Tiers totalement bloqués : 90.
|
||
|
||
Motifs de blocage détectés :
|
||
|
||
| Niveau | Motif bloquant |
|
||
|---|---|
|
||
| Adresse | **aucun site rattaché** (RG-1.10/2.06/3.03), code postal absent/invalide, ville absente, rue absente, facturation sans email (client) |
|
||
| Tiers | nom absent, `VIREMENT` sans banque, `LCR` sans RIB, **prestataire sans aucun site** (RG-3.03), catégorie `À QUALIFIER` |
|
||
|
||
Filtrer « Site manquant = OUI » dans chaque onglet permet de corriger la donnée à la source (re-cocher les organisations dans Mixgraine, compléter les emails) **avant** de relancer l'import (le cache accélère).
|
||
|
||
---
|
||
|
||
## 8. Séquence d'import recommandée
|
||
|
||
1. **Extraire la donnée complète** depuis l'endpoint unitaire (§ 9) — pas depuis l'export en masse.
|
||
2. **Relecture** : `build_tiers_xlsx.py` → `mixgraine-tiers.xlsx` (un onglet par type, filtre « Site manquant »), faire corriger à la source (§ 7bis).
|
||
2. **Sites** : créer les 3 sites (Châtellerault, Saint-Jean, Pommevic) + un site par défaut éventuel (§ 6).
|
||
3. **Référentiels** : créer/compléter `Category` (~100 valeurs + défaut `À QUALIFIER`), `paymentDelay` (ajouter `J20`), `tvaMode`, vérifier `paymentType`, `bank` (CIC/SG/CA déjà OK) (§ 5).
|
||
4. **Nettoyer** : 10 codes postaux invalides, parser les noms de contacts (civilité), convertir codes pays ISO → libellés (table fournie par le schéma `country`).
|
||
5. **Importer dans l'ordre** : référentiels + sites → tiers → adresses (+ rattachement site via `organization_n`) → contacts (+ rattachement adresse + tél de l'objet de base) → RIB (`banks[]`).
|
||
6. **Tiers mixtes** (106) : créer Client + Supplier (§ 4).
|
||
7. **Gérer les 422 attendus** : adresses `isBilling=true` sans `billingEmail` ; `paymentType=VIREMENT` sans `bank`.
|
||
|
||
---
|
||
|
||
## 9. Stratégie d'extraction via l'API Mixgraine
|
||
|
||
L'export en masse est insuffisant (§ entête). La donnée complète s'obtient en deux temps depuis `https://liot.mixsuite.fr` (auth : `Authorization: Bearer <JWT>`) :
|
||
|
||
1. **Lister les ids par groupe, page par page** :
|
||
`GET /api/customer/?fields=["name"]&filters={...}&limit=200&order=name&page=N`
|
||
La réponse porte `count` (total) et `data[].id`. On pagine **trois listes filtrées** puis on fait l'union dédupliquée :
|
||
- `{"customer":true}` → clients (`Client`),
|
||
- `{"supplier":true}` → fournisseurs (`Supplier`),
|
||
- `{"prestataire":true}` → prestataires (`Provider`, module Technique).
|
||
|
||
La **classification se fait par appartenance** à ces listes (plus fiable que les flags du formulaire `__data`) : un id dans `customer` ET `supplier` = mixte (Client + Supplier) ; un id dans `prestataire` = `Provider`. Le script produit `clients.json`, `suppliers.json`, `providers.json` + `referentials.json`.
|
||
|
||
2. **Récupérer le détail complet de chaque tiers** :
|
||
`GET /api/customer/{id}` (objet riche : contacts, addresses, banks, liability, paymentType…)
|
||
ou `PUT /api/customer/{id}` avec corps `{"__data":true}` qui renvoie en plus le **schéma de formulaire** (libellés des organisations/sites, choix des référentiels, FK distributor/courtier) **et** les valeurs sous `__data`.
|
||
|
||
Champs clés présents uniquement au détail : `banks[]` (RIB), `vatNumber`, `accountingBank`, `courtier`, `distributor`, `mails[]`, `addresses[].organization_1/2/3` (sites), `addresses[].carrierType`, `details.geo` (lat/lng par adresse).
|
||
|
||
> ⚠️ Le JWT fourni est un secret de session : ne pas le committer dans le repo. À passer en variable d'environnement au script d'extraction.
|
||
|
||
---
|
||
|
||
## 📦 Tickets Lesstime générés
|
||
|
||
TaskGroup Lesstime : **#32 — Migration tiers Mixgraine → Starseed** (projet STARSEED / ERP)
|
||
|
||
| # | Ticket | Réf. | Effort | Tag |
|
||
|---|---|---|---|---|
|
||
| 1.1 | Extraire et normaliser les tiers Mixgraine en JSON | ERP-174 | M | Backend |
|
||
| 1.2 | Seeder les référentiels et les 3 sites | ERP-175 | M | Backend |
|
||
| 1.3 | Importer les clients (Commercial) + adresses/contacts/RIB | ERP-176 | L | Backend |
|
||
| 1.4 | Importer les fournisseurs (Commercial) + adresses/contacts/RIB | ERP-177 | M | Backend |
|
||
| 1.5 | Importer les prestataires (Technique/Provider) | ERP-178 | M | Backend |
|
||
| 1.6 | Relier distributeurs/courtiers, traiter les mixtes et vérifier | ERP-179 | M | Backend |
|
||
|
||
Tous au statut **Prêt à dev**. Prochaine action : lancer le 1.1 (le script `extract_mixgraine.py` est déjà écrit, reste à le tester avec un vrai token).
|
||
|
||
---
|
||
|
||
*Document basé sur l'analyse des exports en masse `client.json.json` / `fournisseur.json` / `fournisseur-client.json` et sur les réponses des endpoints `/api/customer/` (liste), `/api/customer/{id}` (détail) et `PUT /api/customer/{id}` (`__data`). Aucune donnée n'a été importée — rapport d'analyse préalable.*
|