Executive documentation · onboarding · arquitectura · negocio

Modelo navegable del POS con una lectura más institucional, visual y compartible

Esta segunda versión transforma la especificación del dominio en una pieza de documentación orientada a alinear negocio, arquitectura e implementación. Explica cómo se estructura el ticket, cómo se distribuyen promociones y pagos en el ledger, y cómo se preserva la trazabilidad completa del cálculo sin mezclar responsabilidades.

Ticket como agregado raíz Ledger distribuido reconciliable Promociones ITEM / PAGO / POSTPAGO Consistencia documental + JSON
Agregado raíz
Ticket
Una sola unidad de consistencia para cálculo, validación, auditoría y reconciliación.
Fuente fiscal
movimientos[]
Ledger distribuido: explica ventas, promociones, pagos, excedentes y vueltos.
Promociones
3 capas
Definición, registro aplicado y efecto económico distribuido.
Garantía fuerte
Σ = 0
Al cierre, la suma algebraica del ledger debe reconciliar exactamente en cero.
Lectura para stakeholders

Qué resuelve este modelo

↑ arriba

El modelo describe una operación POS completa de forma explicable. La venta no termina en el detalle comercial del ticket: también debe poder explicarse cómo impactó cada promoción, cómo se distribuyó cada pago, qué saldo quedó, si hubo excedente, en qué medio se entregó el vuelto y por qué el resultado final concilia exactamente.

Negocio

Explicación del resultado

Permite responder con precisión cuánto valía el ticket, qué promociones modificaron los importes, qué medios de pago participaron y cuál fue el total efectivamente cancelado.

Arquitectura

Separación de responsabilidades

Separa catálogo, captura operativa, registros maestros y ledger distribuido. Esa separación evita sobrecargar estructuras y simplifica evolución futura.

Auditoría

Trazabilidad fuerte

Cada transformación económica vive en movimientos[], de modo que el cierre del ticket y la explicación fiscal no dependan de inferencias posteriores.

Idea central: items[] conserva cómo se ingresó la venta; movimientos[] conserva cómo impactó económicamente. Esa diferencia es la base de toda la claridad del modelo.
Mapa del dominio

Arquitectura del Ticket en una sola vista

↑ arriba
Ticket unidad principal de consistencia y reconciliación datosreferenciales contexto y totales cliente comprador y convenio articulos[] catálogo interno items[] captura original promociones[] registro maestro pagos[] registro maestro movimientos[] ledger distribuido: fuente de verdad económica y fiscal
El ticket centraliza el estado. Pero la explicación económica definitiva no vive en items[], promociones[] ni pagos[], sino en el ledger distribuido de movimientos[].

Tres decisiones de diseño que ordenan todo el sistema

  • Catálogo vs. registro: articulos[] evita duplicación; items[] conserva el ingreso operativo.
  • Maestro vs. distribuido: promociones[] y pagos[] son maestros; su efecto económico se distribuye en movimientos[].
  • Reconciliación fuerte: el ticket cerrado debe poder validarse algebraicamente sin reglas implícitas.
datosreferencialesclientearticulos[]items[]promociones[]pagos[]movimientos[]
Vista de proceso

Flujo operativo end-to-end

↑ arriba

La secuencia de cálculo es tan importante como las estructuras. El modelo define un orden estable para evitar dobles interpretaciones del precio vigente, de las promociones acumulables y del saldo aplicable a pagos posteriores.

1

Ingreso comercial

Se identifica cliente, se deduplican artículos y se registran items como captura original de la venta.

2

Promociones ITEM

Se computan promociones por método y prioridad, recalculando la base vigente del ítem según corresponda.

3

Núcleo impositivo

Con el precio resultante se calcula el núcleo impositivo del ticket y su total operativo antes de pagos.

4

Promociones de pago

Se consultan o aplican beneficios/recargos por medio de pago, según el modo del motor.

5

Ledger y cierre

Se distribuyen pagos, excedentes y vueltos. Luego se valida que el ledger cierre exactamente en cero.

25Secciones relevantes detectadas en model.md, usadas como base para esta guía navegable.
25Archivos JSON revisados para asegurar correlación entre documento y estructura concreta.
Java 21Lenguaje objetivo confirmado para implementación, con foco en claridad del dominio y tipado fuerte.
Contabilidad del ticket

Ledger, signos y reconciliación

↑ arriba

Convención de signos

