CoeveraBlueprints

Blueprint 001 · Modello dati

Come gestisce cinque tipi diversi di richieste dei clienti in un unico help desk?

Ordini, richieste di preventivo, richieste di nuovi prodotti, richieste di certificazione e domande generiche. Una coda, un numero di riferimento, un ciclo di vita degli stati — ma cinque insiemi di domande completamente diversi da porre.

Scritto da Pubblicato 2026-09-23Verificato in uno spazio Coevera in produzioneTradotto dall'originale ingleseVersione Markdown ↓

La risposta breve

Modelli un'unica entità personalizzata per il case e ogni tipo di richiesta come sottotipo CustomEntityType al suo interno. Il campo nativo typeId di Coevera — una chiave esterna su ogni record che punta al suo tipo di entità — è il discriminante e determina in modo nativo la selezione del modulo, il filtraggio dei processi e le viste salvate. Definisca ogni campo una sola volta sull'entità padre; il modulo di ciascun sottotipo sceglie il sottoinsieme di cui ha bisogno.

L'unico aspetto che ridisegnerà il suo progetto: i processi di approvazione non possono avere come destinazione un'entità personalizzata, e il record Approval non accetta campi personalizzati. Se uno dei suoi tipi di richiesta richiede l'approvazione di più parti, questa passa per un Quote proxy — si veda il §6.

Il problema di business

Un produttore vende attraverso una rete di distributori indipendenti. Questi distributori hanno continuamente bisogno di qualcosa dal produttore, e le richieste non hanno tutte la stessa forma:

  • Un ordine di acquisto da evadere ai prezzi concordati
  • Una richiesta di preventivo per un elenco di componenti, con quantità e una data entro cui servono
  • Una richiesta di nuovo prodotto — qualcosa che non è a catalogo e che richiede una verifica di fattibilità tecnica e l'approvazione di sei reparti interni prima di poter indicare un prezzo
  • Una richiesta di certificazione del materiale per un lotto di produzione specifico, per completare il fascicolo di conformità di un cliente finale
  • Una domanda o un reclamo generico che non rientra in nessuno dei casi precedenti

Prima del progetto, tutte e cinque arrivavano come email e telefonate a caselle di posta condivise. Nulla aveva un numero di riferimento, nessuno poteva rispondere a "a che punto è la mia richiesta?" senza chiedere a una persona, e non c'era modo di vedere quanto tempo avesse richiesto qualsiasi cosa.

Ciò che l'azienda ha chiesto era un help desk con:

  • una sola coda e una sola serie di numeri di riferimento, così che qualsiasi richiesta possa essere citata in un'email;
  • un ciclo di vita degli stati comprensibile a tutti, da nuovo fino a risolto;
  • self-service per i distributori — chi invia una richiesta vede le proprie e nessun'altra;
  • un thread di conversazione per ogni richiesta, con una separazione rigorosa tra ciò che vede il distributore e ciò che resta interno;
  • un audit trail di chi ha modificato cosa e quando;
  • e, solo per le richieste di nuovo prodotto, un'approvazione formale da parte di più reparti.

La tensione sta tutta nella combinazione. L'uniformità è desiderata per la coda. La differenziazione è necessaria nel contenuto: una richiesta di certificazione ha bisogno di un numero di lotto e di un numero di colata, e non sa che farsene di una data di consegna; una richiesta di preventivo ha bisogno di un insieme ripetuto di righe di componenti, che una domanda generica non ha mai.

Perché l'approccio ovvio non funziona

Esistono due istinti iniziali, ed entrambi sono sbagliati in un modo che costa una ricostruzione.

Istinto uno: un'entità più un menu a tendina "Request Type"

È il riflesso, perché un menu a tendina è la cosa più economica da creare. Fallisce su un punto specifico e strutturale: il valore di un menu a tendina non può selezionare un modulo.

