Especificación para el proveedor del ERP

Servicios que necesita Ramaflex Móvil

Qué información necesita la aplicación para funcionar sobre su ERP. No es una lista de rutas a copiar: es el contenido que cada respuesta debe traer. Ustedes exponen los servicios como prefieran; la traducción a nuestro formato corre por nuestra cuenta.

Cada campo se puede expandir para ver tipo, formato y un ejemplo. Cada servicio trae un ejemplo completo de respuesta.

Cómo funciona la conexión

Ustedes publican una API; nosotros escribimos el traductor

No hace falta que respeten nuestros nombres de ruta, de campo ni el formato de la respuesta. Lo que se acuerda es qué dato trae cada servicio y qué significa.

Del lado nuestro hay una pieza que traduce su API a la forma que la aplicación espera. Mientras el dato exista y su significado esté declarado, el mapeo es trabajo nuestro. Lo que no exista en su API sencillamente no se implementa: la aplicación pierde esa función y el resto sigue operando.

Esa pieza consulta los datos del ERP en su API. Por diseño, lo que la aplicación necesita guardar también queda en su infraestructura, en tablas que ustedes crean con un script nuestro (ver Tablas de Ramaflex).

Los ejemplos de este documento usan nombres de campo ilustrativos, solo para mostrar el dato esperado. No son un formato obligatorio.

Aplican a todos los servicios

Reglas generales

Montos en enteros

Todo valor monetario en pesos enteros. Los redondeos intermedios cambian los totales y descuadran la factura; conviene que el redondeo ocurra una sola vez y del lado de ustedes.

Precios: una sola convención

La convención pedida es: el precio unitario va antes de descuento; los totales de línea, después. Si su ERP trabaja al revés no es impedimento — declárenlo y entreguen además el descuento aplicado y la regla de redondeo, y nosotros traducimos en ambos sentidos. Lo que no puede quedar es sin declarar: traducir entre convenciones solo es exacto si el redondeo está especificado, y si no, aparecen diferencias de un peso al cuadrar.

Impuestos por producto

Cada producto debe traer su tasa de IVA y la de impuestos adicionales. La aplicación totaliza sin red, así que no puede deducirlas: si faltan, asume una tasa fija y factura mal los productos exentos.

Fechas con zona horaria

ISO-8601 con offset. Una fecha sin zona hace que los documentos del día desaparezcan del filtro del vendedor cuando el teléfono y el servidor no coinciden.

Paginación e incremental

Ninguna consulta puede obligar a traer todo. Se requiere filtro desde fecha y paginación. Un catálogo de 30 mil productos sin paginar no llega por red móvil.

Errores con código estable

Cada condición de error necesita un código legible por máquina que no cambie. No sirve distinguir casos por el texto del mensaje.

Códigos sin relleno

Los códigos viajan recortados, sin espacios de relleno a la derecha. Un código con espacio sobrante se compara como distinto y hace desaparecer, en silencio, todo el historial del vendedor.

Ausente no es cero

Un campo que no aplica debe venir nulo, no en cero ni en blanco. La aplicación distingue "no hay dato" de "el dato es cero" y toma decisiones distintas.

Cómo conversan los dos sistemas

Contrato técnico

Lo anterior define qué dato viaja. Esta sección define cómo viaja. Son las decisiones que no se ven en un ejemplo de respuesta y que, si quedan sin acordar, aparecen recién cuando algo falla en terreno.

Estados HTTP y dónde va el código de error

Hoy tratamos cualquier respuesta 400 o superior como un fallo del servicio, sin distinguir entre ellas. Para que podamos reaccionar distinto hace falta acordar dos cosas: qué familia de estado usan para cada situación —no encontrado, dato inválido, sin permiso, error interno— y en qué campo del cuerpo viaja el código estable. Un ejemplo concreto: "la modalidad no existe" es un problema de configuración que hay que mostrarle a alguien, y "la base no responde" es un problema transitorio que hay que reintentar. Si las dos llegan como un 500 sin código, las tratamos igual y una de las dos se maneja mal.

Las dos formas de paginar que sabemos consumir

Soportamos exactamente dos, y hay que elegir una por servicio: por cursor, donde nos devuelven un valor que pedimos en la llamada siguiente hasta que deje de venir; o por número de página, donde enviamos página y tamaño y cortamos cuando la página viene incompleta. Si su API pagina de otra forma —offset y limit, cabecera Link, token con vencimiento— es perfectamente viable, pero hay que declararlo antes: es trabajo de traducción, no un mapeo.

Hay un tope de páginas, a propósito

Cortamos a las 100 páginas por consulta y devolvemos aviso. Es una protección contra un cursor que nunca cierra, no un límite de negocio. Si un servicio suyo puede superar ese volumen con el tamaño de página que acordemos, conviene saberlo de antemano y subir el tope para ese servicio: entregar un catálogo incompleto en silencio es peor que entregarlo con advertencia.

El formato de lo que enviamos nosotros

Las fechas que ustedes devuelven van en ISO-8601 con offset. Falta acordar lo simétrico: en qué formato y zona enviamos el "modificado desde" de las consultas incrementales. Nuestra propuesta es el mismo ISO-8601 con offset explícito, para que no dependa de la zona del servidor de ninguno de los dos. Si su API espera otra cosa —una fecha sin hora, un epoch, hora local— díganlo y lo enviamos así.

Qué hacemos si falta un campo obligatorio

Hoy un campo obligatorio ausente no genera error: el registro llega incompleto y el efecto aparece más tarde y lejos del origen. Por eso conviene acordarlo explícitamente por servicio: si falta el precio de un producto, ¿se descarta ese producto, se descarta la página, o falla la sincronización completa? Las tres son defendibles y la peor es la que está por defecto.

Cómo llegamos a su API

Necesitamos saber si la API es alcanzable desde nuestros servidores, si el certificado es de una autoridad reconocida o autofirmado, y si hay que solicitar apertura de firewall o declarar direcciones IP de origen. No es un detalle de última hora: es lo primero que detiene un piloto, y suele depender de un tercero con sus propios plazos.

Límite de peticiones frente al reintento

Estas dos reglas se cruzan y hay que resolverlas juntas. La aplicación reintenta indefinidamente un documento sin respuesta, como dice la sección siguiente. Si su API tiene un límite de peticiones por minuto, ese reintento legítimo puede quedar bloqueado y el documento no se graba nunca. Si existe el límite, necesitamos el número y, sobre todo, qué estado devuelven al alcanzarlo, para distinguirlo de un fallo real y esperar en vez de insistir.

Versionado y aviso de cambios

Si cambia un campo, un vocabulario o una ruta, hoy nos enteraríamos cuando se rompa en un teléfono. Basta con acordar algo mínimo: una versión en la ruta o en una cabecera, y un canal para avisar de cambios incompatibles con antelación. El traductor está de nuestro lado y se adapta rápido, pero solo si sabe que tiene que adaptarse.

Lo más importante del documento

Reglas de grabación

1. La misma clave no puede crear dos documentos

Cada documento que la aplicación envía lleva una clave única generada en el teléfono, antes del primer intento. Si no hay respuesta o la respuesta tarda, la aplicación reintenta con la misma clave, indefinidamente. Es su comportamiento normal, no una falla.

Su servicio debe garantizar que una clave repetida no crea un segundo documento, y que el reintento devuelve el folio del documento que ya existe. Sin ese folio la aplicación no puede cerrar el ciclo y el documento queda pendiente para siempre en el teléfono del vendedor.

2. Un error debe garantizar que no se grabó nada

La aplicación trata un error confirmado como permiso para reemplazar el documento: el vendedor lo edita y se envía uno nuevo, con clave nueva.

Si su servicio responde error después de haber grabado, se genera un duplicado real en el ERP. Si no pueden garantizarlo, respondan sin error y dejen que la aplicación reintente con la misma clave: el reintento es seguro, el error no.

3. La respuesta debe devolver la clave recibida

Los documentos que la aplicación descarga después se aparean con los del teléfono por esa clave. Si el documento creado no la conserva y no la devuelve al consultarlo, la aplicación no reconoce lo que ella misma creó.

Bloque obligatorio

Los once servicios sin los cuales la app no opera

Con estos la aplicación inicia sesión, sincroniza y registra una venta. Todo lo demás es opcional y puede quedar para una segunda etapa.

1 · Validar credenciales del vendedor

obligatorio
Para qué: es la puerta de la aplicación. El vendedor ingresa su código y su clave.
Le enviamoscódigo de vendedor y clave
CampoOblig.Para qué
Credencial válidasíVerdadero o falso›

tipo

booleano

ejemplo

true

Nunca devuelvan la clave ni su hash. Solo el veredicto. Si la credencial es incorrecta, un error con código estable — no una respuesta vacía.

Código de vendedorsíEl código canónico, para el resto de las consultas›

tipo

texto

formato

sin espacios de relleno

ejemplo

"11"

Es el código con que la aplicación pedirá después su cartera, su historial y sus metas. Si aquí viene con espacios y en otro servicio sin ellos, las comparaciones fallan y el vendedor ve todo vacío, sin ningún error.

Ejemplo de respuesta
{
  "valida": true,
  "vendedor": "11"
}

2 · Datos del vendedor

obligatorio
Para qué: identifica al vendedor y define con qué modalidad de venta trabaja.
Le enviamoscódigo de vendedor
CampoOblig.Para qué
CódigosíIdentifica al vendedor en todo el sistema›

tipo

texto

formato

sin relleno

ejemplo

"11"

NombresíSe muestra en pantalla y en el comprobante›

tipo

texto

ejemplo

"Claudia Fuentes"

RUTnoIdentificación tributaria del vendedor›

tipo

texto

formato

con dígito verificador

ejemplo

"15482930-4"

CorreonoEnvío de comprobantes›

tipo

texto

ejemplo

"cfuentes@empresa.cl"

ModalidadsíCon qué configuración de venta opera›

tipo

texto

ejemplo

"PREVENTA"

Es la llave con que se consulta el servicio de reglas de la modalidad, que define bodega, caja y lista de precio. Un vendedor sin modalidad no puede abrir una venta.

InactivosíUn vendedor inactivo no debe poder operar›

tipo

booleano

ejemplo

false

Ejemplo de respuesta
{
  "codigo": "11",
  "nombre": "Claudia Fuentes",
  "rut": "15482930-4",
  "correo": "cfuentes@empresa.cl",
  "modalidad": "PREVENTA",
  "inactivo": false
}

3 · Empresa

obligatorio
Para qué: datos de la empresa que emite los documentos.
Le enviamosRUT de la empresa
CampoOblig.Para qué
CódigosíIdentifica la empresa en el resto de las consultas›

tipo

texto

ejemplo

"01"

Casi todas las consultas llevan este código. Si su ERP maneja una sola empresa, igual necesitamos un valor constante con el que identificarla.

RUT y razón socialsíEncabezado del comprobante›

tipo

texto

ejemplo

"76543210-3"
"Distribuidora del Sur SpA"

El pedido consume créditosíSi un pedido pendiente descuenta del cupo del cliente›

tipo

booleano

ejemplo

true

