Skip to main content

Introdução

Ao consultar débitos veiculares, é comum encontrar situações onde alguns débitos possuem relacionamentos e restrições entre si. A API Uvvipague retorna três tipos de relacionamentos que você precisa entender e tratar corretamente:

Dependentes

Débitos que devem ser pagos juntos

Distintos

Débitos que não podem ser pagos juntos

Obrigatórios

Débitos que sempre devem ser incluídos
Importante: Ignorar essas regras resultará em erro na solicitação de pagamento. É fundamental validar esses relacionamentos antes de processar o pagamento.

Débitos Dependentes (dependsOn)

O que são?

Débitos dependentes são aqueles que precisam ser pagos em conjunto. Quando um débito possui dependência de outro, ambos devem ser incluídos na mesma solicitação de pagamento.

Exemplo Comum

O caso mais comum é o licenciamento, que geralmente depende da quitação do IPVA e/ou multas pendentes.
Regra: Para licenciar um veículo, é necessário estar com o IPVA e multas em dia. Por isso, o débito de licenciamento pode ter dependência desses outros débitos.

Estrutura da Resposta

Cada débito retornado pela API possui um campo dependsOn que é um array contendo os IDs dos débitos dos quais ele depende:
{
  "debts": [
    {
      "id": "17BD43F1-E345-4A43-BFFF-0952CA7A3FAB",
      "amount": 206.86,
      "title": "Infração a Vencer",
      "description": "Infração de Trânsito - Auto: 5B3022271 - Guia: 173622147",
      "dueDate": "2022-04-18T00:00:00",
      "type": "ticket",
      "required": false,
      "dependsOn": [],
      "distinct": []
    },
    {
      "id": "B8E1F87B-E2A7-483B-8BAF-977291B2A3CC",
      "amount": 144.86,
      "title": "Licenciamento",
      "description": "Licenciamento 2024",
      "type": "licensing",
      "year": 2024,
      "required": false,
      "dependsOn": [
        "17BD43F1-E345-4A43-BFFF-0952CA7A3FAB"
      ],
      "distinct": []
    }
  ]
}
No exemplo acima:
  • O débito de Licenciamento (B8E1F87B-...) depende da Infração (17BD43F1-...)
  • Para pagar o licenciamento, você deve incluir a infração na mesma solicitação

Como Validar

Antes de enviar a solicitação de pagamento, valide se todos os débitos dependentes estão incluídos:
function validarDebitos(debitosSelecionados, todosDebitos) {
  for (const debito of debitosSelecionados) {
    // Verifica se o débito tem dependências
    if (debito.dependsOn && debito.dependsOn.length > 0) {
      // Verifica se todas as dependências estão selecionadas
      for (const dependenciaId of debito.dependsOn) {
        const dependenciaIncluida = debitosSelecionados.some(
          d => d.id === dependenciaId
        );
        
        if (!dependenciaIncluida) {
          const dependencia = todosDebitos.find(d => d.id === dependenciaId);
          throw new Error(
            `O débito "${debito.title}" depende de "${dependencia.title}" ` +
            `e ambos devem ser pagos juntos.`
          );
        }
      }
    }
  }
  
  return true;
}

// Exemplo de uso
try {
  validarDebitos(debitosSelecionados, todosDebitos);
  // Prosseguir com o pagamento
} catch (error) {
  console.error(error.message);
  // Exibir mensagem ao usuário
}

Erro ao Não Respeitar Dependências

Se você tentar pagar um débito sem incluir suas dependências, a API retornará um erro:
{
  "error": {
    "code": "DEPENDENT_DEBTS_MISSING",
    "message": "Existem débitos dependentes que devem ser pagos juntos ao débito informado",
    "details": {
      "missingDebts": [
        {
          "id": "17BD43F1-E345-4A43-BFFF-0952CA7A3FAB",
          "title": "Infração a Vencer"
        }
      ]
    }
  }
}

Débitos Distintos (distinct)

O que são?

Débitos distintos são aqueles que não podem ser pagos juntos na mesma transação. Eles são mutuamente exclusivos.

Exemplo Comum

O caso mais comum é quando o IPVA possui duas opções de pagamento:
  • Cota Única (com desconto)
  • Cotas Parceladas (sem desconto)
Você deve escolher apenas uma dessas opções, não pode pagar ambas.
Atenção: Tentar pagar débitos distintos juntos resultará em erro. O usuário deve escolher qual opção deseja.

Estrutura da Resposta

