Clientes, fornecedores, transportadoras e funcionários pela API: endereços, contatos e o que duplica
O QUE ESTA FONTE COBRE
Como criar, listar, ver, editar e apagar clientes, fornecedores, transportadoras e funcionários pela API do ERP, com foco na parte que mais gera chamado: endereço, contato e o que acontece com essas listas quando você edita.
Duas coisas não são reexplicadas aqui. O renome dos campos do cliente no JSON (pj_razao_social sai como razao_social, pj_cnpj como cnpj, pf_cpf como cpf, loja_virtual_email como email_acesso) está na fonte de campos e retorno do cliente. E as regras do cadastro em si (CPF/CNPJ válido, duplicidade, limite de crédito) são do módulo de cadastro do ERP e têm fonte própria: a API só repassa a mensagem que o cadastro devolve.
Também não confunda com /api/clientes/ecommerce_adicionar, ecommerce_login e os demais ecommerce_, nem com /api/pessoas. Esses são da loja virtual e têm outro comportamento.
OS QUATRO CADASTROS E O QUE CADA UM ACEITA
Clientes, fornecedores, transportadoras e funcionários têm as mesmas cinco operações: GET /api/<cadastro> lista, GET /api/<cadastro>/{id} mostra um, POST /api/<cadastro> cria, PUT /api/<cadastro>/{id} edita e DELETE /api/<cadastro>/{id} apaga.
Essas cinco operações não têm rota escrita nome a nome: quem escolhe a ação é a rota genérica de cada método HTTP. Por isso o verbo importa tanto quanto o caminho. Duas consequências práticas: o id na URL precisa ser numérico (visualizar, editar e deletar com id não numérico respondem 400 "O parametro ID é inválido!"), e num POST você não pode mandar id dentro do corpo — a mesma mensagem 400 aparece.
Cliente, fornecedor e transportadora aceitam duas listas aninhadas no corpo: enderecos e contatos. Cada item da lista vem dentro de uma chave, endereco e contato respectivamente.
Funcionário é diferente e isso costuma pegar quem trata os quatro igual. Funcionário não tem lista de endereços nem lista de contatos. O endereço dele é campo solto no próprio registro: cep, logradouro, numero, complemento, bairro, cidade_id, nome_cidade e estado, gravados junto com nome, cpf, rg, data_nascimento, sexo, telefone, email_secundario e comissao. Fora dessa lista (e da lista de campos extras, tratada mais abaixo), o que você mandar no corpo do funcionário é ignorado — a API remonta o registro só com esses campos. Duas particularidades: o campo email que você enviar é gravado como email_secundario, e nome é obrigatório (sem ele, 400 "É obrigatório informar o nome!").
O ACHADO QUE MAIS GERA CHAMADO: EDITAR DUPLICA ENDEREÇO E CONTATO
Antes de salvar, tanto no criar quanto no editar, a API remove o id de cada endereço e de cada contato que você enviou. Isso vale para cliente, fornecedor e transportadora.
A consequência é direta. Se você faz um PUT no cadastro mandando a lista completa de endereços que leu antes, esperando que cada item com id atualize o endereço correspondente, os ids não chegam ao cadastro. O que sobra é uma lista de itens sem identidade, e o resultado pode ser endereço e contato repetidos no cadastro. Quem integra rodando sincronização periódica sente isso rápido: cada rodada empilha mais linhas.
O caminho seguro para mexer em endereço de cliente é o recurso próprio, /api/clientes_enderecos. Lá o endereço tem id e você trata um por vez.
O RECURSO PRÓPRIO DE ENDEREÇO DO CLIENTE
/api/clientes_enderecos tem listar, ver, criar, editar e apagar, e todas as ações exigem cliente_id na querystring. Se faltar, a resposta é 406 com a mensagem "É necessário informar o campo cliente_id!". Você também pode mandar cliente= no lugar de cliente_id=, que a API converte.
Esse recurso existe só para cliente. Não há equivalente para fornecedor nem para transportadora: nesses dois, o endereço só entra aninhado no cadastro, com o risco de duplicidade descrito acima.
Os campos do recurso são cep, logradouro, numero, complemento, bairro, cidade_id, codigo_cidade, nome_cidade, estado, pais, tipo_id, nome_tipo, referencia e padrao, além de id e cliente_id. Repare que referencia, padrao e codigo_cidade só aparecem por aqui: no cadastro do cliente, a lista aninhada de endereços não devolve esses três no JSON.
Uma diferença importante entre criar e editar nesse recurso: ao criar, a API roda o tratamento de endereço que resolve cidade e estado a partir do que você mandou. Ao editar, ela não roda. Então, num PUT de endereço, mandar só o nome da cidade não preenche cidade_id: mande cidade_id.
CONTATO NÃO TEM RECURSO PRÓPRIO
Não existe um /api/clientes_contatos. Contato só entra aninhado no cadastro, e por isso ele carrega o mesmo risco de duplicidade descrito acima. Se o contato duplicou, a correção é feita no ERP, não pela API.
Os campos do contato de cliente são tipo_id, nome_tipo, nome, contato, cargo e observacao. Em fornecedor e transportadora o contato tem só nome, contato, cargo e observacao: não há tipo de contato nesses dois.
COMO A API RESOLVE CIDADE E ESTADO NO ENDEREÇO
O tratamento de endereço funciona pelo NOME da cidade, não pelo id. Se você mandar o campo cidade, ele vira nome_cidade. Com um nome preenchido, a API procura uma cidade com aquele nome. Se achar, ela sobrescreve cidade_id, codigo_cidade, nome_cidade e estado com os dados da cidade encontrada.
O detalhe que custa caro: a primeira busca é só pelo nome. Se você também mandou estado e o estado da cidade encontrada for diferente, a API refaz a busca com nome mais estado e usa esse resultado. Ou seja, sem o estado junto, um nome que existe em vários estados pode resolver para a cidade errada. Mande sempre nome e estado, ou, melhor ainda, mande o cidade_id certo.
Se o nome não bater com nenhuma cidade, o tratamento não reclama e não corrige: ele simplesmente devolve o endereço como veio.
Esse tratamento roda nos endereços aninhados de cliente, fornecedor e transportadora, e no criar de /api/clientes_enderecos. Ele não roda no editar desse recurso nem no funcionário: no cadastro de funcionário, mandar nome_cidade não preenche cidade_id, então mande cidade_id.
OS APOIOS PARA DESCOBRIR OS IDS ANTES DE GRAVAR
Antes de gravar, use os recursos de apoio para achar o id certo.
/api/cep recebe cep na querystring. Ele limpa tudo que não for número e exige oito dígitos: menos que isso responde 400 com "Insira um CEP válido!", e sem o parâmetro responde 400 com "É necessário informar um CEP!". O retorno é uma lista fechada: cep, logradouro, complemento, bairro, cidade_id, nome_cidade, codigo_cidade e estado.
/api/cidades lista cidades com id, codigo e nome, e é a forma de pegar o cidade_id. /api/estados lista id, codigo, nome e sigla. /api/tipos_contatos e /api/tipos_enderecos listam id e nome, e servem para preencher o tipo_id do contato e do endereço do cliente.
CAMPOS EXTRAS: FORNECEDOR E FUNCIONÁRIO USAM O CADASTRO DO CLIENTE
Cliente, fornecedor e funcionário aceitam campos extras no corpo, na lista atributos, cada item dentro da chave atributo. O formato é o mesmo nos três.
O que muda, e quase ninguém espera: fornecedor e funcionário são validados contra o cadastro de campos extras de CLIENTE. O atributo_id que vale nos três é o do cadastro de campos extras de cliente. O detalhe de como cadastrar e consultar esses campos extras está na fonte de campos extras pela API.
Dois detalhes de cada cadastro. No funcionário, item de campo extra sem atributo_id é descartado sem aviso, e na edição também é descartado o item com conteudo vazio — para limpar o valor de um campo extra do funcionário, use o ERP. No fornecedor, o campo extra grava normalmente, mas o atributo_id não volta no JSON de resposta (só id, tipo, conteudo e descricao); em cliente e funcionário o atributo_id volta.
Transportadora é a exceção: ela não tem campos extras. Se você mandar a lista atributos num POST ou PUT de transportadora, ela não é gravada.
Se você mandar um atributo_id que não existe, a resposta é 400 com "Falha ao salvar dados, campo extra inválido.". Se houver campo extra marcado como obrigatório e você não mandar, a resposta é 400 listando os que faltaram, com nome e número: "Os seguintes atributos obrigatórios não foram enviados: ...". Como a validação é sempre contra o cadastro de campos extras de cliente, um campo obrigatório criado lá também bloqueia o POST de fornecedor e de funcionário.
O PARÂMETRO RESUMIDO NA LISTAGEM DE CLIENTES
Na listagem de clientes você pode acrescentar resumido na querystring. Com ele, cada cliente volta com uma lista fechada de seis campos: id, nome, tipo_pessoa, razao_social, cadastrado_em e modificado_em. Nada mais: nem endereços, nem contatos, nem campos extras.
Isso existe porque cada requisição da API tem limite de dez segundos. Base grande com endereço, contato e campo extra por cliente estoura esse limite com facilidade. Se a sua listagem de clientes está caindo por tempo, resumido é a saída: use para varrer a base e descobrir quem mudou por modificado_em, e depois busque o cliente inteiro um a um pelo id. Esse parâmetro existe só na listagem de clientes.
DATA DE NASCIMENTO E A CONFERÊNCIA DE TIPO
Data de nascimento vai no formato AAAA-MM-DD. Qualquer outro formato (30/01/1990, 1990-1-5, data com hora junto) para o salvamento com 400 antes de gravar qualquer coisa.
Quem barra é a conferência geral de tipos, que compara o formato de cada campo com o tipo da coluna no banco. Ela cobre campos de número inteiro, de data e de valor decimal — campo de texto não passa por ela. As mensagens são "O campo (x) não é um valor inteiro (y)!", "O campo (x) não é uma data válida (y)!" e "O campo (x) não é um valor válido (y)!".
Atenção ao nome entre parênteses: é o nome INTERNO do campo, não o nome que você mandou. Você manda data_nascimento no cliente e o erro fala em pf_data_nascimento. O de-para entre os dois nomes está na fonte de campos e retorno do cliente.
O QUE COSTUMA DAR ERRADO
Endereço ou contato duplicado depois de editar. Causa real: o id de cada item da lista é descartado antes de salvar. Use /api/clientes_enderecos por id para endereço; para contato, corrija no ERP.
Endereço gravou na cidade errada. Causa real: só o nome da cidade foi enviado e o nome existe em mais de um estado. Mande estado junto ou mande cidade_id.
Editei o endereço pelo recurso próprio e o cidade_id não mudou. Causa real: o editar desse recurso não resolve cidade pelo nome. Mande cidade_id. O mesmo vale para o cadastro de funcionário.
Chamada em /api/clientes_enderecos responde 406. Causa real: faltou cliente_id na querystring. Ele é obrigatório em todas as ações, inclusive listar e apagar.
400 "O parametro ID é inválido!". Ou o id na URL não é numérico, ou você mandou id dentro do corpo de um POST. No POST, tire o id do corpo; para atualizar, use PUT com o id na URL.
"Falha ao cadastrar cliente." com 400. Essa mensagem é genérica: é o que a API responde quando a criação estoura de um jeito que ela não sabe traduzir. Ela não diz qual campo. Refaça o POST com o mínimo (nome e tipo_pessoa) e vá acrescentando bloco por bloco até achar o que quebra. Os outros cadastros têm mensagens genéricas parecidas, mas com status 500: "Falha ao editar cliente.", "Falha ao salvar fornecedor" (no criar e no editar), "Falha ao adicionar funcionário." e "Falha ao salvar funcionário.". Em transportadora não há texto fixo: o 500 devolve a mensagem crua do erro. Em todos, a receita é a mesma: reduzir o corpo até isolar o bloco que quebra.
Mensagem falando de CPF/CNPJ já cadastrado ou de limite de crédito. Isso não é regra da API: quem devolve o texto é o módulo de cadastro do ERP, e a API só repassa. A resposta está na fonte do cadastro correspondente.
Erro 400 citando um campo que você não mandou, entre parênteses. É o nome interno do campo. Veja o parágrafo acima sobre a conferência de tipo.
Listagem cai por tempo. Cada requisição tem dez segundos. Use resumido na listagem de clientes e reduza o limite por página.
Números e verdadeiro/falso do cliente voltam como texto no JSON. O trecho que converteria esses tipos existe, mas está desligado no código: a chave que o liga nunca é ativada. Trate id, cidade_id e ativo como texto ao ler.
Campo extra não gravou na transportadora. Não é falha: transportadora não tem campos extras.
O e-mail de acesso do cliente. Ele sai no JSON com o nome email_acesso, e não com o nome interno. Se você procura por loja_virtual_email na resposta, não vai achar.