Pular para o conteúdo principal

Guia de integração — Desconto avançado

Serviço: API principal (back-end Soba)
Autenticação: token Bearer (JWT) e contexto de tenant — ver Login Combinado


Introdução​

Este guia descreve a integração com a API para gerir descontos avançados (regras com vários modelos: ofertas “leve X pague Y”, desconto sobre total do documento, desconto sobre linha, campanha).

O corpo do pedido é polimórfico: a propriedade discriminadora é templateType. A API exige templateType no JSON e interpreta o restante do corpo (em especial object) em função desse valor (ver Dependências condicionais do payload).

Após validação, o serviço normaliza targetObject e preenche target com o filtro de cliente já processado; em geral envie targetObject no pedido e trate target como saída derivada.


Endpoints disponíveis​

MétodoURLDescrição resumida
GET/gateway/advanced-discountLista descontos com paginação opcional (page, pageSize); ver regras de paginação.
GET/gateway/advanced-discount/{id}Obtém um desconto pelo identificador único (UUID).
GET/gateway/advanced-discount/next-orderDevolve o próximo número de ordem sugerido.
POST/gateway/advanced-discountCria um desconto; resposta 200 OK com o UUID criado no corpo.
PUT/gateway/advanced-discount/{id}Atualiza; id na rota e no corpo coerente; resposta 204 No Content.
DELETE/gateway/advanced-discount/{id}Elimina pelo identificador único (UUID); resposta 204 No Content.

Listar​

URL: /gateway/advanced-discount — Método: GET

Ver mais →

Obter por identificador​

URL: /gateway/advanced-discount/{id} — Método: GET

Ver mais →

Próxima ordem​

URL: /gateway/advanced-discount/next-order — Método: GET

Ver mais →

Criar​

URL: /gateway/advanced-discount — Método: POST

Ver mais →

Atualizar​

URL: /gateway/advanced-discount/{id} — Método: PUT

Ver mais →

Eliminar​

URL: /gateway/advanced-discount/{id} — Método: DELETE

Ver mais →


Estrutura do corpo do pedido​

Os nomes das propriedades seguem camelCase no JSON. O discriminador templateType é obrigatório; sem ele o pedido é rejeitado na deserialização.

CampoTipoDescrição
idstring (UUID)Em atualização, identificador existente; em criação, pode ser vazio — o servidor gera um novo identificador.
descriptionstringObrigatório.
activeboolAtivo.
includedTerminalsint[]Terminais onde a regra se aplica; ver validação Terminais.
discountOrderint?Ordem de aplicação.
cumulativeboolPermitir cumular com outros descontos.
startDatedate-time?Início de vigência (data; ver validação de “passado”).
endDatedate-time?Fim de vigência.
appliedTointOnde a regra se aplica: linha (0) ou documento (1); ver tabela em Enums.
weekDaysAvailabilityint (bitmask)Dias da semana em que a regra vigora; ver Dependências condicionais e tabela de valores.
targetObjectobjeto?Critérios de filtro do cliente alvo (estrutura aninhada com operadores lógicos e critérios).
targetstring?Preenchido pelo servidor a partir de targetObject; não é o foco do pedido de criação/edição.
templateTypeintDiscriminador do modelo; ver tabela abaixo.
objectobjetoConteúdo específico do modelo; estrutura depende de templateType.

templateType e forma de object​

templateTypeModelo
0Ofertas (leve X pague Y)
1Desconto sobre o total do documento
2Desconto sobre linha do documento
3Campanha (várias linhas-promoção)

object — modelo Offers (templateType === 0)​

CampoTipoDescrição
eligibilityint0 = artigo, 1 = família — interpreta o significado dos IDs em eligibleIdList.
eligibleIdListstring[]Referências elegíveis (códigos de artigo ou identificadores de família, conforme eligibility).
triggerAmountdecimalQuantidade “por cada” (gatilho).
offerAmountdecimalQuantidade oferecida.
selectedOfferItemKeyIdstring?Artigo oferta (opcional).
offerItemMayNotBePresentboolSe o artigo oferta pode não existir na venda.

object — total do documento (templateType === 1)​

CampoTipoDescrição
selectedCalcTypeintModo de cálculo no produto (ex.: total superior a vs por cada); o servidor não valida este inteiro contra o conjunto de tipos de oferta usado na linha.
amountdecimalLimiar / montante associado ao modo de cálculo.
offerTypeintTipo de oferta no total do documento; tem de ser um valor válido segundo a tabela Tipos de oferta / cálculo na linha.
discountdecimalValor do desconto (montante ou percentagem conforme offerType).
activeAfterNumberOfDaysintDias até vigor do voucher/desconto (semântica de produto).
expiresAfterNumberOfDaysintValidade em dias (semântica de produto).

