Referencia

Cada herramienta, en un solo lugar

Esta página es la referencia práctica para cada herramienta de la barra lateral de tu entorno Archestack. Cada sección cubre qué hace la herramienta, cuándo recurrirías a ella, los pasos para los flujos de trabajo más comunes y las trampas que atrapan a los usuarios nuevos. La sección final (APIs invocables desde scripts) documenta la superficie exacta que pueden invocar los scripts.

Schema Designer

Un editor visual del esquema de base de datos, tablas, columnas, tipos, relaciones, índices, que guarda automáticamente en un documento JSON. El esquema es la fuente de la verdad para tu aplicación; ninguna otra herramienta ve una columna hasta que la añades aquí y despliegas.

Flujo de trabajo habitual: en la barra de herramientas, haz clic en Add Table (botón con icono). Se abre un diálogo titulado "Create New Table"; escribe un nombre (el texto de ayuda recuerda "Lowercase with underscores recommended"), haz clic en Create. La tabla aparece en el lienzo con una clave primaria SERIAL id autogenerada. Selecciona la tabla, el panel de la derecha se abre en la pestaña Columns. Añade columnas desde el cuadro "Add column" con borde discontinuo en la parte inferior (campo de nombre, desplegable Type, haz clic en Add Column). Haz clic en cada columna para expandirla y ajustar los conmutadores (PK, NULL, UQ), Length y Default value.

Tipos de columna disponibles en el desplegable Type: SERIAL, BIGSERIAL, INTEGER, BIGINT, SMALLINT, DECIMAL, NUMERIC, REAL, DOUBLE PRECISION, VARCHAR, CHAR, TEXT, BOOLEAN, DATE, TIME, TIMESTAMP, TIMESTAMPTZ, UUID, JSON, JSONB, BYTEA, INET, CIDR, MACADDR, MONEY, INTERVAL, más tipos geométricos y de array. La longitud solo es relevante para VARCHAR y CHAR.

Las claves foráneas viven en la pestaña Relations del panel derecho (no "Relationships"). Desplázate hasta "Add relationship": elige la columna FK en la tabla actual, el tipo de relación (One-to-One / One-to-Many / Many-to-Many), la tabla referenciada, la columna referenciada, y haz clic en Add Relationship. Cada relación tiene desplegables On Delete y On Update (CASCADE / SET NULL / SET DEFAULT / RESTRICT / NO ACTION).

Guardado automático: el indicador en la parte inferior izquierda parpadea entre "Auto saving…" y "Saved". También hay un botón Save en la barra de herramientas (solo se activa cuando hay cambios pendientes) para guardados explícitos.

Otros botones de la barra de herramientas: Add Group (contenedores visuales para tablas relacionadas, panel de propiedades titulado "Group"), Add Text (anotaciones libres, panel de propiedades titulado "Text Annotation"), controles de zoom, filtro de paquetes, búsqueda de tablas.

Trampas: renombrar una columna produce un plan destructivo (eliminar + añadir), edita el SQL en la pestaña Generated SQL del despliegue para usar RENAME COLUMN si quieres preservar los datos. Añadir una columna NOT NULL a una tabla no vacía sin un valor por defecto falla, hazla nullable, rellena datos y luego altera. El botón Deploy de la barra de herramientas te lleva a una página de configuración de despliegue, no directamente a un despliegue.

Database Deployments

Lista cada despliegue con su estado (Draft / Executing / Succeeded / Failed) y ejecuta los nuevos. Ruta: /database-deployments/overview.

Botones de la parte superior derecha: Refresh (recarga la lista), New Deployment (crea un nuevo borrador).

Desde Schema Designer: el botón con icono de cohete verde Deploy en la barra de herramientas de Schema Designer te lleva a /database-deployments/configure/new, una página de configuración de despliegue nueva.

Página de configuración de despliegue: dos pestañas.

  • Configuration - editores de scripts pre/post-despliegue (PostgreSQL) con un botón Test Run cada uno. El pre-script se ejecuta antes de los cambios de esquema; el post-script, después. Útil para rellenos, reconstrucciones de índices o limpieza de zonas de staging. Más un conmutador Force Deploy (Allow Data Loss) para operaciones destructivas.
  • Generated SQL - muestra la migración que ha producido la plataforma. El SQL es editable. Un botón Regenerate (icono de refresco) vuelve a ejecutar el diff si has hecho cambios de esquema desde que abriste esta página.

Botones superiores: Back, Save (guarda la configuración como Draft), Deploy (ejecuta el despliegue inmediatamente). Deploy es lo último que pulsas.