ConceptoSigno esperadoLectura de negocio
VENTA_ITEMPositivoValor comercial que el ticket genera.
PROMOCION descuentoNegativoReduce el valor a cobrar.
PROMOCION recargoPositivoIncrementa el valor a cobrar.
PAGO aplicadoNegativoCancela parte del valor del ticket.
PAGO excedenteNegativoRefleja un ingreso mayor al saldo requerido.
PAGO vueltoPositivoDevuelve valor al cliente por el medio definido.

La regla de oro del cierre

El ticket no se considera consistente si el ledger final no concilia. La condición fuerte del modelo es:

Σ movimientos[].nucleoimpositivo.monto = 0 al cierre final del ticket.

Esto obliga a que ventas, promociones, pagos, excedentes y vueltos queden plenamente explicados en la misma estructura distribuida.

Ventas + VENTA_ITEM Promociones ± descuento / recargo Pagos cancelación del saldo Vuelto + valor devuelto 0 ticket conciliado
Motor de promociones

Cómo se organiza la lógica promocional

↑ arriba

Las promociones no se modelan como un bloque único. El diseño adopta tres capas para mantener trazabilidad y permitir que la promoción sea, al mismo tiempo, una regla configurable, un registro aplicado y un efecto económico distribuido.

Capa 1

listapromociones

Define reglas de negocio configurables: método, beneficio, condiciones, vigencia, sucursales, medios de pago y listas de ítems.

Capa 2

promociones[]

Registra qué promoción concreta quedó aplicada en un ticket, cuál fue su monto total y qué elementos alcanzó.

Capa 3

movimientos[]

Distribuye el efecto económico final sobre movimientos de venta específicos. Es la capa fiscalmente explicable.

Modos operativos

  • ITEM: promociones sobre ítems, antes del pago.
  • PAGO (consulta): informa beneficios/recargos posibles para un medio de pago sin modificar el ticket.
  • PAGO (aplicar): registra el pago y aplica la promoción correspondiente sobre el saldo neto.
  • POSTPAGO: distribuye un ajuste informado por un sistema externo luego de autorizar el pago.

Orden de métodos en ITEM

  1. MAYORISTA
  2. VENTAXBULTO
  3. GRUPOMAYORISTA
  4. COMBO
  5. CANTIDAD

La prioridad importa: el ítem pasa por estos métodos con su último precio vigente, y algunas promociones excluyen evaluación en métodos posteriores.

Regla útil para negocio: en COMBO y CANTIDAD primero se computan las promociones acumulativas; las no acumulativas solo se evalúan si ninguna acumulativa se cumplió para ese ítem.
Ajuste posterior al pago

POSTPAGO explicado para arquitectura y operación

↑ arriba

Qué problema resuelve

Después de autorizar un pago en un sistema externo, puede aparecer un descuento o un recargo adicional. Ese ajuste no viene de listapromociones, pero debe quedar trazado en el ticket y distribuido proporcionalmente sobre los ítems alcanzados.

Cómo queda registrado

  • Se crea una promoción sintética en promociones[].
  • promocionid = 0 reservado para promociones de sistema.
  • pagoorigenid vincula el ajuste al pago que lo disparó.
  • El impacto económico vive en movimientos[] como PROMOCION.
Pago autorizado externamente puede devolver descuento o recargo posterior Calcular saldo vigente de cada ítem antes del pago origen Distribuir proporcionalmente el ajuste sobre ítems con saldo positivo Registrar promoción sintética + movimientos PROMOCION descuento = negativo · recargo = positivo
Correlato estructural

Artefactos JSON incluidos en el modelo

↑ arriba

Además del documento maestro, el ZIP aporta ejemplos estructurales concretos para validar naming, jerarquía y objetos del dominio. Esto ayuda a alinear la documentación con el modelo serializable real.

