Aller au contenu principal

Create new user (Admin only)

POST 

/company/users/

Créez un nouvel utilisateur pour l'entreprise. Seuls les administrateurs peuvent créer des utilisateurs.

Objectif

Permettre aux administrateurs d'ajouter de nouveaux utilisateurs à leur entreprise avec un contrôle sur la limite d'utilisateurs selon le plan d'abonnement.

Cas d'utilisation

  • Ajouter un nouvel employé à la plateforme
  • Créer un compte pour un nouveau gestionnaire
  • Ajouter des utilisateurs administratifs

Authentification & Autorisation

  • Requiert un JWT valide (middleware m.isLoged)
  • Requiert le rôle admin ou dev (middleware m.isAdmin)
  • Requiert une validation UTC (mTools.checkUTC)
  • Vérifie la limite d'utilisateurs du plan (mPlan.canCreateUser)

Comportement

  • Vérifie que l'email n'existe pas dans la base de données
  • Si aucun mot de passe n'est fourni, en génère un automatiquement avec tools.generatePass()
  • Crée l'utilisateur avec model.createData()
  • Ajoute l'utilisateur au tableau company.users[]
  • Enregistre l'utilisation dans le service de facturation (BillingService.recordUsage)
  • Envoie un email avec les identifiants en utilisant mail.sendNewPass()

Validations

  • Email requis et unique
  • L'utilisateur est ajouté à l'entreprise de l'administrateur
  • Le service de facturation vérifie la limite d'utilisateurs du plan
  • Si l'ajout à l'entreprise échoue, supprime l'utilisateur (rollback)

Gestion du mot de passe

  • Si un mot de passe est fourni dans le body, celui-ci est utilisé
  • Si aucun n'est fourni, il est généré automatiquement : 8 caractères, majuscules, minuscules, chiffres
  • Le mot de passe est haché avec model.getPassword()
  • Il est envoyé par email à l'utilisateur

Flux de validation

flowchart TD
A[Recevoir POST /] --> B\{Utilisateur Admin?\}
B -->|Non| C[403 Forbidden]
B -->|Oui| D\{Email Fourni?\}

<Heading
id={"request"}
as={"h2"}
className={"openapi-tabs__heading"}
children={"Request"}
>
</Heading>

<ParamsDetails
parameters={undefined}
>

</ParamsDetails>

<RequestSchema
title={"Body"}
body={{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"E-mail du nouvel utilisateur (unique dans la base de données)","example":"nuevo_usuario@empresa.com"},"name":{"type":"string","minLength":2,"maxLength":50,"description":"Nom de l'utilisateur","example":"Juan"},"lastname":{"type":"string","minLength":2,"maxLength":100,"description":"Nom de famille de l'utilisateur","example":"Pérez García"},"password":{"type":"string","minLength":8,"maxLength":50,"description":"Mot de passe de l'utilisateur (facultatif). S'il n'est pas fourni,\nil sera généré automatiquement. Exigences : minimum 8 caractères,\nau moins 1 majuscule, 1 minuscule, 1 chiffre.","example":"Contraseña123","nullable":true},"role":{"type":"string","enum":["dev","admin","gestor"],"description":"Rôle de l'utilisateur (par défaut : gestionnaire). Définit les autorisations sur la plateforme.\n- dev : Superadmin avec accès total\n- admin : Gestion complète de l'entreprise\n- gestionnaire : Opérations quotidiennes","example":"gestor","default":"gestor"},"i18n":{"type":"string","enum":["es","en","fr","de"],"description":"Langue préférée de l'utilisateur","example":"es","default":"es"}}},"example":{"email":"nuevo_usuario@empresa.com","name":"Juan","lastname":"Pérez García","password":"MiContraseña123","role":"gestor","i18n":"es"}}}}}
>

</RequestSchema>