Define si un pedido aún no facturado descuenta del cupo disponible. Si el dato falta, el control de crédito no se aplica y no se emite ningún aviso: el vendedor puede vender por sobre el cupo sin enterarse.

Ejemplo de respuesta
{
  "codigo": "01",
  "rut": "76543210-3",
  "razonSocial": "Distribuidora del Sur SpA",
  "pedidoConsumeCredito": true
}

4 · Permisos y tope de descuento

obligatorio
Para qué: qué puede hacer el vendedor y hasta qué descuento puede otorgar sin autorización.
Le enviamoscódigo de vendedor
CampoOblig.Para qué
Tope de descuentosíMáximo que puede aplicar, ya resuelto a un valor›

tipo

número

formato

porcentaje

ejemplo

15

Debe venir resuelto, no como una regla a interpretar. Si en su ERP el tope depende del producto, de la lista o del cliente, resuélvanlo antes de responder: la aplicación aplica el número tal cual, sin red.

Listas de precio permitidassíSobre qué listas puede vender›

tipo

lista de textos

ejemplo

["01P", "02P"]

Bodegas permitidassíDesde qué bodegas puede despachar›

tipo

lista de textos

ejemplo

["910", "920"]

Si la lista viene vacía, el vendedor no ve stock de ningún producto y no puede vender. Es una causa frecuente de "la aplicación no muestra productos" que en realidad es configuración del ERP.

Tipos de documento permitidossíQué puede emitir: pedido, boleta, factura›

tipo

lista de textos

ejemplo

["PEDIDO", "BOLETA", "FACTURA"]

Los valores concretos se acuerdan al definir el vocabulario de tipos de documento. Lo importante es que sea una lista cerrada y estable.

Ejemplo de respuesta
{
  "topeDescuento": 15,
  "listasPermitidas": ["01P", "02P"],
  "bodegasPermitidas": ["910", "920"],
  "tiposDocumento": ["PEDIDO", "BOLETA", "FACTURA"]
}

5 · Reglas de la modalidad

obligatorio
Para qué: con qué bodega, caja y lista de precio opera el vendedor. Define el contexto de toda la venta.
Le enviamosempresa y modalidad
CampoOblig.Para qué
Lista de precio de ventasíLista por defecto de la modalidad›

tipo

texto

ejemplo

"01P"

Sin este valor el catálogo queda vacío o cae a la lista del cliente, que puede no ser la correcta. Es el campo que más veces explica un catálogo sin precios.

BodegasíDesde dónde se despacha y contra qué se mide el stock›

tipo

texto

ejemplo

"910"

SucursalsíSucursal emisora del documento›

tipo

texto

ejemplo

"001"

CajanoPara el arqueo de caja›

tipo

texto

ejemplo

"CAJA01"

Tipos de documento excluidosnoQué tipos no se ofrecen en el selector›

tipo

lista de textos

ejemplo

["FACTURA_EXENTA"]

Ejemplo de respuesta
{
  "empresa": "01",
  "modalidad": "PREVENTA",
  "listaVenta": "01P",
  "bodega": "910",
  "sucursal": "001",
  "caja": "CAJA01",
  "tiposExcluidos": ["FACTURA_EXENTA"]
}

6 · Clientes

obligatorio
Para qué: la cartera del vendedor, con todo lo necesario para visitarla y venderle sin red.
Le enviamosempresa, código de vendedor y fecha desde (sincronización incremental)
CampoOblig.Para qué
Código y sucursalsíUn cliente puede tener varias sucursales de entrega›

tipo

texto y texto

ejemplo

"CL0042" · "001"

Las dos identifican la venta. Si su ERP no maneja sucursales, devuelvan un valor constante: la aplicación necesita el par completo para grabar.

Razón socialsíSe muestra y se imprime›

tipo

texto

ejemplo

"Minimarket Los Aromos"

RUTsíCon dígito verificador›

tipo

texto

formato

cuerpo-DV

ejemplo

"77921052-9"

Incluyan el dígito verificador. Si llega sin él, calcularlo por checksum produce un RUT que también valida pero no es el del cliente; ya ocurrió y el documento sale a nombre equivocado.

Dirección, comuna, ciudadsíRuta de visita y datos del documento›

tipo

texto

ejemplo

"Av. Los Aromos 1450"
"Puente Alto" · "Santiago"

CoordenadasnoUbicación en el mapa de la ruta›

tipo

número decimal

ejemplo

-33.61169 · -70.57556

Lista de precio del clientesíQué lista se le aplica cuando difiere de la modalidad›

tipo

texto

ejemplo

"02P"

Si el cliente no tiene lista propia, devuelvan nulo y se usa la de la modalidad. No devuelvan vacío ni cero.

Condición de pagosíContado o plazo, y a cuántos días›

tipo

texto y número

ejemplo

"CREDITO" · 30

Sin este dato el arqueo de caja emite avisos de pagos faltantes que son falsos positivos: el vendedor cree que dejó de cobrar algo que en realidad era a plazo.

Día de visita y de cobronoArma la ruta semanal del vendedor›

tipo

texto

ejemplo

"MA" (martes)

BloqueadonoImpide venderle›

tipo

booleano

ejemplo

false

Ausente se interpreta como "no se sabe" y no bloquea. Si su ERP maneja bloqueo de clientes, conviene enviarlo siempre.

Fecha de modificaciónsíHabilita la sincronización incremental›

tipo

fecha y hora

formato

ISO-8601 con offset

ejemplo

"2026-09-10T14:32:05-03:00"

Es lo que permite pedir solo lo que cambió. Sin ella, cada sincronización tiene que bajar la cartera completa.

Ejemplo de respuesta
{
  "pagina": 1,
  "total": 214,
  "clientes": [
    {
      "codigo": "CL0042",
      "sucursal": "001",
      "razonSocial": "Minimarket Los Aromos",
      "rut": "77921052-9",
      "direccion": "Av. Los Aromos 1450",
      "comuna": "Puente Alto",
      "ciudad": "Santiago",
      "latitud": -33.61169,
      "longitud": -70.57556,
      "listaPrecio": "02P",
      "condicionPago": "CREDITO",
      "diasPlazo": 30,
      "diaVisita": "MA",
      "diaCobro": "VI",
      "bloqueado": false,
      "modificadoEl": "2026-09-10T14:32:05-03:00"
    }
  ]
}

7 · Catálogo de productos

obligatorio
Para qué: qué se puede vender. Se combina con la lista de precio: un producto sin precio no aparece.
Le enviamosempresa, modalidad y el indicador de venta
CampoOblig.Para qué
CódigosíIdentifica el producto en precio, stock y venta›

tipo

texto

ejemplo

"116231"

Debe ser el mismo código en catálogo, lista de precio y stock. Si en alguno viene distinto, el producto aparece sin precio o sin existencia.

DescripciónsíLo que ve el vendedor y sale impreso›

tipo

texto

ejemplo

"Bebida Cola 1,5 L"

Código alternativonoCódigo de barras o código del cliente›

tipo

texto

ejemplo

"7801234567890"

Habilita buscar el producto escaneando o tecleando el código de barras.

Unidad primariasíEn qué se vende: unidad, caja, kilo›

tipo

texto

ejemplo

"UN"

Unidad secundarianoSegunda unidad de venta, si existe›

tipo

texto

ejemplo

"CJ"

Si el producto se vende en dos unidades, hace falta además cuántas unidades primarias contiene la secundaria, para poder convertir.

Divisible por unidadsíSi se admite vender fracción›

tipo

booleano, uno por unidad

ejemplo

true · false

Determina si el vendedor puede digitar 1,5 o solo enteros. Se necesita uno por cada unidad de venta.

Familia, subfamilia, líneasíTres niveles de agrupación del catálogo›

tipo

texto por nivel

ejemplo

"BEB" · "GAS" · "COLA"

Los niveles pueden repetir código entre sí. El mismo código puede significar cosas distintas en el nivel 1 y en el 2, así que el nivel debe venir junto al código. Resolver el nombre sin el nivel da el nombre equivocado.

MarcanoFiltro de búsqueda›

tipo

texto

ejemplo

"ANDINA"

Peso unitario en kgnoSolo si hay metas medidas en kilos›

tipo

número decimal

ejemplo

1.62

Ejemplo de respuesta
{
  "pagina": 1,
  "total": 3480,
  "productos": [
    {
      "codigo": "116231",
      "descripcion": "Bebida Cola 1,5 L",
      "codigoBarras": "7801234567890",
      "unidad1": "UN",
      "unidad2": "CJ",
      "unidadesPorUnidad2": 6,
      "divisible1": true,
      "divisible2": false,
      "familia":    { "nivel": 1, "codigo": "BEB",  "nombre": "Bebidas" },
      "subfamilia": { "nivel": 2, "codigo": "GAS",  "nombre": "Gaseosas" },
      "linea":      { "nivel": 3, "codigo": "COLA", "nombre": "Cola" },
      "marca": "ANDINA",
      "pesoKg": 1.62
    }
  ]
}

8 · Precio por cliente

obligatorio
Para qué: a cuánto se le vende cada producto a ese cliente. Ya resuelto: la aplicación no combina listas ni interpreta reglas de precio.
Le enviamosempresa, lista de precio, modalidad y el cursor de paginación
CampoOblig.Para qué
Código de productosíCon qué producto del catálogo se corresponde›

tipo

texto

ejemplo

"116231"

Precio unitariosíEl precio aplicable, con la convención declarada›

tipo

entero

formato

pesos, antes de descuento

ejemplo

1290

Si su convención es otra, decláren la y entreguen la regla de redondeo. Ver "Precios: una sola convención" en las reglas generales.

Tasa de IVAsíLa aplicación totaliza sin red y no puede deducirla›

tipo

número

formato

porcentaje

ejemplo

19

Tasa de impuesto adicionalsíCero si no aplica›

tipo

número

formato

porcentaje

ejemplo

10

Si este dato falta, la aplicación asume una tasa fija y termina cobrando IVA a un producto exento. Envíen cero explícito cuando no aplique.

Tramos por cantidadnoPrecio distinto según cantidad comprada›

tipo

lista de objetos

ejemplo

desde 12 → 1190

Qué trae cada tramo
propiedadoblig.tipoqué es
desdeCantidadsínúmeroLa cantidad a partir de la cual rige ese precio. El tramo se entiende abierto hacia arriba hasta el siguiente.
precioUnitariosíenteroEl unitario de ese tramo, antes de descuento, igual que el precio base.

Solo si esa política existe en su ERP. La aplicación los muestra como referencia al vendedor. No los aplica sola: el precio que se cobra sigue siendo el que el vendedor elige en pantalla.

Descuento automáticonoDescuento de la lista, que la app aplica sola›

tipo

número

formato

porcentaje

ejemplo

5

Este servicio tiene que estar paginado. Hay instalaciones con más de 30 mil productos; sin paginación la consulta no termina. Necesitamos poder pedir páginas con un cursor y un tamaño.
Ejemplo de respuesta
{
  "lista": "01P",
  "pagina": 1,
  "cursorSiguiente": "116240",
  "precios": [
    {
      "producto": "116231",
      "precioUnitario": 1290,
      "tasaIva": 19,
      "tasaAdicional": 0,
      "descuentoLista": 5,
      "tramos": [
        { "desdeCantidad": 12, "precioUnitario": 1190 }
      ]
    }
  ]
}