Coevera associa un layout di modulo modificabile a ciascun CustomEntityType. Non esiste alcun meccanismo con cui il valore di un campo personalizzato cambi il layout. Con un menu a tendina si ottiene quindi esattamente un modulo per tutti e cinque i tipi, con l'unione dei campi di ogni tipo — in questa implementazione sono 42 campi, contati sul modulo unificato nello spazio verificato il 2 settembre 2026, di cui circa una dozzina si applica a una data richiesta. Ai distributori viene chiesto un numero di lotto su un ordine di acquisto. Non c'è modo di rendere sensato il modulo.

Due ulteriori conseguenze, meno evidenti ma altrettanto dannose:

  • Modificabilità. typeId viene impostato alla creazione ed è immutabile — lo impone la piattaforma, senza bisogno di automazioni di protezione. Un menu a tendina è modificabile da chiunque abbia accesso al campo, quindi un ordine di acquisto può diventare silenziosamente una richiesta di certificazione a metà del suo ciclo di vita, invalidando ogni report costruito su di esso.
  • Ridondanza. Ogni record porterebbe allora due risposte alla stessa domanda — il suo vero typeId (che esiste che lei lo usi o no) e il suo menu a discesa — senza nulla che le mantenga coerenti.

Istinto due: cinque entità personalizzate separate

Il riflesso opposto: se i cinque tipi sono davvero diversi, li si modella separatamente. Si ottengono moduli puliti, e si perde tutto ciò che l'azienda aveva effettivamente chiesto:

  • Cinque sequenze di numeri di riferimento invece di una serie condivisa
  • Nessuna coda unica. "Mostrami tutto ciò che è aperto per questo distributore" diventa cinque viste elenco che una persona deve unire mentalmente
  • Cinque insiemi di campi da mantenere allineati. Stato, gravità, richiedente, obiettivo SLA e una dozzina di altri sono comuni a tutti i tipi; ora esistono cinque volte e divergono
  • Nessuna conversione sul posto. Una domanda generica che si rivela una richiesta di nuovo prodotto non può diventarlo — deve essere reinserita in un'altra entità, perdendo la sua cronologia e il suo numero di riferimento
  • Ogni report è un'unione di cinque fonti, e ogni nuovo tipo la porta a sei

La domanda decisiva. Queste cose condividono un ciclo di vita e una coda, e si differenziano soprattutto per i campi che contengono? Allora sono sottotipi di un'unica entità. Hanno cicli di vita davvero indipendenti che non compaiono mai nello stesso elenco? Allora sono entità separate. La gestione dei case appartiene decisamente al primo gruppo.

La forma generale di questa domanda — campo, sottotipo, record correlato o nuova entità — è affrontata nel Blueprint 003, su dove debba risiedere un nuovo requisito.

Modello dati

Un'entità personalizzata, Case, con cinque record CustomEntityType al suo interno. Due entità personalizzate di supporto e tre estensioni di entità predefinite.

L'entità Case e i suoi sottotipi

ElementoValore
Entità personalizzataCase
Campo nomeCase Number
SequenzaHD{Year}{Number6} → HD2026000001
FunzionalitàActivities, Documents, Notes, Global Search
SottotipiPurchase Order · Quote Request · New Product Request · Certification Request · General Enquiry
DiscriminantetypeId nativo — FK verso il record del tipo di entità. Nessun campo personalizzato.

Tutti i 42 campi personalizzati vengono creati una sola volta, sull'entità padre. I cinque sottotipi condividono quell'unico insieme di campi; la definizione del modulo di ciascuno seleziona il sottoinsieme pertinente. È questa la proprietà che rende conveniente l'intero pattern — 13 campi sono comuni a tutti e cinque i tipi e vengono definiti e mantenuti esattamente una volta.

Ovunque serva una distinzione per tipo — selezione del modulo, trigger delle automazioni, viste salvate, filtri API — il tipo viene indicato direttamente tramite typeId o type.name. Nessun wrapper, nessun campo personalizzato, nessuna logica di sincronizzazione.

Una trappola da conoscere prima di iniziare. La creazione di una nuova entità personalizzata crea automaticamente un sottotipo predefinito al suo interno, con lo stesso nome dell'entità e un modulo vuoto. Se poi aggiunge cinque sottotipi propri, gli utenti vedono sei voci nel menu "+", una delle quali è un vicolo cieco.