object — linha do documento (templateType === 2)​

CampoTipoDescrição
eligibilityintArtigo (0) ou família (1) para eligibleIdList.
eligibleIdListstring[]Lista de referências; o serviço não exige não vazio neste modelo, mas o motor de negócio pode exigi-lo.
selectedCalcTypeintTipo de cálculo na linha; tem de ser um valor válido segundo a tabela Tipos de oferta / cálculo na linha.
discountdecimalDesconto (valor efetivo validado como > 0).
activeAfterNumberOfDaysintIgual ao modelo de total, à escala de linha.
expiresAfterNumberOfDaysintIdem.

object — campanha (templateType === 3)​

CampoTipoDescrição
campaignItemsarrayLista de linhas da campanha; ver propriedades na tabela seguinte.

Elementos de campaignItems​

CampoTipoNotas
promoIdint?Opcional.
idstringIdentificador da linha de campanha; obrigatório não vazio na validação do servidor.
typeint0 = artigo, 1 = família / grupo.
percentage, retailPriceHolder, fixedPrice, newPriceHolder, quantity, priceLine, …numéricos / stringCampos de suporte ao tipo de desconto da campanha.
discountTypeintTipo de desconto da campanha; obrigatório e tem de ser um valor da tabela Tipos de desconto em campanha.
afterQuantity, afterQuantityHolder, descriptionHolderváriosConforme cenário de campanha.

Enums (valores numéricos no JSON)​

templateType​

ValorSignificado
0Ofertas (leve X pague Y)
1Desconto no total do documento
2Desconto na linha do documento
3Campanha

appliedTo​

ValorSignificado
0Linha
1Documento

Recomendação de consistência com o produto: com templateType 0 ou 1 use appliedTo = 1 (documento); com templateType 2 ou 3 use appliedTo = 0 (linha). O servidor aceita qualquer valor definido na tabela acima; combinações incoerentes podem não fazer sentido no runtime.

weekDaysAvailability (bitmask de dias)​

ValorDia
1Segunda
2Terça
4Quarta
8Quinta
16Sexta
32Sábado
64Domingo

Combine com soma OR bit a bit (ex.: dias úteis = 31; todos os dias = 127).

Elegibilidade em ofertas e na linha (eligibility)​

ValorSignificado
0Artigo
1Família

Tipo de oferta e cálculo na linha​

Usado em object.offerType (total do documento) e em object.selectedCalcType (linha do documento). Os valores válidos são os mesmos códigos numéricos:

ValorSignificado
0Oferta por quantidade
1Desconto em percentagem
2Desconto em valor
3Pontos (montante)
4Voucher em valor
5Voucher em percentagem
6Preço
7Oferta por quantidade (novo artigo)

Tipos de desconto em campanha​

Valor de discountType em cada elemento de campaignItems:

ValorSignificado
0Desconto global
1Preço fixo
2Desconto de linha / tabela de preço

Dependências condicionais do payload​

CondiçãoO que muda no JSON / semântica
templateTypeDefine a estrutura esperada em object. Valor desconhecido → código AdvancedDiscount.InvalidTemplateType. Se a forma de object não corresponder ao templateType, pode ocorrer AdvancedDiscount.InvalidObjectPayload.
object.eligibility (modelo de ofertas, templateType 0)Determina o tipo de IDs em eligibleIdList (artigo vs família). Tem de ser 0 ou 1 conforme a tabela de elegibilidade.
object em ofertaseligibleIdList tem de existir e ter pelo menos um elemento. offerAmount ≤ triggerAmount; ambos > 0.
object em total documentoamount e discount têm de ser > 0 em conjunto. offerType tem de ser um código válido na tabela Tipos de oferta / cálculo na linha.
object em linha documentodiscount > 0. eligibility e selectedCalcType têm de ser valores definidos nas tabelas de enums acima.
object em campanhacampaignItems obrigatório e não vazio. Cada item: id não vazio; discountType válido segundo Tipos de desconto em campanha.
startDate e endDate ambos preenchidosweekDaysAvailability não pode incluir um dia da semana que nunca ocorre no intervalo de calendário entre as duas datas (código AdvancedDiscount.WeekDaysOutsideDateRange). Se o intervalo cobre 6 dias ou mais de diferença de calendário, todos os dias da semana são permitidos no bitmask.
includedTerminalsSempre necessário: array não nulo e não vazio, com IDs que existam na base.

Validações: servidor​

Regras gerais (antes do ramo object)​