9 · Stock

obligatorio
Para qué: qué hay disponible para vender, por bodega.
Le enviamosempresa y bodegas
CampoOblig.Para qué
Código de productosíCorrespondencia con el catálogo›

tipo

texto

ejemplo

"116231"

BodegasíEl stock es por bodega, no global›

tipo

texto

ejemplo

"910"

Existencia por unidadsíUna cifra por cada unidad de venta›

tipo

número decimal

ejemplo

248 (UN) · 41 (CJ)

Si el producto se vende en dos unidades, hacen falta las dos existencias, o la conversión para calcularlas.

No filtren por existencia mayor a cero. Se necesitan también las filas en cero y negativas: sin ellas es imposible ajustar el inventario de un producto agotado.
Ejemplo de respuesta
{
  "stock": [
    { "producto": "116231", "bodega": "910", "existencia1": 248, "existencia2": 41 },
    { "producto": "116232", "bodega": "910", "existencia1": 0,   "existencia2": 0  }
  ]
}

10 · Documentos del vendedor

obligatorio
Para qué: el historial de ventas, para consultarlo, reimprimirlo y aparearlo con lo que el teléfono tiene guardado. Devuelve todos los tipos de documento: boletas, facturas y notas de venta o pedidos por igual.
Le enviamosvendedor y fecha desde. También por folio o por clave de origen, para buscar uno puntual
La búsqueda por clave de origen no es opcional. Cuando un envío queda sin respuesta, la aplicación reintenta y después pregunta por la clave para averiguar si el documento llegó a crearse. Si ese filtro no existe, no hay forma de distinguir "no se grabó" de "se grabó y se perdió la respuesta", y es ahí donde aparecen los duplicados.
CampoOblig.Para qué
Clave de origensíLa clave que la aplicación envió al crearlo›

tipo

texto

formato

UUID v4

ejemplo

"ca12ec77-62bb-4f00-8226-1bc654d5a0c1"

Sin esta clave la aplicación no puede reconocer los documentos que ella misma creó, y quedan duplicados en pantalla: uno local y otro descargado. Es el campo que cierra el ciclo de idempotencia.

Folio y tiposíIdentificación del documento emitido›

tipo

texto

ejemplo

"0000001244" · "BOLETA"

Fecha de emisiónsíOrdena el historial y alimenta el filtro por día›

tipo

fecha y hora

formato

ISO-8601 con offset

ejemplo

"2026-09-10T11:05:00-03:00"

Cliente y sucursalsíA quién se le vendió›

tipo

texto

ejemplo

"CL0042" · "001"

VendedorsíQuién emitió el documento›

tipo

texto

ejemplo

"11"

Es el código del vendedor, el mismo del servicio 2. Sin él la aplicación no puede separar los documentos propios de los del resto del equipo, y el historial del vendedor sale mezclado.

Estado del documentonoEn qué etapa va›

tipo

texto

ejemplo

"PENDIENTE"

Aplica sobre todo a las notas de venta y pedidos: pendiente de despacho, despachado, facturado, anulado. Los valores concretos se acuerdan en el vocabulario. Sin este campo la aplicación muestra el documento pero no puede decir en qué etapa va, y el vendedor no sabe si lo que tomó ya se despachó.

Totales neto, impuestos y brutosíSe muestran y se reimprimen›

tipo

entero

ejemplo

12354 · 2347 · 14701

Líneas del documentosíProducto, cantidad, precios, descuentos e impuestos›

tipo

lista de objetos

Qué trae cada línea
propiedadoblig.tipoqué es
productosítextoEl código del catálogo. Si no calza con el del servicio 7, la línea se muestra sin nombre ni imagen.
descripcionsítextoLa del momento de la venta. Se usa para reimprimir el documento tal como salió, aunque el producto se haya renombrado después.
cantidadsínúmeroEn la unidad de la línea.
unidadsítextoCuál de las dos unidades del producto se usó.
precioUnitariosíenteroAntes de descuento.
descuentononúmeroEl porcentaje aplicado. Si hubo cascada, puede venir el consolidado.
tasaIvasínúmeroLa de esa línea. Un documento puede mezclar afectos y exentos.
tasaAdicionalsínúmeroCero si no tiene.
netoLineasíenteroDespués de descuento.
brutoLineasíenteroDespués de descuento, con impuestos.

Si mandan el total bruto de una línea, manden también el neto. Los dos van después de descuento. Si llega solo el bruto, la aplicación vuelve a restar el descuento y muestra un neto equivocado.

Estado de pagonoSi está pagado o pendiente, para la cobranza›

tipo

texto

ejemplo

"PENDIENTE"

Ejemplo de respuesta
{
  "documentos": [
    {
      "claveOrigen": "ca12ec77-62bb-4f00-8226-1bc654d5a0c1",
      "tipo": "BOLETA",
      "folio": "0000001244",
      "emitidoEl": "2026-09-10T11:05:00-03:00",
      "cliente": "CL0042",
      "sucursal": "001",
      "vendedor": "11",
      "totalNeto": 12354,
      "totalImpuestos": 2347,
      "totalBruto": 14701,
      "estadoPago": "PENDIENTE",
      "lineas": [
        {
          "producto": "116231",
          "descripcion": "Bebida Cola 1,5 L",
          "cantidad": 12,
          "unidad": "UN",
          "precioUnitario": 1290,
          "descuento": 5,
          "tasaIva": 19,
          "tasaAdicional": 0,
          "netoLinea": 14706,
          "brutoLinea": 17500
        }
      ]
    }
  ]
}

11 · Registrar la venta

obligatorio · escritura
Para qué: es el servicio que justifica la aplicación. Recibe el documento que el vendedor cerró en terreno.
Le enviamosla clave única del documento, el encabezado y las líneas
Este es el único servicio de grabación de venta, cualquiera sea el documento. Boleta, factura, nota de venta o pedido: lo que cambia es el tipo de documento que viaja en el encabezado, no el servicio. No hace falta un endpoint por tipo. Si su ERP modela el pedido como una entidad aparte de la venta —con su propia tabla y su propio folio— díganlo y lo enrutamos del lado nuestro: la aplicación seguirá viendo un solo servicio.
Campo del encabezadoOblig.Qué es
Clave únicasíClave de idempotencia generada en el teléfono›

tipo

texto

formato

UUID v4

ejemplo

"ca12ec77-62bb-4f00-8226-1bc654d5a0c1"

Se genera una sola vez, al crear el documento, y no cambia entre reintentos. Es la clave sobre la que se apoyan las tres reglas de grabación.

Vendedor, empresa, modalidadsíQuién vende y bajo qué configuración›

ejemplo

"11" · "01" · "PREVENTA"

Tipo de documentosíPedido, boleta o factura›

tipo

texto

ejemplo

"BOLETA"

Del vocabulario acordado. Es uno de los valores que hay que fijar antes de empezar a mapear.

Cliente y sucursalsíA quién se le vende›

ejemplo

"CL0042" · "001"

Total brutosíTotal del documento calculado por la aplicación›

tipo

entero

ejemplo

14701

La aplicación totaliza sin red y envía su resultado. Si su ERP recalcula y llega a otro número, hay que decidir cuál manda: lo esperable es que el ERP mande, pero el vendedor ya le mostró el suyo al cliente, así que la diferencia tiene que ser cero.

Condición de pagosíContado o plazo›

ejemplo

"CREDITO"

Fecha de entreganoCuándo se compromete el despacho›

formato

ISO-8601 con offset

ejemplo

"2026-09-12T00:00:00-03:00"

ObservaciónnoNota que escribe el vendedor›

tipo

texto

LíneassíProducto, cantidad, unidad, precios, descuentos y tasas›

tipo

lista de objetos

Qué trae cada línea
propiedadoblig.tipoqué es
productosítextoEl código del catálogo, servicio 7. Es la llave con que ustedes resuelven qué se vendió.
descripcionnotextoLa que vio el vendedor al cerrar. Viaja para dejar constancia de lo que se le mostró, no para que ustedes la usen como dato.
cantidadsínúmeroExpresada en la unidad de la línea, no en la unidad base. Puede ser decimal si el producto es divisible.
unidadsítextoCuál de las dos unidades del producto se usó. Sin ella, doce cajas y doce unidades son el mismo número.
bodeganotextoSolo cuando difiere de la bodega de la modalidad. Si no viene, se entiende la de la modalidad.
precioUnitariosíenteroAntes de descuento, según la convención de las reglas generales.
descuentosnolista de númerosHasta cuatro porcentajes en cascada: cada uno se aplica sobre el resultado del anterior, no sobre el precio original. Si su ERP admite uno solo, díganlo y consolidamos.
montoDescuentoLineasíenteroEl descuento ya resuelto en pesos. Va aparte de los porcentajes porque el redondeo de la cascada tiene que quedar fijado por quien lo calculó.
tasaIvasínúmeroLa del producto, no una tasa general. Un exento viaja en cero y eso es un dato, no un faltante.
tasaAdicionalsínúmeroImpuesto adicional del producto. Cero si no tiene.
netoLineasíenteroDespués de descuento. Es el valor que cuadra contra el total del documento.
brutoLineasíenteroNeto más impuestos de la línea, ya redondeado.

La convención de precios es la de las reglas generales: el unitario antes de descuento, los totales de línea después. Enviamos las dos cosas —los porcentajes y el monto ya resuelto— para que el redondeo no quede a interpretación: es la diferencia entre cuadrar y no cuadrar por un peso.

PagosnoSi la venta se cobra en el momento›

tipo

lista de objetos

Qué trae cada pago
propiedadoblig.tipoqué es
mediosítextoEfectivo, cheque, transferencia o tarjeta. Los valores concretos se acuerdan en el vocabulario.
montosíenteroEn pesos. La suma de los pagos puede ser menor que el bruto del documento: el saldo queda por cobrar.
banconotextoSolo si el medio es cheque. Es el código del maestro de bancos, si ese servicio existe.
numeroChequenotextoSolo si el medio es cheque.
fechaChequenofechaSolo si el medio es cheque. Es la fecha de cobro, que puede ser futura.

Si la venta se cobra en el momento, el pago viaja junto al documento y no por el servicio de cobranza. Los tres campos del cheque son obligatorios cuando el medio es cheque, y sobran en cualquier otro caso.

Qué debe responderOblig.Para qué
Folio asignadosíLa app lo muestra al vendedor y cierra el documento›

tipo

texto

ejemplo

"0000001244"

Identificador internosíPermite consultarlo después›

tipo

texto o número

ejemplo

91

La clave recibidasíCierra el ciclo de idempotencia›

ejemplo

"ca12ec77-62bb-4f00-8226-1bc654d5a0c1"

Si la clave ya se había usadosíIndicador explícito, con el folio existente›

tipo

booleano

ejemplo

true

Ante una clave repetida, respondan éxito con este indicador en verdadero y el folio del documento que ya existe. No respondan error: para la aplicación un error significa que puede reemplazar el documento, y ahí se produce el duplicado.

