Construye tu primera aplicación
Al final de este tutorial tendrás un pequeño CRM con tres tablas (company,
contact, deal), una página de lista-y-detalle para cada una, un
Business Event que sella automáticamente el updated_at del deal, y un Script
Module que recalcula el valor total de oportunidades abiertas de la empresa cada vez que cambia
una deal. Cada concepto de Conceptos esenciales aparece al menos
una vez.
Asume que has terminado Primeros pasos y tienes un entorno Archestack abierto en otra pestaña. Tiempo total: unos 45 minutos si lees con atención, 25 si lees por encima.
Convenciones usadas aquí: los nombres de tablas y columnas son snake_case (la
plataforma sugiere esta convención en el campo "Table name" de Schema Designer). Los ejemplos
de código usan la API Db real, consulta Referencia ->
APIs invocables desde scripts para la superficie completa.
Paso 1 - Diseña el esquema (10 min)
Abre Schema Designer y añade tres tablas.
Para cada tabla: haz clic en Add Table en la barra de herramientas, escribe el
nombre en el campo "Table name" del diálogo, haz clic en Create. La nueva
tabla aparece en el lienzo con una clave primaria SERIAL id autogenerada
(bloqueada). El panel derecho se abre en la pestaña Columns, añade columnas adicionales desde
el cuadro "Add column" con borde discontinuo en la parte inferior. Haz clic en cada columna
para expandirla y ajusta los conmutadores (PK / NULL / UQ), Length y Default value.
Tabla: company
id· SERIAL, PK (auto)name· VARCHAR(200), obligatorioindustry· VARCHAR(80), nullableopen_deal_value· DECIMAL, default0- se mantiene en sincronía vía el script del Paso 5created_at· TIMESTAMPTZ, defaultnow()
Tabla: contact
id· SERIAL, PK (auto)first_name,last_name· VARCHAR(100), obligatorioemail· VARCHAR(200), obligatorio, UQ activadocompany_id· INTEGER, obligatorio - clave foránea, se configura a continuación
Tabla: deal
id· SERIAL, PK (auto)company_id· INTEGER, obligatorio - clave foránea, se configura a continuacióntitle· VARCHAR(200), obligatoriostage· VARCHAR(40), obligatorio, default'New'- valores: New, Qualified, Proposal, Won, Lostamount· DECIMAL, obligatorio, default0created_at,updated_at· TIMESTAMPTZ, defaultnow()
Claves foráneas
Las claves foráneas viven en la pestaña Relations del panel derecho.
Selecciona la tabla contact, cambia a Relations, desplázate a la sección "Add
relationship":
- FK column (on this table) =
company_id - Relation type =
One-to-Many - References -> table =
company - References -> column =
id - Haz clic en Add Relationship.
Repite para la tabla deal (su company_id -> company.id).
company / contact / deal tendrá la misma forma con dos líneas FK apuntando a company.)Despliegue
Haz clic en Deploy en la barra de herramientas de Schema Designer. Aterrizas en
la página de configuración del despliegue. Cambia a la pestaña Generated SQL,
deberías ver tres sentencias CREATE TABLE más las restricciones FK. Haz clic en
Deploy arriba a la derecha.
Qué verás si funciona: la fila de despliegue en Database Deployments -> Overview pasa de "Executing" a "Succeeded" en unos pocos segundos. Las tres tablas aparecen en la lista de tablas de Object Browser.
Si el despliegue falla por restricciones FK: el Generated SQL emite las tablas en el orden en que se guardaron. Abre la pestaña SQL y reordena para quecompanyse cree antes decontactydeal, luego vuelve a desplegar. O haz clic en Regenerate tras reordenar manualmente las tablas en el lienzo de Schema Designer.
Paso 2 - Compón las Business Entities (10 min)
Cada página necesita una Business Entity a la que vincularse. Abre Business Entities y haz clic en Create para cada una. Pulsa Run Preview tras cada guardado para confirmar que las columnas vuelven correctamente.
BE: company
Entity Name company, Master Table company, Label Column
name. Guarda.
Haz clic en Add Join dos veces para añadir dos joins agregados:
-
Join a
contact: From columncompany.id, To columncontact.company_id. Activa Aggregate Mode. Elige la columnaid, establece Aggregate Function =COUNT, nómbralocontact_count. -
Join a
deal: From columncompany.id, To columndeal.company_id. Aggregate Mode activado. Elige la columnaid, funciónCOUNT, nómbraloopen_deal_count. (Filtraremos a deals "abiertas" mediante un Business Event en el Paso 5; por ahora cuenta todas las deals.)
BE: contact
Entity Name contact, Master Table contact, Label Column
email. Añade un Join a company vía
contact.company_id -> company.id, expón la columna name como
company_name. (No actives Aggregate Mode aquí, es un join normal.)
BE: deal
Entity Name deal, Master Table deal, Label Column
title. Añade un Join a company vía
deal.company_id -> company.id, expón la columna name como
company_name.
Verifica: en cada BE, haz clic en Run Preview. Verás una rejilla vacía (aún sin datos) con las cabeceras de columna que definiste. Si falta la cabecera de una columna unida, probablemente olvidaste marcar la columna en el selector de columnas del join. Vuelve a abrir el join, marca la columna, guarda, vuelve a ejecutar la vista previa.
Paso 3 - Construye las páginas (10 min)
Abre Page Editor. Para cada Business Entity, haz clic en Create:
- Companies - Page Name
Companies, Page Route/companies, Business Entitycompany. La pestaña Visual autogenera una lista y un formulario de detalle. En el formulario de detalle, haz clic en Add Tab dos veces para añadir las pestañas Contacts y Deals. En cada pestaña, añade una sección RelatedGrid vinculada a la BE contact / deal con el filtro de join establecido a company.id = id del registro actual. - Contacts - Page Name
Contacts, Page Route/contacts, Business Entitycontact. El campocompany_iddel formulario de detalle autogenerado aparecerá como un número, cambia su Type a Select y establece el autocompletado Entity acompanypara que los usuarios elijan una empresa por nombre. - Deals - Page Name
Deals, Page Route/deals, Business Entitydeal. Mismo tratamiento paracompany_id(TypeSelect, Entitycompany). Parastage, deja Type comoTextpor ahora, los valores pueden forzarse vía un eventoValidatemás adelante si quieres.
Para cada página, cambia el conmutador Published en la cabecera superior a ON.
Las páginas aparecen en la barra lateral bajo una sección APPLICATION
(agrupadas por categoría). Añade una Company, luego un Contact vinculado a esa Company, luego
una Deal, confirma que las relaciones se renderizan correctamente. La página Companies debería
mostrar ahora 1 en contact_count.
company. Nota: la sección APPLICATION de la barra lateral aparece en cuanto publicas, las páginas se agrupan bajo una cabecera de categoría (aquí: CUSTOMER PORTAL). El panel de detalle renderiza migas de pan, un botón Edit (arriba a la derecha), iconos de lápiz por campo para ediciones en línea, pestañas adicionales en la parte superior (Details / Vehicles / etc.) y debajo del formulario una sección de rejilla relacionada que extrae filas de otra BE filtrada por el registro actual. La insignia de la campana ("1") muestra que hay un fallo de Event Log no leído. Si algo se ve vacío: la causa más común es olvidar cambiar el conmutador
Published, si está OFF, navegar a /companies en la barra lateral no muestra
nada. Cámbialo a ON y refresca.
Paso 4 - Limpia un campo al guardar con un Business Event (5 min)
Los Business Events ejecutan un poco de lógica en cuanto se escribe un registro. El más
pequeño y útil: recortar los espacios sobrantes del title de un deal en cada
guardado, para que " Acme renewal " quede como "Acme renewal".
Vamos a montarlo.
Nunca necesitas una regla paracreated_at/updated_at/created_by/updated_by. Archestack añade esas cuatro columnas de auditoría a cada tabla y las sella en cada insert y update automáticamente, así que un trigger de "sellar updated_at" solo duplicaría el trabajo que la plataforma ya hace.
- Abre Business Events -> Create.
- Rule Name:
Normalize deal.title. Activa Enabled. - Business Entity:
deal. Triggers: marca Before Update. - Sin condiciones, se dispara en cada update.
-
Añade una acción: elige Execute Script. El cuerpo del script de la acción:
string title = Entity.title; Entity.title = title?.Trim(); -
Haz clic en la pestaña Simulate -> elige cualquier Deal existente -> haz clic en Run
Simulation. El panel de salida muestra que el script se ejecutó correctamente y a qué
se estableció
Entity.title. (No ocurre ninguna escritura real, la simulación se ejecuta en una transacción revertida.) - Guarda. Prueba editando un Deal cuyo título tenga espacios al principio o al final, el título debería volver recortado en cada guardado.
¿Por qué Before Update + Execute Script en lugar de una acción que
"establezca un campo"? Archestack no tiene una acción discreta "Set field". La forma
de mutar un registro desde un trigger es asignar a Entity.column_name dentro de
un Execute Script en un momento Before*. La plataforma persiste el Entity modificado
como parte de la escritura en curso, sin consulta extra, sin riesgo de recursión.
Paso 5 - Recalcula company.open_deal_value con un Script Module (10 min)
Un total computado como "valor de deal abierta por empresa" es demasiado dinámico para que una columna almacenada permanezca exacta a mano. Lo mantendremos en sincronía con un pequeño script que se vuelve a ejecutar cada vez que cualquier deal de una empresa se inserta, actualiza o elimina.
Escribe el script
Abre Script Modules -> Create. Nómbralo
RecalcCompanyOpenDealValue.
Cambia a la pestaña Parameters. Haz clic en Add. Name
company_id, Type int, Required marcado.
Vuelve a la pestaña Edit. Cuerpo:
var openDeals = await Db.From("deal")
.Where("company_id", "=", company_id)
.Where("stage", "!=", "Won")
.Where("stage", "!=", "Lost")
.ToListAsync();
decimal total = 0;
foreach (var d in openDeals) total += (decimal)(d.amount ?? 0);
await Db.UpdateAsync("company", company_id, new { open_deal_value = total });
return total;
Cambia a la pestaña Test. Escribe un company_id real de tu
página Companies en el campo de parámetro, haz clic en Run. La salida muestra
el valor de retorno (el total) y un indicador de éxito. Recarga la página Companies,
open_deal_value en esa empresa ya está sincronizado.
Tropiezos comunes:
- Olvidar
awaiten.ToListAsync(), el script compila peroopenDealsacaba conteniendo unTask, no las filas. El error en el panel Test mencionará "cannot be enumerated".- Usar
Db.Queryen lugar deDb.From, no existe un métodoQuery. Cíñete aDb.From(table)para consultas encadenadas yDb.GetAsync(table, id)para una sola fila por PK.- Probar
.SumAsync(...), no existe. Recupera + suma en C#, como arriba.
Conéctalo a un Business Event
- Abre Business Events -> Create.
- Rule Name:
Recalc company.open_deal_value on deal change. Enabled activado. - Business Entity:
deal. Triggers: marca After Create, After Update y Before Delete. - Sin condiciones, se dispara en cada cambio.
-
Añade una acción: Execute Script. Cuerpo:
var entity = OldEntity != null && OldEntity.company_id != null ? OldEntity // for delete & update, OldEntity has the original company_id : Entity; // for create, only Entity is populated await Modules.CallAsync("RecalcCompanyOpenDealValue", new Dictionary<string, object?> { ["company_id"] = entity.company_id }); - Guarda. Edita el
amountde una Deal y recarga la página Company, el total se mantiene sincronizado.
¿Por qué llamarlo a través de Modules en lugar de inlinear la lógica en el script de este trigger? Dos razones. Primero, el recálculo es reutilizable, también puedes llamarlo desde un Scheduled Event (recuperación nocturna) o directamente desde el front-end. Segundo, aísla la lógica en un único lugar con nombre, más fácil de probar, más fácil de encontrar después.
Paso 6 - Empaquétalo como un package (5 min)
Has construido un mini-CRM real y funcional. Empaquétalo para poder moverlo a otro entorno.
- Abre Packages -> Create. Nómbralo
MiniCRM v1. - Añade las tres Pages. Abre el panel de elementos enlazados para ver la cascada: el paquete arrastrará las BEs que las páginas referencian, las tablas fuente de las que esas BEs leen, más los dos Business Events y el Script Module que las tocan.
- Desmarca lo que prefieras omitir, normalmente lo mantendrías todo para un tutorial como este.
- Haz clic en Export -> descarga el ZIP.
Importar el ZIP en un entorno distinto recrea todo (asumiendo que el esquema se ha desplegado primero). Así es como los socios envían configuraciones específicas de un vertical y como promoverías el trabajo de una prueba a un entorno de pago más adelante.
Resumen - lo que acaba de ocurrir
- Diseñaste un esquema, lo desplegaste como tablas PostgreSQL reales y nunca escribiste SQL a mano.
- Expusiste esas tablas a través de Business Entities, eligiendo qué exponer, uniendo etiquetas para usabilidad, agregando recuentos de filas relacionadas.
- Compusiste tres páginas solo a partir de configuración, con pestañas embebidas y búsqueda.
- Añadiste un comportamiento con un Business Event (auto-sellado) y un cálculo más complejo con un Script Module, ocho líneas de C# que se ejecutan cada vez que los datos cambian.
- Empaquetaste todo como un Package, portable entre entornos.
El mismo ciclo escala a docenas de tablas y cientos de páginas. La Referencia cubre el resto de la plataforma, branding, sincronización de datos de terceros, andamiaje asistido por IA, pero la habilidad esencial es la que acabas de aprender.
Hacia dónde ir después
- Genera facturas en PDF - añade una plantilla de factura imprimible a las Deals que acabas de construir. Mismos datos, nuevo canal de salida.
- Scheduled Events - extiende el script de recálculo con una recuperación nocturna que se ejecute sin acción del usuario.
- AI Builder - apúntalo a "add support for service appointments, date, customer, vehicle, status" y obsérvalo andamiar una funcionalidad similar.