RegraCódigo de erro
startDate definida e data anterior a hoje (comparação só à parte da data)AdvancedDiscount.StartDateInPast
startDate e endDate definidas e startDate > endDateAdvancedDiscount.InvalidDateRange
appliedTo não é um valor conhecido (0 ou 1)AdvancedDiscount.InvalidAppliedTo
templateType não é um valor conhecido (0–3)AdvancedDiscount.InvalidTemplateType
weekDaysAvailability é 0 ou contém bits fora dos sete diasAdvancedDiscount.InvalidWeekDaysAvailability
Com startDate e endDate, bitmask de dias inclui dia fora do intervalo de calendárioAdvancedDiscount.WeekDaysOutsideDateRange
includedTerminals nulo ou vazioAdvancedDiscount.TerminalsRequired
includedTerminals com IDs inexistentesAdvancedDiscount.InvalidTerminalIds
Forma de object incompatível com templateTypeAdvancedDiscount.InvalidObjectPayload

Por tipo de object​

ContextoRegraCódigo
OfertastriggerAmount ≤ 0AdvancedDiscount.OffersTriggerAmountRequired
OfertasofferAmount ≤ 0AdvancedDiscount.OffersOfferAmountRequired
OfertasofferAmount > triggerAmountAdvancedDiscount.OffersOfferExceedsTrigger
Ofertaseligibility inválidoAdvancedDiscount.InvalidEligibleType
OfertaseligibleIdList nulo ou vazioAdvancedDiscount.OffersEligibleListRequired
Total documentoamount ≤ 0 ou discount ≤ 0AdvancedDiscount.DocTotalAmountAndDiscountRequired
Total documentoofferType inválidoAdvancedDiscount.InvalidOfferType
Linha documentodiscount ≤ 0AdvancedDiscount.DocLineDiscountRequired
Linha documentoeligibility inválidoAdvancedDiscount.InvalidEligibleType
Linha documentoselectedCalcType inválidoAdvancedDiscount.InvalidSelectedCalcType
CampanhacampaignItems nulo ou vazioAdvancedDiscount.CampaignItemsRequired
Campanhaid de um item em brancoAdvancedDiscount.CampaignItemIdRequired
CampanhadiscountType inválidoAdvancedDiscount.InvalidCampaignDiscountType

Permissões e persistência​

CódigoQuando
AdvancedDiscount.PermissionDeniedUtilizador sem permissão para criar, editar ou eliminar descontos avançados.
AdvancedDiscount.NotFoundGET/PUT/DELETE com id inexistente.
AdvancedDiscount.CreateFailFalha ao persistir na criação.
AdvancedDiscount.UpdateFailFalha ao persistir na atualização.
AdvancedDiscount.DeleteFailFalha ao eliminar.

Validações de listagem (GET)​

ParâmetroComportamento
pageSe omitido ou < 1, usa-se página 1.
pageSizeSe omitido ou ≤ 0, usa-se 20; caso contrário aplica-se o pedido até ao máximo 100.

Exemplos JSON por cenário​

Os exemplos usam camelCase e valores de enum em número. Ajuste includedTerminals a terminais existentes no tenant, startDate a uma data não anterior a hoje (validação do servidor) e weekDaysAvailability a um bitmask compatível com o intervalo startDate–endDate quando ambas estão definidas (ver Dependências condicionais do payload).

Ofertas — artigos elegíveis (templateType 0, eligibility 0)​

{
"description": "Compre 5 unidades, oferta de 2 (artigos elegíveis)",
"active": true,
"includedTerminals": [1],
"discountOrder": 1,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["ART001", "ART002"],
"triggerAmount": 5,
"offerAmount": 2,
"selectedOfferItemKeyId": "ART-GIFT",
"offerItemMayNotBePresent": false
}
}

Ofertas — famílias elegíveis (templateType 0, eligibility 1)​

{
"description": "Leve 10 pague 8 em famílias selecionadas",
"active": true,
"includedTerminals": [1, 2],
"discountOrder": 2,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-09-30T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 1,
"eligibleIdList": ["10", "20"],
"triggerAmount": 10,
"offerAmount": 2,
"selectedOfferItemKeyId": null,
"offerItemMayNotBePresent": true
}
}

Total do documento — percentagem acima de limiar (templateType 1)​

selectedCalcType 0 = “total superior a” (semântica de produto; o servidor valida sobretudo amount e discount > 0 e offerType).

{
"description": "10% de desconto em compras acima de 100 €",
"active": true,
"includedTerminals": [1],
"discountOrder": 3,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 100,
"offerType": 1,
"discount": 10,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}

Total do documento — valor fixo de desconto (templateType 1)​

{
"description": "15 € de desconto acima de 200 € de total",
"active": true,
"includedTerminals": [1],
"discountOrder": 4,
"cumulative": true,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 200,
"offerType": 2,
"discount": 15,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}

Total do documento — voucher percentagem com vigência em dias (templateType 1)​

