La respuesta corta
Modele una sola entidad personalizada para el caso y cada tipo de solicitud como un
subtipo CustomEntityType por debajo de ella. El campo nativo typeId de
Coevera —una clave foránea en cada registro que apunta a su tipo de entidad— es el
discriminador, y controla de forma nativa la selección del formulario, el filtrado de
procesos y las vistas guardadas. Defina cada campo una sola vez en la entidad principal; el
formulario de cada subtipo elige el subconjunto que necesita.
Lo único que obligará a replantear su diseño: los procesos de aprobación no pueden tener como destino una entidad personalizada, y el registro Approval no admite campos personalizados. Si alguno de sus tipos de solicitud necesita la aprobación de varias partes, esta se ejecuta sobre un Quote proxy; consulte el §6.
El problema de negocio
Un fabricante vende a través de una red de distribuidores independientes. Esos distribuidores necesitan cosas del fabricante constantemente, y las solicitudes no tienen todas la misma forma:
- Una orden de compra que debe tramitarse con los precios acordados
- Una solicitud de presupuesto para una lista de piezas, con cantidades y una fecha límite de necesidad
- Una solicitud de producto nuevo: algo que no está en el catálogo y que requiere un estudio de viabilidad de ingeniería y la aprobación de seis departamentos internos antes de poder dar un precio
- Una solicitud de certificado de material para un lote de producción concreto, necesaria para el expediente de cumplimiento normativo de un cliente final
- Una consulta general o reclamación que no encaja en ninguna de las anteriores
Antes del proyecto, las cinco llegaban por correo electrónico y por teléfono a buzones compartidos. Nada tenía número de referencia, nadie podía responder a «¿dónde está mi solicitud?» sin preguntar a una persona, y no había forma de ver cuánto había tardado cualquier cosa.
Lo que pidió el negocio fue un único servicio de asistencia, con:
- una sola cola y una sola serie de números de referencia, para que cualquier solicitud pueda citarse en un correo;
- un único ciclo de vida de estados que todos entiendan, desde nuevo hasta resuelto;
- autoservicio para distribuidores: quien envía una solicitud ve las suyas y las de nadie más;
- un hilo de conversación por solicitud, con una separación estricta entre lo que ve el distribuidor y lo que queda como interno;
- un registro de auditoría de quién cambió qué y cuándo;
- y, solo para las solicitudes de productos nuevos, una aprobación formal de varios departamentos.
La tensión está por completo en la combinación. Se quiere uniformidad en la cola. Se requiere divergencia en el contenido: una solicitud de certificado necesita un número de lote y un número de colada, y no le sirve de nada una fecha de entrega; una solicitud de presupuesto necesita un conjunto repetible de líneas de piezas, algo que una consulta general nunca tiene.
Por qué falla el enfoque obvio
Hay dos primeros instintos, y ambos se equivocan de una forma que obliga a reconstruir.
Primer instinto: una entidad más un desplegable «Request Type»
Es el reflejo habitual, porque un desplegable es lo más barato de crear. Falla en un punto concreto y estructural: un valor de desplegable no puede seleccionar un formulario.
Coevera asocia un diseño de formulario editable a cada CustomEntityType. No
existe ningún mecanismo por el que el valor de un campo personalizado cambie el diseño. Así
que con un desplegable se obtiene exactamente un formulario para los cinco tipos, que reúne
los campos de todos ellos: en esta implementación son 42 campos, contados en el formulario
unificado del espacio verificado el 2 de septiembre de 2026, de los que aproximadamente una
docena se aplican a cada solicitud. A los distribuidores se les pide un número de lote en
una orden de compra. No hay manera de que el formulario tenga sentido.
Dos consecuencias más, menos evidentes pero igual de dañinas:
-
Mutabilidad.
typeIdse fija al crear el registro y es inmutable: lo impone la plataforma, sin necesidad de ninguna automatización de control. Un desplegable lo puede editar cualquiera con acceso al campo, de modo que una orden de compra puede convertirse sin avisar en una solicitud de certificado a mitad de su vida, invalidando todos los informes construidos sobre ella. -
Redundancia. Cada registro tendría entonces dos respuestas a la misma pregunta
—su
typeIdreal (que existe tanto si lo usa como si no) y su desplegable— sin nada que las mantenga de acuerdo.
Segundo instinto: cinco entidades personalizadas separadas
El reflejo contrario: si los cinco tipos son realmente distintos, modélelos por separado. Así se obtienen formularios limpios y se pierde todo lo que el negocio pidió realmente:
- Cinco secuencias de números de referencia en lugar de una serie compartida
- Ninguna cola única. «Muéstreme todo lo abierto para este distribuidor» se convierte en cinco vistas de lista que una persona tiene que fusionar mentalmente
- Cinco conjuntos de campos que mantener sincronizados. Estado, gravedad, remitente, objetivo de SLA y una docena más son comunes a todos los tipos; ahora existen cinco veces y divergen
- Ninguna conversión in situ. Una consulta general que resulta ser una solicitud de producto nuevo no puede convertirse en una: hay que volver a introducirla en otra entidad, con lo que pierde su historial y su número de referencia
- Cada informe es una unión de cinco fuentes, y cada tipo nuevo la convierte en una unión de seis
La pregunta que lo decide. ¿Comparten estas cosas un ciclo de vida y una cola, y se diferencian sobre todo en qué campos llevan? Entonces son subtipos de una entidad. ¿Tienen ciclos de vida realmente independientes que nunca aparecen en la misma lista? Entonces son entidades separadas. La gestión de casos pertenece claramente al primer grupo.
La forma general de esa pregunta —campo, subtipo, registro relacionado o entidad nueva— se desarrolla en el Blueprint 003, sobre dónde debe vivir un requisito nuevo.
Modelo de datos
Una entidad personalizada, Case, con cinco registros
CustomEntityType por debajo. Dos entidades personalizadas de apoyo y tres
extensiones de entidades integradas.
La entidad de caso y sus subtipos
| Elemento | Valor |
|---|---|
| Entidad personalizada | Case |
| Campo de nombre | Case Number |
| Secuencia | HD{Year}{Number6} → HD2026000001 |
| Funcionalidades | Activities, Documents, Notes, Global Search |
| Subtipos | Purchase Order · Quote Request · New Product Request · Certification Request · General Enquiry |
| Discriminador | typeId nativo: clave foránea al registro del tipo de entidad. Ningún campo personalizado. |
Los 42 campos personalizados se crean una sola vez, en la entidad principal. Los cinco subtipos comparten ese único conjunto de campos; la definición del formulario de cada uno selecciona el subconjunto que le corresponde. Esta es la propiedad que hace rentable todo el patrón: 13 campos son comunes a los cinco tipos y se definen y mantienen exactamente una vez.
Allí donde se necesita una distinción por tipo —selección del formulario, disparadores de
automatización, vistas guardadas, filtros de la API— el tipo se referencia directamente
mediante typeId o type.name. Sin envoltorios, sin campo
personalizado, sin lógica de sincronización.
Una trampa que conviene conocer antes de empezar. Al crear una entidad personalizada nueva se crea automáticamente un subtipo por defecto por debajo de ella, con el mismo nombre que la entidad y un formulario vacío. Si después añade cinco subtipos propios, los usuarios ven seis entradas en el menú «+», una de las cuales no lleva a ninguna parte.
En su lugar, aloje uno de sus subtipos en el subtipo por defecto creado automáticamente: cámbiele el nombre por el de ese subtipo y cree solo N−1 subtipos nuevos. Aquí, «General Enquiry» vive en el subtipo por defecto y los otros cuatro se crean explícitamente.
Entidades de apoyo
| Entidad | Por qué existe | Subtipos |
|---|---|---|
RequestedPart |
Líneas de piezas repetibles en una solicitud de presupuesto. Un lookup al registro principal Case, que se muestra como cuadrícula en línea solo en el formulario de solicitud de presupuesto. |
1 |
Note |
El hilo de conversación del caso, con dos niveles de visibilidad. Consulte el §6: las notas integradas no podían expresarlo. | 2 — Note (visible para el distribuidor) e Internal Note |
La entidad Note es el mismo patrón aplicado una segunda vez, a menor escala:
en lugar de un booleano personalizado «es interna», la visibilidad la determina
typeId. Dos subtipos, ningún indicador que se pueda marcar mal, y la
automatización filtra directamente por el tipo. El autor y la marca de tiempo proceden de
los campos nativos de propietario y de creación: no hacen falta campos personalizados para
ninguno de los dos.
Entidades integradas, ampliadas
| Entidad | Añadido |
|---|---|
| Account (el distribuidor) | Nivel, región, territorio, lookup al distribuidor principal |
| Contact (el remitente) | Rol en el distribuidor, grupo del servicio de asistencia |
| Product | Referencia de pieza del fabricante (única, con búsqueda global), más desplegables de atributos del sector y un campo de estado de existencias cuyo valor «pedido especial necesario» marca una pieza como candidata a solicitud de producto nuevo |
Configuración a nivel de campo
42 campos personalizados en Case, repartidos en 13 comunes más una parte
específica de cada tipo:
| Grupo | Campos | Ejemplos |
|---|---|---|
| Comunes a los cinco tipos | 13 | Estado, gravedad, contacto remitente, cuenta del distribuidor, analista asignado, contactos a notificar, descripción, resolución, resumen visible para el cliente, objetivo de SLA, horas trabajadas |
| Purchase Order | 5 | Número de orden de compra, fecha de entrega prevista |
| Quote Request | 4 | Cliente final, área de mercado, moneda, fecha límite de necesidad |
| New Product Request | 12 | Especificación técnica, notas de viabilidad, lookup al proxy de aprobación, campos de resumen del estado de aprobación |
| Certification Request | 5 | Número de lote, número de colada, tipo de certificado |
| General Enquiry | 3 | Categoría de la consulta, origen de la derivación |
La denominación de los campos sigue la convención de la propia plataforma: los campos
personalizados llevan el prefijo cf_, los desplegables y los lookups llevan el
sufijo _id porque se resuelven en UUID de opciones o de registros en lugar de
en literales, y los lookups se nombran según la relación que expresan. Los nombres de campo
de este blueprint están anonimizados; las formas, los tipos y las relaciones son los de la
implementación real.
Permisos de campo por rol
Tres roles, y la separación de visibilidad se aplica a nivel de campo, no ocultando cosas en la interfaz: None, Read o Full por campo y por rol.
| Campo | Distribuidor | Analista | Responsable |
|---|---|---|---|
| Resumen visible para el cliente | Read | Full | Full |
| Resolución (relato interno) | None | Full | Full |
| Horas trabajadas | None | Full | Full |
| Analista asignado | None | Read | Read |
Una decisión de diseño surgida con el uso. El campo de resolución permitía inicialmente acceso de lectura al distribuidor. Se cambió a sin acceso, y se añadió a su lado un resumen aparte visible para el cliente. El motivo: cuando los analistas saben que el cliente puede leer un campo, se autocensuran, y el registro interno pierde el detalle técnico que lo hace útil más adelante. Dos campos —uno sin filtrar y otro escrito deliberadamente para el cliente— funcionan mejor que un campo escrito para dos públicos.
Automatización y lógica
Nueve procesos de automatización, todos con ámbito de espacio. La regla que los organiza:
un proceso por cometido, filtrado por typeId. Como los cinco subtipos
comparten una entidad, cada proceso se define una sola vez y un nodo de filtro al principio
del grafo lo restringe a los tipos a los que se aplica, en lugar de tener cinco copias casi
idénticas.
| Disparador | Qué hace |
|---|---|
| Caso creado | Crea una nota visible para el distribuidor, «Case created by <user>», que abre el hilo |
| Caso actualizado | Escribe una nota interna «<actor> changed status to <status>» como registro de auditoría |
| Caso actualizado → el estado pasa a un estado activo y el analista asignado está vacío | Registra al usuario que actúa como analista asignado. La comprobación del campo vacío es una guarda de un solo disparo, de modo que los cambios de estado posteriores no lo vuelven a registrar |
| Nota creada | Resuelve los destinatarios a través de los lookups del caso y encadena con un segundo proceso que envía el correo |
| Caso creado, filtrado a solicitud de producto nuevo | Genera el Quote proxy de aprobación; consulte el §6 |
| Quote actualizado, filtrado a decisión de aprobación | Escribe la decisión de vuelta en el caso y registra una nota interna |
| Botón manual | «Announce status change»: publica con un clic una nota visible para el remitente y para todos los contactos a notificar |
| Programación diaria | Cierra los casos que llevan resueltos y sin cambios 14 días |
Dos patrones que vale la pena reutilizar
La guarda de un solo disparo. Para registrar «quien lo aceptó primero» sin volver a registrarlo en cada actualización posterior, filtre por que el campo de destino esté vacío y asígnele el usuario que actúa. La condición hace que la escritura sea idempotente, sin un indicador aparte de «¿ya se ha ejecutado?».
La propiedad no cambia. El distribuidor que envió el caso sigue siendo el propietario del registro durante toda su vida; el analista interno se registra en un campo lookup aparte. El instinto es reasignar la propiedad al analista al aceptar el caso, pero la propiedad es lo que determina el acceso del distribuidor a su propio registro. Reasignarla eliminaría la visibilidad del remitente sobre su propia solicitud.
Límites y compromisos
Los procesos de aprobación no pueden tener como destino una entidad personalizada
Límite de la plataforma. ApprovalProcess solo puede dispararse desde
Account, Contact, Lead, Opportunity o Quote, o vincularse a ellos. Y el propio
registro Approval no es personalizable: no se le puede añadir un
campo, así que no se le puede dar un vínculo de vuelta a su entidad personalizada.
La introspección del esquema no lo revela. La mutación de creación de campos acepta un nombre de entidad como una simple cadena, por lo que la llamada parece válida y se rechaza en tiempo de ejecución. Conviene saberlo antes de diseñar en torno a ello.
Fuente. Centro de ayuda de Coevera, Working with Approval Processes: las aprobaciones «pueden aplicarse a Accounts, Contacts, Leads, Opportunities y Quotes». Las entidades personalizadas no están en esa lista, y la documentación sobre entidades personalizadas del centro de ayuda indica el límite de forma explícita: «los administradores no pueden crear procesos de aprobación para entidades personalizadas».
Solo uno de los cinco tipos —las solicitudes de productos nuevos— necesita aprobación formal, de seis departamentos. El patrón que funciona es un proxy sobre Quote:
- Se crea un caso de solicitud de producto nuevo.
- Un proceso genera un Quote vinculado bajo un tipo de presupuesto dedicado, reservado para aprobaciones.
- El proceso de aprobación se ejecuta sobre ese Quote de forma nativa, con la interfaz de aprobación y el registro de auditoría estándar; el umbral, el bloqueo del registro y el rol aprobador se configuran como en cualquier presupuesto.
- El Quote lleva un lookup de vuelta al caso; la plataforma mantiene automáticamente el vínculo inverso.
- Al tomarse la decisión, un segundo proceso escribe el resultado de vuelta en el estado del caso.
- Después, el Quote puede archivarse.
Por qué Quote y no Opportunity, si ambos pueden alojar una aprobación:
- Encaje semántico. Una solicitud de producto nuevo es una decisión de precio: «¿podemos fabricarlo y a qué precio?». Quote modela eso. Opportunity entendida como negocio es el enfoque equivocado.
- Sin contaminar el pipeline. Los proxies en Opportunity aparecerían en el pipeline de negocios y contaminarían las previsiones y los informes de ingresos. Un Quote queda en su propio tipo de presupuesto, fuera de la vista.
- El precio se registra de forma nativa. Las líneas del Quote son el precio propuesto, así que no hace falta ningún campo de precio personalizado en el caso.
- Caducidad integrada. Quote tiene una fecha de vencimiento nativa, útil para acotar en el tiempo una solicitud de aprobación.
Dos campos de resumen en el caso muestran el estado del proxy —estado de aprobación y motivo del rechazo—, derivados del Quote vinculado en lugar de almacenados. Para ellos no hace falta lógica de escritura de vuelta; la plataforma los actualiza cuando cambia el Quote.
Las notas integradas no podían expresar dos niveles de visibilidad
El requisito es un único hilo en el que algunas entradas son visibles para el distribuidor y otras son estrictamente internas. La funcionalidad de notas integrada no tiene dimensión de visibilidad, así que el hilo pasó a ser una entidad personalizada con dos subtipos. Un coste que conviene dejar explícito: el caso tiene por tanto notas integradas y un hilo de notas personalizado, y hay que mantener las integradas fuera de los formularios para evitar dos lugares donde escribir que compitan entre sí.
Los campos de texto libre no pueden alimentar listas de distribución
El campo de contactos a notificar empezó siendo un área de texto para escribir direcciones de correo. Se sustituyó por un lookup de selección múltiple a Contact, porque la automatización no puede extraer de forma fiable direcciones de un texto libre para construir una lista de destinatarios. Si hay que actuar sobre el valor de un campo, y no solo leerlo, tiene que ser una referencia tipada.
Otras restricciones encontradas en esta implementación
- Un campo tiene que estar en el formulario para funcionar. Los campos calculados y los campos rellenados por IA que no están en el diseño de formulario de un registro no hacen nada sin avisar: no se calculan y no dan error. Estar en el formulario es funcional, no estético.
- Las secuencias no pueden reiniciarse cada año. El contador de secuencia nativo se incrementa al crear el registro y no tiene reinicio anual. Se puede componer un año en el número como prefijo, pero el contador en sí avanza de forma continua.
- Los procesos con ámbito de espacio necesitan credenciales autenticadas por sesión. Las credenciales de API de acceso personal se rechazan al escribir procesos del espacio, aunque sí pueden leerlos.
Carencias conocidas de esta implementación
Se documentan en lugar de omitirse:
- La escritura de vuelta de la aprobación solo gestiona la rama aprobada. Un proxy rechazado deja el caso en su estado de trabajo sin propagación automática; hace falta un filtro paralelo para el resultado de rechazo.
- El proceso de notas de auditoría se dispara con cada actualización del caso y no solo con los cambios de estado; necesita una condición de campos modificados para dejar de generar ruido.
- Las fechas objetivo de SLA se fijan manualmente. Derivarlas de la gravedad se diseñó y se aplazó deliberadamente.
Verificación
Los diseños con subtipos fallan en silencio: un formulario que se muestra, un proceso que informa de éxito y un discriminador conectado a lo que no es. Lo que se comprobó realmente:
- Inventario de campos releído por entidad. Cada campo se volvió a leer desde la API después de crearlo y se cotejó con la especificación por nombre de API, no por etiqueta. Tres campos habían salido con nombres distintos de la especificación, y uno no se había creado en absoluto sin que nada lo indicara; ninguna de las dos cosas era visible desde la interfaz.
- Número e identidad de los subtipos. Se confirmó que existen cinco subtipos bajo la entidad de caso y ningún sexto huérfano: la trampa del subtipo por defecto del §3 aparece exactamente aquí.
-
Inmutabilidad del discriminador. Se intentó cambiar
typeIden un registro existente y se confirmó que la plataforma lo rechaza. - Acceso a campos por rol, probado con cada rol. No se leyó de la configuración: se inició sesión y se confirmó que los campos que deben ser invisibles para un distribuidor realmente no aparecen.
- Cada proceso confirmado como activado, y su grafo de nodos releído. Dos procesos estaban activados pero se detenían en nodos provisionales: informaban de éxito y no hacían nada. Esta es la comprobación que encontró la carencia en la escritura de vuelta de la aprobación.
- Un recorrido completo por subtipo en la interfaz: crear, avanzar por el ciclo de vida, confirmar que las notas y los correos llegan y confirmar que la vista del distribuidor muestra solo lo que debe.
La regla general. Que una mutación devuelva éxito significa que la escritura se aceptó, no que el comportamiento sea correcto. Vuelva a leer el estado y compruebe el resultado como usuario de cada rol antes de dar por terminada cualquier fase.
Qué indicaría una regresión: casos que aparecen con un subtipo vacío o incorrecto; el campo de analista asignado que cambia después de la primera aceptación; notas escritas como internas que llegan a un distribuidor; solicitudes de productos nuevos sin proxy de aprobación vinculado.
Preguntas frecuentes
¿Cómo gestiona cinco tipos distintos de solicitudes de clientes en un único servicio de asistencia del CRM?
Modele una entidad personalizada para el caso y cada tipo de solicitud como un subtipo CustomEntityType por debajo de ella. En Coevera CRM, el campo nativo typeId de cada registro es el discriminador —una clave foránea al registro del tipo de entidad— y controla de forma nativa la selección del formulario por tipo, el filtrado de procesos y las vistas guardadas. Todos los campos se definen una sola vez en la entidad principal; el formulario de cada subtipo selecciona el subconjunto que le corresponde. Así se mantienen una cola, una secuencia de números de referencia y un ciclo de vida de estados para todos los tipos, y cada tipo puede pedir información completamente distinta.
¿Por qué no usar un campo desplegable personalizado para el tipo de solicitud en lugar de subtipos?
Un desplegable personalizado no puede seleccionar un formulario. Coevera asocia un diseño de formulario editable a cada CustomEntityType, de modo que con un desplegable todos los usuarios se quedan en un único formulario que reúne los campos de todos los tipos: en esta implementación, 42 campos, de los que aproximadamente una docena se aplican a cada solicitud. El enfoque de subtipos aporta además inmutabilidad sin coste adicional: typeId se fija al crear el registro y no puede cambiarse después, algo que un valor de desplegable no puede garantizar.
¿Por qué no crear cinco entidades personalizadas separadas, una por tipo de solicitud?
Cinco entidades suponen cinco conjuntos de campos que mantener sincronizados, cinco secuencias de números de referencia en lugar de una serie compartida, ninguna cola ni vista de lista común a todos los tipos, ninguna conversión in situ cuando una solicitud resulta ser de otro tipo, y que cada informe se convierta en una unión de cinco fuentes. El enfoque de una entidad principal compartida mantiene todo eso en un solo lugar.
¿Puede un proceso de aprobación de Coevera ejecutarse sobre una entidad personalizada?
No. En Coevera, ApprovalProcess solo puede tener como destino Account, Contact, Lead, Opportunity o Quote, y el propio registro Approval no admite campos personalizados, así que no puede ampliarse con un vínculo de vuelta a una entidad personalizada. El patrón que funciona es un proxy: cuando se crea un caso que requiere aprobación, un Process genera un Quote vinculado bajo un tipo de presupuesto dedicado, el proceso de aprobación se ejecuta sobre ese Quote y un segundo Process escribe la decisión de vuelta en el caso.