PL
Modelo POS / Ticket / Ledger
Documentación técnica integrada

POS, motor de promociones y ledger distribuido

Versión web estática del documento técnico consolidado. Resume la estructura del agregado Ticket, el submodelo de promociones, el flujo ITEM/PAGO, el ledger y las convenciones de implementación Java 8, basándose en el PDF v202604070300.

core.md 202604070108 promociones.md 202604070300 Java 8 clásico jsonmodel_optionB_consolidado_promociones_v4
Documento
MODELO POS / TICKET / LEDGER
Versión
v202604070300
Base
core.md + promociones.md
Objetivo
Dominio de ventas, motor de promociones y ledger distribuido
Implementación
Java 8 · POJOs · sin Lombok

1. Introducción y principios

Este documento es la referencia técnica integrada del dominio POS / Ticket / Ledger. Consolida el modelo central y el submodelo de promociones en una sola narrativa técnica, incluyendo estructura de datos, reglas de cálculo, ejemplos JSON y lineamientos de implementación. fileciteturn8file0

Detallado
línea por línea
Trazable
movimientos[]
Reconciliable
Σ ledger = 0
Regla fundamental: no introducir reglas no confirmadas. Ante dudas de modelado, se prioriza la solución más conservadora y explicable en términos de cálculo y auditoría. fileciteturn8file0

Jerarquía documental

PrioridadDocumentoDescripción
1core.md (202604070108)Referencia central del modelo POS/Ticket/Ledger.
2promociones.md (202604070300)Complemento específico del motor de promociones.
3jsonmodel_optionB_v4Ejemplos JSON del modelo corregido.
4Documentos históricosReferencias previas, con menor prioridad ante contradicción.

2. Agregado raíz: Ticket

Ticket es el agregado raíz y representa el estado completo de una operación POS. Es la unidad principal para cálculo, validación, testing, auditoría y reconciliación. fileciteturn8file0

Registros operativos
items · promociones · pagos
Fuente de verdad
movimientos[]

Estructura base

{ "ticket": { "datosreferenciales": {}, "cliente": {}, "articulos": [], "items": [], "promociones": [], "pagos": [], "movimientos": [] } }
Idea central: el ticket no es solo captura comercial. También contiene la explicación distribuida y reconciliable de toda la operación. fileciteturn8file0

3. Granos del modelo

El modelo trabaja con tres granos funcionales separados y obligatorios. Esa separación no debe perderse. fileciteturn8file0

articulos[]
catálogo reutilizable
items[]
captura original
movimientos[]
ledger distribuido

Regla de itemización

Si un item tiene unidades enteras, deben generarse tantos movimientos VENTA_ITEM como unidades registradas en movimientos[]. items[] es captura comercial; movimientos[] es impacto económico distribuido. fileciteturn8file0
CampoTipoDescripción
idintegerIdentificador incremental del movimiento
conceptoenumVENTA_ITEM · PROMOCION · PAGO
origenidintegerReferencia al registro maestro origen
movimientoidinteger/nullReferencia al VENTA_ITEM afectado; null para excedentes y vuelto
nucleoimpositivo[]arrayImportes e impuestos del movimiento

4. Objetos del dominio

NucleoImpositivo

Es la estructura central de importes e impuestos. Se utiliza en artículos, movimientos y datos referenciales. No debe asumirse que todo impuesto sea porcentual. fileciteturn8file0

{ "nucleoimpositivo": [ { "impuesto": { "id": "NETO_IVA_21" }, "monto": 1000.0 }, { "impuesto": { "id": "IVA_21" }, "monto": 210.0 }, { "impuesto": { "id": "IMPUESTOINTERNO_IVA_21" }, "monto": 100.0 } ] }

Articulo

CampoDescripción
ean / pluIdentificación comercial del producto
descripcionNombre visible del artículo
pesableIndica si admite cantidades decimales
preciolistaPrecio base para cálculo de promociones
rubro / depto / marcaAtributos comerciales para filtros
nucleoimpositivo[]Composición impositiva base

TipoDePago y Pago

TipoDePago es catálogo/configuración externo al ticket. Pago es el registro maestro operativo dentro del ticket y su monto puede ser positivo o negativo. Un pago negativo es válido cuando participa como vuelto. fileciteturn8file0

No agregar campos como montoingresado o montoaplicado a la versión base. El modelo conservador usa solo monto. fileciteturn8file0

5. Modelo de promociones

El modelo promocional distingue tres capas obligatorias: definición en listapromociones, registro maestro aplicado en promociones[] y efecto económico final en movimientos[]. fileciteturn8file0

