Ir al contenido

El manifiesto

El manifiesto (manifest.ts) es un contrato puramente declarativo que leen el servicio local y la interfaz. Se define con definePluginManifest y se valida al instalar, al activar y al arrancar, antes de ejecutar ningún código del plugin.

import { definePluginManifest } from '@simplegest/plugin-sdk'
export const manifest = definePluginManifest({
id: 'simplegest.demo-product-extension', // <publisher>.<nombre>
name: 'Garantía de productos (demo)',
description: '…',
version: '0.2.0', // semver
publisher: { id: 'simplegest', name: 'SimpleGest' },
sdk: { range: '^0.1.0' }, // compatibilidad con el host
permissions: ['products:read', 'products.fields:read', 'products.fields:write',
'ui:product-form', 'ui:product-list', 'ui:product-actions', 'print:blocks'],
offline: 'full', // full | degraded | online-only
contributes: {
fields: [{ entity: 'product', definitions: [
{ key: 'warrantyMonths', label: 'Garantía (meses)', type: 'integer', min: 0, max: 120, listable: true },
{ key: 'origin', label: 'Origen', type: 'enum', options: [{ value: 'ES', label: 'España' }, /* … */] },
]}],
listColumns: [{ id: 'warranty', entity: 'product', field: 'warrantyMonths', header: 'Garantía', align: 'center', suffix: ' meses' }],
actions: [{ id: 'warranty-end', entity: 'product', label: 'Fin de garantía', icon: 'ShieldCheck' }],
printBlocks: [{ id: 'warranty', label: 'Garantía de lo vendido', documentKinds: ['SIMPLIFIED_INVOICE', 'INVOICE'] }],
},
})

Lo que añade el SDK 0.2 (extracto de Alimentación)

Sección titulada «Lo que añade el SDK 0.2 (extracto de Alimentación)»
export const manifest = definePluginManifest({
id: 'simplegest.food',
sdk: { range: '^0.2.0' },
permissions: ['products:read', 'products.fields:read', 'products.fields:write', 'ui:product-form', 'ui:product-list',
'storage', 'ui:pages', 'ui:dashboard', 'ui:detail', 'ui:filters', 'sales:validate', 'sales:read', 'stock:write', 'pos:barcode'],
userPermissions: [
{ key: 'receive', label: 'Dar entrada a lotes', inherits: ['products'] },
// …
],
configuration: [
{ key: 'alertDays', label: 'Avisar de caducidades con (días)', type: 'integer', min: 0, max: 90, defaultValue: 7 },
// …
],
storage: { collections: [{ name: 'lots', unique: [['productId', 'lotCode']] }, { name: 'movements' }, { name: 'wastes' }] },
migrations: [{ id: '0001_initial', description: '…' }, { id: '0002_lot_status', description: '…' }],
contributes: {
commands: [{ id: 'receive', label: 'Entrada de lote', permission: 'receive' }],
dataSources: [{ id: 'lots', permission: 'reports' }],
forms: [{ id: 'receiveLot', title: 'Entrada de lote', submitLabel: 'Dar entrada', fields: [/* … */] }],
pages: [{ id: 'lots', title: 'Lotes y caducidades', tabs: [/* tablas, métricas, acciones */] }],
navigation: [{ id: 'lots', label: 'Lotes y caducidad', icon: 'Calendar', page: 'lots' }],
widgets: [/* Dashboard */], detailSections: [/* ficha de producto */],
saleCaptures: [{ id: 'lots', label: 'Lote', kind: 'lots', whenField: 'lotTracked', required: false, suggestSource: 'fefo' }],
barcodeParsers: [{ id: 'scale', label: 'Etiquetas de balanza (EAN 20-29)' }],
},
catalog: { category: 'Sector', icon: 'Calendar', summary: '…', price: { type: 'free' /* … */ }, manual: '…', dataPolicy: '…',
changelog: [{ version: '1.0.0', date: '2026-10-20', notes: ['…'] }] },
})
Campo Para qué
storage.collections Colecciones propias del plugin, con claves unique que garantiza la base de datos
migrations Migraciones NNNN_nombre, aplicadas una vez, con copia previa y verificación de que el núcleo no cambia
userPermissions Permisos de usuario propios, heredados de un permiso del programa hasta que se cambian en Ajustes › Usuarios
configuration Opciones con valor por defecto, editables en la pestaña Configuración de la Shop. Si falta una obligatoria, el plugin queda en pending_configuration
catalog Ficha de la tienda: categoría, icono, resumen, modelo comercial (price), capturas, novedades (changelog, que debe empezar por la versión actual), manual y política de datos (dataPolicy, obligatoria)
replaces Módulo anterior al que sustituye (por ejemplo, loyalty-system), cuyos datos puede importar en una migración
dependencies / conflicts Otros plugins que necesita o con los que no puede convivir