Trampas: los despliegues no se envuelven automáticamente en una transacción, un fallo a mitad deja la base de datos en un estado parcial. Para migraciones arriesgadas, envuelve tus sentencias en BEGIN; … COMMIT; en el pre-script. El conmutador Force Deploy se salta las comprobaciones de seguridad; úsalo deliberadamente.

Business Entities

Vistas curadas construidas sobre una tabla maestra más joins de tablas relacionadas. Las páginas se vinculan a Business Entities, nunca a tablas crudas. Consulta Conceptos -> Business Entities para el porqué.

Flujo de trabajo habitual: haz clic en Create. El editor a página completa se abre con las pestañas Visual, JSON, Events. Establece Entity Name, Master Table (Autocomplete) y Label Column (la columna cuyo valor representa un registro en los selectores). Guarda, la BE autogenera una lista de columnas nativas a partir de la tabla maestra.

Añadir joins: desplázate a la tarjeta Join Configurator y haz clic en Add Join. Cada join se abre como un Accordion con: Join Type (INNER / LEFT / RIGHT), From Table, To Table (Autocomplete agrupado por Database Tables / Third Party), From Column, To Column, desplegables opcionales Type Cast, y un selector de columnas para qué columnas del destino exponer.

Agregaciones: en un join, activa Aggregate Mode. Por cada columna elegida, eliges una Aggregate Function: COUNT / SUM / AVG / MIN / MAX / COUNT DISTINCT. "Número de contactos abiertos de esta empresa" es el ejemplo canónico.

Run Preview: el editor tiene un botón Run Preview (con un icono de reproducción) que ejecuta la configuración completa de join y muestra filas reales en una rejilla de datos. Si una columna unida vuelve vacía, la columna no estaba marcada en el selector de columnas del join (o la relación está mal configurada).

Múltiples BEs por tabla maestra: nada te impide tener customer (sales view) y customer (support view) sobre la misma tabla maestra. Páginas diferentes, columnas apropiadas para cada audiencia, la misma fila subyacente.

Trampas: las columnas unidas y agregadas son de solo lectura. Para editar la etiqueta unida, navega al registro fuente. Las columnas agregadas se recalculan en cada consulta, bien para cientos de filas, lento con millones.

Page Editor

Configura la interfaz de tiempo de ejecución vinculando una Page a una Business Entity. La plataforma autogenera secciones basadas en las columnas de la BE; ajustas el diseño a partir de ahí. No hay un "selector de plantillas" separado, cada página parte de la misma línea base generada y la personalizas.

Flujo de trabajo habitual: haz clic en Create. Rellena Page Name, Page Route (por ejemplo /companies), elige la Business Entity (Autocomplete). La página se abre en el editor con las pestañas Visual, Overview, Create, Entities, Events, JSON. Ajusta el diseño autogenerado, luego cambia el conmutador Published de la cabecera superior a ON.

Tipos de widget de campo (el Select Type de cada campo): Text, Textarea, Number, Date, Select, Checkbox, Email. Usa Select para campos de clave foránea y establece el autocompletado Entity del campo a la BE referenciada para que los usuarios elijan por etiqueta.

Pestañas en el formulario de detalle: haz clic en Add Tab en la pestaña Visual. Dentro de una pestaña puedes añadir una sección RelatedGrid vinculada a otra BE filtrada por la PK del registro actual. Ejemplo clásico: una página Company con pestañas "Contacts" y "Deals", cada una filtrada por company_id = id de la empresa actual.

Botones de acción en el inspector Header del formulario de detalle. Haz clic en Add en la sección Actions. Cada acción tiene Label, Icon (Save / Delete / Add / Refresh / Download / Bolt / None), Variant (Contained / Outlined / Text), Color (Primary / Secondary / Error / Success / Warning / Info) y Steps (una lista ordenada de: Save Record, Delete Record, Navigate, Business Events, Custom). Para ejecutar un Business Event desde un botón, establece el tipo de paso a Business Events y elige el evento por nombre.

Publicación: el conmutador Published en la cabecera superior es el único control de publicación. ON = la página aparece bajo Published Pages en la barra lateral y los usuarios finales que naveguen a su ruta la ven. OFF = borrador (solo tú en el editor ves tus cambios).

Trampas: las columnas autogeneradas en una Page nueva reflejan la BE, si la BE cambia (añades un join), la Page no recogerá automáticamente las nuevas columnas. Recarga la página en el editor o añade la columna manualmente a la sección. Actualmente no hay un tipo de paso de acción "Generate PDF", para conectar un PDF a un botón de página, expón el PDF mediante un Script Module que llame al endpoint REST y usa un paso Custom (o llama al endpoint directamente desde una Frontend Template).

Business Events