offerType 5 = voucher em percentagem (ver tabela de tipos de oferta). Os campos activeAfterNumberOfDays e expiresAfterNumberOfDays fazem parte do modelo; o servidor não aplica regras extra a estes inteiros além das gerais de object.

{
"description": "Voucher 5% válido após 7 dias, expira ao fim de 30 dias",
"active": true,
"includedTerminals": [1],
"discountOrder": 5,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 1,
"targetObject": null,
"object": {
"selectedCalcType": 0,
"amount": 50,
"offerType": 5,
"discount": 5,
"activeAfterNumberOfDays": 7,
"expiresAfterNumberOfDays": 30
}
}

Linha do documento — desconto percentual por família (templateType 2)​

selectedCalcType deve ser um código válido na tabela Tipos de oferta / cálculo na linha (ex.: 1 = desconto em percentagem).

{
"description": "5% em linhas de famílias de eletrónica",
"active": true,
"includedTerminals": [1],
"discountOrder": 6,
"cumulative": true,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 31,
"templateType": 2,
"targetObject": null,
"object": {
"eligibility": 1,
"eligibleIdList": ["FAM001"],
"selectedCalcType": 1,
"discount": 5,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}

Linha do documento — desconto em valor por artigos (templateType 2)​

{
"description": "2 € de desconto por linha em artigos-chave",
"active": true,
"includedTerminals": [1],
"discountOrder": 7,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 2,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["SKU-A", "SKU-B"],
"selectedCalcType": 2,
"discount": 2,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}

Linha do documento — targetObject com filtro de cliente​

{
"description": "Desconto em linha só para clientes de Lisboa",
"active": true,
"includedTerminals": [1],
"discountOrder": 8,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 2,
"targetObject": {
"logicOperator": "And",
"criterias": [
{
"variableName": "City",
"operation": "==",
"value": "Lisboa"
}
]
},
"object": {
"eligibility": 0,
"eligibleIdList": ["PROMO-LINE-01"],
"selectedCalcType": 1,
"discount": 3,
"activeAfterNumberOfDays": 0,
"expiresAfterNumberOfDays": 0
}
}

Campanha — vários itens e tipos de desconto (templateType 3)​

{
"description": "Campanha de verão — múltiplas linhas",
"active": true,
"includedTerminals": [1],
"discountOrder": 9,
"cumulative": false,
"startDate": "2026-07-01T00:00:00",
"endDate": "2026-08-31T23:59:59",
"appliedTo": 0,
"weekDaysAvailability": 127,
"templateType": 3,
"targetObject": null,
"object": {
"campaignItems": [
{
"promoId": null,
"id": "ART100",
"type": 0,
"quantity": 2,
"percentage": 10,
"retailPriceHolder": 0,
"fixedPrice": 0,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 0,
"priceLine": 1
},
{
"promoId": null,
"id": "ART200",
"type": 0,
"quantity": 1,
"percentage": 0,
"retailPriceHolder": 0,
"fixedPrice": 9.99,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 1,
"priceLine": 1
},
{
"promoId": null,
"id": "GRP01",
"type": 1,
"quantity": 1,
"percentage": 0,
"retailPriceHolder": 0,
"fixedPrice": 0,
"newPriceHolder": 0,
"descriptionHolder": null,
"afterQuantity": false,
"afterQuantityHolder": null,
"discountType": 2,
"priceLine": 2
}
]
}
}

Atualização — corpo com id​

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"description": "Regra atualizada",
"active": true,
"includedTerminals": [1],
"discountOrder": 10,
"cumulative": false,
"startDate": "2026-06-01T00:00:00",
"endDate": "2026-12-31T23:59:59",
"appliedTo": 1,
"weekDaysAvailability": 127,
"templateType": 0,
"targetObject": null,
"object": {
"eligibility": 0,
"eligibleIdList": ["ART001"],
"triggerAmount": 4,
"offerAmount": 1,
"selectedOfferItemKeyId": null,
"offerItemMayNotBePresent": false
}
}

Para outras combinações, consulte os exemplos da operação Criar na documentação OpenAPI (Swagger) exposta pelo gateway.


Notas de integração​

  • Use GET /gateway/advanced-discount/next-order para sugerir discountOrder em novas regras.
  • Em atualização, envie o id coerente com a rota PUT /gateway/advanced-discount/{id}.
  • Alguns códigos de erro adicionais existem na lista interna da API (ex.: DuplicateId, TemplateTypeRequired, ObjectRequired) mas não são devolvidos pelo fluxo atual de validação e persistência deste recurso; em caso de dúvida, baseie-se no código de problema devolvido na resposta.

Recursos relacionados​


Última atualização: 14 Maio de 2026