Las tres reglas de grabación aplican enteras a este servicio. Es donde un incumplimiento produce documentos duplicados en el ERP, no una falla de pantalla.
Un documento grande puede partirse en varios. Si su ERP tiene tope de líneas por documento y decide dividirlo, devuelvan la lista de folios creados; la aplicación sabe representar una venta repartida en documentos hermanos. Lo que no puede pasar es que el reintento con la misma clave genere una segunda tanda.
Ejemplo de lo que enviamos
{
  "clave": "ca12ec77-62bb-4f00-8226-1bc654d5a0c1",
  "vendedor": "11",
  "empresa": "01",
  "modalidad": "PREVENTA",
  "tipoDocumento": "BOLETA",
  "cliente": "CL0042",
  "sucursal": "001",
  "condicionPago": "CREDITO",
  "fechaEntrega": "2026-09-12T00:00:00-03:00",
  "observacion": "Dejar en bodega trasera",
  "totalBruto": 14701,
  "lineas": [
    {
      "producto": "116231",
      "descripcion": "Bebida Cola 1,5 L",
      "cantidad": 12,
      "unidad": "UN",
      "bodega": "910",
      "precioUnitario": 1290,
      "descuentos": [5, 0, 0, 0],
      "montoDescuentoLinea": 774,
      "tasaIva": 19,
      "tasaAdicional": 0,
      "netoLinea": 14706,
      "brutoLinea": 17500
    }
  ],
  "pagos": [
    { "medio": "EFECTIVO", "monto": 9701 },
    { "medio": "CHEQUE", "monto": 5000,
      "banco": "012", "numeroCheque": "0004471", "fechaCheque": "2026-10-15" }
  ]
}
Ejemplo de respuesta — documento creado
{
  "clave": "ca12ec77-62bb-4f00-8226-1bc654d5a0c1",
  "folio": "0000001244",
  "tipoDocumento": "BOLETA",
  "id": 91,
  "claveYaUsada": false
}
Ejemplo de respuesta — la clave ya se había usado
{
  "clave": "ca12ec77-62bb-4f00-8226-1bc654d5a0c1",
  "folio": "0000001244",
  "tipoDocumento": "BOLETA",
  "id": 91,
  "claveYaUsada": true
}

Bloque obligatorio · actualizado en la versión 8

Las tablas de Ramaflex en su infraestructura

La aplicación y su backoffice guardan datos propios de la operación: licencias, versión y estado de cada teléfono, actividades, concursos y ubicaciones. Por diseño, esos datos viven junto al ERP, en dieciséis tablas en su infraestructura: así toda la información del negocio queda en un solo lugar, con el respaldo y la seguridad que ustedes ya administran.

Les pedimos tres cosas: (1) crear las tablas con nuestro script; (2) publicar un solo endpoint, {urlBase}/{tabla}, que responda a GET, PUT y DELETE; (3) pasar la prueba de conformidad y avisarnos. Les entregamos el script, el código del endpoint en cuatro lenguajes y la prueba.

Estas tablas no se relacionan con las de su ERP (sin claves foráneas) y pueden ir en otra base o esquema. Su ERP no necesita usarlas: las leemos y escribimos nosotros, a través de su API. Respáldenlas como al resto de sus datos.

1 · Las tablas y su script un solo archivo por motor, que instala y actualiza

El script de las tablas

obligatorio
Las tablas se crean con un solo script por motor de base de datos. Elijan el de su motor y ejecútenlo en la base donde irán las tablas.
MotorScript
SQL Server (probado en 2017, 2019, 2022 y 2025)migraciones/sqlserver.sql
PostgreSQL (probado en 10, 11, 12, 13, 14, 15, 16, 17 y 18)migraciones/postgresql.sql
MySQL (probado en 5.7, 8.0, 8.4 y 9.7)migraciones/mysql.sql
Es el único archivo que reciben. Además de crear las tablas, el script guarda en la base el catálogo (la lista de tablas, claves y columnas con su tipo) en XESQUEMA.CATALOGO. El código del endpoint lo lee de ahí, así que no hay ningún otro archivo que mantener.
Si usan otro motor, avísennos y preparamos su script.

Cuando la aplicación necesite algo nuevo

actualizaciones
La aplicación sigue creciendo y a veces necesita una tabla o una columna nueva. Entonces publicamos una versión nueva del mismo script, que reemplaza a la anterior. Una instalación nueva ejecuta solo ese archivo, y una existente ejecuta el mismo para ponerse al día, venga de la versión que venga.
ReglaDetalle
Crea solo lo que faltaRevisa cada tabla, columna e índice y crea el que no exista. Lo que ya existe queda igual.
Nunca borraNo borra, renombra ni cambia nada, y no toca los datos. Se ejecuta sin detener la aplicación y se puede repetir sin efecto.
Anota su versiónAl terminar anota su número en XESQUEMA. Así sabemos qué versión tiene cada instalación.
Se toma soloEl script guarda el catálogo nuevo en XESQUEMA y el endpoint lo lee en menos de un minuto. No hay que reiniciar ni cambiar el código del endpoint.
Si no lo ejecutanSolo la función de la aplicación que necesita lo nuevo queda apagada; el resto sigue funcionando. Les avisamos por correo con la versión y para qué sirve.

Las dieciséis tablas

qué guarda cada una
La clave son las columnas que identifican una fila: al guardar, si ya existe una fila con esa clave se actualiza, y si no, se crea. Las columnas de cada tabla están en el script.
TablaClaveQué guarda
XESQUEMAVERSIONLa versión del script aplicada y, en CATALOGO, el catálogo de las tablas que lee el endpoint. La escribe solo el script; por la API es de solo lectura.
XREGISTERKOFULicencia de cada vendedor: si está habilitado y cuántos equipos puede usar.
XREGISTERDEVKOFU + DEVICEIDEquipos asociados a cada licencia.
XLICCONFIDXLICCONF (siempre 1)Tope de vendedores con licencia. Una sola fila.
XMOVILDOCHASHDocumentos emitidos desde la aplicación, para medir su uso.
XMOVILVERSIONKOFU + DEVICEIDVersión de la aplicación en cada equipo.
XMOVILESTADOEMPRESA + KOFU + DEVICEIDÚltimo estado informado por cada equipo: pendientes por subir, reloj y zona horaria.
XMOVILESTADOSYNCEMPRESA + KOFU + DEVICEID + SYNCResultado de cada tipo de sincronización por equipo.
XACTFLUJOEMPRESADefinición de las actividades que el vendedor completa en la visita.
XACTREGUUIDCada actividad registrada en terreno, con sus respuestas.
XACTFOTOUUIDREG + CAMPO + HASHReferencia a las fotos de cada actividad. Los archivos no van en la tabla.
XCONCURSOEMPRESA + PERIODODefinición del concurso de ventas del período.
XCONCURSOAVEMPRESA + PERIODO + KOFUAvance de cada vendedor en el concurso.
XTABFUMEIGNEMPRESA + AGRUPACION + CODIGOFamilias de productos que no cuentan para las metas.
XMAEENGPSUUIDUbicaciones de clientes capturadas en terreno y si fueron aprobadas.
XPRODIMGKOPR + ORDENReferencia a las imágenes de cada producto. Los archivos no van en la tabla.
Las filas de XREGISTER, XREGISTERDEV y XLICCONF llevan una firma que solo nuestro servidor puede calcular. Una fila creada o modificada fuera de la API deja de ser válida y ese vendedor no puede entrar a la aplicación. Por eso no deben editarse a mano.

2 · El endpoint uno solo, con tres métodos HTTP, para las dieciséis tablas

Un endpoint: {urlBase}/{tabla}

obligatorio
Para las dieciséis tablas se crea un solo endpoint, que responde a tres métodos HTTP: GET (listar), PUT (guardar) y DELETE (borrar). El nombre de la tabla va en la URL.
El endpoint{urlBase}/{tabla}
ParteQué es
{urlBase}La definen ustedes y nos la informan. No cambia. Por ejemplo, https://api.myerp.cl/ramaflex.
{tabla}Cambia en cada llamada: es el nombre de la tabla que leemos o escribimos (XACTREG, XMOVILESTADO…). Solo se aceptan las dieciséis del catálogo.
Ejemplohttps://api.myerp.cl/ramaflex/{tabla}
MétodoOperaciónLlamada de ejemplo
GETListarGET https://api.myerp.cl/ramaflex/XACTREG?KOFU=11
PUTGuardarPUT https://api.myerp.cl/ramaflex/XMOVILESTADO
DELETEBorrarDELETE https://api.myerp.cl/ramaflex/XCONCURSOAV?PERIODO=2026-11
Las tres llamadas llegan al mismo endpoint: no se crea un endpoint por tabla. Así se declara en cada framework:
FrameworkDeclaración del endpoint
Node.js · Expressapp.all('/ramaflex/:tabla', …)
Python · Flask@app.route('/ramaflex/<tabla>', methods=['GET', 'PUT', 'DELETE'])
Java · HttpServer del JDKservidor.createContext("/ramaflex/", …)
C# · ASP.NET Coreapp.Map("/ramaflex/{tabla}", …)
Autenticación: la misma del resto de su API, con una credencial de servicio (no la de un vendedor), porque parte de las escrituras vienen de nuestro panel de administración. El código que les entregamos ya trae el endpoint declarado; solo conectan su autenticación y su base.

Cómo un solo endpoint atiende las dieciséis tablas

cómo funciona
El endpoint toma {tabla} de la URL y busca esa tabla en el catálogo que el script guardó en XESQUEMA.CATALOGO, que dice qué columnas tiene, cuál es su clave y de qué tipo es cada columna. Con eso arma la consulta en el momento. Por eso se programa una vez y sirve para todas las tablas, también para las que traigan las próximas versiones del script.
Por ejemploGET https://api.myerp.cl/ramaflex/XMOVILVERSION?KOFU=11&orden=-ULTIMAVEZ
PasoQué hace el código
1 · La tablaToma XMOVILVERSION de la ruta y la busca en el catálogo. Si no está, responde TABLA_DESCONOCIDA y no consulta la base.
2 · Las columnasBusca cada columna del filtro y del orden (KOFU, ULTIMAVEZ) entre las columnas de esa tabla en el catálogo. Si alguna no está, responde COLUMNA_DESCONOCIDA.
3 · Los valoresConvierte cada valor según el tipo que dice el catálogo: KOFU es texto de hasta 20 caracteres. Si no calza, responde DATO_INVALIDO.
4 · La consultaEscribe la consulta con los nombres ya validados y los valores como parámetros, nunca pegados al texto. Completa el orden con la clave de la tabla para que las páginas no se repitan.
5 · La respuestaConvierte cada fila a JSON según el tipo de cada columna (las fechas, en UTC con Z) y responde { "filas": [...] }.
Lo que dice el catálogo de XMOVILVERSION y la consulta que resulta
"XMOVILVERSION": {
  "desde": 1,
  "clave": ["KOFU", "DEVICEID"],
  "columnas": {
    "KOFU":         { "tipo": "texto", "largo": 20, "nulo": false },
    "DEVICEID":     { "tipo": "texto", "largo": 64, "nulo": false },
    "APPVERSION":   { "tipo": "texto", "largo": 20, "nulo": false, "defecto": "" },
    "PLATAFORMA":   { "tipo": "texto", "largo": 32, "nulo": true },
    "EMPRESA":      { "tipo": "texto", "largo": 10, "nulo": true },
    "PRIMERAVEZ":   { "tipo": "fecha", "nulo": false },
    "DESDEVERSION": { "tipo": "fecha", "nulo": false },
    "ULTIMAVEZ":    { "tipo": "fecha", "nulo": false }
  }
}