Reglas que se disparan ante cambios de datos (o según un horario, o manualmente) y ejecutan acciones cuando se cumplen las condiciones. El mecanismo "cuando ocurra X, haz Y" de la plataforma. Consulta Conceptos -> Business Events para el modelo.

Flujo de trabajo habitual: haz clic en Create. Establece Rule Name, activa Enabled. Elige Business Entity. Marca uno o más Triggers: BeforeCreate / BeforeUpdate / AfterCreate / AfterUpdate / BeforeDelete / OnSchedule / Manual / InitialValue. Construye el árbol de condiciones (grupos AND/OR). Añade una o más acciones en el FlowCanvas. Guarda.

Tipos de acción (RuleActionKind):

  • Execute Script - ejecuta un script C#. La acción más flexible; recurre a ella siempre que quieras establecer un campo, hacer un cálculo, llamar a una API, cualquier cosa personalizada. El script recibe Entity (múdalo en triggers Before* para cambiar la escritura en curso), OldEntity, Log, Db, Modules.
  • Validate - un script que devuelve bool. true significa que la validación falla y el guardado se bloquea con el mensaje de error configurado. Opcionalmente resalta columnas específicas vía errorColumns.
  • Block Operation - detiene la operación de golpe con un mensaje configurado. Sin script.
  • Create Entity - inserta un registro en otra BE. Los valores de los campos admiten expresiones de plantilla como {{ Entity.column_name }} y {{ now() }}.
  • Update Entity - actualiza registros en otra BE que coincidan con un filtro de condiciones. Las mismas expresiones de plantilla.
  • Delete Entity - elimina registros que coincidan con una condición. Se niega a dispararse sin una condición (seguridad).
  • Send Email / Send Webhook / Publish Event - definidos en el esquema como mejoras futuras; actualmente se registran y se omiten.

Patrón "establecer un campo": no hay una acción discreta Set Field. La forma de establecer un campo es un Execute Script sobre un trigger Before* que muta Entity. Ejemplo: Entity.title = ((string)Entity.title)?.Trim();. La plataforma persiste el Entity modificado como parte de la escritura en curso, sin consulta extra, sin riesgo de recursión.

Run Simulation: la pestaña Simulate del editor de trigger tiene un selector de registros y un botón Run Simulation. La plataforma ejecuta tus condiciones y acciones contra el registro elegido sin persistir cambios, las operaciones de escritura se ejecutan en una transacción que se revierte. El panel de salida muestra qué condiciones coincidieron y qué habría hecho cada acción. Úsalo para detectar configuraciones erróneas antes de que los datos en vivo los encuentren.

Expresiones de plantilla en la configuración de acciones usan llaves {{ … }}. Los tokens reales: {{ Entity.column }}, {{ OldEntity.column }}, {{ now() }} / {{ getdate() }}, {{ today() }}, {{ guid() }} / {{ newid() }}, {{ year() }}, {{ month() }}, {{ day() }}, {{ timestamp() }}, agregados como {{ SUM(column) }} dentro de contextos de join, y aritmética post-sustitución como {{ Entity.quantity * Entity.price }}. No hay token {{ user.email }}.

Operadores de condición: Equals, NotEquals, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, Like (= ILIKE), Contains (= ILIKE %value%), IsNull, IsNotNull, In (= ANY(...)).

Trampas: un trigger que muta el mismo registro que observa se volverá a disparar a sí mismo si usas Update Entity; usa en su lugar Before Update + Execute Script + Entity.field = …. Dos triggers sobre el mismo evento se disparan en orden de prioridad. Los eventos desactivados siguen apareciendo en la lista, el conmutador Enabled es independiente de la configuración del trigger.

Script Modules

Scripts C# reutilizables compilados en tiempo de ejecución por Roslyn. Aceptan parámetros, consultan la base de datos mediante el helper Db, devuelven un valor. Invocables desde Business Events, Scheduled Events y directamente desde el front-end.

Flujo de trabajo habitual: haz clic en Create. El editor se abre con las pestañas Edit, Parameters, Test, JSON. Da nombre al módulo (PascalCase, por ejemplo RecalcOpenRevenue). En la pestaña Parameters, haz clic en Add por cada parámetro (Name, Type, Required, Default Value). Opciones de tipo: string, int, decimal, double, bool, DateTime. Vuelve a la pestaña Edit, escribe C#. IntelliSense está activado.

Panel de prueba: la pestaña Test muestra tus entradas de parámetros a la izquierda y un botón Run (icono PlayArrow). Haz clic en Run; el lado derecho muestra un indicador de éxito o error, el log de salida y el valor de retorno (formateado como JSON).

El helper Db, consulta la referencia completa en APIs invocables desde scripts más abajo.