Collochi invece uno dei suoi sottotipi sul predefinito creato automaticamente: lo rinomini con il nome di quel sottotipo e crei solo N−1 nuovi sottotipi. Qui "General Enquiry" risiede sul predefinito automatico e gli altri quattro vengono creati esplicitamente.

Entità di supporto

EntitàPerché esisteSottotipi
RequestedPart Righe di componenti ripetute in una richiesta di preventivo. Un lookup padre verso Case, visualizzato come griglia inline solo sul modulo della richiesta di preventivo. 1
Note Il thread di conversazione del case, con due livelli di visibilità. Si veda il §6 — le note predefinite non potevano esprimerlo. 2 — Note (visibile al distributore) e Internal Note

L'entità Note è lo stesso pattern applicato una seconda volta, su scala ridotta: invece di un booleano personalizzato "è interna", la visibilità è portata da typeId. Due sottotipi, nessun flag da impostare in modo errato, e le automazioni filtrano direttamente sul tipo. Autore e data e ora provengono dai campi nativi del proprietario e della creazione — per nessuno dei due servono campi personalizzati.

Entità predefinite, estese

EntitàAggiunto
Account (il distributore)Livello, regione, territorio, lookup verso il distributore padre
Contact (il richiedente)Ruolo del distributore, gruppo di help desk
ProductCodice articolo del produttore (univoco, ricerca globale), più menu a tendina per gli attributi di dominio e un campo sullo stato delle scorte il cui valore "special order required" contrassegna un componente come candidato a una richiesta di nuovo prodotto

Configurazione a livello di campo

42 campi personalizzati su Case, distribuiti in 13 comuni più una parte specifica per tipo:

GruppoCampiEsempi
Comuni a tutti e cinque i tipi13Stato, gravità, contatto del richiedente, account del distributore, analista assegnato, contatti da notificare, descrizione, risoluzione, riepilogo visibile al cliente, obiettivo SLA, ore lavorate
Purchase Order5Numero dell'ordine di acquisto, data di consegna prevista
Quote Request4Cliente finale, area di mercato, valuta, data entro cui serve
New Product Request12Specifica tecnica, note di fattibilità, lookup verso il proxy di approvazione, rollup dello stato di approvazione
Certification Request5Numero di lotto, numero di colata, tipo di certificazione
General Enquiry3Categoria della domanda, fonte di segnalazione

La denominazione dei campi segue la convenzione della piattaforma — i campi personalizzati hanno il prefisso cf_, i campi a discesa e di lookup hanno il suffisso _id perché si risolvono in UUID di opzioni o di record anziché in valori letterali, e i campi di lookup prendono il nome dalla relazione che esprimono. I nomi dei campi in questo blueprint sono anonimizzati; forme, tipi e relazioni sono quelli realizzati.

Permessi sui campi per ruolo

Tre ruoli, e la separazione della visibilità è imposta a livello di campo anziché nascondendo elementi nell'interfaccia: None, Read o Full per ogni campo e ogni ruolo.

CampoDistributoreAnalistaManager
Riepilogo visibile al clienteReadFullFull
Risoluzione (resoconto interno)NoneFullFull
Ore lavorateNoneFullFull
Analista assegnatoNoneReadRead

Una nota di progettazione emersa con l'uso. Il campo della risoluzione consentiva originariamente l'accesso in lettura ai distributori. È stato impostato su nessun accesso, e accanto è stato aggiunto un riepilogo separato visibile al cliente. Il motivo: quando gli analisti sanno che il cliente può leggere un campo, si autocensurano, e il record interno perde i dettagli tecnici che lo rendono utile in seguito. Due campi — uno non filtrato, uno scritto deliberatamente per il cliente — funzionano meglio di un campo scritto per due pubblici.

Automazione e logica

