From 69bcd297f87804304c260402a6449fcaebea7c20 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 16:30:35 +0000 Subject: [PATCH 1/2] docs: contexto del sistema PANELS para agentes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Añade un documento copiable con arquitectura, contratos RPC, módulos de negocio, rutas y reglas de no-romper según el código actual. Co-authored-by: alberto.martinez --- docs/contexto-sistema.md | 264 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/contexto-sistema.md diff --git a/docs/contexto-sistema.md b/docs/contexto-sistema.md new file mode 100644 index 0000000..3e71ede --- /dev/null +++ b/docs/contexto-sistema.md @@ -0,0 +1,264 @@ +# PANELS — contexto del sistema (estado actual) + +Documento para pegar en reglas / contexto de un agente. Describe el producto **como está en el código hoy**, no un roadmap. + +Producto: **PANELS** (repo `panels`, paquete raíz `panel-obra`). Escritorio de obra para constructoras (México): padrón, obras, presupuesto, programa de obra, costos, almacén, gafetes, nómina. Multi-tenant. Consola SaaS aparte para operadores de plataforma. + +--- + +## 1. Mapa de repos / carpetas + +| Ruta | Qué es | +|------|--------| +| `api/` | API Deno 2 + Hono. Punto de entrada `api/main.ts`. Puerto **8000**. Prefijo `/v1`. | +| `web-panel/` | Escritorio de obra. Nuxt 3 SPA (`ssr: false`), PrimeVue 4, puerto **3000**. | +| `web-saas/` | Consola operadores. Nuxt 3 SPA, mismo stack, puerto **3001**. | +| `web/` | Reservada para website pública. **No** es el producto. | +| `db/` | Liquibase: `db/platform`, `db/iam`, `db/core`. Provision: `db/provision/`. | +| `docs/` | Ops (`coolify.md`), padrón (`padron-v2.md`), este contexto. | +| `overlays/compose.local.yml` | Solo local: Postgres + Redis + provision. **No** usar en Coolify. | +| `docker-compose.yml` | Producción: `migrate` → `api` → `web-panel` + `web-saas`. | + +Fronts (dev y nginx) proxifican `/v1` a la API. Cookies same-origin. En prod la API **no** lleva FQDN propio. + +--- + +## 2. Stack y runtime + +- **API:** Deno 2.9, Hono 4, `xlsx`, `pdf-lib`, `qrcode`. Sin FFI. Sin Java en el contenedor de tráfico. +- **Migraciones:** Liquibase + JRE 21 en imagen `migrate` (un shot por deploy). **No** corren al arrancar la API. +- **Fronts:** Nuxt 3.16, Vue 3, PrimeVue 4, PrimeIcons, Chart.js (panel). Build → nginx Alpine. +- **Postgres 16** + **Redis 7**. Object storage S3-compatible (**Cloudflare R2**). Fallback disco `/app/data` solo local. +- **CI:** GitHub Actions — provision + Liquibase + aislamiento + `deno check`/`deno test` + build de ambos fronts. + +Dev local: + +```bash +npm run dev:api # api +npm run dev:web # web-panel :3000 +npm run dev:saas # web-saas :3001 +# Docker todo-en-uno: +docker compose -f docker-compose.yml -f overlays/compose.local.yml up --build +``` + +--- + +## 3. Arquitectura de datos (monolito modular) + +Una instancia Postgres **por ambiente**, **dos bases**. Sin FK entre bases ni entre esquemas `iam` y `core`. `tenant_id` es referencia **lógica** validada en aplicación. + +| Base | Esquema | Changelog | Contenido | +|------|---------|-----------|-----------| +| `panels_platform` | `public` | `db/platform/` | Control plane: `tenants`, `platform_users`, `smtp_settings`. Solo rutas `/v1/saas/*`. | +| `panels_product` | `iam` | `db/iam/` | Usuarios/roles/permisos **del tenant** (quien entra a `web-panel`). | +| `panels_product` | `core` | `db/core/` | Negocio: empresas, obras, personal, docs, presupuesto, nómina, gafetes, gastos, almacén, programa de obra. | + +**Dos identidades distintas:** + +- `platform_users` = operadores PANELS (`web-saas`). Rol conceptual `platform_admin`. +- `iam.users` = usuarios de obra. Roles `tenant_admin` y `user`. + +Roles Postgres: `panels_{platform,iam,core}_{owner,app}`. Runtime usa `_app`. Liquibase/bootstrap usan `_owner`. Login por username usa `DATABASE_URL_IAM_OWNER` a propósito (bypassa RLS). + +Redis ACL: `panels_iam_redis` (sesiones) y `panels_core_redis` (cache). + +RLS en `core` e `iam`: el middleware `withCoreScope` (`api/scope.ts`) fija `app.tenant_id` en la transacción. Sin eso, políticas fail-closed devuelven 0 filas. + +Liquibase: **siempre** pasar `--context-filter`. `context="dev"` = demo (tenant ARCTEC `ARCT2608`). Producción: `--context-filter=!dev`. Sin filtro, Liquibase corre **todo**, incluido demo. + +--- + +## 4. Contrato RPC (capa de negocio) + +Lógica de `core` (y parte de `iam`) vive en funciones Postgres `core.fn_*` / `iam.fn_*`. La API **no** debe `db.prepare()` contra tablas de negocio en rutas nuevas; solo `api/rpc.ts` (`callCoreFn` → `SELECT fn($1::jsonb)`). + +Envelope JSON de toda función: + +```json +{ + "ok": true, + "code": "OK", + "layer": "db", + "message": "Mensaje específico en español", + "context": { "fn": "fn_..." }, + "data": {}, + "errors": null +} +``` + +Códigos: `OK`, `CREATED`, `VALIDATION`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `INTERNAL`, `NETWORK`. +`api/http_errors.ts` mapea a HTTP y añade `status`. `layer` puede ser `db` | `api` | `front`. Mensajes genéricos están prohibidos. + +Convenciones SQL: un parámetro `payload jsonb`, `SECURITY INVOKER`, `SET search_path = core` (o `iam`), `GRANT EXECUTE` al rol `_app` en el mismo changeset. Changesets core RPC: `006` (infra) … `030` (programa de obra). + +--- + +## 5. Auth, sesión, permisos + +- Login `POST /v1/auth/login`. Sesión en Redis IAM, cookie. `COOKIE_SECURE` en HTTPS. +- `GET /v1/auth/me`, `POST /v1/auth/logout`, `POST /v1/auth/change-password`. +- Alternativa: header `X-API-Key` **+** `X-Tenant-Id` obligatorio (no ve todos los tenants). +- `web-panel` rechaza `realm === "platform"` y manda al SaaS. +- Primer admin: `api/scripts/bootstrap-admin.ts` + `SEED_PASSWORD` (no seed de hash en Liquibase). + +Permisos (`iam.permissions`): + +| Código | Uso | +|--------|-----| +| `manage_workers` | Padrón / expediente | +| `manage_projects` | Obras | +| `manage_companies` | Empresas | +| `manage_budget` | Presupuesto | +| `manage_payroll` | Nómina / préstamos | +| `manage_documents` | Documentos | +| `manage_users` | Usuarios del tenant | +| `manage_settings` | Config tenant | +| `view_reports` | Tableros | +| `view_expenses` / `manage_expenses` | Gastos y control de costos | +| `view_warehouse` / `manage_warehouse` / `close_warehouse` | Almacén | +| `manage_cost_settings` | Umbrales presupuestales | + +`tenant_admin` tiene todos. `user` de fábrica: workers, documents, reports, view_expenses, view_warehouse. `tenant_admin` no se puede recortar por API. + +Granularidad: gastos/almacén/costos ya chequean permiso; el resto de rutas core a menudo solo `requireCoreAuth`. Evolucionar permisos de forma incremental. + +--- + +## 6. Dominio de negocio (`core`) + +### Empresas y obras + +- `companies`: `kind` `principal` | `sub`, `parent_id`, datos fiscales IMSS/RFC, `tenant_id`. +- `projects`: obra. Status `activo` | `pausado` | `concluido` | `cancelado`. Tema de gafete, logos, contrato, SIROC, `%` impuesto nómina, empresa dueña. + +### Padrón (persona maestra) + +Invariantes (`docs/padron-v2.md`): + +1. No se borra persona: `status=baja`. +2. Solo asignar/importar a proyectos **activos**. +3. Asignación repetida se **reactiva**; al finalizar se guarda `end_date`. Unique `(worker_id, project_id)`. +4. `pipeline_status` **no** es editable. Baja es manual; el resto se recalcula. +5. CURP, RFC, NSS únicos (`citext` CURP/RFC). +6. Expediente obligatorio: foto, INE, CURP, NSS/IMSS, RFC. +7. Color de gafete = código exacto de `risk_levels`; Alto/Medio/Bajo es UI. + +Campos clave worker: nombres, `hire_type`, `work_type` `N` (jornal) | `D` (destajo), `daily_wage`, `needs_badge`, `imss_status` (`sin_alta` | `alta` | `baja_imss`), empresa IMSS. + +Validación MX en `api/mx.ts`. Import Excel: template + `POST /v1/workers/import`. + +Documentos cifrados AES-GCM (`DOCS_KEY` = 64 hex). Storage key + `iv` en tablas; bytes en R2. Tipos con `validity_mode` `none` | `freshness` | `expiry`. Mismo patrón para docs de proyecto y empresa. + +### Gafetes + +Temas, jobs PDF, QR/vCard (`VCARD_BASE`). Pipeline “listo para gafete” derivado. Kanban visualiza ese pipeline. + +### Presupuesto + +Capítulos + partidas (`budget_chapters` / `budget_items`). Import/export Excel, preview. Ligado a proyecto. + +### Programa de obra + +Un programa por proyecto. Periodos semanales. Slots de concepto, montos por partida, avance. Import, wizard, recálculo vs costo. Export Excel. + +### Gastos y control de costos + +`expense_entries` + adjuntos, IVA por periodo, `tenant_cost_settings`. Control: summary / items / chapters / deviations vs presupuesto. + +### Almacén + +Materiales, almacén central y por obra, stock, movimientos, transferencias. Abrir/cerrar almacén de obra. Links material ↔ presupuesto. + +### Nómina + +Dos líneas históricas: + +- Semanas (`payroll_weeks`, sheets, lines): asistencia, assemble/pay, CSV, jornal, líneas admin, destajo (unidades, jobs, cortes). +- Periodos (`payroll_periods` / `payroll_lines`) y settings. +- Préstamos (`loans` / `loan_payments`) + PDFs de recibo. +- Sync de costo de nómina hacia control de costos (`025-rpc-payroll-cost-sync`). + +--- + +## 7. API HTTP (resumen) + +Prefijo `/v1`. CORS credenciales; origins locales 3000/3001 + `CORS_ORIGINS`. + +| Área | Rutas | +|------|--------| +| Salud | `GET /health` — core, platform, redis.iam, redis.core, storage | +| Auth | `/auth/login`, `logout`, `me`, `change-password` | +| SaaS | `/saas/tenants`, SMTP get/put/test, send-access | +| Catálogos / config | `/catalogs`, `/configuracion` | +| Empresas | CRUD + documentos | +| Proyectos | CRUD + documentos + budget nested | +| Workers | list/get/create/patch, validate, pipeline, assign, import, documents, photo | +| Gafetes | `/badge-qr`, `/projects/:id/badge-jobs`, `/badge-jobs` | +| Nómina | `/attendance`, `/payroll/*`, `/destajo/*`, `/loans` | +| Gastos | `/expenses`, `/iva/summary`, `/cost-settings` | +| Costos | `/projects/:id/cost-control/{summary,items,chapters,deviations}` | +| Almacén | `/warehouses`, stock, movements, entries, exits, transfers, open/close | +| Programa | `/projects/:id/work-program` (+ concepts, partidas, vs-cost, import, generate, export, patches de periodos) | +| IAM | `/iam/users`, `/iam/permissions`, `/iam/roles/:role/permissions`, `/me/permissions` | + +Handlers: `main.ts` + `payroll_http.ts`, `expenses_http.ts`, `warehouse_http.ts`, `cost_control_http.ts`, `work_program_http.ts`, `iam_http.ts`. + +--- + +## 8. Fronts + +### `web-panel` — escritorio + +UI tipo escritorio: layout `default.vue` (cinta, árbol, detalle, statusbar). Estado compartido `useDesktop()`. Tema claro/oscuro (`panel-theme`). Auth global; `must_change_password` → `/cambiar-password`. + +Menú: + +| Ruta | Módulo | +|------|--------| +| `/` | Inicio | +| `/proyectos` | Obras / empresas | +| `/presupuesto` | Presupuesto | +| `/programa-obra` | Programa de obra | +| `/control-presupuesto` | Control de costos | +| `/gastos` | Gastos | +| `/almacen` | Almacén | +| `/padron`, `/padron/:id` | Padrón + ficha | +| `/kanban` | Pipeline de personal | +| `/gafetes` | Impresión de gafetes | +| `/nomina` | Nómina | +| `/usuarios` | IAM del tenant | +| `/configuracion` | Config (menú perfil) | +| `/login` | Login | + +También existe `/obras.vue` en pages (legado / no está en el menú principal). + +Cliente HTTP: `composables/useApi.ts` + envelopes en `api-response.ts`. Proxy Nitro `/v1` → `127.0.0.1:8000`. + +### `web-saas` + +Rutas: `/login`, `/` (tenants), `/smtp`. Misma API. No comparte UI con el escritorio. + +--- + +## 9. Reglas para agentes (no romper) + +1. **No SQLite.** Postgres + Redis + R2. +2. **No JOIN/FK** `iam` ↔ `core` ni platform ↔ product. Snapshots desnormalizados (`uploaded_by`, etc.). +3. Schema nuevo = changeset Liquibase nuevo. Nunca editar changeset ya aplicado en ambiente compartido. +4. Negocio nuevo en `core.fn_*` + `callCoreFn`. Envelope + mensaje en español específico. +5. Rutas `core`: `...requireCoreAuth` para RLS. +6. Documentos: cifrar; no guardar plaintext en disco/R2. +7. Padrón: no DELETE de personas; pipeline derivado; unicidad CURP/RFC/NSS. +8. Coolify: solo `docker-compose.yml`. Overlay local no va a prod. +9. Secrets: `.env` gitignored. `DOCS_KEY` 64 hex. Fail-fast en production si faltan secretos. +10. Fronts: SPA + PrimeVue; proxy `/v1`; no llamar la API cross-origin en prod. +11. Tests API: `cd api && deno task check && deno task test`. Front: `npm run build` en `web-panel` / `web-saas`. +12. `web/` no es el panel. Producto = `web-panel` + `web-saas` + `api`. + +--- + +## 10. Deploy (Coolify) + +Por ambiente: 1 Postgres (2 bases), 1 Redis (2 ACL), 1 bucket R2, 4 contenedores (`migrate`, `api`, `web-panel`, `web-saas`). Red Docker `coolify` para hostnames internos UUID (no container id de 12 chars). + +Health: `GET /v1/health` debe reportar core, platform, redis.iam, redis.core y storage (`backend: "s3"` en prod). Detalle ops: `docs/coolify.md`, `db/README.md`, `db/provision/README.md`. From e5ab055c76eccaa20c6d436d68edc53eb23c3f8e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 16:38:06 +0000 Subject: [PATCH 2/2] docs: historias de usuario del producto actual Contexto general en formato HU (roles y capacidades de negocio) para copiar al MD de sistema, sin detalle de arquitectura. Co-authored-by: alberto.martinez --- docs/historias-usuario.md | 144 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 docs/historias-usuario.md diff --git a/docs/historias-usuario.md b/docs/historias-usuario.md new file mode 100644 index 0000000..306ba06 --- /dev/null +++ b/docs/historias-usuario.md @@ -0,0 +1,144 @@ +# PANELS — Historias de usuario (estado actual) + +Contexto de producto para pegar en reglas / sistema. Describe **qué puede hacer el usuario hoy**, no arquitectura ni roadmap. + +PANELS es un escritorio de obra para constructoras en México. Cada cliente (tenant) opera su propia empresa, personal, obras, costos y nómina. Los operadores de PANELS administran tenants desde una consola aparte. + +## Roles + +| Rol | Quién es | +|-----|----------| +| Operador de plataforma | Personal de PANELS. Crea y da acceso a constructoras. | +| Administrador del tenant | Usuario de la constructora con control total (empresas, usuarios, config). | +| Usuario operativo | Personal de la constructora con acceso a módulos según permisos (padrón, gastos, almacén, etc.). | + +Formato: **Como** rol, **quiero** capacidad, **para** beneficio. + +--- + +## Acceso y cuenta + +- Como usuario, quiero iniciar sesión con usuario y contraseña, para entrar a mi escritorio o a la consola según mi tipo de cuenta. +- Como usuario, quiero cerrar sesión, para que nadie use mi sesión en el mismo equipo. +- Como usuario nuevo o con clave temporal, quiero cambiar mi contraseña al primer acceso, para no trabajar con una clave compartida. +- Como administrador del tenant, quiero que un operador de plataforma no entre al escritorio de obra, para separar la consola de PANELS del día a día de la constructora. +- Como usuario, quiero tema claro u oscuro, para trabajar con la interfaz que me resulte más cómoda. + +--- + +## Operación SaaS (consola) + +- Como operador de plataforma, quiero ver y crear tenants, para dar de alta constructoras en PANELS. +- Como operador de plataforma, quiero suspender o actualizar un tenant, para controlar vigencia y estado del servicio. +- Como operador de plataforma, quiero enviar el acceso al administrador de un tenant por correo, para que la constructora entre sin intercambio informal de claves. +- Como operador de plataforma, quiero configurar y probar el SMTP, para que los correos de acceso salgan de forma fiable. + +--- + +## Empresas y obras + +- Como administrador, quiero registrar la empresa principal y subempresas (fiscales / patronales), para operar varias razones sociales en un mismo tenant. +- Como usuario, quiero dar de alta una obra con datos de contrato, fechas, residente, SIROC y empresa dueña, para tener el expediente de proyecto en un solo lugar. +- Como usuario, quiero pausar, concluir o cancelar una obra, para que el padrón y las asignaciones respeten solo obras activas cuando corresponde. +- Como usuario, quiero adjuntar documentos de empresa y de obra, para conservar el expediente (contratos, altas, etc.) junto al registro. +- Como usuario, quiero ver el detalle de una obra desde el escritorio (árbol / ficha), para consultar presupuesto y documentos sin salir del flujo. + +--- + +## Padrón de personal + +- Como usuario de RH / obra, quiero dar de alta a una persona con datos México (CURP, RFC, NSS, puesto, riesgo, jornal o destajo), para tener un registro maestro único. +- Como usuario, quiero editar los datos de una persona sin borrarla, para conservar historial; la baja es un estado, no un delete. +- Como usuario, quiero validar CURP, RFC y NSS (formato y unicidad), para no duplicar gente ni cargar identificadores inválidos. +- Como usuario, quiero importar personal desde Excel y bajar una plantilla, para altas masivas. +- Como usuario, quiero asignar a una persona a una obra activa, reactivar una asignación previa o darla de baja conservando la fecha de fin, para saber quién está en qué frente. +- Como usuario, quiero cargar el expediente (foto, INE, CURP, NSS/IMSS, RFC y opcionales) con vigencia cuando aplique, para saber qué falta y qué está vencido o desactualizado. +- Como usuario, quiero ver el estado de pipeline de cada persona (expediente, IMSS, gafete) calculado por el sistema, para no editar a mano un semáforo que se desactualiza. +- Como usuario, quiero filtrar el padrón (con/sin IMSS, incompletos, listos para gafete, sin asignar, frescura de docs), para priorizar trámites. +- Como usuario, quiero ver la ficha de una persona (datos, documentos, asignaciones, préstamos) en un panel lateral, para operar sin perder el listado. +- Como usuario, quiero un tablero tipo kanban del pipeline de personal, para ver de un vistazo quién está atorado y en qué etapa. + +--- + +## Gafetes + +- Como usuario, quiero generar trabajos de impresión de gafetes por obra, para sacar identificaciones del personal que ya está listo. +- Como usuario, quiero un PDF de gafetes con foto, datos, color de riesgo y QR/vCard, para cumplir el control de acceso en campo. +- Como usuario, quiero marcar o consultar quién ya se imprimió en un job, para no reimprimir a ciegas. + +--- + +## Presupuesto + +- Como usuario de costos, quiero cargar el presupuesto de una obra por capítulos y partidas (alta manual o Excel), para tener la base contractual en el sistema. +- Como usuario, quiero previsualizar un Excel antes de importar, para corregir errores sin romper el presupuesto. +- Como usuario, quiero exportar el presupuesto, para compartirlo o archivarlo. +- Como usuario, quiero editar o eliminar capítulos y partidas, para ajustar el alcance mientras la obra vive. + +--- + +## Programa de obra + +- Como usuario, quiero armar el programa de una obra (fechas, semanas, importación o asistente), para calendarizar erogaciones y avance. +- Como usuario, quiero distribuir montos por concepto y por partida en cada periodo, para ver qué se gasta o ejecuta cada semana. +- Como usuario, quiero registrar avance por periodo, para comparar lo programado contra lo real. +- Como usuario, quiero contrastar el programa contra el control de costos, para detectar desvíos de calendario vs dinero. +- Como usuario, quiero exportar el programa a Excel, para llevarlo a junta o a campo. + +--- + +## Gastos y control de costos + +- Como usuario, quiero capturar gastos de obra (con adjuntos) y consultarlos, para registrar lo erogado día a día. +- Como usuario, quiero ver un resumen de IVA por periodo, para apoyar la contabilidad. +- Como administrador, quiero configurar umbrales de alerta presupuestal, para que el tablero avise cuando un rubro se desvía. +- Como usuario, quiero ver control de costos por obra (resumen, partidas, capítulos y desviaciones vs presupuesto), para saber si la obra se come el contrato. +- Como usuario, quiero que la nómina alimente el control de costos, para no capturar dos veces la mano de obra. + +--- + +## Almacén + +- Como usuario, quiero un almacén central y almacenes por obra, para separar existencias de patio vs frente. +- Como usuario, quiero catálogo de materiales y ligarlos al presupuesto, para que las salidas impacten el control de obra. +- Como usuario, quiero entradas, salidas y transferencias, para mover material con historial. +- Como usuario, quiero consultar stock global y por almacén, para saber qué hay y dónde. +- Como usuario, quiero abrir o cerrar el almacén de una obra, para congelar movimientos cuando el frente termina. + +--- + +## Nómina + +- Como usuario de nómina, quiero registrar asistencia por semana, para pagar jornales con base en asistencia. +- Como usuario, quiero armar y pagar la semana (jornal, destajo, líneas administrativas), para cerrar la nómina del periodo. +- Como usuario, quiero gestionar destajo (unidades, trabajos, cortes), para pagar por avance y no solo por día. +- Como usuario, quiero otorgar préstamos y registrar abonos, con recibo PDF, para descontar en nómina con soporte. +- Como usuario, quiero exportar la nómina a CSV, para banco o contabilidad. +- Como usuario, quiero consultar periodos de nómina históricos, para auditorías y aclaraciones. + +--- + +## Usuarios y permisos (dentro del tenant) + +- Como administrador del tenant, quiero dar de alta usuarios operativos, para que RH, costos o almacén entren sin compartir mi cuenta. +- Como administrador, quiero ver el catálogo de permisos y ajustar lo que puede un rol operativo, para no dar de más (el administrador del tenant no se recorta a sí mismo). +- Como usuario, quiero que el sistema respete lo que puedo ver o capturar (gastos, almacén, padrón, etc.), para no operar módulos que no me tocan. + +--- + +## Configuración del tenant + +- Como administrador, quiero configurar datos del tenant (zona horaria y parámetros de operación), para que fechas y reglas coincidan con cómo trabaja la constructora. + +--- + +## Criterios transversales (aceptación general) + +- Los datos de una constructora no se mezclan con los de otra. +- Una persona del padrón no se elimina: queda en baja con historial. +- El semáforo de expediente / gafete / IMSS lo calcula el sistema; no se edita a mano salvo la baja. +- Los documentos de expediente se guardan cifrados; el usuario los sube y descarga, no ve archivos en claro en disco. +- Solo se asigna o importa personal a obras activas. +- CURP, RFC y NSS no se duplican. +- Mensajes de error y de éxito son específicos y en español. +- El escritorio de obra y la consola SaaS son productos distintos: misma cuenta de API, pantallas y roles separados.