Llamar desde un Business Event: añade una acción Execute Script cuyo cuerpo llame al módulo:

await Modules.CallAsync("RecalcCompanyOpenDealValue", new Dictionary<string, object?> {
    ["company_id"] = Entity.company_id
});

Llamar desde un Scheduled Event: crea un evento con el trigger On Schedule, establece el cron, añade una acción Execute Script con el mismo cuerpo Modules.CallAsync.

Registro: cada invocación escribe una entrada en Event Logs con el tiempo de ejecución, parámetros y resultado (o error).

Trampas: siempre await las llamadas a Db, olvidarlo compila pero devuelve un Task. Los retornos se pasan como objeto (no los serializa el framework como JSON); para consumo desde el front-end, devuelve objetos anónimos con campos primitivos. La sugerencia al carácter de confirmación está intencionadamente desactivada en el editor; usa Tab para aceptar.

Frontend Templates

Fragmentos de UI reutilizables escritos en TSX que el editor de páginas puede insertar en una página. Útil cuando las plantillas configuradas no son suficientes, gráficos a medida, diseños inusuales, integraciones de widgets de terceros.

Flujo de trabajo habitual: haz clic en Create, da nombre a la plantilla, escribe un componente TSX en el editor. El componente recibe props: record (la fila actual cuando va embebida en un formulario de detalle), refresh (volver a obtener los datos), y unos cuantos helpers. Guarda. Referencia la plantilla desde un campo del Page Editor.

Empaquetable: las plantillas fluyen por el sistema de Package como cualquier otro objeto de configuración, se incluyen en cascada cuando una página que las usa se añade a un paquete.

Trampas: el TSX está en sandbox, no puedes importar paquetes npm arbitrarios. Cíñete a React + los helpers proporcionados.

PDF Templates

HTML + plantilla Scriban + un pequeño script C# de datos que renderiza a un PDF (Chromium headless). Cada plantilla es invocable por nombre desde un script vía REST o directamente desde un botón en una Page (con un pequeño adaptador, ver más abajo).

Flujo de trabajo habitual: haz clic en + Create. El editor se abre con campos de texto Name y Description en la parte superior, cinco pestañas en el lado izquierdo (HTML Template, Data Script, Settings, Params, JSON) y un panel PDF Preview siempre visible a la derecha. Las pestañas Data Script y HTML Template abren cada una un editor Monaco en la mitad izquierda; la vista previa se vuelve a renderizar cuando guardas (botón verde Update arriba a la derecha).

Declara parámetros en la pestaña Params mediante el botón + Add (Name, Type, Required, Default Value). La etiqueta de la pestaña se actualiza a Params (n) con el recuento. Formato de página / orientación / márgenes / escala / cabecera / pie viven en la pestaña Settings.

Forma del data script (consulta el tutorial de PDF Invoices para uno real):

var deal = await Db.GetAsync("deal", deal_id);
var lines = await Db.From("deal_line")
    .Where("deal_id", "=", deal_id).ToListAsync();

return new {
    InvoiceNumber = $"INV-{deal.id:D6}",
    Lines = lines.Select(l => new { l.description, l.quantity })
};

Forma de la plantilla HTML (Scriban, similar a Liquid/Handlebars):

<h1>{{ InvoiceNumber }}</h1>
<table>
  {{ for line in Lines }}
  <tr><td>{{ line.description }}</td><td>{{ line.quantity }}</td></tr>
  {{ end }}
</table>

Panel de vista previa: siempre visible en el lado derecho del editor. Se vuelve a renderizar cuando guardas (botón verde Update). Incluye una columna de miniaturas para PDFs multipágina y una barra de herramientas de visor integrada (zoom, rotar, descargar, imprimir). Los botones con icono en la parte superior derecha del editor conmutan la visibilidad del panel de vista previa y la edición a pantalla completa.

Llamarla desde fuera del editor:

  • POST /api/v1/pdf-templates/{id}/preview - renderiza por ID, devuelve application/pdf. Usado por el panel de vista previa del editor.
  • POST /api/v1/pdf-templates/generate/{name} - renderiza por nombre, devuelve application/pdf.
  • POST /api/v1/pdf-templates/generate/{name}/base64 - igual pero devuelve { "data": "<base64>" }.

Pasa los valores de los parámetros en el cuerpo JSON. La autenticación es el token Bearer estándar.

Limitación de la prueba: tanto el endpoint generate como el endpoint {id}/preview (usado por el panel de vista previa del editor) devuelven 403 en modo de prueba, el renderizado de PDF está completamente restringido. Puedes seguir creando la plantilla (data script, HTML, ajustes, parámetros) y guardarla; simplemente no verás un PDF renderizado hasta que estés en un entorno de pago.