Nove processi di automazione, tutti a livello di spazio. La regola che li organizza: un processo per ogni aspetto, filtrato per typeId. Poiché tutti e cinque i sottotipi condividono un'entità, ogni processo viene definito una sola volta e un nodo filtro all'inizio del grafo lo restringe ai tipi a cui si applica — invece di cinque copie quasi identiche.

TriggerCosa fa
Case creatoCrea una nota visibile al distributore, "Case created by <user>", che apre il thread
Case aggiornatoScrive una nota interna "<actor> changed status to <status>" come audit trail
Case aggiornato → lo stato entra in uno stato attivo e l'analista assegnato è vuotoRegistra l'utente che agisce come analista assegnato. Il controllo sul campo vuoto è una protezione che scatta una sola volta, così i cambi di stato successivi non lo registrano di nuovo
Nota creataDetermina i destinatari tramite i lookup del case, quindi concatena un secondo processo che invia l'email
Case creato, filtrato sulle richieste di nuovo prodottoGenera il Quote proxy per l'approvazione — si veda il §6
Quote aggiornato, filtrato sulla decisione di approvazioneRiporta la decisione sul case e registra una nota interna
Pulsante manuale"Announce status change" — pubblica con un clic una nota visibile per il richiedente e per tutti i contatti da notificare
Pianificazione giornalieraChiude i case risolti e non toccati da 14 giorni

Due pattern da riutilizzare

La protezione che scatta una sola volta. Per registrare "chi l'ha accettato per primo" senza sovrascriverlo a ogni aggiornamento successivo, filtri sul campo di destinazione vuoto e lo imposti sull'utente che agisce. La condizione rende la scrittura idempotente — nessun flag separato "è già stato eseguito?".

La proprietà resta dov'è. Il distributore che ha inviato il case rimane il proprietario del record per tutta la sua durata; l'analista interno è registrato in un campo di lookup separato. L'istinto è riassegnare la proprietà all'analista al momento dell'accettazione — ma è la proprietà a determinare l'accesso del distributore al proprio record. Riassegnarla toglierebbe al richiedente la visibilità sulla propria richiesta.

Limiti e compromessi

I processi di approvazione non possono avere come destinazione un'entità personalizzata

Limite della piattaforma. ApprovalProcess può essere attivato da o collegato solo a Account, Contact, Lead, Opportunity o Quote. E il record Approval stesso non è personalizzabile — non è possibile aggiungervi un campo, quindi non è possibile dargli un collegamento verso la sua entità personalizzata.

L'introspezione dello schema non lo rivela. La mutation per la creazione dei campi accetta il nome di un'entità come semplice stringa, quindi la chiamata sembra valida e viene rifiutata in fase di esecuzione. Meglio saperlo prima di progettare attorno a questo limite.

Fonte. Centro assistenza di Coevera, Working with Approval Processes: le approvazioni «possono essere applicate ad Accounts, Contacts, Leads, Opportunities e Quotes». Le entità personalizzate non sono in quell'elenco, e la documentazione sulle entità personalizzate del centro assistenza indica il limite in modo esplicito: «gli amministratori non possono creare processi di approvazione per le entità personalizzate».

Solo uno dei cinque tipi — le richieste di nuovo prodotto — richiede un'approvazione formale, da parte di sei reparti. Il pattern che funziona è un proxy su Quote:

  1. Viene creato un case di tipo richiesta di nuovo prodotto.
  2. Un processo genera un Quote collegato con un tipo di Quote dedicato, riservato alle approvazioni.
  3. Il processo di approvazione viene eseguito su quel Quote, in modo nativo, con l'interfaccia di approvazione standard e l'audit trail — soglia, blocco del record e ruolo dell'approvatore configurati come su qualsiasi Quote.
  4. Il Quote contiene un lookup verso il case; la piattaforma mantiene automaticamente il collegamento inverso.
  5. Alla decisione, un secondo processo riporta l'esito nello stato del case.
  6. Il Quote può poi essere archiviato.

