Create new user (Admin only)
POST/company/users/
Crea un nuovo utente per l'azienda. Solo gli amministratori possono creare utenti.
Obiettivo
Consentire agli amministratori di aggiungere nuovi utenti alla propria azienda con controllo sul limite di utenti in base al piano di abbonamento.
Casi d'Uso
- Aggiungere un nuovo dipendente alla piattaforma
- Creare un account per un nuovo gestore
- Aggiungere utenti amministrativi
Autenticazione & Autorizzazione
- Richiede JWT valido (middleware m.isLoged)
- Richiede ruolo admin o dev (middleware m.isAdmin)
- Richiede validazione UTC (mTools.checkUTC)
- Verifica il limite utenti del piano (mPlan.canCreateUser)
Comportamento
- Verifica che l'email non esista nel database
- Se non viene fornita una password, ne genera una automaticamente con tools.generatePass()
- Crea l'utente con model.createData()
- Aggiunge l'utente all'array company.users[]
- Registra l'utilizzo nel servizio di fatturazione (BillingService.recordUsage)
- Invia un'email con le credenziali utilizzando mail.sendNewPass()
Validazioni
- Email obbligatoria e unica
- L'utente viene aggiunto all'azienda dell'amministratore
- Il servizio di fatturazione verifica il limite utenti del piano
- Se la creazione nell'azienda fallisce, elimina l'utente (rollback)
Gestione Password
- Se viene fornita una password nel body, viene utilizzata quella
- Se non viene fornita, viene generata automaticamente: 8 caratteri, maiuscole, minuscole, numeri
- La password viene hashata con model.getPassword()
- Viene inviata via email all'utente
Flusso di Validazione
flowchart TD
A[Ricevi POST /] --> B\{Utente Admin?\}
B -->|No| C[403 Forbidden]
B -->|Sì| D\{Email Fornita?\}
D -->|No| E[400 FORM_DATA_NOT_VALID]
D -->|Sì
<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":"Email del nuovo utente (unico nel database)","example":"nuevo_usuario@empresa.com"},"name":{"type":"string","minLength":2,"maxLength":50,"description":"Nome dell'utente","example":"Juan"},"lastname":{"type":"string","minLength":2,"maxLength":100,"description":"Cognome dell'utente","example":"Pérez García"},"password":{"type":"string","minLength":8,"maxLength":50,"description":"Password dell'utente (opzionale). Se non fornita,\nne viene generata una automaticamente. Requisiti: minimo 8 caratteri,\nalmeno 1 maiuscola, 1 minuscola, 1 numero.","example":"Contraseña123","nullable":true},"role":{"type":"string","enum":["dev","admin","gestor"],"description":"Ruolo dell'utente (predefinito: gestore). Definisce i permessi nella piattaforma.\n- dev: Superadmin con accesso totale\n- admin: Gestione completa dell'azienda\n- gestore: Operazioni quotidiane","example":"gestor","default":"gestor"},"i18n":{"type":"string","enum":["es","en","fr","de"],"description":"Lingua preferita dell'utente","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":"Utente creato con successo.","content":{"application/json":{"schema":{"type":"object","description":"Rappresenta un utente aziendale (company_user) con accesso alla piattaforma Cargoffer.\n**Funzionalità**: - Utente dipendente di un'azienda di trasporto che gestisce aste, consegne e documentazione - Autenticazione tramite JWT (token con scadenza configurabile in base a refresh_time) - Sistema di ruoli gerarchico: dev > admin > gestore (permessi discendenti) - Associato a un'azienda (l'array company.users contiene riferimenti a _id degli utenti)\n**Modello**: `src/features/models/company_user.model.js`\n**Controller**: - `src/features/company/users/company_user.account.controller.js` (CRUD, password) - `src/features/company/users/company_user.profile.controller.js` (profilo, preferenze) - `src/features/company/auth/auth.account.controller.js` (login, token)\n**Middleware**: `src/features/common/middleware/company.middleware.js` - `m.isLoged` - Verifica JWT valido - `m.isGestor` - Richiede ruolo 'gestore' o superiore - `m.isAdmin` - Richiede ruolo 'admin' o 'dev'","properties":{"_id":{"type":"string","description":"Identificatore unico MongoDB dell'utente (24 caratteri esadecimali). Generato automaticamente alla registrazione. Referenziato in: company.users[], auction.user, delivery.user, notifications.user. Incluso nel payload JWT come claim '_id'.","pattern":"^[a-f0-9]{24}$","example":"63d7907cbe76403b35da63df"},"email":{"type":"string","format":"email","description":"Email univoco dell'utente (utilizzato per l'accesso). **Obbligatorio** - Univoco nel database (indice unique in produzione). Normalizzato automaticamente in minuscolo (hook pre-save). Utilizzato per: autenticazione, recupero password, notifiche. Convalidato con formato email standard.","example":"usuario@cargoffer.com"},"name":{"type":"string","description":"Nome dell'utente (firstName in contesto internazionale). Utilizzato in: interfaccia, firma documenti CMR, identificazione nelle notifiche. Minimo 2 caratteri (validazione frontend).","minLength":2,"maxLength":50,"example":"Juan"},"lastname":{"type":"string","description":"Cognome dell'utente (lastName in API, lastname nel modello). Utilizzato insieme a 'nome' per l'identificazione completa nei documenti ufficiali. Concatenato in: contratti, CMR, fatture.","minLength":2,"maxLength":100,"example":"García López"},"role":{"type":"string","enum":["dev","admin","gestor"],"description":"Ruolo dell'utente che definisce i permessi nella piattaforma. \n**dev**: Accesso totale + debug + gestione di tutte le aziende (superadmin) \n**admin**: Gestione completa della propria azienda (creare utenti, visualizzare fatturazione, modificare dati) \n**gestor**: Operazioni giornaliere (creare aste/consegne, visualizzare dashboard, modificare il proprio profilo) \n\n**Middleware checks**: \n- m.isGestor accetta: ['gestor', 'admin', 'dev'] \n- m.isAdmin accetta: ['admin', 'dev'] \n\nMemorizzato nel payload JWT (utilizzato nella validazione dei permessi).","default":"gestor","example":"admin"},"status":{"type":"boolean","description":"Stato attivo/inattivo dell'utente. **true**: L'utente può effettuare il login e operare normalmente. **false**: Utente bloccato (non può effettuare il login, JWT non validi). \nUtilizzato in: il middleware m.isLoged verifica status=true prima di consentire l'accesso. Modificato tramite: POST /company/users/status/:id (solo admin). Diverso da 'deleted' (soft delete) – questo è un blocco reversibile.","default":true,"example":true},"reason":{"type":"string","enum":["NONE","BAD_USER","PENDING","ACTIVE","BLOCKED"],"description":"Codice motivo dello stato dell'utente. **NONE**: Utente normale attivo senza problemi **PENDING**: Registrazione completata, in attesa di attivazione via email **ACTIVE**: Utente verificato e operativo **BAD_USER**: Bloccato per comportamento scorretto (segnalazioni, frode) **BLOCKED**: Bloccato amministrativamente (mancato pagamento, sicurezza)\nUtilizzato insieme a 'status' e 'reasonMessage' per audit. Memorizzato con 'reasonDate' (timestamp della modifica).","default":"NONE","example":"ACTIVE"},"reasonMessage":{"type":"string","description":"Messaggio descrittivo della ragione del blocco/stato attuale. Opzionale - Compilato dall'amministratore quando cambia lo stato in false. Visibile a: amministratori nel pannello utenti, utente bloccato nel messaggio di errore di accesso. Esempio: Account sospeso per mancato pagamento, Utente segnalato per pratiche scorrette.","example":"Usuario bloqueado temporalmente por verificación de documentación"},"reasonDate":{"type":"string","format":"date-time","description":"Timestamp dell'ultima modifica del campo 'reason'. Automatico - Impostato nell'hook pre-save al rilevare una modifica in 'reason'. Utilizzato in: audit, calcolo durata blocco, cronologia modifiche.","example":"2025-01-15T14:30:00.000Z"},"i18n":{"type":"string","enum":["es","en","fr","de"],"description":"Codice lingua preferita dell'utente (internazionalizzazione). **es**: Spagnolo (predefinito per la Spagna) **en**: Inglese (predefinito internazionale)\nUtilizzato in: email automatiche, interfaccia utente, messaggi di errore, documenti CMR. Memorizzato nel payload JWT per la personalizzazione dal backend. Modificato tramite: POST /company/users/lang","default":"es","example":"en"},"birthDate":{"type":"string","format":"date-time","description":"Data di nascita dell'utente (opzionale). Utilizzato per: validazioni dell'età minima (18 anni per i conducenti), statistiche demografiche. Formato ISO 8601. Nullable.","nullable":true,"example":"1990-05-15T00:00:00.000Z"},"emailVerified":{"type":"boolean","description":"Indica se l'utente ha verificato la propria email. **false**: Utente registrato ma non ha cliccato sul link di attivazione **true**: Email confermata tramite token di attivazione\nFlusso: register → email con token → GET /company/auth/activate/:token → emailVerified=true\nUtenti con emailVerified=false possono avere funzionalità limitate (dipende dalla configurazione).","default":false,"example":true},"emailVerifiedDate":{"type":"string","format":"date-time","description":"Timestamp di quando l'email è stata verificata (null se emailVerified=false). Impostato automaticamente alla conferma del token di attivazione. Utilizzato in: audit, calcolo del tempo trascorso dalla verifica, requisiti di ri-verifica.","nullable":true,"example":"2025-01-10T09:20:30.000Z"},"phone":{"type":"string","description":"Numero di telefono dell'utente (opzionale ma consigliato). Formato: preferibilmente internazionale (+prefisso paese), minimo 9 cifre, massimo 15. Utilizzato per: coordinamento logistico, avvisi SMS, contatto di emergenza. Validazione: `minlength: 9, maxlength: 15` nel modello.","pattern":"^\\+?[0-9]{9,15}$","example":"+34612345678"},"lastSignInAt":{"type":"string","format":"date-time","description":"Timestamp dell'ultimo accesso riuscito dell'utente. Aggiornato automaticamente nel login controller (POST /company/auth/login). Utilizzato in: dashboard attività, rilevamento account inattivi, audit di sicurezza.","example":"2025-10-23T08:15:45.000Z"},"lastSignInIp":{"type":"string","description":"Indirizzo IP da cui è stato effettuato l'ultimo accesso. Estratto da req.ip o req.headers['x-forwarded-for']. Utilizzato per: rilevamento di accessi sospetti, log di sicurezza, geolocalizzazione degli accessi.","format":"ipv4","example":"192.168.1.100"},"country":{"type":"string","description":"Codice paese ISO 3166-1 alpha-3 dell'utente (3 lettere minuscole). Utilizzato in: filtri del fuso orario, formati di data, impostazioni locali, validazioni del TaxID. Predefinito: esp (Spagna). Deve esistere nella collection 'countries'.","pattern":"^[a-z]{3}$","default":"esp","example":"esp"},"timezone":{"type":"string","description":"Fuso orario dell'utente in formato IANA Time Zone Database. Utilizzato per: conversione di timestamp UTC in locale, visualizzazione di date, calcolo degli orari di caricamento/scaricamento. Deve corrispondere a zone valide della libreria moment-timezone. Predefinito: Europe/Madrid","default":"Europe/Madrid","example":"Europe/Madrid"},"taxid":{"type":"string","description":"Numero di identificazione fiscale personale (NIF, DNI, NIE, ecc.). **Unico in produzione** (indice unique). Minimo 6 caratteri, massimo 9. Normalizzato automaticamente in maiuscolo (uppercase: true nel modello). Utilizzato in: fatturazione, contratti, documentazione legale. Default temporaneo: '---------' (9 trattini) per consentire la creazione senza dato.","minLength":6,"maxLength":9,"example":"12345678A"},"img":{"type":"string","format":"uri","description":"URL dell'immagine del profilo dell'utente (archiviata in AWS S3). Opzionale - Caricata tramite endpoint documenti con multerS3. Utilizzata in: avatar nell'interfaccia, firma visiva sui documenti. Formato: URL completo del bucket S3.","nullable":true,"example":"https://cargoffer-storage.s3.amazonaws.com/users/63d7907cbe76403b35da63df.jpg"},"recovery_token":{"type":"string","description":"Token temporaneo per recupero password (hash). Generato in: POST /company/auth/recovery (richiesta di reset) Utilizzato in: GET /company/auth/recovery/:token (validare il token) Cancellato dopo: cambio password riuscito o scadenza (48h) Default: '' (stringa vuota quando non c'è un processo di recovery attivo)","example":"a7f5e8d3c2b9..."},"refresh_time":{"type":"integer","enum":[1,3,5,10],"description":"Tempo di scadenza del JWT in giorni. Utilizzato in: generazione del token (claim exp = iat + refresh_time * 24h). **1**: Massima sicurezza (re-login giornaliero) - Per ruoli sensibili **3**: Default bilanciamento sicurezza/comodità **5**: Uso frequente **10**: Applicazioni mobili con funzionalità offline.\nMemorizzato nel payload JWT, validato nel middleware m.isLoged.","default":3,"example":3},"is_bot":{"type":"boolean","description":"Indica se l'utente è un bot automatizzato (per integrazioni API). **true**: Utente creato per integrazione automatica (webhook, script) **false**: Utente umano reale\nUtilizzato in: statistiche, audit, escludere i bot dalle metriche di attività umana. I bot possono avere permessi speciali e limiti di frequenza diversi.","default":false,"example":false},"deleted":{"type":"boolean","description":"Flag per soft delete (plugin mongoose-delete). **false**: Utente attivo **true**: Utente eliminato (non appare nelle query normali). \nLe query includono automaticamente deleted:false (plugin con overrideMethods:true). Recuperabile tramite: POST /company/users/disabled/reactivate/:id. Correlato al timestamp 'deletedAt'.","default":false,"example":false},"deletedAt":{"type":"string","format":"date-time","description":"Timestamp di quando l'utente è stato eliminato (soft delete). Null se deleted=false. Impostato automaticamente dal plugin mongoose-delete. Utilizzato in: audit delle eliminazioni, recupero utenti, conformità GDPR.","nullable":true,"example":"2025-10-01T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp di creazione dell'utente (automatico tramite mongoose timestamps). Immutabile - Non può essere modificato dopo la creazione. Utilizzato per: ordinamento, statistiche di crescita, audit.","example":"2024-12-01T10:30:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp dell'ultima modifica dell'utente (automatico da mongoose timestamps). Aggiornato ad ogni save() - Riflette l'ultima modifica di qualsiasi campo. Utilizzato per: sincronizzazione, invalidazione della cache, rilevamento delle modifiche.","example":"2025-10-20T15:45:30.000Z"},"lastAccess":{"type":"string","format":"date-time","description":"Timestamp dell'ultimo accesso registrato a qualsiasi endpoint autenticato. Aggiornato da: middleware m.getAction ad ogni richiesta autenticata. Diverso da lastSignInAt (quest'ultimo si aggiorna ad ogni richiesta, non solo al login). Utilizzato in: rilevamento inattività, metriche di 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":"Richiesta non valida. Possibili cause:\n- Email non fornita\n- Errore durante il salvataggio (rollback automatico)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Codice di stato HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Codice di errore del sistema.\n\nCodici di errore comuni (vedi listado_errores_http.txt per la lista completa):\n- NO_TOKEN (401): Token JWT non fornito\n- TOKEN_NOT_VALID (401): Token JWT non valido o scaduto\n- NO_ADMIN_ROLE (401): L'utente non dispone dei privilegi di amministratore\n- INVALID_PARAMETERS (400): Parametri della richiesta non validi\n- NOT_FOUND (404): Risorsa richiesta non trovata\n- ALREADY_EXIST (401): Risorsa con lo stesso identificatore già esistente\n- UTC_VALIDATION_FAILED (400): Validazione del timestamp UTC fallita\n- INTERNAL_ERROR (500): Si è verificato un errore imprevisto del server\n\nIl messaggio è risolto da `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Formato standard di risposta per errori utilizzato in tutti gli endpoint API.\n\nTutti gli errori seguono lo schema `{status: number, message: string}`.\nIl codice di stato è ripetuto sia nella risposta HTTP che nel corpo.\n\nI messaggi di errore sono costanti definite nel codice e devono essere\ngestiti lato client con messaggi appropriati rivolti all'utente.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"FORM_DATA_NOT_VALID"}}}},"401":{"description":"Non autorizzato (è richiesto il ruolo admin o dev)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Codice di stato HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Codice di errore del sistema.\n\nCodici di errore comuni (vedi listado_errores_http.txt per la lista completa):\n- NO_TOKEN (401): Token JWT non fornito\n- TOKEN_NOT_VALID (401): Token JWT non valido o scaduto\n- NO_ADMIN_ROLE (401): L'utente non dispone dei privilegi di amministratore\n- INVALID_PARAMETERS (400): Parametri della richiesta non validi\n- NOT_FOUND (404): Risorsa richiesta non trovata\n- ALREADY_EXIST (401): Risorsa con lo stesso identificatore già esistente\n- UTC_VALIDATION_FAILED (400): Validazione del timestamp UTC fallita\n- INTERNAL_ERROR (500): Si è verificato un errore imprevisto del server\n\nIl messaggio è risolto da `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Formato standard di risposta per errori utilizzato in tutti gli endpoint API.\n\nTutti gli errori seguono lo schema `{status: number, message: string}`.\nIl codice di stato è ripetuto sia nella risposta HTTP che nel corpo.\n\nI messaggi di errore sono costanti definite nel codice e devono essere\ngestiti lato client con messaggi appropriati rivolti all'utente.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"NO_TOKEN"}}}},"403":{"description":"Vietato. Possibili cause:\n- L'utente non è amministratore\n- Il piano non consente di creare ulteriori utenti","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Codice di stato HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Codice di errore del sistema.\n\nCodici di errore comuni (vedi listado_errores_http.txt per la lista completa):\n- NO_TOKEN (401): Token JWT non fornito\n- TOKEN_NOT_VALID (401): Token JWT non valido o scaduto\n- NO_ADMIN_ROLE (401): L'utente non dispone dei privilegi di amministratore\n- INVALID_PARAMETERS (400): Parametri della richiesta non validi\n- NOT_FOUND (404): Risorsa richiesta non trovata\n- ALREADY_EXIST (401): Risorsa con lo stesso identificatore già esistente\n- UTC_VALIDATION_FAILED (400): Validazione del timestamp UTC fallita\n- INTERNAL_ERROR (500): Si è verificato un errore imprevisto del server\n\nIl messaggio è risolto da `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Formato standard di risposta per errori utilizzato in tutti gli endpoint API.\n\nTutti gli errori seguono lo schema `{status: number, message: string}`.\nIl codice di stato è ripetuto sia nella risposta HTTP che nel corpo.\n\nI messaggi di errore sono costanti definite nel codice e devono essere\ngestiti lato client con messaggi appropriati rivolti all'utente.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"PLAN_LIMIT_REACHED"}}}},"406":{"description":"L'email esiste già nel database.","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Codice di stato HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Codice di errore del sistema.\n\nCodici di errore comuni (vedi listado_errores_http.txt per la lista completa):\n- NO_TOKEN (401): Token JWT non fornito\n- TOKEN_NOT_VALID (401): Token JWT non valido o scaduto\n- NO_ADMIN_ROLE (401): L'utente non dispone dei privilegi di amministratore\n- INVALID_PARAMETERS (400): Parametri della richiesta non validi\n- NOT_FOUND (404): Risorsa richiesta non trovata\n- ALREADY_EXIST (401): Risorsa con lo stesso identificatore già esistente\n- UTC_VALIDATION_FAILED (400): Validazione del timestamp UTC fallita\n- INTERNAL_ERROR (500): Si è verificato un errore imprevisto del server\n\nIl messaggio è risolto da `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Formato standard di risposta per errori utilizzato in tutti gli endpoint API.\n\nTutti gli errori seguono lo schema `{status: number, message: string}`.\nIl codice di stato è ripetuto sia nella risposta HTTP che nel corpo.\n\nI messaggi di errore sono costanti definite nel codice e devono essere\ngestiti lato client con messaggi appropriati rivolti all'utente.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"USER_ALREADY_EXIST"}}}}}}
>
</StatusCodes>