Nota sobre el renderizado invocable desde scripts: el contexto de scripting C# no expone un global Pdf. Para renderizar un PDF desde un Script Module hoy, llama al endpoint REST generate mediante HttpClient. (El autocompletado del editor de scripts anuncia Pdf.RenderAsync por compatibilidad futura; el binding en tiempo de ejecución aún no está conectado.)

Trampas: Scriban escapa HTML por defecto. Los saltos de página están guiados por CSS, page-break-before: always en una sección. Las fuentes disponibles en el servidor de renderizado se limitan a las fuentes del sistema más las familias Noto y Liberation, embebe la tuya vía base64 @font-face si necesitas una fuente de marca.

Scheduled Events

El mismo sistema de Event Trigger que Business Events, con el trigger On Schedule marcado. Úsalo para recálculos nocturnos, resúmenes semanales, limpiezas periódicas, informes mensuales.

Flujo de trabajo habitual: crea un Business Event con el trigger On Schedule (en lugar de, o además de, los triggers de cambio de datos). Configura la expresión cron. Añade una acción Execute Script cuyo cuerpo invoque un Script Module mediante Modules.CallAsync(...).

Expresiones cron: 6 o 7 campos al estilo Quartz, consulta Conceptos -> Scheduled Events para patrones comunes. Las horas son hora del servidor (UTC en la pila de producción).

Trampas: un trabajo largo que se pasa de su intervalo cron no se duplica, Quartz no disparará una segunda instancia del mismo trabajo a la vez, el segundo tick se omite. Las ejecuciones fallidas no reintentan automáticamente; integra el reintento en el script.

Packages

Paquetes exportables de objetos de configuración (BEs, páginas, eventos, scripts, plantillas, esquema) con dependencias en cascada. Añadir una Page a un paquete arrastra automáticamente todo lo que esa página referencia.

Flujo de trabajo habitual (exportar): haz clic en Create, da nombre y versión al paquete. Añade los objetos de nivel superior que te interesan, normalmente un puñado de páginas. Abre el panel de elementos enlazados para ver la lista en cascada (lo que el paquete contendrá realmente). Desmarca lo que quieras omitir. Decide si incluyes datos (conmutador al exportar). Haz clic en Export, obtienes un ZIP.

Flujo de trabajo habitual (importar): en el entorno destino, abre Packages, haz clic en Import, sube el ZIP. La plataforma muestra qué se añadirá o actualizará. Los deltas de esquema no se aplican automáticamente, si el paquete referencia una columna que no existe en el destino, la importación falla.

Trampas: los identificadores son por nombre, no por ID numérico. Una BE llamada "customer" en origen se vincula a una BE llamada "customer" en destino, renombra cualquier lado y el enlace se rompe.

Third-Party Data Connections

Conexiones a bases de datos externas (Postgres, SQL Server, MySQL, otras) que importan tablas según un horario. Las tablas importadas se comportan como tablas nativas, aparecen en Schema Designer y pueden ser referenciadas por Business Entities, Pages y Scripts.

Flujo de trabajo habitual: crea una conexión (cadena de conexión, prueba, guarda). Lista las tablas remotas disponibles; elige cuáles importar. Por cada tabla importada, establece el modo de sincronización (Full / Delta) y el disparador de sincronización (cron / manual / intervalo fijo).

Replicado, no federado: los datos importados viven en tu propia omnicore-db. Las lecturas son rápidas (Postgres local). Contrapartida: el desfase entre sincronizaciones.

Columnas gestionadas por la aplicación: marca algunas columnas como "managed by Archestack", no se sobrescribirán por sincronizaciones futuras. Útil cuando quieres anotar los datos del proveedor con tus propias banderas de estado.

Trampas: la primera sincronización de una tabla grande puede tardar un rato. Las sincronizaciones delta requieren una columna que el conector pueda usar como marca de agua, normalmente una marca temporal modified_at.

AI Builder

Un asistente guiado donde describes una funcionalidad en lenguaje natural y el asistente genera un plan de construcción, adiciones de esquema, cambios de BE, páginas, eventos, que revisas línea por línea y aplicas. La generación del plan la hace un LLM externo de tu elección (Claude, ChatGPT, Gemini), el asistente compone un prompt autocontenido para que pegues en él y acepta la respuesta JSON de vuelta. Ya no hay un asistente de chat integrado en la aplicación.

Flujo de trabajo habitual: describe la funcionalidad en 1-3 frases ("add support for service appointments, each one has a date, a customer, a vehicle, and a status"). Haz clic en Generate prompt, pégalo en tu LLM externo, pega la respuesta JSON de vuelta en el paso Import, haz clic en Review. Desmarca lo que no quieras. Haz clic en Apply.