Catálogos auxiliares

CatálogoValoresDescripción
promocionalcanceITEM · PAGOEtapa funcional donde aplica la promoción
promocionbeneficioPORCENTAJE · MONTO · NUEVOPRECIOITEMCómo interpretar valor
promocionmetodoCANTIDAD · COMBOLógica base de resolución
promociondecisionNOACUMULATIVA · ACUMULATIVABase de cálculo del precio
promocionestadoPOSIBLE · APLICADA · NOAPLICA · ANULADAEstado de una promoción aplicada
promocionlistatypeINCLUSION · EXCLUSIONTipo de entrada de lista
promocionlistanumberLISTA1 · LISTA2Agrupa listas lógicas

promociontipoelemento

Incluye valores como EAN, PLU, DEPTO, RUBRO, CODIGOCLASIFICACION, PROVEEDOR, MARCA, MEDIODEPAGO, SUCURSAL, TICKET y CANTIDAD_MAX_PROMOS, que limita la cantidad de aplicaciones de una promoción dentro del ticket y siempre se informa con valordeelemento. fileciteturn8file0

Definición de promoción

{ "listapromociones": [ { "id": 1, "descripcion": "PROMO_2X1_ARROZ", "promocionalcance": { "id": "ITEM" }, "promocionbeneficio": { "id": "PORCENTAJE" }, "valor": 50.0, "promocionmetodo": { "id": "CANTIDAD" }, "promociondecision": { "id": "NOACUMULATIVA" }, "vigencia": { "fechadesde": "2026-03-01", "fechahasta": "2026-03-31", "diassemana": ["MIERCOLES"], "horadesde": "10:00", "horahasta": "11:00" }, "lista": [ { "listaindex": 1, "promocionlistatype": { "id": "INCLUSION" }, "promocionlistanumber": { "id": "LISTA1" }, "promociontipoelemento": { "id": "EAN" }, "valordeelemento": "7791234567890", "cantidad": 2.0 }, { "listaindex": 2, "promocionlistatype": { "id": "INCLUSION" }, "promocionlistanumber": { "id": "LISTA1" }, "promociontipoelemento": { "id": "CANTIDAD_MAX_PROMOS" }, "valordeelemento": "1" } ] } ] }
Regla CANTIDAD_MAX_PROMOS: limita cuántas veces puede resolverse una promoción, independientemente de cuántas unidades válidas existan. Con un máximo de 1 y 3 unidades, la promoción se aplica una sola vez y la tercera unidad queda fuera del beneficio. fileciteturn8file0

6. Motor de promociones

MODO ITEM

Se ejecuta una sola vez por ticket. Cada invocación resetea promociones previamente calculadas, evalúa solo promociones con alcance ITEM, genera registros en promociones[] y distribuye su efecto en movimientos[]. No devuelve promociones de medio de pago. fileciteturn8file0

MODO PAGO

Puede invocarse múltiples veces por ticket. Evalúa promociones con alcance PAGO y opera en dos sub-etapas: CONSULTA y APLICAR. Los pagos se comparan siempre contra el saldo real luego de promociones e impuestos. fileciteturn8file0

CONSULTA
informativa
No modifica el ticket
APLICAR
efectiva
Registra pago, promo y vuelto

Escenarios de monto

EscenarioCondiciónComportamiento
Pago parcialmonto < saldo netoPromoción proporcional; queda saldo pendiente
Pago exactomonto = saldo netoPromoción completa; ticket saldado
Pago con excedentemonto > saldo netoExcedente resuelto como vuelto según tiposdepago

Lógica de vuelto

CasodavueltovueltomediodepagoResultado
AtrueVuelto en el mismo medio; operación aceptada
BfalsenullNo se permite vuelto; operación denegada
Cfalseid_medioVuelto en medio alternativo indicado; aceptada

7. Ledger — movimientos[]

El ledger distribuido es la fuente de verdad fiscal y contable. Las convenciones de signo son obligatorias para que la operación cierre algebraicamente en 0. fileciteturn8file0

VENTA_ITEM
positivo
PROMOCION
negativo
PAGO
negativo / positivo
Conceptoorigenidmovimientoid
VENTA_ITEMitems[].idnull
PROMOCIONpromociones[].idid del VENTA_ITEM afectado
PAGO contra ítempagos[].idid del VENTA_ITEM cancelado
PAGO excedentepagos[].idnull
PAGO vueltopagos[].id (vuelto)null
Regla fuerte del ledger: la suma de todos los montos de nucleoimpositivo en movimientos[] debe dar exactamente 0 al cierre final del ticket. fileciteturn8file0

