Promoções
Manual de Integração — Promoções (API do Concentrador)
Guia para ERPs que criam/editam promoções chamando a API do Concentrador diretamente (a mesma API que a tela de cadastro do Concentrador usa, porém sem passar por ela).
⚠️ Leitura obrigatória — a API não valida regra de negócio. O backend só valida formato (campos obrigatórios de estrutura, enums, tamanhos). Ele não verifica se o payload faz sentido para o tipo escolhido (ex.: aceita
PRECO_FIXOsem preço,LEVA_PAGAcom operador>=, Scanntech com operador errado). Todas as regras de negócio moram na tela do Concentrador — quem integra direto precisa replicá-las (ver seção 7). Um JSON aceito pela API pode gerar uma promoção que não desconta nada no PDV.
1. As duas famílias de tipos
As promoções se dividem em duas famílias, distinguidas pelo campo origem:
| Família | origem |
tipo |
Comportamento |
|---|---|---|---|
| Scanntech | SCANNTECH |
os 6 tipos SCANNTECH_* |
Homologado/fixo. O ERP envia as promoções vindas da Scanntech. Não use os campos "nossos" (limite por produto, considerar produtos diferentes). Operador sempre =. |
| Manual (nossa) | TILLIT, CONCENTRADOR, ADAPTIVE, CONTROLLER_PLUS |
LEVA_PAGA, PRECO_FIXO, DESCONTO_FIXO, DESCONTO_VARIAVEL |
Cadastro manual, com particularidades próprias (seção 5). Semântica por unidade. |
Tipo e origem são acoplados: tipos SCANNTECH_* só com origem = SCANNTECH; os 4 tipos manuais
só com origem não-Scanntech. Não misture (a API aceita, mas o comportamento fica indefinido).
Valores exatos dos enums (string em JSON):
tipo:SCANNTECH_LEVA_PAGA,SCANNTECH_PRODUTO_BRINDE,SCANNTECH_PRECO_FIXO,SCANNTECH_PRODUTO_DESCONTO,SCANNTECH_DESCONTO_VARIAVEL,SCANNTECH_DESCONTO_FIXO,LEVA_PAGA,PRECO_FIXO,DESCONTO_FIXO,DESCONTO_VARIAVEL.origem:TILLIT,CONCENTRADOR,SCANNTECH,ADAPTIVE,CONTROLLER_PLUS.operador(na condição-item):=ou>=.
2. Estrutura do payload
Árvore enviada em POST/PUT (ver seção 8):
Promocao
├─ tipo, origem, titulo, inicioVigencia, fimVigencia, limiteAplicacaoPorCupom, ...
└─ detalhePromocao
├─ preco / desconto / quantidadePagar ← valor dos tipos SCANNTECH (nível detalhe)
├─ condicao
│ └─ itens[] (condicaoPromocaoItem)
│ ├─ operador, quantidade (gatilho)
│ ├─ preco / desconto / quantidadePagar ← valor dos tipos MANUAIS (nível item)
│ ├─ limiteAplicacaoPorProduto ← só manuais
│ ├─ considerarProdutosDiferentes ← só manuais
│ └─ idProdutos[]
└─ beneficio (opcional)
└─ itens[] ├─ quantidade └─ idProdutos[] ← só SCANNTECH_PRODUTO_BRINDE / _PRODUTO_DESCONTO
Onde vai o "valor" da promoção — o ponto que mais confunde:
| Família | Nível do preco/desconto/quantidadePagar |
|---|---|
| Scanntech | em detalhePromocao (nível detalhe) |
| Manual | em cada detalhePromocao.condicao.itens[] (nível item de condição) |
quantidade (o gatilho — "levando N") vai sempre no item de condição, nas duas famílias.
3. Tabela-resumo por tipo
✔ = obrigatório · — = não usar · detalhe / item = nível onde vai o valor.
| tipo | origem | operador | valor (nível/campo) | quantidadePagar |
benefício | limiteAplicacaoPorProduto |
considerarProdutosDiferentes |
limiteAplicacaoPorCupom |
|---|---|---|---|---|---|---|---|---|
LEVA_PAGA |
manual | só = |
— | item ✔ | — | opcional | opcional | — |
PRECO_FIXO |
manual | = ou >= |
item · preco ✔ |
— | — | opcional | opcional | — |
DESCONTO_FIXO |
manual | = ou >= |
item · desconto (R$) ✔ |
— | — | opcional | opcional | — |
DESCONTO_VARIAVEL |
manual | = ou >= |
item · desconto (%) ✔ |
— | — | opcional | opcional | — |
SCANNTECH_LEVA_PAGA |
SCANNTECH | = |
— | detalhe ✔ | — | — | — | ✔ |
SCANNTECH_PRECO_FIXO |
SCANNTECH | = |
detalhe · preco ✔ |
— | — | — | — | ✔ |
SCANNTECH_DESCONTO_FIXO |
SCANNTECH | = |
detalhe · desconto (R$) ✔ |
— | — | — | — | ✔ |
SCANNTECH_DESCONTO_VARIAVEL |
SCANNTECH | = |
detalhe · desconto (%) ✔ |
— | — | — | — | ✔ |
SCANNTECH_PRODUTO_BRINDE |
SCANNTECH | = |
— | — | ✔ (itens) | — | — | ✔ |
SCANNTECH_PRODUTO_DESCONTO |
SCANNTECH | = |
detalhe · desconto ✔ |
— | ✔ (itens) | — | — | ✔ |
Sempre obrigatórios (todos os tipos): titulo, inicioVigencia, fimVigencia, tipo, origem,
ao menos 1 item de condição com produto (idProdutos), e por item o operador e a quantidade.
Para SCANNTECH_PRODUTO_BRINDE/_PRODUTO_DESCONTO exige-se, além da condição, ao menos 1 item de
benefício com produto.
4. Família Scanntech (resumo)
Comportamento homologado, não mexer. O ERP envia as promoções da Scanntech como estão:
- Valor no
detalhePromocao(preco/desconto/quantidadePagar), conforme a tabela da seção 3. - Operador sempre
=. limiteAplicacaoPorCupom(nívelPromocao): limite de aplicações por cupom/venda — na tela do Concentrador é obrigatório para Scanntech.0= ilimitado.SCANNTECH_PRODUTO_BRINDEeSCANNTECH_PRODUTO_DESCONTOusam o blocobeneficio.itens(o produto que ganha brinde/desconto) além dacondicao.itens(a condição de compra).- Não envie
limiteAplicacaoPorProdutonemconsiderarProdutosDiferentes(são da família manual).
5. Particularidades da família manual (o núcleo)
Os 4 tipos manuais têm semântica por unidade e três alavancas próprias: operador,
limiteAplicacaoPorProduto e considerarProdutosDiferentes. Entender isso é o que separa uma
promoção que funciona bem de uma que não desconta o esperado.
5.1 O que cada tipo faz (por unidade)
PRECO_FIXO— cada unidade beneficiada passa a custarpreco(por unidade). Nunca cobra acima do preço de prateleira (desconto trava em zero).DESCONTO_FIXO— cada unidade beneficiada recebedescontoreais de abatimento.DESCONTO_VARIAVEL— cada unidade beneficiada recebedesconto% de abatimento.LEVA_PAGA— a cadaquantidade(gatilho) unidades,quantidade − quantidadePagarsaem "grátis". O valor das unidades grátis é rateado (diluído) entre asquantidadeunidades do pacote (todas saem com um desconto proporcional; o total do pacote equivale às unidades grátis).
5.2 Operador = vs >= — muda como o gatilho e o limite funcionam
=(pacotes): a venda é fragmentada em pacotes dequantidadeunidades. Cada pacote completo é 1 aplicação que recebe o benefício inteiro. Unidades que não fecham um pacote ficam no preço normal até completar o próximo.limiteAplicacaoPorProduto= nº máximo de aplicações (pacotes).>=(a partir de N): atingido o gatilhoquantidade, todas as unidades recebem o benefício por unidade.limiteAplicacaoPorProduto= nº máximo de unidades com desconto.LEVA_PAGAnão aceita>=(só=).
Exemplos:
PRECO_FIXO,=,quantidade=3,preco=5,limite=2, 6 un → 2 pacotes de 3 → as 6 a R$5 (2 aplicações). Com 9 un → só 6 a R$5 (teto de 2 aplicações), 3 no preço normal.DESCONTO_FIXO,>=,quantidade=2,desconto=1,limite=10, 13 un → 10 unidades com R$1 de desconto, 3 no preço normal.
5.3 limiteAplicacaoPorProduto — significado depende do operador
Campo escalar no item de condição. Vazio, ausente ou 0 = ilimitado.
| operador | o limite conta |
|---|---|
= |
aplicações (pacotes de quantidade unidades) |
>= |
unidades com desconto |
5.4 considerarProdutosDiferentes — considerar produtos diferentes vs. produto único
Só importa quando o mesmo item de condição tem 2+ produtos (idProdutos com vários). Boolean,
padrão false.
false(produto único, padrão): cada pacote/grupo é de um só produto — produtos diferentes não se misturam num mesmo pacote. O limite conta por produto (cada produto tem seu próprio teto).true(considerar produtos diferentes): produtos diferentes da condição contam juntos para formar o pacote (útil para variações intercambiáveis — mesmo produto em sabores/fragrâncias). O limite conta total por condição (não por produto).
⚠️
LEVA_PAGAcomconsiderarProdutosDiferentes = true: o rateio da unidade grátis usa o preço do primeiro produto do pacote. Ative essa opção no leva-paga apenas com produtos de mesmo preço de venda (o caso de variações). Com preços diferentes, o valor descontado distorce.
5.5 permitirAcumularDescontos e exibirNoDescontoTotal
permitirAcumularDescontos(nívelPromocao, padrãofalse): setrue, o desconto desta promoção soma a outros descontos do item em vez de sobrescrever.exibirNoDescontoTotal(padrãotrue): controla exibição no "sua economia".
6. Como escolher a configuração (guia rápido)
- "Leve 3, pague 2" →
LEVA_PAGA,operador="=",quantidade=3,quantidadePagar=2. - "Leve 4, pague 3, no máximo 2 vezes" → idem,
limiteAplicacaoPorProduto=2. - "Qualquer unidade a R$5" →
PRECO_FIXO,operador=">=",quantidade=1,preco=5. - "A partir de 3, cada uma R$5, até 10 unidades" →
PRECO_FIXO,>=,quantidade=3,preco=5,limiteAplicacaoPorProduto=10. - "10% em qualquer quantidade" →
DESCONTO_VARIAVEL,>=,quantidade=1,desconto=10. - "Leve 3 de qualquer sabor (mesmo preço), pague 2" →
LEVA_PAGA,=,quantidade=3,quantidadePagar=2,considerarProdutosDiferentes=true,idProdutoscom todos os sabores.
7. Validações a replicar
O backend não faz nada disto — replique no ERP antes de enviar:
-
origemcoerente comtipo(Scanntech ↔SCANNTECH_*; manual ↔ os 4 tipos). -
operador = "="para todos os Scanntech e paraLEVA_PAGA. SóPRECO_FIXO/DESCONTO_FIXO/DESCONTO_VARIAVELpodem usar>=. - Valor no nível certo: detalhe para Scanntech, item de condição para manuais.
- Campo de valor presente conforme o tipo (seção 3):
preco(PRECO_FIXO),desconto(DESCONTO_FIXO/VARIAVEL e Scanntech desconto),quantidadePagar(LEVA_PAGA). -
quantidade(gatilho) > 0 em cada item de condição. - Ao menos 1 item de condição com produto. Para
SCANNTECH_PRODUTO_BRINDE/_PRODUTO_DESCONTO, ao menos 1 item de benefício com produto. - Não enviar
limiteAplicacaoPorProduto/considerarProdutosDiferentesem Scanntech. -
inicioVigencia < fimVigencia(o backend não checa). -
idProdutosexistentes — IDs inexistentes são descartados silenciosamente (sem erro), o que pode gerar item/promoção sem produto.
8. Exemplos de payload (POST/PUT)
Campos de infra (id, idUsuarioAlteracao) são opcionais; datas em ISO-8601.
8.1 LEVA_PAGA — leve 3 pague 2 (manual, =)
{
"tipo": "LEVA_PAGA",
"origem": "CONCENTRADOR",
"titulo": "Leve 3 Pague 2 - Refrigerante",
"inicioVigencia": "2026-07-01T00:00:00Z",
"fimVigencia": "2026-07-31T23:59:59Z",
"detalhePromocao": {
"condicao": {
"itens": [
{
"operador": "=",
"quantidade": 3,
"quantidadePagar": 2,
"limiteAplicacaoPorProduto": 0,
"considerarProdutosDiferentes": false,
"idProdutos": ["11111111-1111-1111-1111-111111111111"]
}
]
}
}
}
8.2 PRECO_FIXO — pacote de 3 a R$5, no máx 2 aplicações, considerar produtos diferentes (=)
{
"tipo": "PRECO_FIXO",
"origem": "CONCENTRADOR",
"titulo": "3 Sabores por R$5 cada",
"inicioVigencia": "2026-07-01T00:00:00Z",
"fimVigencia": "2026-07-31T23:59:59Z",
"detalhePromocao": {
"condicao": {
"itens": [
{
"operador": "=",
"quantidade": 3,
"preco": 5.00,
"limiteAplicacaoPorProduto": 2,
"considerarProdutosDiferentes": true,
"idProdutos": [
"22222222-2222-2222-2222-222222222222",
"33333333-3333-3333-3333-333333333333"
]
}
]
}
}
}
8.3 DESCONTO_VARIAVEL — 10% a partir de 2, teto de 10 unidades (>=)
{
"tipo": "DESCONTO_VARIAVEL",
"origem": "CONCENTRADOR",
"titulo": "10% acima de 2 unidades",
"inicioVigencia": "2026-07-01T00:00:00Z",
"fimVigencia": "2026-07-31T23:59:59Z",
"detalhePromocao": {
"condicao": {
"itens": [
{
"operador": ">=",
"quantidade": 2,
"desconto": 10,
"limiteAplicacaoPorProduto": 10,
"considerarProdutosDiferentes": false,
"idProdutos": ["44444444-4444-4444-4444-444444444444"]
}
]
}
}
}
8.4 SCANNTECH_LEVA_PAGA — valor no detalhe, limite por cupom
{
"tipo": "SCANNTECH_LEVA_PAGA",
"origem": "SCANNTECH",
"titulo": "Scanntech Leve 3 Pague 2",
"inicioVigencia": "2026-07-01T00:00:00Z",
"fimVigencia": "2026-07-31T23:59:59Z",
"limiteAplicacaoPorCupom": 1,
"detalhePromocao": {
"quantidadePagar": 2,
"condicao": {
"itens": [
{ "operador": "=", "quantidade": 3, "idProdutos": ["55555555-5555-5555-5555-555555555555"] }
]
}
}
}
8.5 SCANNTECH_PRODUTO_BRINDE — condição + benefício
{
"tipo": "SCANNTECH_PRODUTO_BRINDE",
"origem": "SCANNTECH",
"titulo": "Compre A, ganhe B",
"inicioVigencia": "2026-07-01T00:00:00Z",
"fimVigencia": "2026-07-31T23:59:59Z",
"limiteAplicacaoPorCupom": 1,
"detalhePromocao": {
"condicao": {
"itens": [
{ "operador": "=", "quantidade": 2, "idProdutos": ["66666666-6666-6666-6666-666666666666"] }
]
},
"beneficio": {
"itens": [
{ "quantidade": 1, "idProdutos": ["77777777-7777-7777-7777-777777777777"] }
]
}
}
}
9. Resumo de armadilhas
- A API aceita regra de negócio inválida — valide no ERP (seção 7).
- Valor no nível errado (detalhe x item) = promoção sem efeito.
LEVA_PAGAcom>=não é suportado; use=.limiteAplicacaoPorProdutosignifica aplicações no=e unidades no>=.considerarProdutosDiferentes=truenoLEVA_PAGAsó com produtos de mesmo preço.idProdutosinexistente é descartado sem aviso.
SuperCMS