Funciona bien para: funcionalidades con forma de CRUD, automatizaciones simples, extensiones sencillas a entidades existentes.

Menos bien para: reglas de negocio no obvias, flujos complejos de varios pasos. El plan es un punto de partida, revísalo como la PR de un desarrollador junior.

Trampas: aplicar es irreversible. Para funcionalidades arriesgadas, haz una copia de seguridad primero (o aplica en un entorno de prueba, evalúa, y luego reconstruye en producción a partir del Package resultante).

Object Browser

Vista cruda de cada tabla nativa e importada, añade, edita, elimina filas directamente. Incluye una importación CSV para carga masiva.

Cuándo recurrir a él:

  • Cargar datos de referencia en masa antes de que existan las páginas (países, monedas, enums de estado).
  • Depuración, echar un vistazo a lo que hay realmente en una tabla cuando una página se comporta mal.
  • Ediciones administrativas rápidas a datos que aún no están expuestos vía una Page.
  • Importación CSV: elige una tabla, sube un archivo, mapea columnas a campos, vista previa, confirma.

Trampas: Object Browser sí dispara Business Events (pasa por la misma ruta de escritura EntityService que el front-end). La excepción es el SQL directo vía Database Deployments, que se salta todo.

Dependency Graph

Un mapa de solo lectura de cómo los objetos de configuración de tu entorno se referencian entre sí. Ocho tipos de recurso aparecen como nodos, coloreados por tipo: Business Entities, Pages, Event Triggers, Script Modules, PDF Templates, Frontend Templates, Scheduled Events y Packages. Una arista entre dos nodos significa que un recurso depende del otro. Recurre a él para responder a las preguntas "¿qué se rompería si elimino esto?" y "¿hay algo sin usar aquí?".

Qué significa una arista: una Page vinculada a una Business Entity, un Event Trigger registrado en una Business Entity, un paso de acción de Page que dispara un Event Trigger, un script que llama a otro Script Module vía Modules.CallAsync("Name"), un script que renderiza un PDF Template, un Scheduled Event que dispara un Event Trigger, o un Package que contiene un recurso. Es una vista de design-time, refleja lo que tu configuración cablea junto, no el tráfico de ejecución en vivo.

Dos tipos de problema se hacen destacar visualmente:

  • Loose ends (huérfanos) - nodos a los que nada referencia y que no referencian nada. Se recogen en una banda "Loose ends" con borde rojo en la parte inferior del lienzo. Un Script Module que nadie llama; una Page vinculada a nada.
  • Dangling references - dibujadas como aristas discontinuas rojas. La referencia existe en la configuración pero su destino no se puede encontrar, por ejemplo un paso de acción de Page que todavía apunta a un Event Trigger eliminado, o un script que llama a un Script Module por un nombre que ya no existe.

Flujo de trabajo habitual: abre Dependency Graph desde la sección Tools de la sidebar. El panel izquierdo tiene una búsqueda por nombre, un selector Layout (Left to right, Top to bottom, By type, Force-directed), los interruptores Group by package, Show loose ends y Show edges, y una lista de comprobación por tipo. Haz clic en un nodo para entrar en modo de enfoque: ese nodo y sus vecinos directos permanecen brillantes mientras el resto del grafo se atenúa, de modo que puedes rastrear exactamente qué toca un recurso. Haz clic en el fondo del lienzo para borrar el enfoque. Cada nodo lleva un pequeño botón de abrir en pestaña nueva que salta directamente al editor de ese recurso. La línea de estadísticas en la parte superior informa del número de nodos, aristas, huérfanos y dangling references.

Group by package: envuelve los miembros de cada package en una caja discontinua etiquetada, para que puedas ver un package como una unidad y detectar los recursos que arrastró por cascada.

Trampas: el grafo se construye bajo demanda a partir del estado actual, usa el botón de refresco después de cambiar recursos en otro sitio. Las aristas derivadas de scripts se encuentran escaneando los cuerpos de script en busca de la forma de llamada literal Modules.CallAsync("Name"), un módulo invocado mediante un nombre calculado no mostrará una arista. Una dangling reference siempre vale la pena corregirla; un huérfano a menudo no, un Script Module recién creado cuenta como huérfano hasta que algo lo llama.

Event Logs

El rastro de auditoría de todo lo que la plataforma ha ejecutado, Business Events, Script Modules, Scheduled Events, sincronizaciones de terceros.