8. Ejemplo completo integrado

Escenario: 3 unidades de ARROZ a $1310, promoción 2x1 (50% sobre dos unidades, máximo una aplicación), pago con CHEQUE por $3000 y vuelto $380 en EFECTIVO. fileciteturn8file0

Resultado MODO ITEM

{ "promociones": [ { "id": 1, "promocionid": 1, "descripcion": "PROMO_2X1_ARROZ", "tipoPromo": "ITEM", "promocionestado": { "id": "APLICADA" }, "monto": -1310.0, "elementos": [ { "movimientoid": 1, "articuloid": 1, "unidadesimpactadas": 1.0, "monto": -655.0 }, { "movimientoid": 2, "articuloid": 1, "unidadesimpactadas": 1.0, "monto": -655.0 } ] } ] }

Resultado MODO PAGO

{ "pagos": [ { "id": 1, "mediodepagoid": 2, "descripcion": "CHEQUE", "monto": 3000.0 }, { "id": 2, "mediodepagoid": 1, "descripcion": "EFECTIVO", "monto": -380.0 } ], "resultado": { "estado": "ACEPTADO", "saldopendiente": 0.0, "vuelto": 380.0, "vueltomediodepagoid": 1 } }

Verificación de reconciliación

VerificaciónCálculoResultado
Items bruto3 × $1310$3930.00 ✓
Promo maestropromociones[1].monto−$1310.00 ✓
Movs PROMOCIONmov4 + mov5−$1310.00 ✓
Neto ticket$3930 − $1310$2620.00 ✓
Pagado neto$3000 − $380$2620.00 ✓
Suma ledger totalΣ mov1..10$0.00 ✓

9. Orden base de cálculo

PasoAcciónObservación
1Identificar clienteDetermina tipo de comprobante y alícuotas aplicables
2Ingresar ítemsDeduplicar articulos[], crear items[]
3Resolver referenciasCada item referencia su articuloid
4Determinar bases inicialespreciolista × unidades
5–6Aplicar promociones ITEMMODO ITEM
7Calcular impuestos y núcleonucleoimpositivo del ticket
8Determinar totaldatosreferenciales.total
9Pre-consulta promos de pagoMODO PAGO CONSULTA
10Registrar pagosMODO PAGO APLICAR
11Calcular excedente y vueltoMovimientos PAGO null
12Distribuir pagos en ledgerContra VENTA_ITEM
13Calcular saldo finaldatosreferenciales.saldo
14Validar reconciliaciónΣ movimientos[] = 0

10. Convenciones de implementación Java 8

La implementación objetivo es Java 8 clásico: POJOs simples, campos privados, getters/setters explícitos, constructor por defecto, sin Lombok, sin records y sin features posteriores a Java 8. fileciteturn8file0

Estilo
POJOs simples
Fuera de scope
Lombok · records · builders

Paquetes sugeridos

  • com.tipre.ruleengine.model
  • com.tipre.ruleengine.model.enums
  • com.tipre.ruleengine.model.catalog
  • com.tipre.ruleengine.logic
  • com.tipre.ruleengine.validation
  • com.tipre.ruleengine.example

POJO ejemplo — Item

package com.tipre.ruleengine.model; public class Item { private Integer id; private Integer articuloid; private Double unidades; public Item() {} public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getArticuloid() { return articuloid; } public void setArticuloid(Integer articuloid) { this.articuloid = articuloid; } public Double getUnidades() { return unidades; } public void setUnidades(Double unidades) { this.unidades = unidades; } }

11. Decisiones diferidas y extensiones futuras

Hay áreas reconocidas por el modelo que todavía no están definidas en esta versión y requieren decisión explícita. fileciteturn8file0

ÁreaEstadoImpacto esperado
Filtros por tipo de cliente / convenio / fidelizaciónDiferidoNuevo atributo en listapromociones y filtro extra en el motor
Orden de evaluación / prioridad entre promocionesDiferidoCampo prioridad y cambio en algoritmo
NUEVOPRECIOITEMDefinido en catálogoManejo especial en acumulación
COMBO como método de cómputoDefinido en catálogoLógica combinatoria aún no especificada
LISTA3, LISTA4...ExtensibleAgregar valores sin cambios estructurales
IDs externos vs internos en artículosPendienteDiferenciar ids locales y externos
Ante cualquier contradicción, la jerarquía es: core.md > promociones.md > JSON > históricos. fileciteturn8file0