-- La consulta que arma el código (SQL Server; @p1 = '11', @p2 = 0, @p3 = 1000):
SELECT KOFU, DEVICEID, APPVERSION, PLATAFORMA, EMPRESA, PRIMERAVEZ, DESDEVERSION, ULTIMAVEZ
FROM XMOVILVERSION
WHERE KOFU = @p1
ORDER BY ULTIMAVEZ DESC, KOFU, DEVICEID
OFFSET @p2 ROWS FETCH NEXT @p3 ROWS ONLY
Seguridad: el endpoint solo acepta las tablas y columnas del catálogo y rechaza cualquier otra con un error. No da acceso a las tablas de su ERP ni recibe SQL: los nombres se comparan contra la lista y los valores van siempre como parámetros.

3 · Las tres operaciones las tres en el mismo endpoint, {urlBase}/{tabla}

Listar · GET

consulta
Para qué: leer filas de una tabla, con filtros y por páginas.
EndpointGET {urlBase}/{tabla}?filtros · {tabla} es la tabla que se lee
EjemploGET https://api.myerp.cl/ramaflex/XACTREG?EMPRESA=01&KOFU=11&FECHA.desde=2026-11-01T00:00:00Z&orden=-FECHA&pagina=1&limite=500
Las actividades de la empresa 01 y el vendedor 11 desde el 1 de noviembre, de la más nueva a la más antigua.
ParámetroSignificado
COLUMNA=valorLa columna es igual al valor. Si se repite la misma columna, basta con que sea igual a cualquiera de los valores (KOFU=11&KOFU=12).
COLUMNA.desde · COLUMNA.hastaRango, con ambos extremos incluidos. Sirve para fechas, números y texto.
ordenColumna por la que se ordena; con un guion delante, de mayor a menor. Se puede repetir.
pagina · limitePágina desde 1 y filas por página, hasta 1000 (por defecto 1 y 1000). Una página con menos filas que el límite es la última.
Sin filtrosDevuelve todas las filas, por páginas.
Responde200 { "filas": [ … ] } con todas las columnas de cada fila, también las nulas. Si ninguna fila cumple el filtro: { "filas": [] }.
Ejemplo de respuesta
200
{
  "filas": [
    {
      "IDXACTREG": 381,
      "UUID": "3f1c2a9e-5b7d-4c11-9a0e-2d6f8b4e7c10",
      "EMPRESA": "01",
      "KOFU": "11",
      "KOEN": "76123456",
      "SUEN": "",
      "ACTIVIDAD": "QUIEBRE_STOCK",
      "FECHA": "2026-11-03T14:22:05.000Z",
      "FECHAREG": "2026-11-03T14:22:41.512Z",
      "VERSION": null,
      "RESPUESTAS": "{\"hayQuiebre\":true}",
      "APPVERSION": "1.15.0",
      "DEVICEID": "a41f09c2e7"
    }
  ]
}

Guardar · PUT

escritura
Para qué: crear o actualizar filas. Cada fila trae las columnas de su clave: si ya existe una fila con esa clave se actualiza, y si no, se crea.
EndpointPUT {urlBase}/{tabla} · {tabla} es la tabla donde se guarda · cuerpo { "filas": [ … ] }, hasta 500 filas
EjemploPUT https://api.myerp.cl/ramaflex/XMOVILESTADO
El último estado informado por un teléfono.
ReglaDetalle
Solo lo enviadoAl actualizar, se cambian solo las columnas que vienen en la fila; las demás quedan como estaban. Al crear, las que no vienen toman su valor por defecto o quedan nulas.
Todo o nadaSi una fila falla, no se guarda ninguna y la respuesta es un error. Aplica la regla 2 de grabación.
RepetibleEnviar dos veces la misma llamada deja el mismo resultado. Así podemos reintentar sin riesgo.
AutonuméricosLas columnas ID… autonuméricas las asigna su base. Nunca las enviamos y la respuesta las devuelve.
Responde200 { "filas": [ … ] } con las filas tal como quedaron guardadas (todas las columnas), en el mismo orden en que llegaron.
Ejemplo de llamada y respuesta
PUT https://api.myerp.cl/ramaflex/XMOVILESTADO
{
  "filas": [
    {
      "EMPRESA": "01",
      "KOFU": "11",
      "DEVICEID": "a41f09c2e7",
      "APPVERSION": "1.15.0",
      "PLATAFORMA": "android",
      "PENDIENTES": "{\"documentosSinSubir\":0}",
      "RECIBIDOEN": "2026-11-03T14:30:01.204Z"
    }
  ]
}

→ 200
{
  "filas": [
    {
      "EMPRESA": "01",
      "KOFU": "11",
      "DEVICEID": "a41f09c2e7",
      "APPVERSION": "1.15.0",
      "PLATAFORMA": "android",
      "ENVIADOEN": null,
      "HUSOMINUTOS": null,
      "PENDIENTES": "{\"documentosSinSubir\":0}",
      "RECIBIDOEN": "2026-11-03T14:30:01.204Z"
    }
  ]
}

Borrar · DELETE

escritura
Para qué: eliminar las filas que cumplen un filtro. Se usa, por ejemplo, para rehacer el avance de un concurso cuando cambia su definición.
EndpointDELETE {urlBase}/{tabla}?filtros · {tabla} es la tabla donde se borra · los mismos filtros de Listar
EjemploDELETE https://api.myerp.cl/ramaflex/XCONCURSOAV?EMPRESA=01&PERIODO=2026-11
El avance de todos los vendedores en el concurso de noviembre.
Responde200 { "borradas": 14 }, con la cantidad de filas borradas (0 si ninguna cumplía el filtro).
Sin ningún filtro debe responder el error FILTRO_REQUERIDO y no borrar nada: un filtro olvidado no puede vaciar una tabla.

Errores

los tres métodos
Ante un error, el endpoint responde el código HTTP de la tabla y el cuerpo { "ok": false, "codigo": "…", "mensaje": "…" }. Nosotros decidimos por codigo; el mensaje es para leerlo en el log.
codigoHTTPCuándo
NO_AUTENTICADO401 o 403Falta la credencial o no es válida. Si la rechaza su gateway antes de llegar al endpoint, basta el estado: el cuerpo puede ser el de su API.
TABLA_DESCONOCIDA404{tabla} no está en el catálogo.
COLUMNA_DESCONOCIDA400Un filtro, un orden o una fila trae una columna que esa tabla no tiene.
CLAVE_INCOMPLETA400Una fila del PUT no trae todas las columnas de la clave, o trae alguna en null.
DATO_INVALIDO400Un valor no calza con el tipo o el largo de su columna, falta un dato obligatorio, o el cuerpo no es { "filas": [ … ] } con hasta 500 filas.
FILTRO_REQUERIDO400DELETE sin ningún filtro.
SOLO_LECTURA405PUT o DELETE sobre XESQUEMA.
METODO_NO_PERMITIDO405Un método distinto de GET, PUT o DELETE.
CONFLICTO409Dos escrituras de la misma fila al mismo tiempo. Reintentamos.
ERROR_INTERNO500Cualquier otra falla. No se grabó nada.

Nombres y formatos

Cada fila es un objeto con los nombres de columna en mayúsculas, tal como en el script, aunque su motor los guarde en minúsculas. Fechas en ISO-8601 en UTC (2026-11-03T14:22:05.000Z), booleanos true/false, números como números y ausencia de dato como null. Los textos vuelven sin espacios de relleno.

La respuesta HTTP

Toda respuesta, de éxito o de error, trae Content-Type: application/json y el JSON en la raíz, sin envoltorio ({ "filas": [ … ] }, no { "code": 0, "data": … }), aunque la llamada traiga las cabeceras del resto de su API. Llamamos con Connection: close: si su servidor cierra la conexión después de responder, la respuesta debe decir Connection: close.

Tiempo de respuesta

Son tablas chicas: crecen con la cantidad de vendedores, equipos y visitas, no con el volumen de ventas. Cada llamada debería responder en menos de un segundo.

Si su ERP quiere usarlas

Pueden leerlas cuando quieran. Por ejemplo, las ubicaciones aprobadas (XMAEENGPS con ESTADO = 'A') sirven para actualizar la ficha del cliente. No es necesario para que la aplicación funcione.

4 · Ejemplos llamadas y respuestas reales, tal como debe responder su endpoint

Todos con la URL base de ejemplo https://api.myerp.cl/ramaflex. En cada llamada, la tabla es lo que va después de la URL base.
1 · Crear una fila: la base asigna el autonumérico
PUT https://api.myerp.cl/ramaflex/XACTREG
{
  "filas": [
    {
      "UUID": "3f1c2a9e-5b7d-4c11-9a0e-2d6f8b4e7c10",
      "EMPRESA": "01",
      "KOFU": "11",
      "KOEN": "76123456",
      "ACTIVIDAD": "QUIEBRE_STOCK",
      "FECHA": "2026-11-03T14:22:05Z",
      "FECHAREG": "2026-11-03T14:22:41.512Z",
      "RESPUESTAS": "{\"hayQuiebre\":true}",
      "APPVERSION": "1.15.0",
      "DEVICEID": "a41f09c2e7"
    }
  ]
}

→ 200   (IDXACTREG lo asignó la base; SUEN tomó su valor por defecto y VERSION quedó nula)
{
  "filas": [
    {
      "IDXACTREG": 1,
      "UUID": "3f1c2a9e-5b7d-4c11-9a0e-2d6f8b4e7c10",
      "EMPRESA": "01",
      "KOFU": "11",
      "KOEN": "76123456",
      "SUEN": "",
      "ACTIVIDAD": "QUIEBRE_STOCK",
      "FECHA": "2026-11-03T14:22:05.000Z",
      "FECHAREG": "2026-11-03T14:22:41.512Z",
      "VERSION": null,
      "RESPUESTAS": "{\"hayQuiebre\":true}",
      "APPVERSION": "1.15.0",
      "DEVICEID": "a41f09c2e7"
    }
  ]
}
2 · Leer con filtros, orden y página
GET https://api.myerp.cl/ramaflex/XACTREG
    ?EMPRESA=01&KOFU=11&FECHA.desde=2026-11-01T00:00:00Z
    &orden=-FECHA&pagina=1&limite=500
(una sola URL; aquí partida en líneas para leerla)

→ 200   (la misma fila del ejemplo 1, con todas sus columnas)
{
  "filas": [
    { "IDXACTREG": 1, "UUID": "3f1c2a9e-…", "EMPRESA": "01",
      "KOFU": "11", …, "VERSION": null, … }
  ]
}
3 · Actualizar: solo cambia lo enviado
Ya existe la fila EMPRESA 01, KOFU 11, DEVICEID a41f09c2e7, con PLATAFORMA "android" y APPVERSION "1.15.0".
Se envía solo la clave y lo que cambió:

PUT https://api.myerp.cl/ramaflex/XMOVILESTADO
{
  "filas": [
    {
      "EMPRESA": "01",
      "KOFU": "11",
      "DEVICEID": "a41f09c2e7",
      "APPVERSION": "1.16.0",
      "RECIBIDOEN": "2026-11-04T09:00:00Z"
    }
  ]
}

→ 200   (PLATAFORMA y PENDIENTES siguen como estaban)
{
  "filas": [
    {
      "EMPRESA": "01",
      "KOFU": "11",
      "DEVICEID": "a41f09c2e7",
      "APPVERSION": "1.16.0",
      "PLATAFORMA": "android",
      "ENVIADOEN": null,
      "HUSOMINUTOS": null,
      "PENDIENTES": "{\"documentosSinSubir\":0}",
      "RECIBIDOEN": "2026-11-04T09:00:00.000Z"
    }
  ]
}
4 · Guardar varias filas y borrarlas con un filtro
PUT https://api.myerp.cl/ramaflex/XCONCURSOAV
{
  "filas": [
    { "EMPRESA": "01", "PERIODO": "2026-11", "KOFU": "11",
      "GENERADOEN": "2026-11-04T09:00:00Z", "AVANCE": "{}",
      "FECHAMOD": "2026-11-04T09:00:00Z" },
    { "EMPRESA": "01", "PERIODO": "2026-11", "KOFU": "12",
      "GENERADOEN": "2026-11-04T09:00:00Z", "AVANCE": "{}",
      "FECHAMOD": "2026-11-04T09:00:00Z" }
  ]
}
→ 200   { "filas": [ …las dos filas guardadas… ] }

DELETE https://api.myerp.cl/ramaflex/XCONCURSOAV?EMPRESA=01&PERIODO=2026-11
→ 200   { "borradas": 2 }
5 · Leer la versión del esquema
GET https://api.myerp.cl/ramaflex/XESQUEMA

→ 200   (el script versión 2 aplicado; CATALOGO acortado aquí)
{
  "filas": [
    {
      "VERSION": 2,
      "DESCRIPCION": "El catalogo de las tablas se guarda en XESQUEMA.CATALOGO",
      "APLICADA": "2026-11-06T12:00:00.000Z",
      "CATALOGO": "{\"descripcion\":\"Tablas de Ramaflex ...\",\"version\":2,\"tablas\":{...}}"
    }
  ]
}
6 · Errores
# Una tabla de su ERP: no está en el catálogo
GET https://api.myerp.cl/ramaflex/MAEEN
→ 404  { "ok": false, "codigo": "TABLA_DESCONOCIDA",
         "mensaje": "Tabla desconocida: MAEEN" }

# XACTREG no tiene la columna CLAVE
GET https://api.myerp.cl/ramaflex/XACTREG?CLAVE=1
→ 400  { "ok": false, "codigo": "COLUMNA_DESCONOCIDA",
         "mensaje": "Columna desconocida: CLAVE" }

# Falta DEVICEID, que es parte de la clave
PUT https://api.myerp.cl/ramaflex/XMOVILESTADO
{ "filas": [ { "EMPRESA": "01", "KOFU": "11" } ] }
→ 400  { "ok": false, "codigo": "CLAVE_INCOMPLETA",
         "mensaje": "Falta la clave: EMPRESA + KOFU + DEVICEID" }

# HUSOMINUTOS es un entero
PUT https://api.myerp.cl/ramaflex/XMOVILESTADO
{ "filas": [ { "EMPRESA": "01", "KOFU": "11", "DEVICEID": "x",
               "HUSOMINUTOS": "tres", "RECIBIDOEN": "2026-11-04T09:00:00Z" } ] }
→ 400  { "ok": false, "codigo": "DATO_INVALIDO",
         "mensaje": "HUSOMINUTOS: valor inválido" }

# XESQUEMA solo la escriben los scripts
PUT https://api.myerp.cl/ramaflex/XESQUEMA
{ "filas": [ { "VERSION": 2 } ] }
→ 405  { "ok": false, "codigo": "SOLO_LECTURA",
         "mensaje": "XESQUEMA es de solo lectura." }

# Borrar sin filtro
DELETE https://api.myerp.cl/ramaflex/XCONCURSOAV
→ 400  { "ok": false, "codigo": "FILTRO_REQUERIDO",
         "mensaje": "Borrar exige al menos un filtro." }

5 · Código listo para copiar el endpoint ya escrito, para integrar en su API

No hace falta escribir el endpoint desde cero. Cada implementación tiene un núcleo que no depende del framework web (recibe método, tabla, parámetros y cuerpo, y devuelve estado y respuesta) y un servidor de ejemplo que muestra cómo montarlo. Soportan SQL Server, PostgreSQL y MySQL, y leen el catálogo de la base (XESQUEMA.CATALOGO), cada minuto. Solo deben conectar su autenticación y su conexión a la base.
LenguajeArchivosEstado
Pseudocódigoramaflex/pseudocodigo.txtEs la regla. Si implementan en otro lenguaje, sigan este texto.
Node.js (Express)tablas-ramaflex.js · servidor.js · package.json · LEEME.txtProbado con Node 24.
Python (Flask)ramaflex_tablas.py · servidor.py · requirements.txt · LEEME.txtProbado con Python 3.11.
Java (JDBC; ejemplo con el servidor del JDK)TablasRamaflex.java · ServidorEjemplo.java ·LEEME.txtProbado con Java 11, con el servidor del JDK.
C# .NET 8 (ASP.NET Core)TablasRamaflex.cs · Motores.cs · Program.cs · .csproj · LEEME.txtProbado con .NET 8.
OpenAPI 3ramaflex/openapi.yamlPara generar el esqueleto en cualquier lenguaje.
"Probado" significa que el código pasa la prueba de conformidad completa contra SQL Server 2022, PostgreSQL 18 y MySQL 8.0, y que las fechas quedan en UTC en la base. Las versiones de los drivers probados están en el LEEME de cada uno.

6 · Cómo se verifica antes de avisarnos, y después, desde nuestro lado

Prueba de conformidad

la ejecutan ustedes
Para qué: comprobar que su endpoint cumple el contrato completo: seguridad, tipos, fechas en UTC, todo o nada, filtros, páginas, errores y la forma de la respuesta HTTP. Es un script de Node, sin dependencias, probado con Node 16, 18 y 24: ramaflex/conformidad.js. Llama igual que nuestro servidor en producción (mismo cliente HTTP, mismas cabeceras de conexión), así que lo que pasa aquí es lo que nosotros vamos a poder usar.
Se ejecutaRAMAFLEX_CABECERAS='{…las cabeceras que les indiquemos…}' node conformidad.js https://api.myerp.cl/ramaflex <token de la credencial de servicio>
DatoCuál usar
URLLa URL base de su endpoint en su ambiente de pruebas: {urlBase}, sin /{tabla}.
TokenUno de la credencial de servicio que nos dieron, obtenido con su login como lo obtendríamos nosotros. Viaja como Authorization: Bearer <token>.
CabecerasLas mismas que enviamos en cada llamada a su API; se las indicamos por correo. Sin ellas la prueba puede pasar y nuestras llamadas fallar (por ejemplo, si una cabecera cambia el formato de la respuesta).
Escribe y borra filas de prueba (claves que empiezan con ZZPRUEBA). Ejecútenla contra una base de pruebas con el script aplicado, nunca en producción. Cuando pase completa, avísennos.

Diagnóstico y prueba de aceptación

lo ejecutamos nosotros
Para qué: revisar su instalación desde nuestro servidor. Muestra la versión de nuestro código y la del esquema que necesitamos frente a la que tienen ustedes en XESQUEMA, prueba cada tabla con una lectura de una fila (que responda y con todas sus columnas), prueba los servicios livianos (empresas, funcionarios, modalidad y permisos) y lista lo que falta.
Cuando nos avisen, corremos la misma prueba de conformidad desde nuestro servidor, con nuestra autenticación y nuestras cabeceras. Si la suya pasa y la nuestra no, el problema está de nuestro lado y lo resolvemos sin pedirles cambios. Esta corrida también escribe y borra filas ZZPRUEBA, solo en su ambiente de pruebas.

Bloque opcional

Los dieciocho servicios de la segunda etapa

Cada uno habilita una función de la aplicación. El que no exista simplemente no se implementa: la función queda apagada y el resto opera igual. Van agrupados por la función que habilitan, y cada uno se detalla igual que los obligatorios.

Pedidos pendientes 1 servicio · habilita no vender dos veces lo mismo cuando hay pedidos sin despachar

Stock comprometido

consulta
Para qué: no vender dos veces lo mismo. Es la existencia menos lo que ya está tomado en pedidos sin despachar.
Le enviamosempresa y bodegas
CampoOblig.Para qué
Producto y bodegasíCorrespondencia con el catálogo›

ejemplo

"116231" · "910"

Existencia y comprometidosíLo que hay y lo que ya está tomado›

tipo

número

ejemplo

248 · 36

DisponiblesíYa calculado por ustedes›

tipo

número

ejemplo

212

Envíen el resultado, no solo los insumos. Si la aplicación tuviera que restar por su cuenta, replicaría una regla que es de ustedes: qué estados de pedido comprometen existencia y cuáles no.

Ejemplo de respuesta
{
  "stock": [
    {
      "producto": "116231",
      "bodega": "910",
      "existencia": 248,
      "comprometido": 36,
      "disponible": 212
    }
  ]
}

Cobranza 3 servicios · habilita que el vendedor cobre documentos vencidos y rinda caja

Deuda del cliente

consulta
Para qué: qué le debe el cliente, para cobrarle en la visita.
Le enviamoscliente y sucursal, o el vendedor para traer toda su cartera
CampoOblig.Para qué
Tipo y foliosíQué documento se está cobrando›

ejemplo

"FACTURA" · "0000004512"

Fecha de emisión y vencimientosíDefine si está vencido y hace cuánto›

formato

ISO-8601 con offset

ejemplo

"2026-08-12T00:00:00-03:00"
"2026-09-11T00:00:00-03:00"

Monto y saldosíTotal del documento y lo que falta pagar›

tipo

entero

ejemplo

89250 · 45000

Los dos, no solo el saldo: el vendedor necesita ver el total original para conversar el abono con el cliente.

Estado de pagosíPendiente, abonado o pagado›

ejemplo

"ABONADO"

Ejemplo de respuesta
{
  "cliente": "CL0042",
  "documentos": [
    {
      "tipo": "FACTURA",
      "folio": "0000004512",
      "emitidoEl": "2026-08-12T00:00:00-03:00",
      "venceEl":   "2026-09-11T00:00:00-03:00",
      "monto": 89250,
      "saldo": 45000,
      "estado": "ABONADO"
    }
  ]
}

Registrar pago

escritura
Para qué: dejar constancia de lo que el vendedor cobró y contra qué documentos se aplica.
Le enviamosclave única, cliente, medio de pago, monto y los documentos que salda
Qué enviamosOblig.Contenido
Medio de pago y montosíCon qué y cuánto›