Flujo de trabajo habitual: abre Event Logs, establece Status a Failed, haz clic en una entrada. El panel de detalle muestra el payload (estado del registro en el momento, parámetros pasados), la excepción con la traza de pila y el tiempo.

Logs y Charts: la página tiene dos pestañas que comparten una misma fila de filtros. Logs es la tabla de arriba. Charts es un panel operativo sobre el mismo filtro: el volumen a lo largo del tiempo apilado por estado, una tendencia de la tasa de éxito, un desglose por source, los events que más fallan y los más lentos, la duración media y máxima a lo largo del tiempo, un desglose por tipo de trigger, y un mapa de calor de día de la semana por hora del día, con los totales principales arriba. Ambas pestañas comparten un rango de fechas Desde / Hasta (con los preajustes Últimas 1h / 24h / 7d); la pestaña Charts elige automáticamente un intervalo de tiempo adecuado para el rango y puede actualizarse a intervalos para monitoreo en vivo.

Notificaciones en tiempo real: el icono de campana en la barra superior de la aplicación de administración expone los fallos no leídos vía SignalR.

Trampas: el log puede crecer mucho. Las entradas antiguas se conservan para siempre por defecto; si el disco es una preocupación, configura un Scheduled Event que pode las entradas más antiguas que N días.

User Management

Crea, invita y gestiona usuarios; asigna roles del realm. Envuelve la API REST de administración de Keycloak.

Roles:

  • admin - todo; puede gestionar todos los Business Units, usuarios, roles, ajustes del sistema.
  • owner - gestiona sus propios Business Units, puede editar páginas de administración y de tiempo de ejecución.
  • editor - puede ver y editar páginas de tiempo de ejecución, pero no cambiar la configuración de schema/BE/event.
  • user - solo lectura en páginas de tiempo de ejecución.

Trampas: degradar a un usuario de admin a mitad de sesión no lo expulsa, el cambio se aplica en el siguiente refresco de token (típicamente en menos de un minuto).

Business Units

Grupos respaldados por Keycloak que restringen la visibilidad de recursos individuales. Consulta Conceptos -> Business Units para el modelo.

Flujo de trabajo habitual: crea un BU. Invita miembros por email y elige el rol. Asigna recursos yendo a cada BE / Page / Trigger o vía el panel de elementos enlazados del BU.

Asignaciones en cascada: cuando asignas una Page a un BU, la asignación se propaga a la BE a la que la página se vincula.

Trampas: el BU activo de un usuario se establece en el selector de BU de la barra superior y persiste en localStorage. Los usuarios nuevos por defecto están en su primer BU; si una página de lista está sospechosamente vacía, comprueba qué BU está activo.

Database Backups

pg_dump nocturno de las tres bases de datos. Disparo manual / subida / restauración desde la interfaz.

Flujo de trabajo habitual: Trigger now -> Download -> Upload -> Restore. El horario por defecto es nocturno a las 03:00 UTC.

Limitación de la prueba: los Backups están desactivados en el entorno de prueba.

Branding

Cambia el nombre mostrado de la aplicación, el logotipo (claro + oscuro) y el esquema de color primario, aplicado en tiempo de ejecución, sin recompilar.

Flujo de trabajo habitual: establece el nombre de la aplicación, sube un logo claro y un logo oscuro (PNG/JPG/SVG, ≤ 10 MB), elige un color primario de marca. Guarda. Refresca el navegador.

Trampas: el favicon no se sobrescribe con Branding, va empaquetado en la SPA.

Translations

Editor clave/valor para las cadenas de UI de la aplicación. Añade nuevos locales, sobrescribe las etiquetas por defecto por cliente.

Flujo de trabajo habitual: elige un locale, busca una clave, escribe un nuevo valor, guarda. Refresca la aplicación de administración.

Trampas: las traducciones se cargan una vez por sesión; los usuarios con una pestaña obsoleta no ven los cambios hasta que refresquen. Algunas etiquetas (las cabeceras de columna de las BEs) vienen de la configuración de la BE, no de las traducciones.


APIs invocables desde scripts

La superficie exacta que los scripts pueden invocar. Verificada contra el código fuente, cada método de abajo existe realmente.

Globales