Cada débito possui um campo distinct que é um array contendo os IDs dos débitos que não podem ser pagos junto com ele:
{
  "debts": [
    {
      "id": "A1B2C3D4-1234-5678-90AB-CDEF12345678",
      "amount": 1500.00,
      "title": "IPVA 2024 - Cota Única",
      "description": "IPVA 2024 em cota única com 3% de desconto",
      "type": "ipva",
      "year": 2024,
      "hasDiscount": true,
      "required": false,
      "dependsOn": [],
      "distinct": [
        "E5F6G7H8-9012-3456-78IJ-KLMN90123456"
      ]
    },
    {
      "id": "E5F6G7H8-9012-3456-78IJ-KLMN90123456",
      "amount": 1545.00,
      "title": "IPVA 2024 - Parcelado",
      "description": "IPVA 2024 parcelado em 3x",
      "type": "ipva",
      "year": 2024,
      "hasDiscount": false,
      "required": false,
      "dependsOn": [],
      "distinct": [
        "A1B2C3D4-1234-5678-90AB-CDEF12345678"
      ]
    }
  ]
}
No exemplo acima:
  • IPVA Cota Única e IPVA Parcelado são mutuamente exclusivos
  • O usuário deve escolher apenas uma das opções

Como Validar

Valide se não há débitos distintos selecionados juntos:
function validarDebitosDistintos(debitosSelecionados) {
  for (const debito of debitosSelecionados) {
    // Verifica se o débito tem restrições de distintos
    if (debito.distinct && debito.distinct.length > 0) {
      // Verifica se algum débito distinto foi selecionado
      for (const distintoId of debito.distinct) {
        const distintoIncluido = debitosSelecionados.some(
          d => d.id === distintoId
        );
        
        if (distintoIncluido) {
          const distintoDebito = debitosSelecionados.find(
            d => d.id === distintoId
          );
          throw new Error(
            `Os débitos "${debito.title}" e "${distintoDebito.title}" ` +
            `não podem ser pagos juntos. Escolha apenas um.`
          );
        }
      }
    }
  }
  
  return true;
}

// Exemplo de uso
try {
  validarDebitosDistintos(debitosSelecionados);
  // Prosseguir com o pagamento
} catch (error) {
  console.error(error.message);
  // Exibir mensagem ao usuário
}

Erro ao Não Respeitar Distintos

Se você tentar pagar débitos distintos juntos, a API retornará um erro:
{
  "error": {
    "code": "DISTINCT_DEBTS_CONFLICT",
    "message": "Existem débitos que não podem ser pagos em conjunto",
    "details": {
      "conflictingDebts": [
        {
          "id": "A1B2C3D4-1234-5678-90AB-CDEF12345678",
          "title": "IPVA 2024 - Cota Única"
        },
        {
          "id": "E5F6G7H8-9012-3456-78IJ-KLMN90123456",
          "title": "IPVA 2024 - Parcelado"
        }
      ]
    }
  }
}

Débitos Obrigatórios (required)

O que são?

Débitos obrigatórios são aqueles que sempre devem ser incluídos no pagamento quando retornados na consulta. Não é possível pagar outros débitos sem incluir os obrigatórios.

Exemplo Comum

O Seguro Obrigatório (DPVAT) é frequentemente marcado como obrigatório quando há outros débitos a serem pagos.
Regra: Se um débito obrigatório está presente na consulta e você está pagando outros débitos, o obrigatório também deve ser incluído.

Estrutura da Resposta

Cada débito possui um campo required (booleano) que indica se ele é obrigatório:
{
  "debts": [
    {
      "id": "87D4B252-1A3D-4918-A390-1C911424485B",
      "amount": 5.23,
      "title": "Seguro Obrigatório 2024",
      "description": "Seguro Obrigatório (DPVAT) 2024",
      "type": "insurance",
      "year": 2024,
      "required": true,
      "dependsOn": [],
      "distinct": []
    },
    {
      "id": "577FE0F7-2143-4236-BBC3-7AD071409298",
      "amount": 1864.04,
      "title": "Licenciamento 2024",
      "description": "Licenciamento 2024",
      "type": "licensing",
      "year": 2024,
      "required": false,
      "dependsOn": [],
      "distinct": []
    }
  ]
}
No exemplo acima:
  • O Seguro Obrigatório tem required: true
  • Se você for pagar o Licenciamento, deve incluir o Seguro Obrigatório também

Como Validar

Valide se todos os débitos obrigatórios estão incluídos quando há outros débitos selecionados:
function validarDebitosObrigatorios(debitosSelecionados, todosDebitos) {
  // Se não há débitos selecionados, não precisa validar
  if (debitosSelecionados.length === 0) {
    return true;
  }
  
  // Encontra todos os débitos obrigatórios
  const debitosObrigatorios = todosDebitos.filter(d => d.required === true);
  
  // Verifica se todos os obrigatórios estão selecionados
  for (const obrigatorio of debitosObrigatorios) {
    const obrigatorioIncluido = debitosSelecionados.some(
      d => d.id === obrigatorio.id
    );
    
    if (!obrigatorioIncluido) {
      throw new Error(
        `O débito "${obrigatorio.title}" é obrigatório e deve ser incluído ` +
        `no pagamento.`
      );
    }
  }
  
  return true;
}