ArchivoTipoPunto de entrada / estructura
articulo.jsonEjemplo estructuralarticulo
articulos.jsonEjemplo estructuralarticulos
cliente.jsonEjemplo estructuralcliente
datosreferenciales.jsonEjemplo estructuraldatosreferenciales
impuestos.jsonEjemplo estructuralimpuestos
item.jsonEjemplo estructuralitem
listapromociones.jsonEjemplo estructurallistapromociones
metodocomputo.jsonEjemplo estructuralmetodocomputo
movimientos.jsonEjemplo estructuralmovimientos
nucleoimpositivo.jsonEjemplo estructuralnucleoimpositivo
pagos.jsonEjemplo estructuralpagos
promociones.jsonEjemplo estructuralpromociones
promociones_postpago_ejemplo.jsonEjemplo estructuralpromociones
promocionestado.jsonEjemplo estructuralpromocionestado
promocionlistanumber.jsonEjemplo estructuralpromocionlistanumber
promocionlistatype.jsonEjemplo estructuralpromocionlistatype
promociontipoelemento.jsonEjemplo estructuralpromociontipoelemento
ticket.jsonEjemplo estructuralticket
tickettipo.jsonEjemplo estructuraltickettipo
tipobeneficio.jsonEjemplo estructuraltipobeneficio
tipocomprobante.jsonEjemplo estructuraltipocomprobante
tipoconcepto.jsonEjemplo estructuraltipoconcepto
tipodecliente.jsonEjemplo estructuraltipodecliente
tipodeimpuesto.jsonEjemplo estructuraltipodeimpuesto
tiposdepago.jsonEjemplo estructuraltiposdepago
Muestras reales

Ejemplos estructurales para lectura rápida

↑ arriba

ticket.json

