Ir al contenido

Puntos de extensión

Cada punto de extensión es un contrato estable: un id, una capacidad requerida, una contribución declarada en el manifiesto y un host que la pinta con componentes oficiales. Los plugins nunca acceden al árbol React ni a ficheros internos.

  • Capacidad: ui:product-form (y products.fields:write).
  • Contribución: contributes.fields[{ entity: 'product', definitions }].
  • Un bloque por plugin con su nombre en la ficha de producto. Carga los valores al abrir y valida antes de enviar; un error bloquea el guardado. Se guarda después del producto: si el plugin falla, el producto queda guardado y se avisa de qué extensión no guardó.
  • Capacidad: ui:product-list.
  • Contribución: contributes.listColumns[{ id, entity: 'product', field, header, align?, suffix? }]. El field debe ser un campo declarado.
  • Columna opcional que cada usuario muestra u oculta desde el menú de columnas. Valores cargados en lote (máximo 200 ids por consulta).
  • Capacidad: ui:product-actions.
  • Contribución: contributes.actions[{ id, entity: 'product', label, icon? }] y ctx.actions.handle(id, handler).
  • El handler recibe un PublicProduct congelado y solo puede responder con ctx.ui.notify o ctx.ui.showInfo (texto plano).
  • Contribución: contributes.printBlocks[{ id, label, documentKinds? }]; el servidor del plugin aporta el texto con ctx.print.provide.
  • El proveedor recibe los datos del documento emitido (tipo, número, fecha y líneas), sin importes.
  • Texto plano, máximo 6 líneas de 64 caracteres, sin textos fiscales y con 2 s de límite. Va en la parte complementaria, después del contenido fiscal.
  • Se congela con el documento: una reimpresión sale igual aunque el plugin se desactive o se desinstale.
Evento Capacidad Payload
product.created / product.updated / product.deleted events:product { productId }
stock.changed (0.1.1) events:stock { productId, type, quantity, unit, stock }, cantidades en texto decimal exacto

Se emiten después del commit, con payload congelado, 5 s de límite por handler y aislamiento de errores; los fallos repetidos pasan el plugin a error.

Id Capacidad Contribución Dónde aparece
app.page / app.navigation ui:pages pages, navigation Pantalla propia del plugin (pestañas, métricas, tablas) y entrada en el menú lateral
plugin.form / plugin.command — forms, commands Diálogos de formulario, confirmación o acción directa; el host valida el formulario. Un encargado puede autorizar con su PIN
plugin.table — dataSources + tablas de la página Búsqueda, filtros, acciones por fila y de barra, paginación y detalle
dashboard.widget ui:dashboard widgets Tarjeta en el Dashboard
record.detail.section ui:detail detailSections Sección en la ficha de producto (la de cliente está reservada)
pos.quick-action ui:pos-actions posActions Botón en el TPV; puede añadir productos al ticket
pos.line-capture + sale.validation sales:validate saleCaptures (lotes, números de serie) Distintivo y diálogo en la línea del ticket; el plugin solo puede rechazar la venta con un mensaje
sale.recorded / sale.corrected sales:read — Hechos tras el commit, con el documento, las posiciones y lo capturado
pos.barcode.parse pos:barcode barcodeParsers Búsqueda del TPV con un código que el núcleo no conoce (por ejemplo, etiquetas de balanza)
list.filter ui:filters filters Filtros del listado de productos sobre campos listable
plugin.settings — configuration Pestaña Configuración de la ficha en la Shop
user.permissions — userPermissions Ajustes › Usuarios, grupo Extensiones (heredados hasta que se cambian) y autorización con PIN

Los módulos anteriores (loyalty-system, company-lookup, repairs) siguen usando hooks internos de src/modules/moduleRegistry.ts y no forman parte del SDK. Su transición:

Módulo Plugin oficial Transición
loyalty-system simplegest.loyalty-pro (replaces: 'loyalty-system') Su migración copia los datos sin borrarlos; mientras el plugin está activo, el módulo deja de sumar y canjear puntos y su interfaz se oculta
repairs simplegest.electronics El plugin trabaja sobre las mismas reparaciones (repairs:read/write); crear, editar y facturar siguen en la pantalla Reparaciones
company-lookup — Sin cambios
Id Capacidad Contribución Dónde aparece
sale.discount sales:discounts discountProviders + ctx.discounts.provide(id, () => DiscountRule[]) Descuentos automáticos en el carrito y en el ticket
  • El plugin propone reglas declarativas (SECOND_UNIT, PACK, CONDITIONAL, de shared/plugin-sdk/discounts.ts) con prioridad, acumulación, exclusividad, compatibilidad con códigos y cupones, franja horaria (hora de Madrid) y clientes. Nunca un importe calculado.
  • El host lee las reglas de los plugins activos con un límite de 2 segundos, descarta las no válidas (discountRuleProblems) y se las da al motor de promociones del núcleo, que calcula con el mismo código en el carrito y al emitir. Si el resultado no coincide con lo que mostró el carrito, el cobro se rechaza (ADVANCED_DISCOUNT_CHANGED).
  • El reparto queda en el documento emitido, línea a línea; devoluciones y rectificativas lo prorratean con el mecanismo del núcleo. Un plugin desactivado no aporta reglas.
  • Ejemplo completo: Promociones Avanzadas. Manual de usuario en Promociones Avanzadas.
Id Capacidad Descripción
customer.form.section / customer.list.column / customer.row.action ui:customer-* Como en producto
customer.detail.tab ui:detail Sección en la ficha de cliente (el contrato existe en 0.2; falta la pantalla del núcleo)
integration.webhook net:webhook:<dominio> Salida a sistemas externos mediada por el host