Perché Quote e non Opportunity, dato che entrambi possono ospitare un'approvazione:

  • Coerenza semantica. Una richiesta di nuovo prodotto è una decisione di prezzo — "possiamo produrlo, e a che prezzo?". È ciò che modella Quote. Opportunity intesa come trattativa è l'inquadramento sbagliato.
  • Nessun inquinamento della pipeline. I proxy su Opportunity comparirebbero nella pipeline delle trattative e altererebbero previsioni e report sui ricavi. Un Quote resta nel proprio tipo di Quote, in disparte.
  • Il prezzo viene registrato in modo nativo. Le righe del Quote sono il prezzo proposto — quindi non serve affatto un campo prezzo personalizzato sul case.
  • Scadenza integrata. Quote ha una data di scadenza nativa, utile per limitare nel tempo una richiesta di approvazione.

Due campi rollup sul case mostrano lo stato del proxy — stato di approvazione e motivo del rifiuto — derivati dal Quote collegato anziché memorizzati. Per questi non serve alcuna logica di riscrittura; la piattaforma li aggiorna quando il Quote cambia.

Le note predefinite non potevano esprimere una visibilità a due livelli

Il requisito è un unico thread in cui alcune voci sono visibili al distributore e altre sono strettamente interne. La funzionalità di note predefinita non ha una dimensione di visibilità, quindi il thread è diventato un'entità personalizzata con due sottotipi. Un costo da dichiarare esplicitamente: il case ha quindi sia le note predefinite sia un thread di note personalizzato, e quelle predefinite devono essere tenute fuori dai moduli per evitare due posti concorrenti in cui scrivere.

I campi di testo libero non possono alimentare liste di distribuzione

Il campo dei contatti da notificare era inizialmente un'area di testo in cui digitare indirizzi email. È stato sostituito da un lookup a selezione multipla verso Contact, perché l'automazione non riesce a estrarre in modo affidabile gli indirizzi dal testo libero per costruire un elenco di destinatari. Se il valore di un campo deve essere elaborato e non soltanto letto, deve essere un riferimento tipizzato.

Altri vincoli incontrati in questa implementazione

  • Un campo deve essere sul modulo per funzionare. I campi calcolati e i campi compilati dall'IA assenti dal layout del modulo di un record non fanno nulla, in silenzio — non calcolano e non segnalano errori. La presenza nel modulo è funzionale, non estetica.
  • Le sequenze non si possono azzerare ogni anno. Il contatore nativo della sequenza si incrementa a ogni creazione e non prevede un azzeramento annuale. Un anno può essere inserito nel numero come prefisso, ma il contatore stesso prosegue senza interruzioni.
  • I processi a livello di spazio richiedono credenziali autenticate tramite sessione. Le credenziali API ad accesso personale vengono rifiutate quando si scrivono processi dello spazio, anche se possono leggerli.

Lacune note in questa implementazione

Riportate anziché omesse:

  • La riscrittura dell'approvazione gestisce solo il ramo approvato. Un proxy rifiutato lascia il case nel suo stato di lavorazione senza alcuna propagazione automatica — per l'esito rifiutato serve un filtro parallelo.
  • Il processo delle note di audit si attiva a ogni aggiornamento del case anziché solo ai cambi di stato; ha bisogno di una condizione sui campi modificati per non generare più rumore.
  • Le date obiettivo SLA vengono impostate manualmente. La loro derivazione dalla gravità è stata progettata e rinviata deliberatamente.

Verifica