Cada script obtiene estas variables de nivel superior (no existen otros nombres en el ámbito del script):

  • Entity - dynamic ExpandoObject. Registro actual. Accede a los campos con Entity.column_name. Mutar Entity en un trigger Before* persiste los cambios como parte de la escritura en curso.
  • OldEntity - dynamic ExpandoObject. Registro anterior (triggers de update + delete). De solo lectura.
  • Log - ILogger. Log.LogInformation("…"), Log.LogWarning(…), Log.LogError(…). Escribe en los logs del servidor y en la entrada del Event Log.
  • Db - DbHelper. Acceso a base de datos (ver más abajo).
  • Modules - ModulesHelper. Llama a otros Script Modules: await Modules.CallAsync("ModuleName", new Dictionary<string, object?> { ["param"] = value }). Devuelve Task<object?>.
  • Pdf - ScriptPdfHelper. Renderiza una PDF Template almacenada por nombre: await Pdf.GenerateAsync("invoice", new Dictionary<string, string> { ["id"] = Entity.id.ToString() }) devuelve byte[]; await Pdf.GenerateBase64Async(…) devuelve el mismo payload codificado en base64. Los parámetros se reenvían al data script de la plantilla como variables locales tipadas como string.

No hay User, Http, Email, Templates ni ningún otro global.

Db - métodos de fila única + escritura

Todos async, todos sobre el objeto Db de nivel superior:

  • await Db.GetAsync(string table, object id) - obtiene una fila por la clave primaria. Devuelve dynamic? (o null si no se encuentra).
  • await Db.CreateAsync(string table, object values) - inserta. values puede ser un objeto anónimo o un Dictionary<string, object?>. Dispara los triggers Before/After Create. Devuelve int (el nuevo ID).
  • await Db.UpdateAsync(string table, object id, object values) - actualiza por PK. Dispara los triggers Before/After Update.
  • await Db.DeleteAsync(string table, object id) - elimina por PK. Dispara el trigger Before Delete.

Db - constructor de consultas fluido

Empieza con Db.From(table). Encadena filtros, luego un terminal:

  • .Where(string column, string op, object? value = null) - añade una cláusula WHERE. Operadores: =, != / <>, >, >=, <, <=, LIKE, ILIKE, IN, IS NULL, IS NOT NULL. Encadena varias llamadas a .Where(...) para AND.
  • .OrderBy(string column, bool desc = false) - establece ORDER BY. Solo se permite uno por consulta.
  • .Limit(int limit) - máximo de filas. El valor por defecto es 1000.

Terminales (cada uno ejecuta la consulta):

  • await ...ToListAsync() - devuelve List<dynamic>.
  • await ...FirstAsync() - devuelve dynamic? (la primera coincidencia o null). Establece temporalmente el límite a 1.
  • await ...CountAsync() - devuelve int.

No existe SumAsync, MaxAsync, FirstOrDefaultAsync ni SingleAsync. Para agregaciones, recupera con ToListAsync() y reduce en C#.

Ejemplo trabajado

// Get a row by ID, query a list, calculate a sum, update.
var order = await Db.GetAsync("orders", 42);
if (order == null) return false;

var items = await Db.From("order_items")
    .Where("order_id", "=", order.id)
    .OrderBy("id")
    .ToListAsync();

decimal totalAmount = 0;
foreach (var item in items) {
    totalAmount += (decimal)(item.amount ?? 0);
}

await Db.UpdateAsync("orders", 42, new {
    total = totalAmount,
    updated_at = DateTime.UtcNow
});

return true;

Modo de simulación

Durante la simulación de trigger (el botón Run Simulation en un Business Event), la plataforma establece Db.SimulationMode = true. Todas las operaciones de escritura (CreateAsync, UpdateAsync, DeleteAsync) se ejecutan dentro de una transacción PostgreSQL que siempre se revierte al final. Las operaciones de lectura funcionan con normalidad. Esto es lo que hace seguro usar Run Simulation contra datos reales.

Modules

  • await Modules.CallAsync(string moduleName, Dictionary<string, object?>? parameters = null) - llama a otro Script Module por nombre. Los parámetros se convierten en variables tipadas en el script del módulo llamado. Devuelve Task<object?> (lo que el módulo haya devuelto).

Expresiones de plantilla (acciones de Business Event)

La configuración de acciones usa llaves {{ … }}. La lista completa de tokens válidos:

  • {{ Entity.column_name }} / {{ Entity.id }}
  • {{ OldEntity.column_name }}
  • {{ now() }} / {{ getdate() }} - datetime ISO 8601 UTC
  • {{ today() }} - cadena de fecha
  • {{ guid() }} / {{ newid() }} - GUID nuevo
  • {{ year() }}, {{ month() }}, {{ day() }}, {{ timestamp() }} - segundos Unix
  • {{ empty() }}, {{ null() }}
  • Agregados dentro de un contexto de join: {{ SUM(column) }}, {{ AVG() }}, {{ COUNT() }}, {{ MIN() }}, {{ MAX() }}
  • Aritmética: {{ Entity.quantity * Entity.price }} - evaluada tras la sustitución mediante DataTable.Compute()

No hay token {{ user.email }} ni ningún otro token con ámbito de usuario.