ejemplo

"EFECTIVO" · 45000

Los medios posibles se acuerdan en el vocabulario: efectivo, cheque, transferencia, tarjeta.

Documentos que saldasíContra qué deuda se aplica›

tipo

lista de objetos

Qué trae cada aplicación del pago
propiedadoblig.tipoqué es
tiposítextoTipo del documento que se salda, del mismo vocabulario que el resto.
foliosítextoFolio del documento que se salda. Junto al tipo lo identifica.
montosíenteroCuánto de este pago se aplica a ese documento. La suma de los montos debe dar el total del pago.

Un pago puede repartirse entre varios documentos. Si su ERP aplica el abono por su cuenta, dígalo: la aplicación puede enviar solo el monto total.

Datos del chequenoSolo cuando el medio es cheque›

tipo

banco, número y fecha

ejemplo

"012" · "0001234" · 2026-09-25

Respuesta: folio del pagosíComprobante del cobro›

ejemplo

"RC0000891"

Los pagos no tienen cola offline. A diferencia de las ventas, requieren red en el momento: la aplicación solo guarda el pago después de que el servicio responde. Si el servicio no está disponible, el cobro se pierde y hay que volver a tomarlo. Conviene considerarlo al dimensionar la disponibilidad.
Ejemplo de lo que enviamos
{
  "clave": "3c9d5e71-8b42-4a0f-9d16-5e7c2b8f3a41",
  "cliente": "CL0042",
  "sucursal": "001",
  "vendedor": "11",
  "medio": "EFECTIVO",
  "monto": 45000,
  "aplicaA": [
    { "tipo": "FACTURA", "folio": "0000004512", "monto": 45000 }
  ]
}
Ejemplo de respuesta
{
  "clave": "3c9d5e71-8b42-4a0f-9d16-5e7c2b8f3a41",
  "numero": "REC-0000891",
  "yaExistia": false
}

Bancos

maestro
Para qué: completar los datos de un cheque con valores válidos.
Le enviamosnada; es una lista completa
CampoOblig.Para qué
Código y nombresíIdentifica el banco del cheque›

ejemplo

"012" · "Banco Estado"

Ejemplo de respuesta
{
  "bancos": [
    { "codigo": "012", "nombre": "Banco Estado" },
    { "codigo": "001", "nombre": "Banco de Chile" }
  ]
}

Crédito 1 servicio · habilita avisar antes de vender que el cliente no tiene cupo

Crédito del cliente

consulta
Para qué: que el vendedor sepa, antes de cerrar, si el cliente puede comprar a plazo.
Le enviamosempresa y vendedor, para traer la cartera completa de una vez
CampoOblig.Para qué
Cupo asignadosíEl máximo autorizado para ese cliente›

tipo

entero

ejemplo

1500000

Deuda vigentesíLo que ya está ocupado del cupo›

tipo

entero

ejemplo

890500

Saldo disponiblesíYa calculado por ustedes›

tipo

entero

ejemplo

609500

Qué entra en el cálculo —si los pedidos pendientes descuentan cupo, si los cheques en cartera cuentan— es una regla de ustedes. Envíen el resultado para que la aplicación no tenga que replicarla.

Este servicio puede crecer mucho. En una instalación real llegó a responder 2,5 MB por llamada al traer la cartera completa. Conviene que admita paginación o filtro por cliente.
Ejemplo de respuesta
{
  "creditos": [
    {
      "cliente": "CL0042",
      "sucursal": "001",
      "cupo": 1500000,
      "deuda": 890500,
      "disponible": 609500
    }
  ]
}

Historial ampliado 4 servicios · habilita consultas que van más allá del listado básico de ventas

Detalle plano de documentos

consulta
Para qué: el arqueo de caja del vendedor, que suma lo vendido y lo cobrado en el día.
Le enviamosvendedor, rango de fechas y tipos de documento
CampoOblig.Para qué
Una fila por documentosíSin anidar líneas: formato plano›

tipo

lista de objetos

Qué trae cada fila
propiedadoblig.tipoqué es
tiposítextoTipo de documento.
foliosítextoFolio del documento.
emitidoElsífecha y horaISO-8601 con offset.
clientesítextoCódigo del cliente.
sucursalsítextoSucursal del cliente.
vendedorsítextoQuién emitió.
totalNetosíenteroDel documento completo.
totalImpuestossíenteroDel documento completo.
totalBrutosíenteroDel documento completo.
saldonoenteroLo que queda por cobrar, si su ERP lo lleva.

Es el mismo contenido que el historial, pero en una estructura plana: encabezado y totales, sin el detalle de líneas. Buscar un campo en el nivel equivocado es una fuente conocida de falsos negativos.

Totales y saldosíCuánto se vendió y cuánto quedó por cobrar›

tipo

entero

ejemplo

14701 · 0

Ejemplo de respuesta
{
  "documentos": [
    {
      "tipo": "BOLETA",
      "folio": "0000001244",
      "emitidoEl": "2026-09-10T11:05:00-03:00",
      "cliente": "CL0042",
      "sucursal": "001",
      "vendedor": "11",
      "totalNeto": 12354,
      "totalImpuestos": 2347,
      "totalBruto": 14701,
      "saldo": 0
    }
  ]
}

Cadena de documentos

consulta
Para qué: mostrar de dónde viene un documento y en qué terminó: pedido, guía, factura.
Le enviamoslos identificadores de los documentos a consultar, en lote
CampoOblig.Para qué
Documentos relacionadossíQué originó a cuál›

tipo

lista de objetos

Qué trae cada cadena
propiedadoblig.tipoqué es
documentosíobjetoEl documento consultado, con tipo y folio. Es el centro de la cadena.
origenessílista de objetosLos documentos que lo originaron, cada uno con tipo y folio. Lista vacía si no viene de ninguno.
destinossílista de objetosLos documentos que resultaron de él, con la misma forma. Lista vacía si todavía no derivó en nada.
Qué trae cada elemento de origenes y destinos
propiedadoblig.tipoqué es
tiposítextoTipo del documento relacionado, del mismo vocabulario que el resto.
foliosítextoFolio del documento relacionado.
relacionnotextoQué clase de vínculo es: facturación, anulación, devolución. Si su ERP solo maneja una clase, se puede omitir.

Basta la relación directa de origen y destino; la aplicación arma el árbol completo encadenando las respuestas. Acepta consultas en lote para no pedir uno por uno.

Ejemplo de respuesta
{
  "cadenas": [
    {
      "documento": { "tipo": "FACTURA", "folio": "0000004512" },
      "origenes": [ { "tipo": "PEDIDO", "folio": "V010066868" } ],
      "destinos": [ { "tipo": "GUIA", "folio": "0000008891" } ]
    }
  ]
}

Origen de la factura

consulta
Para qué: saber qué pedido dio origen a una factura, para cerrarlo en la aplicación cuando se factura.
Le enviamoslos identificadores de las facturas, en lote
CampoOblig.Para qué
Factura y pedido de origensíEl par que relaciona ambos›

ejemplo

"0000004512" → "V010066868"

Es un caso puntual de la cadena de documentos. Si ese servicio ya cubre la relación pedido–factura, este no hace falta.

Ejemplo de respuesta
{
  "origenes": [
    { "factura": "0000004512", "pedido": "V010066868" }
  ]
}

Comprobante impreso

consulta · prescindible
Para qué: entregar la imagen del comprobante lista para la impresora térmica.
Le enviamosel identificador del documento y el ancho de impresión
CampoOblig.Para qué
Imagen del comprobantenoLista para imprimir, sin procesar›

tipo

imagen PNG

ancho

576 px (80 mm)

Es el servicio más prescindible de todos: la aplicación sabe componer el comprobante por su cuenta con los datos del documento. Solo tiene sentido si el comprobante tiene un formato legal o de marca que deba salir del ERP.

Ejemplo de respuesta
{
  "documento": { "tipo": "BOLETA", "folio": "0000001244" },
  "ancho": 576,
  "formato": "image/png",
  "imagen": "iVBORw0KGgoAAAANSUhEUgAAAkAAAA... (base64)"
}

// También sirve devolver el PNG directo, con Content-Type: image/png.
// Si eligen esa forma, declárenlo: el traductor no adivina cuál de las dos es.

Alta de clientes 2 servicios · habilita crear o actualizar clientes desde terreno

Crear o actualizar cliente

escritura
Para qué: que el vendedor incorpore un cliente nuevo en la visita y pueda venderle de inmediato.
Le enviamosrazón social, RUT, dirección, comuna, contacto y el vendedor que lo da de alta
Qué enviamosOblig.Contenido
RUT y razón socialsíIdentificación del cliente›

formato

RUT con dígito verificador

ejemplo

"77921052-9"
"Minimarket Los Aromos"

Dirección y comunasíDónde se le entrega y se le factura›

ejemplo

"Av. Los Aromos 1450" · "Puente Alto"

La comuna se elige de la lista del servicio de territorios, para que llegue con un valor que su ERP reconozca.

Respuesta: código asignadosíCon qué código queda el cliente›

ejemplo

"CL0318" · "001"

La aplicación lo necesita de inmediato: sin el código no puede emitirle el documento en la misma visita, que es el motivo de dar el alta en terreno.

Ejemplo de respuesta
{
  "codigo": "CL0318",
  "sucursal": "001",
  "creado": true
}

Comunas y territorios

maestro
Para qué: que la dirección del cliente nuevo se complete con valores que su ERP reconoce, y no con texto libre.
Le enviamosel nivel que se quiere listar
CampoOblig.Para qué
Código y nombresíLo que se guarda y lo que se muestra›

ejemplo

"13201" · "Puente Alto"

Nivel y dependenciasíPaís, región, comuna›

ejemplo

nivel 3, depende de "13"

La aplicación usa el nivel para encadenar los selectores: elegida la región, solo ofrece sus comunas.

Ejemplo de respuesta
{
  "territorios": [
    { "codigo": "13",    "nombre": "Región Metropolitana", "nivel": 2, "padre": "CL" },
    { "codigo": "13201", "nombre": "Puente Alto",          "nivel": 3, "padre": "13" }
  ]
}

Complementos de catálogo 4 servicios · habilitan agrupación, promociones e impuestos como maestros aparte

Familias

maestro
Para qué: poner nombre a los códigos de agrupación del catálogo. Solo hace falta si el catálogo trae los códigos sin su nombre.
Le enviamosempresa y el nivel que se quiere listar
CampoOblig.Para qué
NivelsíA cuál de los tres niveles pertenece›

tipo

número (1, 2 o 3)

ejemplo

2

El nivel es obligatorio junto al código. Un mismo código puede existir en dos niveles con nombres distintos; resolver el nombre sin el nivel entrega el equivocado. Medido en una instalación real: pasaba en 11 de 13 empresas.

Código y nombresíLo que trae el producto y lo que se muestra›

ejemplo

"GAS" · "Gaseosas"

Ejemplo de respuesta
{
  "familias": [
    { "nivel": 1, "codigo": "BEB",  "nombre": "Bebidas" },
    { "nivel": 2, "codigo": "GAS",  "nombre": "Gaseosas" },
    { "nivel": 3, "codigo": "COLA", "nombre": "Cola" }
  ]
}