<StatusCodes
id={undefined}
label={undefined}
responses={{"200":{"description":"Utilisateur créé avec succès.","content":{"application/json":{"schema":{"type":"object","description":"Représente un utilisateur d'entreprise (company_user) ayant accès à la plateforme Cargoffer. \n**Fonctionnalité** : \n- Utilisateur employé d'une entreprise de transport qui gère les enchères, les livraisons et la documentation \n- Authentification via JWT (jetons avec expiration configurable selon refresh_time) \n- Système de rôles hiérarchique : dev > admin > gestionnaire (permissions descendantes) \n- Associé à une entreprise (company.users array contient des références aux _id des utilisateurs) \n\n**Modèle** : `src/features/models/company_user.model.js` \n**Contrôleurs** : \n- `src/features/company/users/company_user.account.controller.js` (CRUD, mots de passe) \n- `src/features/company/users/company_user.profile.controller.js` (profil, préférences) \n- `src/features/company/auth/auth.account.controller.js` (connexion, jetons) \n\n**Middleware** : `src/features/common/middleware/company.middleware.js` \n- `m.isLoged` – Vérifie la validité du JWT \n- `m.isGestor` – Requiert le rôle 'gestionnaire' ou supérieur \n- `m.isAdmin` – Requiert le rôle 'admin' ou 'dev'","properties":{"_id":{"type":"string","description":"Identifiant unique MongoDB de l'utilisateur (24 caractères hexadécimaux). Généré automatiquement lors de l'enregistrement. Référencé dans : company.users[], auction.user, delivery.user, notifications.user. Inclus dans le payload JWT en tant que claim '_id'.","pattern":"^[a-f0-9]{24}$","example":"63d7907cbe76403b35da63df"},"email":{"type":"string","format":"email","description":"Adresse e-mail unique de l'utilisateur (utilisée pour la connexion). **Requis** - Unique dans la base de données (index unique en production). Normalisée automatiquement en minuscules (hook pre-save). Utilisée pour : l'authentification, la récupération de mot de passe, les notifications. Validée avec le format d'e-mail standard.","example":"usuario@cargoffer.com"},"name":{"type":"string","description":"Nom de l'utilisateur (firstName dans un contexte international). Utilisé dans : interface, signature des documents CMR, identification dans les notifications. Minimum 2 caractères (validation en frontend).","minLength":2,"maxLength":50,"example":"Juan"},"lastname":{"type":"string","description":"Nom de famille de l'utilisateur (lastName dans l'API, lastname dans le modèle). Utilisé conjointement avec 'name' pour une identification complète dans les documents officiels. Concaténé dans : contrats, CMR, factures.","minLength":2,"maxLength":100,"example":"García López"},"role":{"type":"string","enum":["dev","admin","gestor"],"description":"Rôle de l'utilisateur définissant les permissions sur la plateforme. \n**dev** : Accès total + débogage + gestion de toutes les entreprises (superadmin) \n**admin** : Gestion complète de son entreprise (créer des utilisateurs, voir la facturation, modifier les données) \n**gestor** : Opérations quotidiennes (créer des enchères/livraisons, voir le tableau de bord, éditer son profil) \n\n**Vérifications middleware** : \n- m.isGestor accepte : ['gestor', 'admin', 'dev'] \n- m.isAdmin accepte : ['admin', 'dev'] \n\nStocké dans le payload JWT (utilisé pour la validation des permissions).","default":"gestor","example":"admin"},"status":{"type":"boolean","description":"État actif/inactif de l'utilisateur. **true** : L'utilisateur peut se connecter et fonctionner normalement. **false** : Utilisateur bloqué (ne peut pas se connecter, JWT invalides).\nUtilisé dans : le middleware `m.isLoged` vérifie que `status=true` avant d'autoriser l'accès. Modifié via : POST `/company/users/status/:id` (admin uniquement). Différent de 'deleted' (suppression douce) - il s'agit ici d'un blocage réversible.","default":true,"example":true},"reason":{"type":"string","enum":["NONE","BAD_USER","PENDING","ACTIVE","BLOCKED"],"description":"Code de raison de l'état de l'utilisateur. **NONE** : Utilisateur normal actif sans incident **PENDING** : Inscription terminée, en attente d'activation par email **ACTIVE** : Utilisateur vérifié et opérationnel **BAD_USER** : Bloqué pour mauvais comportement (signalements, fraude) **BLOCKED** : Bloqué administrativement (impayé, sécurité)\nUtilisé conjointement avec 'status' et 'reasonMessage' pour l'audit. Stocké avec 'reasonDate' (horodatage du changement).","default":"NONE","example":"ACTIVE"},"reasonMessage":{"type":"string","description":"Message descriptif de la raison du blocage/état actuel. Optionnel - Rempli par l'administrateur lors du changement de statut à false. Visible par : les administrateurs dans le panneau des utilisateurs, l'utilisateur bloqué dans le message d'erreur de connexion. Exemple : « Compte suspendu pour défaut de paiement », « Utilisateur signalé pour pratiques incorrectes »","example":"Usuario bloqueado temporalmente por verificación de documentación"},"reasonDate":{"type":"string","format":"date-time","description":"Horodatage de la dernière modification du champ 'reason'. Automatique - Défini dans le hook pre-save lors de la détection d'un changement dans 'reason'. Utilisé dans : audit, calcul de la durée de blocage, historiques des modifications.","example":"2025-01-15T14:30:00.000Z"},"i18n":{"type":"string","enum":["es","en","fr","de"],"description":"Code de langue préféré de l'utilisateur (internationalisation). **es** : Espagnol (par défaut pour l'Espagne) **en** : Anglais (par défaut international)\nUtilisé dans : emails automatiques, interface utilisateur, messages d'erreur, documents CMR. Stocké dans le payload JWT pour la personnalisation depuis le backend. Modifié via : POST /company/users/lang","default":"es","example":"en"},"birthDate":{"type":"string","format":"date-time","description":"Date de naissance de l'utilisateur (optionnelle). Utilisée pour : les validations d'âge minimum (18 ans pour les conducteurs), les statistiques démographiques. Format ISO 8601. Nullable.","nullable":true,"example":"1990-05-15T00:00:00.000Z"},"emailVerified":{"type":"boolean","description":"Indique si l'utilisateur a vérifié son adresse e-mail. **false** : Utilisateur inscrit mais n'a pas cliqué sur le lien d'activation **true** : E-mail confirmé via le jeton d'activation\nFlux : inscription → e-mail avec jeton → GET /company/auth/activate/:token → emailVerified=true Les utilisateurs avec emailVerified=false peuvent avoir des fonctionnalités limitées (dépend de la configuration).","default":false,"example":true},"emailVerifiedDate":{"type":"string","format":"date-time","description":"Horodatage de la vérification de l'email (null si emailVerified=false). Défini automatiquement lors de la confirmation du jeton d'activation. Utilisé pour : l'audit, le calcul du temps écoulé depuis la vérification, les exigences de re-vérification.","nullable":true,"example":"2025-01-10T09:20:30.000Z"},"phone":{"type":"string","description":"Numéro de téléphone de l'utilisateur (optionnel mais recommandé). Format : international de préférence (+code pays), minimum 9 chiffres, maximum 15. Utilisé pour : coordination logistique, alertes SMS, contact d'urgence. Validation : `minlength: 9, maxlength: 15` dans le modèle.","pattern":"^\\+?[0-9]{9,15}$","example":"+34612345678"},"lastSignInAt":{"type":"string","format":"date-time","description":"Horodatage de la dernière connexion réussie de l'utilisateur. Mise à jour automatique dans le contrôleur de connexion (POST /company/auth/login). Utilisé dans : tableau de bord d'activité, détection des comptes inactifs, audit de sécurité.","example":"2025-10-23T08:15:45.000Z"},"lastSignInIp":{"type":"string","description":"Adresse IP depuis laquelle la dernière connexion a été effectuée. Extraite de req.ip ou req.headers['x-forwarded-for']. Utilisé dans : détection d'accès suspects, journaux de sécurité, géolocalisation des accès.","format":"ipv4","example":"192.168.1.100"},"country":{"type":"string","description":"Code pays ISO 3166-1 alpha-3 de l'utilisateur (3 lettres minuscules). Utilisé dans : filtres de fuseau horaire, formats de date, paramètres régionaux, validations de TaxID. Par défaut : esp (Espagne). Doit exister dans la collection 'countries'.","pattern":"^[a-z]{3}$","default":"esp","example":"esp"},"timezone":{"type":"string","description":"Fuseau horaire de l'utilisateur au format IANA Time Zone Database. Utilisé pour : conversion des horodatages UTC en heure locale, affichage des dates, calcul des plages horaires de chargement/déchargement. Doit correspondre aux zones valides de la bibliothèque moment-timezone. Par défaut : Europe/Madrid.","default":"Europe/Madrid","example":"Europe/Madrid"},"taxid":{"type":"string","description":"Numéro d'identification fiscale personnelle (NIF, DNI, NIE, etc.). **Unique en production** (index unique). Minimum 6 caractères, maximum 9. Normalisé automatiquement en majuscules (uppercase: true dans le modèle). Utilisé dans : facturation, contrats, documentation légale. Valeur par défaut temporaire : '---------' (9 tirets) pour permettre la création sans donnée.","minLength":6,"maxLength":9,"example":"12345678A"},"img":{"type":"string","format":"uri","description":"URL de l'image de profil de l'utilisateur (stockée dans AWS S3). Optionnel - Téléchargée via l'endpoint de documents avec multerS3. Utilisé dans : avatar dans l'interface, signature visuelle sur les documents. Format : URL complète du bucket S3.","nullable":true,"example":"https://cargoffer-storage.s3.amazonaws.com/users/63d7907cbe76403b35da63df.jpg"},"recovery_token":{"type":"string","description":"Jeton temporaire pour récupération de mot de passe (hash). Généré par : POST /company/auth/recovery (demande de réinitialisation) Utilisé par : GET /company/auth/recovery/:token (valider le jeton) Supprimé après : changement de mot de passe réussi ou expiration (48h) Par défaut : '' (chaîne vide lorsqu'aucun processus de récupération n'est actif)","example":"a7f5e8d3c2b9..."},"refresh_time":{"type":"integer","enum":[1,3,5,10],"description":"Durée d'expiration du JWT en jours. Utilisé dans : génération de token (exp claim = iat + refresh_time * 24h). **1** : Sécurité maximale (re-connexion quotidienne) - Pour les rôles sensibles **3** : Équilibre par défaut sécurité/commodité **5** : Usage fréquent **10** : Applications mobiles avec mode hors-ligne\nStocké dans le payload JWT, validé dans le middleware m.isLoged.","default":3,"example":3},"is_bot":{"type":"boolean","description":"Indique si l'utilisateur est un bot automatisé (pour les intégrations API). **true** : Utilisateur créé pour une intégration automatique (webhooks, scripts) **false** : Utilisateur humain réel\nUtilisé dans : statistiques, audit, exclusion des bots des métriques d'activité humaine. Les bots peuvent avoir des permissions spéciales et des limites de débit différentes.","default":false,"example":false},"deleted":{"type":"boolean","description":"Indicateur de suppression logique (plugin mongoose-delete). **false** : Utilisateur actif **true** : Utilisateur supprimé (n'apparaît pas dans les requêtes normales). \nLes requêtes incluent automatiquement deleted:false (plugin overrideMethods:true). Récupérable via : POST /company/users/disabled/reactivate/:id. Lié au timestamp 'deletedAt'.","default":false,"example":false},"deletedAt":{"type":"string","format":"date-time","description":"Horodatage de la suppression de l'utilisateur (soft delete). Null si deleted=false. Défini automatiquement par le plugin mongoose-delete. Utilisé pour : audit des suppressions, récupération d'utilisateurs, conformité RGPD.","nullable":true,"example":"2025-10-01T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"Horodatage de création de l'utilisateur (automatique par mongoose timestamps). Immuable - Ne peut pas être modifié après la création. Utilisé pour : tri, statistiques de croissance, audit.","example":"2024-12-01T10:30:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"Horodatage de dernière modification de l'utilisateur (automatique par mongoose timestamps). Mis à jour à chaque save() - Reflète la dernière édition de n'importe quel champ. Utilisé dans : synchronisation, invalidation du cache, détection des changements.","example":"2025-10-20T15:45:30.000Z"},"lastAccess":{"type":"string","format":"date-time","description":"Horodatage de la dernière accès enregistré à n'importe quel point de terminaison authentifié. Mis à jour par : le middleware m.getAction à chaque requête authentifiée. Différent de lastSignInAt (celui-ci est mis à jour à chaque requête, pas seulement à la connexion). Utilisé dans : détection d'inactivité, métriques d'engagement.","example":"2025-10-23T10:22:15.000Z"}},"title":"User"},"example":{"_id":"63d7907cbe76403b35da70f","email":"nuevo_usuario@empresa.com","name":"Juan","lastname":"Pérez García","role":"gestor","status":true,"i18n":"es","emailVerified":false,"createdAt":"2025-02-12T10:30:00.000Z"}}}},"400":{"description":"Demande invalide. Causes possibles :\n- Email non fourni\n- Erreur lors de la sauvegarde (rollback automatique)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"FORM_DATA_NOT_VALID"}}}},"401":{"description":"Non autorisé (rôle admin ou dev requis)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"NO_TOKEN"}}}},"403":{"description":"Interdit. Causes possibles :\n- L'utilisateur n'est pas administrateur\n- Le forfait ne permet pas de créer plus d'utilisateurs","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"PLAN_LIMIT_REACHED"}}}},"406":{"description":"L'e-mail existe déjà dans la base de données.","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"USER_ALREADY_EXIST"}}}}}}
>

</StatusCodes>