// Exemplo de uso
try {
  validarDebitosObrigatorios(debitosSelecionados, todosDebitos);
  // Prosseguir com o pagamento
} catch (error) {
  console.error(error.message);
  // Exibir mensagem ao usuário
}

Erro ao Não Incluir Obrigatórios

Se você tentar pagar débitos sem incluir os obrigatórios, a API retornará um erro:
{
  "error": {
    "code": "REQUIRED_DEBTS_MISSING",
    "message": "Existem débitos obrigatórios que devem ser pagos",
    "details": {
      "requiredDebts": [
        {
          "id": "87D4B252-1A3D-4918-A390-1C911424485B",
          "title": "Seguro Obrigatório 2024"
        }
      ]
    }
  }
}

Validação Completa

Para garantir que sua solicitação de pagamento seja processada corretamente, você deve validar todas as três regras:
function validarSelecaoDebitos(debitosSelecionados, todosDebitos) {
  try {
    // 1. Valida débitos obrigatórios
    validarDebitosObrigatorios(debitosSelecionados, todosDebitos);
    
    // 2. Valida débitos dependentes
    validarDebitos(debitosSelecionados, todosDebitos);
    
    // 3. Valida débitos distintos
    validarDebitosDistintos(debitosSelecionados);
    
    return {
      valid: true,
      message: 'Seleção de débitos válida'
    };
  } catch (error) {
    return {
      valid: false,
      message: error.message
    };
  }
}

// Exemplo de uso
const resultado = validarSelecaoDebitos(debitosSelecionados, todosDebitos);

if (resultado.valid) {
  // Prosseguir com o pagamento
  processarPagamento(debitosSelecionados);
} else {
  // Exibir mensagem de erro ao usuário
  alert(resultado.message);
}

Experiência do Usuário

Sugestões de Interface

Para uma melhor experiência do usuário, considere:
Quando o usuário selecionar um débito que possui dependências, selecione automaticamente os débitos dependentes e desabilite a opção de desmarcá-los.
function selecionarDebito(debitoId, todosDebitos) {
  const debito = todosDebitos.find(d => d.id === debitoId);
  const selecionados = [debito];
  
  // Adiciona dependências automaticamente
  if (debito.dependsOn && debito.dependsOn.length > 0) {
    debito.dependsOn.forEach(depId => {
      const dependencia = todosDebitos.find(d => d.id === depId);
      selecionados.push(dependencia);
    });
  }
  
  return selecionados;
}
Quando o usuário selecionar um débito, desabilite automaticamente os débitos que são distintos dele.
function desabilitarDistintos(debitoSelecionado, todosDebitos) {
  return todosDebitos.map(debito => ({
    ...debito,
    disabled: debitoSelecionado.distinct.includes(debito.id)
  }));
}
Marque visualmente os débitos obrigatórios e não permita que sejam desmarcados.
function renderizarDebito(debito) {
  return (
    <div className={debito.required ? 'debito-obrigatorio' : ''}>
      <input 
        type="checkbox" 
        checked={debito.selected}
        disabled={debito.required}
      />
      <span>{debito.title}</span>
      {debito.required && <span className="badge">Obrigatório</span>}
    </div>
  );
}
Exiba mensagens claras explicando por que certos débitos foram selecionados ou desabilitados.
function getMensagemDebito(debito, todosDebitos) {
  if (debito.required) {
    return 'Este débito é obrigatório e deve ser pago';
  }
  
  if (debito.dependsOn && debito.dependsOn.length > 0) {
    const deps = debito.dependsOn.map(id => 
      todosDebitos.find(d => d.id === id).title
    ).join(', ');
    return `Depende de: ${deps}`;
  }
  
  if (debito.distinct && debito.distinct.length > 0) {
    return 'Não pode ser pago junto com outras opções de IPVA';
  }
  
  return '';
}

Testando com Placas de Homologação

Use as placas de teste específicas para validar o tratamento dessas regras:

Resumo das Regras

1

Débitos Obrigatórios

Se houver débitos com required: true, eles devem ser incluídos no pagamento
2

Débitos Dependentes

Se um débito possui IDs no array dependsOn, todos esses débitos devem ser incluídos juntos
3

Débitos Distintos

Se um débito possui IDs no array distinct, nenhum desses débitos pode ser incluído junto
4

Validação Completa

Valide as três regras antes de enviar a solicitação de pagamento para evitar erros

Referências