Promociones

consulta
Para qué: que el vendedor ofrezca las promociones vigentes sin tener que recordarlas.
Le enviamoslas listas de precio del vendedor y el indicador de vigentes
CampoOblig.Para qué
Identificador y descripciónsíQué promoción es›

ejemplo

"PR-2026-14" · "Lleve 12, pague 10"

VigenciasíDesde cuándo y hasta cuándo›

formato

ISO-8601 con offset

ejemplo

01-sep a 30-sep

La aplicación sincroniza y opera sin red durante horas: necesita las fechas para dejar de ofrecer una promoción vencida aunque no haya vuelto a sincronizar.

A qué aplicasíProductos, familias o clientes alcanzados›

tipo

lista de objetos

ejemplo

productos ["116231"]

Qué trae cada alcance
propiedadoblig.tipoqué es
dimensionsítextoSobre qué se define el alcance: producto, familia, cliente o zona. Los valores se acuerdan en el vocabulario.
valoressílista de textosLos códigos alcanzados en esa dimensión. Lista vacía significa "ninguno", no "todos": si la promoción es universal, díganlo con una dimensión propia y no con una lista vacía.

Una promoción puede tener varios alcances a la vez —por ejemplo una familia de productos y una zona de clientes— y se entienden combinados: aplica cuando se cumplen todos.

BeneficiosíQué obtiene el cliente›

tipo

objeto

ejemplo

descuento 12%

Qué trae el beneficio
propiedadoblig.tipoqué es
tiposítextoQué clase de beneficio es: descuento porcentual, descuento en monto, o bonificación en producto. Los valores se acuerdan en el vocabulario.
valorsínúmeroEl porcentaje o el monto, según el tipo. La aplicación no deduce cuál es: lo decide el tipo.
productoBonificadonotextoSolo si el tipo es bonificación en producto: qué producto se regala.

Si su ERP maneja promociones complejas —combos, escalas, condiciones cruzadas— conviene acordar primero cuáles se pueden expresar de forma declarativa. La aplicación no ejecuta reglas arbitrarias: muestra el beneficio, no lo calcula.

Ejemplo de respuesta
{
  "promociones": [
    {
      "id": "PR-2026-14",
      "descripcion": "Lleve 12, pague 10",
      "desde": "2026-09-01T00:00:00-03:00",
      "hasta": "2026-09-30T23:59:59-03:00",
      "aplicaA": [
        { "dimension": "PRODUCTO", "valores": ["116231"] },
        { "dimension": "ZONA",     "valores": ["SUR"] }
      ],
      "beneficio": { "tipo": "DESCUENTO_PORCENTAJE", "valor": 12 }
    }
  ]
}

Definición de impuestos

maestro
Para qué: conocer las tasas vigentes. Es una alternativa a traerlas dentro de la lista de precio.
Le enviamosla empresa
CampoOblig.Para qué
Código y nombresíIdentifica el impuesto›

ejemplo

"IVA" · "IVA 19%"

TasasíEl porcentaje que se aplica›

tipo

número

ejemplo

19

TiposíSi es el impuesto general o uno adicional›

ejemplo

"GENERAL" · "ADICIONAL"

La aplicación los trata distinto al totalizar y al mostrar el desglose, así que la distinción tiene que venir declarada.

Ejemplo de respuesta
{
  "impuestos": [
    { "codigo": "IVA",  "nombre": "IVA 19%",            "tasa": 19, "tipo": "GENERAL" },
    { "codigo": "ILA",  "nombre": "Impuesto bebidas",   "tasa": 10, "tipo": "ADICIONAL" }
  ]
}

Impuesto por producto

consulta
Para qué: saber qué impuestos afectan a cada producto, cuando no vienen en la lista de precio.
Le enviamosla empresa
CampoOblig.Para qué
Producto e impuestossíQué códigos de impuesto le aplican›

tipo

lista de objetos

ejemplo

"116231" → ["IVA", "ILA"]

Qué trae cada fila
propiedadoblig.tipoqué es
productosítextoCódigo del catálogo.
impuestossílista de textosLos códigos del maestro de impuestos que le aplican. Cada código lleva su tasa en «Definición de impuestos»; aquí van solo los códigos, para no repetir la tasa producto por producto.

Un producto exento debe venir con lista vacía, no ausente. La diferencia entre "no aplica ninguno" y "no vino el dato" cambia el total.

Ejemplo de respuesta
{
  "impuestosPorProducto": [
    { "producto": "116231", "impuestos": ["IVA", "ILA"] },
    { "producto": "220145", "impuestos": [] }
  ]
}

Metas y visitas 3 servicios · habilitan el seguimiento de objetivos y el registro de visitas sin venta

Metas del vendedor

consulta
Para qué: que el vendedor vea su avance contra el objetivo del período.
Le enviamosempresa, vendedor y el indicador de vigentes
CampoOblig.Para qué
PeríodosíA qué mes o rango corresponde›

ejemplo

"2026-09"

Meta y avancesíCuánto tiene que lograr y cuánto lleva›

tipo

número

ejemplo

8500000 · 5320400

El avance debe venir calculado. Si la aplicación lo dedujera de los documentos que tiene sincronizados, mostraría una cifra distinta de la del ERP.

Qué midesíA cuál definición de meta corresponde›

ejemplo

"VENTA_PESOS"

Ejemplo de respuesta
{
  "metas": [
    {
      "vendedor": "11",
      "periodo": "2026-09",
      "definicion": "VENTA_PESOS",
      "meta": 8500000,
      "avance": 5320400
    }
  ]
}

Definición de metas

maestro
Para qué: saber cómo se mide cada meta, para mostrarla con la unidad correcta.
Le enviamosla empresa
CampoOblig.Para qué
Código y nombresíIdentifica la definición›

ejemplo

"VENTA_PESOS" · "Venta en pesos"

Unidad de medidasíPesos, unidades o kilos›

ejemplo

"PESOS"

Si alguna meta se mide en kilos, el catálogo tiene que traer el peso unitario del producto; sin ese dato la meta no se puede representar.

Ejemplo de respuesta
{
  "definiciones": [
    { "codigo": "VENTA_PESOS", "nombre": "Venta en pesos",  "unidad": "PESOS" },
    { "codigo": "VENTA_KILOS", "nombre": "Venta en kilos",  "unidad": "KILOS" }
  ]
}

Motivos de no venta

maestro
Para qué: que una visita sin pedido quede registrada con su explicación, en vez de no quedar registrada.
Le enviamosnada; es una lista completa
CampoOblig.Para qué
Código y descripciónsíLo que se guarda y lo que elige el vendedor›

ejemplo

"03" · "Local cerrado"

Una lista corta y estable funciona mejor: el vendedor la usa de pie, en la puerta del local.

Ejemplo de respuesta
{
  "motivos": [
    { "codigo": "01", "descripcion": "Tiene stock suficiente" },
    { "codigo": "02", "descripcion": "Sin capacidad de pago" },
    { "codigo": "03", "descripcion": "Local cerrado" }
  ]
}

Fuera del alcance

Lo que no tienen que implementar

  • La lógica de sesión, licencia y configuración de la aplicación. Es nuestra y no depende del ERP. Los datos de licencia se guardan en sus tablas, pero quien los valida y los firma es nuestro servidor.
  • Notificaciones push. Nuestras. La telemetría de uso se guarda en sus tablas; la recoge y la interpreta la aplicación.
  • Motor de pedidos sugeridos y servicios de fidelización. Viven fuera del ERP.
  • Optimizador de rutas. Servicio externo con alternativa local.
  • Cálculo de totales en pantalla. Lo hace la aplicación, sin red. Lo que necesitamos de ustedes son las tasas y los precios, no el total.

Cómo se prueba y con qué se mide

Puesta en marcha

Un ambiente de pruebas, con datos que se parezcan

Necesitamos un ambiente contra el cual equivocarnos. Sin él, la conformidad se prueba contra la producción de un cliente, que es exactamente donde no conviene descubrir que un campo llega distinto. No hace falta que sea grande, pero sí que tenga los casos incómodos: un cliente con varias sucursales, un producto exento, una existencia en cero, un documento ya pagado y un vendedor sin permisos. Con datos perfectos no se prueba nada.

Un objetivo de tiempo, no solo de forma

La paginación resuelve el tamaño, no la espera. Conviene fijar un objetivo verificable: por ejemplo, páginas de mil registros respondiendo en menos de cinco segundos en el peor caso. El antecedente es concreto y medido en nuestra instalación: un catálogo de treinta y tres mil productos servido sin paginar dejaba la sincronización en tiempo de espera, y el vendedor abría la aplicación sin precios y sin saber por qué.

Quién llama a quién, y cada cuánto

La aplicación consulta; su API responde. No necesitamos que nos notifiquen nada. Lo que sí conviene declarar es la frecuencia esperada: un equipo de veinte vendedores sincronizando al inicio de la jornada produce un pico concentrado, y es mejor dimensionarlo antes que descubrirlo el primer lunes.

Con quién hablamos cuando algo falla

Un contacto técnico con nombre, y una vía para reportar. Durante la puesta en marcha la mayoría de los hallazgos son de datos, no de código —un vocabulario que trae un valor nuevo, un campo que en un cliente viene nulo— y se resuelven en minutos si hay a quién preguntarle.

Antes de empezar

Qué necesitamos que confirmen

  • Si su API puede garantizar la idempotencia por clave del cliente, y si un error suyo garantiza que no se grabó nada. Son las dos condiciones cuyo incumplimiento produce documentos duplicados.
  • Si el precio por cliente y el tope de descuento vienen ya resueltos, o si entregan las reglas para que los calculemos. Lo primero es un mapeo; lo segundo es un desarrollo.
  • Qué límites de paginación y tamaño soporta su API, cuál de las dos formas de paginar usa cada servicio, y si admite filtro por fecha de modificación en catálogo, clientes y precios.
  • Cómo autentican las llamadas y con qué vigencia.
  • Qué estado HTTP devuelven en cada situación y en qué campo viaja el código de error. Es lo que nos permite distinguir un problema de configuración, que hay que mostrar, de uno transitorio, que hay que reintentar.
  • Si existe un límite de peticiones, cuál es y qué estado devuelven al alcanzarlo. Se cruza con el reintento indefinido de la sección de grabación.
  • Si la API es alcanzable desde nuestros servidores: certificado, apertura de firewall y direcciones de origen, si hacen falta.
  • Si habrá un ambiente de pruebas y con qué datos. Es lo que evita que la conformidad se pruebe contra la producción de un cliente.
  • Cómo nos avisan de un cambio incompatible: una versión en la ruta o en una cabecera, y un canal de aviso.
  • Los vocabularios: qué valores usan para tipo de documento, unidad de medida, condición de pago, medio de pago y estado de un documento. Se acuerdan una vez y se mapean.
  • Con qué motor de base de datos crearán las tablas de Ramaflex y qué URL base tendrá su endpoint {urlBase}/{tabla}.
  • Qué servicios del bloque opcional existen hoy, para decidir el alcance de la primera etapa.