{
  "ticket": {
    "datosreferenciales": {
      "nroTicket": 12213,
      "comercio": "SuperX",
      "nroSucursal": 1,
      "nroPv": 54,
      "fechaHora": "2026-03-11 10:16:00",
      "tickettipo": {
        "id": "VENTA"
      },
      "tipocomprobante": {
        "id": "A"
      },
      "total": 2620.0,
      "saldo": 0.0,
      "vuelto": 380.0,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
          },
          "monto": 2000.0
        },
        {
          "impuesto": {
            "id": "IVA_21"
          },
          "monto": 420.0
        },
        {
          "impuesto": {
            "id": "IMPUESTOINTERNO_IVA_21"
          },
          "monto": 200.0
        }
      ]
    },
    "cliente": {
      "id": 1,
      "nombre": "GOMEZ",
      "cuit": "30112233451",
      "tipodecliente": {
        "id": 3,
        "descripcion": "RESPONSABLE_INSCRIPTO",
        "tipocomprobante": {
          "id": "A"
        }
      },
      "convenio": {
        "id": 1,
        "nombre": "CONVENIO EMPLEADOS"
      },
      "impuestos": [
        {
          "id": "PERCEPCION_IIBB",
          "porcentaje": 1.0,
          "netominimo": 0.0
        },
        {
          "id": "PERCEPCION_COMIND",
          "porcentaje": 1.0,
          "netominimo": 0.0
        },
        {
          "id": "PERCEPCION_IVA_21",
          "porcentaje": 1.0,
          "netominimo": 0.0
        },
        {
          "id": "PERCEPCION_IVA_10_5",
          "porcentaje": 0.5,
          "netominimo": 0.0
        }
      ]
    },
    "articulos": [
      {
        "id": 1,
        "articulo": {
          "ean": "7791234567890",
          "plu": "112233",
          "descripcion": "ARROZ",
          "pesable": false,
          "preciolista": 1310.0,
          "rubro": "ALIMENTOS",
          "depto": "ALMACEN",
          "marca": "GALLO",
          "codigoclasificacion": 102234,
          "proveedor": "LEVER",
          "esarticuloconoferta": false,
          "nucleoimpositivo": [
            {
              "impuesto": {
                "id": "NETO_IVA_21"
              },
              "monto": 1000.0
            },
            {
              "
...

listapromociones.json

{
  "listapromociones": [
    {
      "id": 1,
      "descripcion": "PROMO_2X1_ARROZ",
      "metodocomputo": {
        "id": "CANTIDAD"
      },
      "tipobeneficio": {
        "id": "PORCENTAJE_DESCUENTO"
      },
      "valorbeneficio": 50.0,
      "codigodescarga": null,
      "conveniosclientes": [],
      "condiciones": {
        "acumulativa": false,
        "maximacantidadpromosxticket": 1,
        "montominimo": 0.0,
        "excluyearticulosoferta": false,
        "usapreciolistaarticulo": true
      },
      "vigencia": {
        "fechadesde": "20260301",
        "fechahasta": "20260331",
        "diassemana": [
          "MIERCOLES"
        ],
        "horadesde": "10",
        "horahasta": "11"
      },
      "sucursales": [],
      "mediosdepago": [],
      "listasitems": [
        {
          "listaindex": 1,
          "tipodeLista": {
            "id": "INCLUSION"
          },
          "nrolista": {
            "id": "LISTA1"
          },
          "tipoelemento": {
            "id": "EAN"
          },
          "cantidad": 2.0,
          "monto": null,
          "valores": [
            "7791234567890"
          ]
        }
      ]
    }
  ]
}

movimientos.json

{
  "movimientos": [
    {
      "id": 1,
      "concepto": "VENTA_ITEM",
      "origenid": 1,
      "movimientoid": null,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
          },
          "monto": 1000.0
        },
        {
          "impuesto": {
            "id": "IVA_21"
          },
          "monto": 210.0
        },
        {
          "impuesto": {
            "id": "IMPUESTOINTERNO_IVA_21"
          },
          "monto": 100.0
        }
      ]
    },
    {
      "id": 2,
      "concepto": "VENTA_ITEM",
      "origenid": 1,
      "movimientoid": null,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
          },
          "monto": 1000.0
        },
        {
          "impuesto": {
            "id": "IVA_21"
          },
          "monto": 210.0
        },
        {
          "impuesto": {
            "id": "IMPUESTOINTERNO_IVA_21"
          },
          "monto": 100.0
        }
      ]
    },
    {
      "id": 3,
      "concepto": "VENTA_ITEM",
      "origenid": 2,
      "movimientoid": null,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
          },
          "monto": 1000.0
        },
        {
          "impuesto": {
            "id": "IVA_21"
          },
          "monto": 210.0
        },
        {
          "impuesto": {
            "id": "IMPUESTOINTERNO_IVA_21"
          },
          "monto": 100.0
        }
      ]
    },
    {
      "id": 4,
      "concepto": "PROMOCION",
      "origenid": 1,
      "movimientoid": 1,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
          },
          "monto": -500.0
        },
        {
          "impuesto": {
            "id": "IVA_21"
          },
          "monto": -105.0
        },
        {
          "impuesto": {
            "id": "IMPUESTOINTERNO_IVA_21"
          },
          "monto": -50.0
        }
      ]
    },
    {
      "id": 5,
      "concepto": "PROMOCION",
      "origenid": 1,
      "movimientoid": 2,
      "nucleoimpositivo": [
        {
          "impuesto": {
            "id": "NETO_IVA_21"
      
...

promociones_postpago_ejemplo.json

{
  "promociones": [
    {
      "id": 2,
      "promocionid": 0,
      "pagoorigenid": 1,
      "descripcion": "PROMO POSTPAGO CHEQUE 0 CUOTAS",
      "tipoPromo": "PAGO",
      "promocionestado": {
        "id": "APLICADA"
      },
      "monto": -20.0,
      "elementos": [
        {
          "movimientoid": 1,
          "articuloid": 1,
          "unidadesimpactadas": 1.0,
          "monto": -10.0
        },
        {
          "movimientoid": 2,
          "articuloid": 1,
          "unidadesimpactadas": 1.0,
          "monto": -10.0
        }
      ]
    }
  ]
}

tiposdepago.json

{
  "tiposdepago": [
    {
      "id": 1,
      "idmdep": 1,
      "ididmdep": 10,
      "cuota": 0,
      "descripcion": "EFECTIVO",
      "davuelto": true,
      "vueltomediodepago": null
    },
    {
      "id": 2,
      "idmdep": 2,
      "ididmdep": 20,
      "cuota": 0,
      "descripcion": "CHEQUE",
      "davuelto": false,
      "vueltomediodepago": 1
    },
    {
      "id": 3,
      "idmdep": 3,
      "ididmdep": 20,
      "cuota": 3,
      "descripcion": "VISA 3 CUOTAS",
      "davuelto": false,
      "vueltomediodepago": 1
    }
  ]
}
Implementación

Lectura recomendada para arquitectura y desarrollo

↑ arriba

Qué mantener estable

No volver a mezclar items[] con ledger, no reintroducir conceptos obsoletos y mantener la prioridad documental: primero model.md, luego JSON.

Qué es clave en Java 21

Usar tipado fuerte, enums para catálogos cerrados, records para value objects simples y clases explícitas donde la mutabilidad del agregado lo necesite.

Qué revisar en cambios futuros

Impacto en cálculo, impacto en JSON, impacto en reconciliación y efecto sobre ejemplos de ticket antes de modificar el núcleo del modelo.

Lectura sugerida de onboarding: 1) ver el mapa del Ticket, 2) entender ledger y signos, 3) revisar motor de promociones, 4) leer POSTPAGO, 5) cerrar con ejemplos JSON.