I progetti basati su sottotipi falliscono in silenzio — un modulo che si visualizza, un processo che segnala successo e un discriminante collegato alla cosa sbagliata. Ciò che è stato effettivamente verificato:

  • Inventario dei campi riletto per ogni entità. Ogni campo è stato riletto dall'API dopo la creazione e confrontato con la specifica in base al nome API, non all'etichetta. Tre campi risultavano con nomi diversi dalla specifica, e uno non era stato creato affatto, in silenzio — nessuno dei due problemi era visibile dall'interfaccia.
  • Numero e identità dei sottotipi. Confermato che sotto l'entità Case esistono cinque sottotipi e nessun sesto orfano — la trappola del predefinito automatico del §3 emerge proprio qui.
  • Immutabilità del discriminante. Tentato di modificare typeId su un record esistente e confermato che la piattaforma lo rifiuta.
  • Accesso ai campi per ruolo, testato come ciascun ruolo. Non letto dalla configurazione — effettuato l'accesso e confermato che i campi che devono essere invisibili a un distributore sono effettivamente assenti.
  • Ogni processo confermato come attivo, e il suo grafo di nodi riletto. Due processi erano attivi ma si fermavano a nodi segnaposto — segnalavano successo e non facevano nulla. È questo il controllo che ha trovato la lacuna nella riscrittura dell'approvazione.
  • Un passaggio completo per ogni sottotipo nell'interfaccia: creare, far avanzare nel ciclo di vita, confermare che note ed email arrivino, confermare che la vista del distributore mostri solo ciò che deve.

La regola generale. Una mutation che restituisce successo significa che la scrittura è stata accettata, non che il comportamento sia corretto. Rilegga lo stato, e verifichi il risultato come utente di ciascun ruolo, prima di considerare conclusa qualsiasi fase.

Cosa segnalerebbe una regressione: case che compaiono con un sottotipo vuoto o sbagliato; il campo dell'analista assegnato che cambia dopo la prima accettazione; note scritte come interne che raggiungono un distributore; richieste di nuovo prodotto senza un proxy di approvazione collegato.

Domande frequenti

Come gestisce cinque tipi diversi di richieste dei clienti in un unico help desk del CRM?

Modelli un'unica entità personalizzata per il case e ogni tipo di richiesta come sottotipo CustomEntityType al suo interno. In Coevera CRM il campo nativo typeId presente su ogni record è il discriminante — una chiave esterna verso il record del tipo di entità — e determina in modo nativo la selezione del modulo per tipo, il filtraggio dei processi e le viste salvate. Tutti i campi sono definiti una sola volta sull'entità padre; il modulo di ciascun sottotipo seleziona il sottoinsieme pertinente. In questo modo restano una sola coda, una sola sequenza di numeri di riferimento e un solo ciclo di vita degli stati per tutti i tipi, mentre ogni tipo può chiedere informazioni completamente diverse.

Perché non usare un campo a discesa personalizzato per il tipo di richiesta invece dei sottotipi?

Un menu a tendina personalizzato non può selezionare un modulo. Coevera associa un layout di modulo modificabile a ciascun CustomEntityType, quindi con un menu a tendina ogni utente resta su un unico modulo che contiene l'unione dei campi di tutti i tipi — in questa implementazione 42 campi, di cui circa una dozzina si applica a una singola richiesta. L'approccio a sottotipi offre inoltre l'immutabilità senza costi aggiuntivi: typeId viene impostato alla creazione e non può più essere modificato, cosa che il valore di un menu a tendina non può garantire.

Perché non creare cinque entità personalizzate separate, una per tipo di richiesta?

Cinque entità significano cinque insiemi di campi da mantenere allineati, cinque sequenze di numeri di riferimento invece di una serie condivisa, nessuna coda o vista elenco unica per tutti i tipi, nessuna conversione sul posto quando una richiesta si rivela di un altro tipo, e ogni report che diventa un'unione di cinque fonti. L'approccio con un'entità padre condivisa mantiene tutto questo in un unico posto.

Un processo di approvazione di Coevera può essere eseguito su un'entità personalizzata?

No. In Coevera ApprovalProcess può avere come destinazione solo Account, Contact, Lead, Opportunity o Quote, e il record Approval stesso non accetta campi personalizzati — quindi non può essere esteso con un collegamento a un'entità personalizzata. Il pattern che funziona è un proxy: quando viene creato un case che richiede un'approvazione, un Process genera un Quote collegato con un tipo di Quote dedicato, il processo di approvazione viene eseguito su quel Quote e un secondo Process riporta la decisione sul case.

Pubblicato da Coevera · ridotto allo schema, nessun dato dei clientiBlueprint 001 · pubblicato 2026-09-23