catalog.price admite free, one-off, subscription, included y trial. Cualquier modelo distinto de free exige una licencia firmada para instalar y activar el plugin. Mientras las condiciones comerciales no estén aprobadas, la Shop no muestra el modelo: dice «Requiere tu cuenta SimpleGest · condiciones por confirmar» (COMMERCIAL_TERMS_APPROVED).

Capacidad Permite
products:read Modelo público del producto
products.fields:read / products.fields:write Leer y escribir sus propios campos de producto
ui:product-form / ui:product-list / ui:product-actions Sección en la ficha, columnas y acciones en el listado de productos
events:product / events:stock Eventos de producto y de stock (servidor)
print:blocks Bloques de texto complementarios en los documentos impresos
storage Colecciones propias y migraciones
ui:pages / ui:dashboard / ui:detail / ui:pos-actions / ui:filters Pantallas y menú, tarjetas del Dashboard, secciones de ficha, botones del TPV, filtros del listado
customers:read Modelo público del cliente
sales:read / sales:validate Hechos de ventas emitidas y sus correcciones; revisar una venta antes de emitirla (solo rechazar) y capturar datos de línea
catalog:write / stock:write Crear productos (variantes); entradas, mermas y ajustes de stock
vouchers:issue Emitir vales del TPV
repairs:read / repairs:write Reparaciones del módulo del núcleo
documents:render PDF para revisar o descargar; nunca impresión silenciosa
pos:barcode Leer códigos escaneados que el núcleo no conoce
users:read Nombres de los usuarios del TPV
legacy:loyalty Leer los datos del módulo de fidelización anterior para migrarlos
sales:discounts (0.2.1) Proponer reglas de descuento declarativas que evalúa el motor de promociones del núcleo; exige declarar contributes.discountProviders
Tipo Restricciones Control
string maxLength, pattern Campo de texto
text (0.2) maxLength Texto de varias líneas
integer / number min, max Campo numérico
decimal (0.2) scale, min, max Decimal exacto, guardado como texto
boolean — Casilla
enum options u optionsSource (0.2: opciones de una fuente de datos del plugin) Desplegable
date AAAA-MM-DD Fecha
product / customer / user (0.2) — Selector

required, help y defaultValue son comunes. La validación es la misma en la interfaz y en el servicio local; decide siempre el servidor.

install enable
available ─────────► installed ──────────────────► active ◄───────┐
▲ │ (config requerida) │ │ │ enable
│ ▼ │ │ disable │
│ pending_configuration ──configurar────┘ ▼ │
│ disabled ──────┘
│ install │
uninstalled ◄────────────── uninstall ──────────────────┘
└─ purge-data (confirmación explícita con el id; exporta antes de borrar)
  • Licencia: sin licencia válida, un plugin de pago no se instala ni se activa; si la pierde, al arrancar queda disabled con sus datos.
  • Desactivar no borra: oculta la interfaz y la API responde 409, pero los datos se conservan. No se puede desactivar si otro plugin activo depende de él.
  • Desinstalar conserva los datos. Solo purge-data borra, si el plugin está desinstalado y se confirma con su id.
  • incompatible: SDK fuera de rango o manifiesto inválido; se marca antes de ejecutar su código.
  • error: fallo al activar, en una migración o fallos repetidos en sus handlers.
  • Actualizar (POST /api/plugins/:id/update, botón «Actualizar a vX»): una versión nueva llega con la aplicación; todas sus migraciones pendientes se aplican en una transacción, con copia previa (backups/pre-plugin-*.db) y comprobación de que ninguna tabla del núcleo cambió. Si falla, se deshace entera y el plugin queda error con la versión anterior.
  • Nunca hacia atrás: si la aplicación trae una versión anterior a la de los datos del plugin, queda incompatible sin ejecutar su código. Reinstalar conserva la versión de los datos; tras purge-data, se instala desde cero.
  • Todas las transiciones quedan en la auditoría y en la pestaña Historial de la Shop.
  • El host expone HOST_SDK_VERSION (hoy 0.2.1) y SUPPORTED_SDK_CONTRACTS (0.1.3, 0.2.0 y 0.2.1). Se comprueba el sdk.range del plugin contra cada contrato antes de ejecutar su código: un plugin ^0.1.0 o ^0.2.0 sigue funcionando porque 0.2.0 y 0.2.1 solo añaden; uno ^0.3.0 queda incompatible.
  • En 0.x cada versión menor puede romper compatibilidad; desde 1.0, solo las mayores.
  • La suite tests/sdk/plugin-sdk.test.ts es el contrato: si se rompe, hay que subir la versión del SDK.