Conciliación
Las cuentas que cierran siempre, y cómo llegar desde tus ventas al mismo número que ve el dueño en Analítica.
Lo que cierra en cada venta
Todas las cuentas cierran al centavo. Pasá los importes a centavos enteros (Math.round(x * 100)) antes de sumar.
| Cuenta | En la venta de ejemplo |
|---|---|
Σ line_total de los renglones con voided: false = total | 2520 + 540 + 0 + 6750 + 0 + 0 = 9810 |
line_total = unit_price × quantity − discount, en cada renglón | Café con leche: 2800 × 1 − 280 = 2520 |
subtotal − discount_total = total | 10900 − 1090 = 9810 |
Σ discounts[].amount = discount_total | 1090 |
Σ amount de los pagos que no están canceled = total | 5000 + 4810 = 9810 |
totales con IVA y neto de descuentos, y no incluye el costo de servicio, la propina ni el envío (van aparte:service_fee,tip_total,delivery_fee). Lo que de verdad pasó por cada medio, con esos extras, está encharged_amountdel pago.- Los renglones anulados (
voided: true) traen su valor propio para que sepas qué se sacó, pero no suman. - Un descuento de la venta se reparte entre los renglones con plata, en proporción. Si el reparto deja una diferencia de centavos, aparece como un descuento
otherllamado «Redondeo» (puede ser negativo). - La cuenta de los pagos vale para las ventas
CLOSEDyREFUNDED, con dos excepciones: una ronda de mesaOPENtodavía no tiene pagos, y una mesa VIP a la que le faltó crédito y no se cobró la diferencia tiene Σamountmenor que eltotal(es la verdad: falta plata). - En una
CANCELEDdepende de qué pasó con la plata: si quedó en la caja, los pagos salenapprovedy suman eltotal; si nunca se cobró (una venta duplicada o cargada por error), salencanceledy no suman nada. - Con una devolución parcial, cada pago dice lo que se cobró por su medio (
charged_amount), lo que volvió por ese medio (refunded_amount) y lo que quedó (amount): la plata devuelta en efectivo sale del pago en efectivo, no de la tarjeta.
La jornada: business_date
Cada venta tiene su business_date: la jornada comercial a la que pertenece, que va de las 06:00 a las 06:00 del día siguiente en el huso de su sucursal. Una venta del domingo a las 02:30 es de la jornada del sábado. Es el mismo corte que usan los reportes de Kicket, y se calcula por la hora en que se abrió la venta.
Para traer una jornada entera: GET /sales?business_date=2026-10-27.
El reporte diario
GET /reports/daily-sales?business_date=… devuelve, por sucursal, el número contra el que se concilia: el mismo que muestra Analítica › Reportes › Ventas para esa jornada. No es un cálculo aparte: sale de las mismas funciones que la pantalla, así que si el dueño ve un número, la API dice el mismo.
{
"object": "list",
"data": [
{
"object": "daily_sales_report",
"branch_id": "65f1c0a2b7e4d91a3c5e7f10",
"business_date": "2026-10-27",
"currency": "ARS",
"sales_count": 214,
"total": 1284650,
"canceled_count": 3,
"canceled_total": 15600
}
],
"has_more": false,
"next_cursor": null
}| Campo | Qué es |
|---|---|
total | La plata de la jornada: la de Analítica y la de Caja. |
sales_count | Las ventas de la jornada como las cuenta Analítica. |
canceled_count | Las anuladas de la jornada. |
canceled_total | Lo que sumaban las anuladas (no suma al total). |
Cómo cuadrar /sales contra el reporte
Sumá, sobre las ventas de esa sucursal y esa jornada:
- sólo las
OPENyCLOSED(las anuladas van aparte, encanceled_total); - de cada una, su
totalmenos lo que pagaron los créditosticket_creditytable_credit(sin bajar de cero). Esa plata ya se contó al vender la entrada o la mesa VIP, y Analítica no la cuenta dos veces. El crédito de empresa, la cuenta corriente y la seña sí suman: son ventas del día.
El resultado tiene que dar el total del reporte, al centavo.
import { llamar } from './kicket.mjs'
const centavos = (n) => Math.round(n * 100)
// La plata de una jornada como la cuenta Analítica, sumada desde /sales, contra /reports/daily-sales.
export async function conciliar(branchId, jornada) {
let suma = 0
let cursor = null
do {
const params = new URLSearchParams({ branch_id: branchId, business_date: jornada, status: 'OPEN,CLOSED', limit: '500' })
if (cursor) params.set('cursor', cursor)
const pagina = await llamar(`/sales?${params}`)
for (const v of pagina.data) {
// Lo que pagó el crédito de una entrada o de una mesa VIP ya se contó al venderlas: no suma acá.
const enOtroLado = v.payments
.filter((p) => (p.kind === 'ticket_credit' || p.kind === 'table_credit') && p.status !== 'canceled')
.reduce((s, p) => s + centavos(p.amount), 0)
suma += Math.max(0, centavos(v.total) - enOtroLado)
}
cursor = pagina.next_cursor
} while (cursor)
const reporte = await llamar(`/reports/daily-sales?business_date=${jornada}&branch_id=${branchId}`)
const esperado = centavos(reporte.data[0].total)
return { suma: suma / 100, reporte: esperado / 100, diferencia: (suma - esperado) / 100 }
}Por qué sales_count puede no coincidir con la cantidad de ventas
Las cortesías y el consumo del personal salen en /sales como ventas CLOSED con total 0, pero Analítica no las cuenta como ventas. No cambian la plata; sólo la cantidad.
updated_at y la sincronización incremental te la trae de nuevo.Por medio de pago y por caja
Cada pago trae su method_id (la clave estable del medio, la misma en todas las sucursales) y su kind: payment es plata que entró; los demás son créditos que cubren parte del total. Para cuadrar contra el cierre de una caja, usá GET /cash-sessions: cada caja cerrada trae lo esperado por medio de pago, el efectivo contado y la diferencia. Las ventas de la caja traen su cash_session_id.
Un cobro de mesa sale repartido en un pago por ronda: para cruzarlo con el posnet, agrupá los pagos por el check_id de su venta y por method_id. La propina de una mesa va en el tip_total de cada ronda.
En la venta de ejemplo, la propina de 1000 está sólo en tip_total: la caja la registra aparte y no sabe por qué medio entró, así que ningún pago la trae. Por eso, en la caja y en la mesa, charged_amount y amount coinciden; se separan en lo online (la pasarela cobra servicio, propina y envío) y después de una devolución parcial. Para cruzar con la terminal, si la propina entró por tarjeta, sumala aparte. Los dos pagos llevan el mismo created_at: la hora del cobro de la venta.