Acesso Externo via API (Web Service)

Índice

Apresentação

Visando atender a necessidade dos contribuintes que precisam enviar ou consultar muitos registros, ou que desejam automatizar o acesso fazendo integração nos seus próprios sistemas, a prefeitura disponibiliza um acesso externo via API (Web Service).

Ambiente de Teste e Produção

O usuário tem à disposição dois ambientes, um somente para realizar testes, chamado de Ambiente de Teste, acessível através da URL https://riachao.teste.cecsystem.com.br, e o outro chamado Ambiente de Produção, acessível através da URL https://riachao.cecsystem.com.br. Antes de submeter qualquer dado para o Ambiente de Produção, é altamente recomendável utilizar primeiro o Ambiente de Teste para entender como a API funciona.

Qualquer usuário cadastrado no sistema pode fazer o acesso via API, desde que tenha a devida permissão para fazer operações de consulta, criação, atualização ou cancelamento sobre o registro em questão. Se ainda não está cadastrado, faça o seu cadastro antes de continuar, bastando acessar os links postados anteriormente. Observe que no Ambiente de Produção, é necessário aguardar a aprovação do cadastro para acessar o sistema (e consequentemente a API).

Autenticação

Para fazer a comunicação com a API, o usuário pode usar qualquer linguagem de programação capaz de fazer chamadas XML-RPC. Também é necessário criar uma Chave de API, bastando acessar o menu Perfil > Preferências:

Acessando aba Segurança da Conta:

Clicando no botão para criar uma Nova Chave de API:

O usuário pode criar quantas chaves quiser, bem como pode revogar o acesso para as chaves existentes. Isso é muito útil para fornecer acesso à sistemas de terceiros, sem precisar informar a sua senha de acesso ao sistema (login).

De posse do nome de usuário (o mesmo usado para fazer login no sistema) e da chave de API (recém criada anteriormente), basta preencher os seguintes parâmetros:


# Ambiente de Produção
# url = 'https://riachao.cecsystem.com.br'

# Ambiente de Teste
url = 'https://riachao.teste.cecsystem.com.br'

banco = 'riachao'
usuario = 'inserir nome de usuário'
chave = 'inserir chave da API'

// Ambiente de Produção
// final String url = "https://riachao.cecsystem.com.br";

// Ambiente de Teste
final String url = "https://riachao.teste.cecsystem.com.br";

final String db = "riachao",
       username = "nserir nome de usuário",
       password = "inserir chave da API";

# Ambiente de Produção
# url = 'https://riachao.cecsystem.com.br'

# Ambiente de Teste
url = 'https://riachao.teste.cecsystem.com.br'

db = 'riachao'
username = 'inserir nome de usuário'
password = 'inserir chave da API'

Utilize os parâmetros anteriores para autenticar:


import xmlrpc.client

common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url))
uid = common.authenticate(banco, usuario, chave, {})
models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))

def call(model, method, *args, **kwargs):
    """
    Função auxiliar para ser usada nas futuras chamadas.
    """
    return models.execute_kw(banco, uid, chave, model, method, args, kwargs)

Note que a biblioteca xmlrpc já vem instalada por padrão em qualquer ambiente Python, não sendo necessária nenhuma dependência externa para realizar a comunicação.


import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.XmlRpcException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

final XmlRpcClient client = new XmlRpcClient();

final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));

int uid = (int)client.execute(
    common_config, "authenticate",
    Arrays.asList(db, username, password, Collections.emptyMap())
);
final XmlRpcClient models = new XmlRpcClient() {{
    setConfig(new XmlRpcClientConfigImpl() {{
        setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
    }});
}};

require "xmlrpc/client"

common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
uid = common.call('authenticate', db, username, password, {})
models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy

Note que a biblioteca xmlrpc já vem instalada por padrão em qualquer ambiente Ruby, não sendo necessária nenhuma dependência externa para realizar a comunicação.

Consultar Notas Fiscais

Liste as dez primeiras identificações id de notas fiscais do usuário:


call('prefeitura.nota_fiscal', 'search', [], limit=10)

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "search",
    Arrays.asList(Collections.emptyList()),
    new HashMap() {{ put("limit", 10); }}
)));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'search', [[]],
                  {limit: 10})

Exemplo de resultado:


[3986, 3985, 3984, 3983, 3982, 3981, 3979, 3978, 3977, 3976]

Pula os três primeiros registros e lista as sete identificações id da sequência de notas fiscais (utilize offset e limit para controlar a paginação):


call('prefeitura.nota_fiscal', 'search', [], offset=3, limit=7)

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "search",
    Arrays.asList(Collections.emptyList()),
    new HashMap() {{ put("offset", 3); put("limit", 7); }}
)));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'search', [[]],
                  {offset: 3, limit: 7})

Exemplo de resultado:


[3983, 3982, 3981, 3979, 3978, 3977, 3976]

Utilize filtros para listar as dez primeiras identificações id de notas fiscais já canceladas, em ordem decrescente pela data de cancelamento (data_hora_cancelamento):


call('prefeitura.nota_fiscal', 'search', [['state', '=', 'CANCELADA']],
     limit=10, order='data_hora_cancelamento DESC')

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "search",
    Arrays.asList(Arrays.asList(Arrays.asList("state", "=", "CANCELADA"))),
    new HashMap() {{
        put("limit", 10);
        put("order", "data_hora_cancelamento DESC");
    }}
)));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'search',
                  [[['state', '=', 'CANCELADA']]],
                  {limit: 10, order: 'data_hora_cancelamento DESC'})

Exemplo de resultado:


[3901, 3833, 3856, 3838, 3818, 3793, 3497, 3781, 3780, 3758]

Leia os campos de identificação id, número da nota name e data de cancelamento data_hora_cancelamento dos registros utilizando cinco identificações id de notas fiscais retornadas pela chamada search realizada anteriormente:


call('prefeitura.nota_fiscal', 'read', [3901, 3833, 3856, 3838, 3818],
     fields=['id', 'name', 'data_hora_cancelamento'])

Exemplo de resultado:


[
    {'id': 3901,
     'name': 'NF-2023-483-00001',
     'data_hora_cancelamento': '2023-01-10 17:51:48'},
    {'id': 3833,
     'name': 'NF-2023-498-00001',
     'data_hora_cancelamento': '2023-01-04 16:41:24'},
    {'id': 3856,
     'name': 'NF-2023-52-00001',
     'data_hora_cancelamento': '2023-01-03 20:13:19'},
    {'id': 3838,
     'name': 'NF-2023-27-00002',
     'data_hora_cancelamento': '2023-01-02 20:04:13'},
    {'id': 3818,
     'name': 'NF-2022-498-00001',
     'data_hora_cancelamento': '2022-12-29 12:43:10'},
]

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "read",
    Arrays.asList(Arrays.asList(3901, 3833, 3856, 3838, 3818)),
    new HashMap() {{
        put("fields", Arrays.asList("id", "name", "data_hora_cancelamento"));
    }}
)));

Exemplo de resultado:


[
    {'id': 3901,
     'name': 'NF-2023-483-00001',
     'data_hora_cancelamento': '2023-01-10 17:51:48'},
    {'id': 3833,
     'name': 'NF-2023-498-00001',
     'data_hora_cancelamento': '2023-01-04 16:41:24'},
    {'id': 3856,
     'name': 'NF-2023-52-00001',
     'data_hora_cancelamento': '2023-01-03 20:13:19'},
    {'id': 3838,
     'name': 'NF-2023-27-00002',
     'data_hora_cancelamento': '2023-01-02 20:04:13'},
    {'id': 3818,
     'name': 'NF-2022-498-00001',
     'data_hora_cancelamento': '2022-12-29 12:43:10'},
]

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'read',
                  [[3901, 3833, 3856, 3838, 3818]],
                  {fields: %w(id name data_hora_cancelamento)})

Exemplo de resultado:


[
  {"id" => 3901,
   "name" => "NF-2023-483-00001",
   "data_hora_cancelamento" => "2023-01-10 17:51:48"},
  {"id" => 3833,
   "name" => "NF-2023-498-00001",
   "data_hora_cancelamento" => "2023-01-04 16:41:24"},
  {"id" => 3856,
   "name" => "NF-2023-52-00001",
   "data_hora_cancelamento" => "2023-01-03 20:13:19"},
  {"id" => 3838,
   "name" => "NF-2023-27-00002",
   "data_hora_cancelamento" => "2023-01-02 20:04:13"},
  {"id" => 3818,
   "name" => "NF-2022-498-00001",
   "data_hora_cancelamento" => "2022-12-29 12:43:10"}
]

Como é muito comun se utilizar filtros para ler os campos dos registros, existe o comando search_read que combina as chamadas search e read em uma só:


call('prefeitura.nota_fiscal', 'search_read', [['state', '=', 'CANCELADA']],
     ['id', 'name', 'data_hora_cancelamento'],
     limit=5, order='data_hora_cancelamento DESC')

Exemplo de resultado:


[
    {'id': 3901,
     'name': 'NF-2023-483-00001',
     'data_hora_cancelamento': '2023-01-10 17:51:48'},
    {'id': 3833,
     'name': 'NF-2023-498-00001',
     'data_hora_cancelamento': '2023-01-04 16:41:24'},
    {'id': 3856,
     'name': 'NF-2023-52-00001',
     'data_hora_cancelamento': '2023-01-03 20:13:19'},
    {'id': 3838,
     'name': 'NF-2023-27-00002',
     'data_hora_cancelamento': '2023-01-02 20:04:13'},
    {'id': 3818,
     'name': 'NF-2022-498-00001',
     'data_hora_cancelamento': '2022-12-29 12:43:10'},
]

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "search_read",
    Arrays.asList(
        Arrays.asList(Arrays.asList("state", "=", "CANCELADA")),
        Arrays.asList("id", "name", "data_hora_cancelamento")
    ),
    new HashMap() {{
        put("limit", 5);
        put("order", "data_hora_cancelamento DESC");
    }}
)));

Exemplo de resultado:


[
    {'id': 3901,
     'name': 'NF-2023-483-00001',
     'data_hora_cancelamento': '2023-01-10 17:51:48'},
    {'id': 3833,
     'name': 'NF-2023-498-00001',
     'data_hora_cancelamento': '2023-01-04 16:41:24'},
    {'id': 3856,
     'name': 'NF-2023-52-00001',
     'data_hora_cancelamento': '2023-01-03 20:13:19'},
    {'id': 3838,
     'name': 'NF-2023-27-00002',
     'data_hora_cancelamento': '2023-01-02 20:04:13'},
    {'id': 3818,
     'name': 'NF-2022-498-00001',
     'data_hora_cancelamento': '2022-12-29 12:43:10'},
]

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'search_read',
                  [[['state', '=', 'CANCELADA']],
                  ['id', 'name', 'data_hora_cancelamento']],
                  {limit: 5, order: 'data_hora_cancelamento DESC'})

Exemplo de resultado:


[
  {"id" => 3901,
   "name" => "NF-2023-483-00001",
   "data_hora_cancelamento" => "2023-01-10 17:51:48"},
  {"id" => 3833,
   "name" => "NF-2023-498-00001",
   "data_hora_cancelamento" => "2023-01-04 16:41:24"},
  {"id" => 3856,
   "name" => "NF-2023-52-00001",
   "data_hora_cancelamento" => "2023-01-03 20:13:19"},
  {"id" => 3838,
   "name" => "NF-2023-27-00002",
   "data_hora_cancelamento" => "2023-01-02 20:04:13"},
  {"id" => 3818,
   "name" => "NF-2022-498-00001",
   "data_hora_cancelamento" => "2022-12-29 12:43:10"}
]

Utilize o método search_count para contar a quantidade de registros de um determinado filtro:


call('prefeitura.nota_fiscal', 'search_count', [['state', '=', 'CANCELADA']])

(Integer)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "search_count",
    Arrays.asList(Arrays.asList(Arrays.asList("state", "=", "CANCELADA")))
));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'search_count',
                  [[['state', '=', 'CANCELADA']]])

Exemplo de resultado:


23

Obtenha todos os nomes dos campos, tipos e descrições de ajuda de um determinado model, sendo nesse caso prefeitura.nota_fiscal (funciona para inspecionar qualquer model):


call('prefeitura.nota_fiscal', 'fields_get',
     attributes=['string', 'help', 'type', 'required', 'selection', 'relation'])

Exemplo de resultado:


{
    'name': {'type': 'char',
             'required': False,
             'string': 'Número da Nota'},
    'state': {'type': 'selection',
              'required': False,
              'selection': [['RASCUNHO', 'Rascunho'],
                            ['CONCLUIDO', 'Emitida'],
                            ['CANCELADA', 'Cancelada']],
              'string': 'Estado'},
    'prestador_id': {'type': 'many2one',
                     'help': 'Identificação do PRESTADOR.\n É importante que a Atividade Econômica esteja previamente registrada no cadastro do Prestador.',
                     'required': True,
                     'string': 'Prestador de Serviço',
                     'relation': 'res.partner'},
    'tomador_id': {'type': 'many2one',
                   'help': "Identificação do TOMADOR.\n Para criar um novo registro basta digitar o nome do prestador e teclar 'Enter'. \nLogo após, uma tela será exibida para completar o cadastro. \nDá próxima vez este tomador estará disponível para seleção.",
                   'required': True,
                   'string': 'Tomador de Serviço',
                   'relation': 'prefeitura.tomador'},
    'city_id': {'type': 'many2one',
                'required': True,
                'string': 'Local de Prestação do Serviço',
                'relation': 'res.city'},
    'description': {'type': 'text',
                    'help': 'Escreva aqui informações sobre os serviços prestados.',
                    'required': False,
                    'string': 'Descrição'},
    'competencia': {'type': 'date',
                    'help': 'Selecione uma data dentro do mês corrente. ',
                    'required': True,
                    'string': 'Competência'},
    'emissao': {'type': 'datetime',
                'required': False,
                'string': 'Data de Emissão'},
    'data_hora_cancelamento': {'type': 'datetime',
                               'required': False,
                               'string': 'Data de Cancelamento'},
    'cnae_ids': {'type': 'many2many',
                 'help': 'Selecione entre os CNAEs informados no cadastro do prestador.',
                 'required': False,
                 'string': 'CNAE',
                 'relation': 'prefeitura.cnae'},
    'servico_ids': {'type': 'many2many',
                    'help': 'Selecione entre os serviços informados no cadastro do prestador.',
                    'required': True,
                    'string': 'Serviços',
                    'relation': 'prefeitura.servico'},
    'local_incidencia_iss': {'type': 'selection',
                             'required': False,
                             'selection': [['PRESTADOR', 'Local do Prestador'],
                                           ['TOMADOR', 'Local do Tomador'],
                                           ['SERVICO', 'Local da prestação de serviço']],
                             'string': 'Local de Incidência do ISS'},
    'tipo_recolhimento': {'type': 'selection',
                          'required': False,
                          'selection': [['ISS_RETIDO', 'ISS retido.'],
                                        ['ISS_A_RECOLHER', 'ISS a recolher.']],
                          'string': 'Tipo de Recolhimento'},
    'item_ids': {'type': 'one2many',
                 'help': 'Especifique os serviços prestados informando a quantidade e valor unitário para cada ítem.',
                 'required': False,
                 'string': 'Itens',
                 'relation': 'prefeitura.item_nota_fiscal'},
    'item_total': {'type': 'float',
                   'required': False,
                   'string': 'Total dos Itens'},
    'base_calculo': {'type': 'monetary',
                     'help': 'Valor utilizado para a base de cálculo do ISS.',
                     'required': False,
                     'string': 'Base de Cálculo'},
    'aliquota': {'type': 'float',
                 'help': 'Alíquota estabelecida pelo CTM.',
                 'required': False,
                 'string': 'Alíquota (%)'},
    'valor_iss': {'type': 'monetary',
                  'required': False,
                  'string': 'Valor do ISS'},
    'valor_total_deducoes': {'type': 'monetary',
                             'required': False,
                             'string': 'Valor Total das Retenções'},
    'nome_arquivo_pdf': {'type': 'char',
                         'help': 'Nome do arquivo PDF.',
                         'required': False,
                         'string': 'Nome do Arquivo PDF'},
    'arquivo_pdf': {'type': 'binary',
                    'help': 'Este arquivo permanecerá como RASCUNHO até a confirmação do pagamento da guia de ISS. \n Exceto para contribuintes optantes pelo Simples Nacional.',
                    'required': False,
                    'string': 'Arquivo PDF'},
    'nome_arquivo_xml': {'type': 'char',
                         'help': 'Nome do arquivo XML.',
                         'required': False,
                         'string': 'Nome do Arquivo XML'},
    'arquivo_xml': {'type': 'binary',
                    'help': 'Arquivo no formato XML.',
                    'required': False,
                    'string': 'Arquivo XML'},
    'boleto_id': {'type': 'many2one', 'required': False, 'string': 'Boleto', 'relation': 'prefeitura.boleto'},
    'boleto_state': {'type': 'selection',
                     'required': False,
                     'selection': [['RASCUNHO', 'Rascunho'],
                                   ['ABERTO', 'Emitido'],
                                   ['VENCIDO', 'Vencido'],
                                   ['PAGO', 'Pago'],
                                   ['CANCELADO', 'Cancelado']],
                     'string': 'Estado do Boleto'},
    'possui_pagamento_pendente': {'type': 'boolean',
                                  'required': False,
                                  'string': 'Possui Pagamento Pendente'},
}

(Map<String, Map<String, Object>>)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "fields_get",
    Collections.emptyList(),
    new HashMap() {{
        put("attributes", Arrays.asList("string", "help", "type", "required",
                                        "selection", "relation"));
    }}
));

Exemplo de resultado:


{
    'name': {'type': 'char',
             'required': False,
             'string': 'Número da Nota'},
    'state': {'type': 'selection',
              'required': False,
              'selection': [['RASCUNHO', 'Rascunho'],
                            ['CONCLUIDO', 'Emitida'],
                            ['CANCELADA', 'Cancelada']],
              'string': 'Estado'},
    'prestador_id': {'type': 'many2one',
                     'help': 'Identificação do PRESTADOR.\n É importante que a Atividade Econômica esteja previamente registrada no cadastro do Prestador.',
                     'required': True,
                     'string': 'Prestador de Serviço',
                     'relation': 'res.partner'},
    'tomador_id': {'type': 'many2one',
                   'help': "Identificação do TOMADOR.\n Para criar um novo registro basta digitar o nome do prestador e teclar 'Enter'. \nLogo após, uma tela será exibida para completar o cadastro. \nDá próxima vez este tomador estará disponível para seleção.",
                   'required': True,
                   'string': 'Tomador de Serviço',
                   'relation': 'prefeitura.tomador'},
    'city_id': {'type': 'many2one',
                'required': True,
                'string': 'Local de Prestação do Serviço',
                'relation': 'res.city'},
    'description': {'type': 'text',
                    'help': 'Escreva aqui informações sobre os serviços prestados.',
                    'required': False,
                    'string': 'Descrição'},
    'competencia': {'type': 'date',
                    'help': 'Selecione uma data dentro do mês corrente. ',
                    'required': True,
                    'string': 'Competência'},
    'emissao': {'type': 'datetime',
                'required': False,
                'string': 'Data de Emissão'},
    'data_hora_cancelamento': {'type': 'datetime',
                               'required': False,
                               'string': 'Data de Cancelamento'},
    'cnae_ids': {'type': 'many2many',
                 'help': 'Selecione entre os CNAEs informados no cadastro do prestador.',
                 'required': False,
                 'string': 'CNAE',
                 'relation': 'prefeitura.cnae'},
    'servico_ids': {'type': 'many2many',
                    'help': 'Selecione entre os serviços informados no cadastro do prestador.',
                    'required': True,
                    'string': 'Serviços',
                    'relation': 'prefeitura.servico'},
    'local_incidencia_iss': {'type': 'selection',
                             'required': False,
                             'selection': [['PRESTADOR', 'Local do Prestador'],
                                           ['TOMADOR', 'Local do Tomador'],
                                           ['SERVICO', 'Local da prestação de serviço']],
                             'string': 'Local de Incidência do ISS'},
    'tipo_recolhimento': {'type': 'selection',
                          'required': False,
                          'selection': [['ISS_RETIDO', 'ISS retido.'],
                                        ['ISS_A_RECOLHER', 'ISS a recolher.']],
                          'string': 'Tipo de Recolhimento'},
    'item_ids': {'type': 'one2many',
                 'help': 'Especifique os serviços prestados informando a quantidade e valor unitário para cada ítem.',
                 'required': False,
                 'string': 'Itens',
                 'relation': 'prefeitura.item_nota_fiscal'},
    'item_total': {'type': 'float',
                   'required': False,
                   'string': 'Total dos Itens'},
    'base_calculo': {'type': 'monetary',
                     'help': 'Valor utilizado para a base de cálculo do ISS.',
                     'required': False,
                     'string': 'Base de Cálculo'},
    'aliquota': {'type': 'float',
                 'help': 'Alíquota estabelecida pelo CTM.',
                 'required': False,
                 'string': 'Alíquota (%)'},
    'valor_iss': {'type': 'monetary',
                  'required': False,
                  'string': 'Valor do ISS'},
    'valor_total_deducoes': {'type': 'monetary',
                             'required': False,
                             'string': 'Valor Total das Retenções'},
    'nome_arquivo_pdf': {'type': 'char',
                         'help': 'Nome do arquivo PDF.',
                         'required': False,
                         'string': 'Nome do Arquivo PDF'},
    'arquivo_pdf': {'type': 'binary',
                    'help': 'Este arquivo permanecerá como RASCUNHO até a confirmação do pagamento da guia de ISS. \n Exceto para contribuintes optantes pelo Simples Nacional.',
                    'required': False,
                    'string': 'Arquivo PDF'},
    'nome_arquivo_xml': {'type': 'char',
                         'help': 'Nome do arquivo XML.',
                         'required': False,
                         'string': 'Nome do Arquivo XML'},
    'arquivo_xml': {'type': 'binary',
                    'help': 'Arquivo no formato XML.',
                    'required': False,
                    'string': 'Arquivo XML'},
    'boleto_id': {'type': 'many2one', 'required': False, 'string': 'Boleto', 'relation': 'prefeitura.boleto'},
    'boleto_state': {'type': 'selection',
                     'required': False,
                     'selection': [['RASCUNHO', 'Rascunho'],
                                   ['ABERTO', 'Emitido'],
                                   ['VENCIDO', 'Vencido'],
                                   ['PAGO', 'Pago'],
                                   ['CANCELADO', 'Cancelado']],
                     'string': 'Estado do Boleto'},
    'possui_pagamento_pendente': {'type': 'boolean',
                                  'required': False,
                                  'string': 'Possui Pagamento Pendente'},
}

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal',
                  'fields_get', [],
                  {attributes: %w(string help type required selection relation)})

Exemplo de resultado:


{
    "name" => {
        "type" => "char",
        "required" => false,
        "string" => "Número da Nota"},
    "state" => {
        "type" => "selection",
        "required" => false,
        "selection" => [
            ["RASCUNHO", "Rascunho"],
            ["CONCLUIDO", "Emitida"],
            ["CANCELADA", "Cancelada"]
        ],
        "string" => "Estado"},
    "prestador_id" => {
        "type" => "many2one",
        "help" => "Identificação do PRESTADOR.\n É importante que a Atividade Econômica esteja previamente registrada no cadastro do Prestador.",
        "required" => true,
        "string" => "Prestador de Serviço",
        "relation" => "res.partner"},
    "tomador_id" => {
        "type" => "many2one",
        "help" => "Identificação do TOMADOR.\n Para criar um novo registro basta digitar o nome do prestador e teclar 'Enter'. \nLogo após, uma tela será exibida para completar o cadastro. \nDá próxima vez este tomador estará disponível para seleção.",
        "required" => true,
        "string" => "Tomador de Serviço",
        "relation" => "prefeitura.tomador"},
    "city_id" => {
        "type" => "many2one",
        "required" => true,
        "string" => "Local de Prestação do Serviço",
        "relation" => "res.city"},
    "description" => {
        "type" => "text",
        "help" => "Escreva aqui informações sobre os serviços prestados.",
        "required" => false,
        "string" => "Descrição"},
    "competencia" => {
        "type" => "date",
        "help" => "Selecione uma data dentro do mês corrente. ",
        "required" => true,
        "string" => "Competência"},
    "emissao" => {
        "type" => "datetime",
        "required" => false,
        "string" => "Data de Emissão"},
    "data_hora_cancelamento" => {
        "type" => "datetime",
        "required" => false,
        "string" => "Data de Cancelamento"},
    "cnae_ids" => {
        "type" => "many2many",
        "help" => "Selecione entre os CNAEs informados no cadastro do prestador.",
        "required" => false,
        "string" => "CNAE",
        "relation" => "prefeitura.cnae"},
    "servico_ids" => {
        "type" => "many2many",
        "help" => "Selecione entre os serviços informados no cadastro do prestador.",
        "required" => true,
        "string" => "Serviços",
        "relation" => "prefeitura.servico"},
    "local_incidencia_iss" => {
        "type" => "selection",
        "required" => false,
        "selection" => [
            ["PRESTADOR", "Local do Prestador"],
            ["TOMADOR", "Local do Tomador"],
            ["SERVICO", "Local da prestação de serviço"]
        ],
        "string" => "Local de Incidência do ISS"},
    "tipo_recolhimento" => {
        "type" => "selection",
        "required" => false,
        "selection" => [
            ["ISS_RETIDO", "ISS retido."],
            ["ISS_A_RECOLHER", "ISS a recolher."]
        ],
        "string" => "Tipo de Recolhimento"},
    "item_ids" => {
        "type" => "one2many",
        "help" => "Especifique os serviços prestados informando a quantidade e valor unitário para cada ítem.",
        "required" => false,
        "string" => "Itens",
        "relation" => "prefeitura.item_nota_fiscal"},
    "item_total" => {
        "type" => "float",
        "required" => false,
        "string" => "Total dos Itens"},
    "base_calculo" => {
        "type" => "monetary",
        "help" => "Valor utilizado para a base de cálculo do ISS.",
        "required" => false,
        "string" => "Base de Cálculo"},
    "aliquota" => {
        "type" => "float",
        "help" => "Alíquota estabelecida pelo CTM.",
        "required" => false,
        "string" => "Alíquota (%)"},
    "valor_iss" => {
        "type" => "monetary",
        "required" => false,
        "string" => "Valor do ISS"},
    "valor_total_deducoes" => {
        "type" => "monetary",
        "required" => false,
        "string" => "Valor Total das Retenções"},
    "nome_arquivo_pdf" => {
        "type" => "char",
        "help" => "Nome do arquivo PDF.",
        "required" => false,
        "string" => "Nome do Arquivo PDF"},
    "arquivo_pdf" => {
        "type" => "binary",
        "help" => "Este arquivo permanecerá como RASCUNHO até a confirmação do pagamento da guia de ISS. \n Exceto para contribuintes optantes pelo Simples Nacional.",
        "required" => false,
        "string" => "Arquivo PDF"},
    "nome_arquivo_xml" => {
        "type" => "char",
        "help" => "Nome do arquivo XML.",
        "required" => false,
        "string" => "Nome do Arquivo XML"},
    "arquivo_xml" => {
        "type" => "binary",
        "help" => "Arquivo no formato XML.",
        "required" => false,
        "string" => "Arquivo XML"},
    "boleto_id" => {
        "type" => "many2one",
        "required" => false,
        "string" => "Boleto",
        "relation" => "prefeitura.boleto"},
    "boleto_state" => {
        "type" => "selection",
        "required" => false,
        "selection" => [
            ["RASCUNHO", "Rascunho"],
            ["ABERTO", "Emitido"],
            ["VENCIDO", "Vencido"],
            ["PAGO", "Pago"],
            ["CANCELADO", "Cancelado"]
        ],
        "string" => "Estado do Boleto"},
    "possui_pagamento_pendente" => {
        "type" => "boolean",
        "required" => false,
        "string" => "Possui Pagamento Pendente"
    }
}

Emitir Notas Fiscais

O processo segue o mesmo procedimento de emissão de nota fiscal utilizando o formulário no navegador, primeiro é necessário criar o registro, que por padrão estará no estado RASCUNHO. Nessa fase, o usuário pode realizar alterações e correções nos dados submetidos. Após tudo conferido, o usuário executa a ação de Emitir, que mudará o estado do registro para CONCLUIDO (emitido).

Antes de realizar a criação da nota fiscal, é preciso definir os valores dos campos obrigatórios. Para identificar esses campos, basta localicar os atributos dos campos marcados como 'required': True (ver saída do comando anterior). No caso da nota fiscal, os campos obrigatórios são: tomador_id, city_id e competencia. Observe que os campos prestador_id e servico_ids também são obrigatórios, mas não é necessário definir um valor, pois o sistema obtem um valor padrão para eles (baseado no usuário logado que está fazendo a requisição).


tomador_id = call('prefeitura.tomador', 'search', [
    ('name', 'ilike', 'peças'),
], limit=1)[0]

city_id = call('res.city', 'search', [
    ('name', 'ilike', 'riachão'),
    ('state_id.name', 'ilike', 'maranhão'),
], limit=1)[0]

Object tomadorId = Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.tomador", "search",
    Arrays.asList(Arrays.asList(Arrays.asList("name", "ilike", "peças"))),
    new HashMap() {{
        put("limit", 1);
    }}
))).get(0);

Object cityId = Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "res.city", "search",
    Arrays.asList(Arrays.asList(
        Arrays.asList("name", "ilike", "riachão"),
        Arrays.asList("state_id.name", "ilike", "maranhão")
    )),
    new HashMap() {{
        put("limit", 1);
    }}
))).get(0);

tomador_id = models.execute_kw(db, uid, password, 'prefeitura.tomador', 'search', [[
    ['name', 'ilike', 'peças']
]], {limit: 1})[0]

city_id = models.execute_kw(db, uid, password, 'res.city', 'search', [[
    ['name', 'ilike', 'riachão'],
    ['state_id.name', 'ilike', 'maranhão'],
]], {limit: 1})[0]

Observe que os comandos anteriores estão realizando consulta de dados em diferentes tabelas. A cidade está sendo consultada na tabela res.city, procurando o primeiro registro que possua o nome riachão em que está localizado no estado maranhão. O tomador é localizado na tabela prefeitura.tomador, a consulta procura o primeiro registro que possua o nome peças. Note que foi utilizado o operador ilike. É possível usar qualquer um dos seguintes operadores:

=
Igual.

!=
Diferente.

>
Maior.

>=
Maior ou igual.

<
Menor.

<=
Menor ou igual.

=?
Indefinido ou igual (retorna true se valor é None ou False, do contrário se comporta como =)

=like
Corresponde field_name no seguinte padrão de valor: um sublinhado _ no padrão significa (corresponde) qualquer caractere único; e um sinal de porcentagem % corresponde qualquer string de zero ou mais caracteres.

like
Corresponde field_name no padrão %valor%. Parecido com =like mas embrulha o valor com % antes de corresponder.

not like
Não corresponde no padrão %value%.

ilike
Caso maúsculo ou minúsculo de like.

not ilike
Caso maúsculo ou minúsculo de not like.

=ilike
Caso maúsculo ou minúsculo de =like.

in
É igual a qualquer um dos itens da lista (nos casos do valor ser uma lista).

not in
É diferente de qualquer um dos itens da lista (nos casos do valor ser uma lista).

É possível criar um ou mais registros de notas fiscais na mesma submissão:


call('prefeitura.nota_fiscal', 'create', [
    {
        'tomador_id': tomador_id,
        'city_id': city_id,
        'competencia': '2023-02-10',
    },
    {
        'tomador_id': tomador_id,
        'city_id': city_id,
        'competencia': '2023-02-10',
    },
])

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "create",
    Arrays.asList(Arrays.asList(
        new HashMap() {{
            put("tomador_id", tomadorId);
            put("city_id", cityId);
            put("competencia", "2023-02-10");
        }},
        new HashMap() {{
            put("tomador_id", tomadorId);
            put("city_id", cityId);
            put("competencia", "2023-02-10");
        }}
    ))
)));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'create', [[
    {
        tomador_id: tomador_id,
        city_id: city_id,
        competencia: '2023-02-10',
    },
    {
        tomador_id: tomador_id,
        city_id: city_id,
        competencia: '2023-02-10',
    },
]])

Caso os dados submetidos estejam corretos, a requisição deverá retornar um ou mais números de identificação id dos registros recém criados:


[4279, 4280]

Utilize esses valores para consultar os estados de cada registro de nota fiscal:


call('prefeitura.nota_fiscal', 'read', [4279, 4280],
     fields=['id', 'name', 'state'])

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "read",
    Arrays.asList(Arrays.asList(4279, 4280)),
    new HashMap() {{
        put("fields", Arrays.asList("id", "name", "state"));
    }}
)));

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'read',
                  [[4279, 4280]],
                  {fields: %w(id name state)})

[
    {'id': 4279, 'name': 'NF-2023-23-00004', 'state': 'RASCUNHO'},
    {'id': 4280, 'name': 'NF-2023-23-00003', 'state': 'RASCUNHO'},
]

Observe que os dois registros estão no estado de RASCUNHO. Significa que esses eles podem ser alterados (ainda não foram emitidos CONCLUIDO). Para adicionar um ou mais Itens da Nota, execute:


call('prefeitura.nota_fiscal', 'write', [4279], {
    'item_ids': [
        (0, 0, {
            'name': "Primeiro Item",
            'price': 123.45,
            'quantity': 2,
        }),
        (0, 0, {
            'name': "Segundo Item",
            'price': 321.54,
            'quantity': 1,
        }),
    ]
})

(boolean)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "write",
    Arrays.asList(
        Arrays.asList(4279),
        new HashMap() {{
            put("item_ids", Arrays.asList(
                Arrays.asList(0, 0, new HashMap() {{
                    put("name", "Primeiro Item");
                    put("price", 123.45);
                    put("quantity", 2);
                }}),
                Arrays.asList(0, 0, new HashMap() {{
                    put("name", "Segundo Item");
                    put("price", 321.54);
                    put("quantity", 1);
                }})
            ));
        }}
    )
))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'write', [[4279], {
    item_ids: [
        [0, 0, {
            name: "Primeiro Item",
            price: 123.45,
            quantity: 2,
        }],
        [0, 0, {
            name: "Segundo Item",
            price: 321.54,
            quantity: 1,
        }],
    ]
}])

Para consultar os campos alterados dos Itens da Nota, execute:


call('prefeitura.nota_fiscal', 'read', [4279],
     fields=['id', 'name', 'state', 'item_ids'])

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "read",
    Arrays.asList(Arrays.asList(4279)),
    new HashMap() {{
        put("fields", Arrays.asList("id", "name", "state", "item_ids"));
    }}
)))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'read',
                  [[4279]],
                  {fields: %w(id name state item_ids)})

[
    {'id': 4279, 'name': 'NF-2023-23-00004', 'state': 'RASCUNHO', 'item_ids': [6171, 6172]},
]

Para remover um item da nota fiscal, execute:


call('prefeitura.nota_fiscal', 'write', [4279], {
    'item_ids': [
        (2, 6171, 0),
    ]
})

(boolean)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "write",
    Arrays.asList(
        Arrays.asList(4279),
        new HashMap() {{
            put("item_ids", Arrays.asList(
                Arrays.asList(2, 6171, 0)
            ));
        }}
    )
))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'write', [[4279], {
    item_ids: [
        [2, 6171, 0],
    ]
}])

Observe que o item de identificação 6171 foi removido da relação. Para consultar os campos alterados dos Itens da Nota, execute:


call('prefeitura.nota_fiscal', 'read', [4279],
     fields=['id', 'name', 'state', 'item_ids'])

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "read",
    Arrays.asList(Arrays.asList(4279)),
    new HashMap() {{
        put("fields", Arrays.asList("id", "name", "state", "item_ids"));
    }}
)))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal', 'read',
                  [[4279]],
                  {fields: %w(id name state item_ids)})

[
    {'id': 4279, 'name': 'NF-2023-23-00004', 'state': 'RASCUNHO', 'item_ids': [6172]},
]

Observe que os comandos anteriores estão acessando os registros relacionados (itens da nota) usando o seguinte formato:

(0, 0, valores)
Adiciona um novo registro, criado a partir do dicionário valores fornecido.

(1, id, valores)
Atualiza um registro existente de id com os valores fornecidos.

(2, id, 0)
Remove o registro de id da relação, e depois deleta da tabela de origem.

(3, id, 0)
Remove o registro de id da relação, mas não deleta da tabela de origem.

(4, id, 0)
Adiciona um registro existente de id na relação.

(5, 0, 0)
Remove todos os registros da relação, e não os remove da tabela de origem.

(6, 0, ids)
Substitui todos os registros existentes da relação pelos ids da lista, e não os remove da tabela de origem.

Finalmente, para emitir a nota fiscal, execute:


call('prefeitura.nota_fiscal', 'action_emitir_nota_fiscal', [4279])

(Object)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "action_emitir_nota_fiscal",
    Arrays.asList(Arrays.asList(4279))
))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal',
                  'action_emitir_nota_fiscal', [[4279]])

Baixar PDF do Boleto da Nota Fiscal

Após emissão da nota fiscal (p.ex. id = 4382), caso exista algum débito a ser pago, você poderá baixar o PDF do boleto de cobrança utilizando a URL do arquivo:


url = 'https://riachao.teste.cecsystem.com.br'
path = call('prefeitura.nota_fiscal', 'action_baixar_arquivo_pdf', [4382])['url']
print('{}/{}'.format(url, path))

String baseUrl = "https://riachao.teste.cecsystem.com.br";

String path = ((Map) models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "action_baixar_arquivo_pdf",
    Arrays.asList(Arrays.asList(4382))
))).get("url");

System.out.println(baseUrl + "/" + path);

url = 'https://riachao.teste.cecsystem.com.br'
path = models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal',
                         'action_baixar_arquivo_pdf', [[4382]])['url']
puts url + '/' + path

"https://riachao.teste.cecsystem.com.br/web/content/prefeitura.nota_fiscal/4382/arquivo_pdf/nota-fiscal-nf-2023-23-00001.pdf?download=true"

Cancelar Notas Fiscais

Para cancelar uma ou mais notas fiscais (p.ex. id = 4279), execute:


call('prefeitura.nota_fiscal', 'action_cancelar', [4279])

(boolean)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.nota_fiscal", "action_cancelar",
    Arrays.asList(Arrays.asList(4279))
))

models.execute_kw(db, uid, password, 'prefeitura.nota_fiscal',
                  'action_cancelar', [[4279]])

Consultar Faturas

Para listar todas as faturas em aberto, execute:


call('account.move', 'search', [['state', '=', 'posted'], ['payment_state', '=', 'not_paid']])

Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "account.move", "search",
    Arrays.asList(Arrays.asList(
        Arrays.asList("state", "=", "posted"),
        Arrays.asList("payment_state", "=", "not_paid")
    )),
    new HashMap() {{ }}
)));

models.execute_kw(db, uid, password, 'account.move', 'search', [[
    ['state', '=', 'posted'],
    ['payment_state', '=', 'not_paid'],
]], {})

Exemplo de resultado:


[356, 341, 134]

Gerar Boleto de Faturas Selecionadas

É possível gerar um boleto para pagar uma ou mais faturas, nesse caso para as faturas ids = [356, 341, 134]. Mas primeiro, certifique-se que todas as faturas possuem o mesmo contribuinte associado:


contribuintes = call('account.move', 'search_read', [['id', 'in', [356, 341, 134]]], ['partner_id'])

for contribuinte in contribuintes:
    print(contribuinte)

List<Object> contribuintes = Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "account.move", "search_read",
    Arrays.asList(
        Arrays.asList(Arrays.asList("id", "in", Arrays.asList(356, 341, 134))),
        Arrays.asList("partner_id")
    ),
    new HashMap() {{ }}
)));

for (Object contribuinte : contribuintes) {
    System.out.println(contribuinte);
}

contribuintes = models.execute_kw(db, uid, password, 'account.move', 'search_read', [[
    ['id', 'in', [356, 341, 134]],
], ['partner_id']], {})

for contribuinte in contribuintes do
  puts contribuinte
end

Exemplo de resultado:


{'id': 356, 'partner_id': [378, 'Felipe Silva']}
{'id': 341, 'partner_id': [378, 'Felipe Silva']}
{'id': 134, 'partner_id': [378, 'Felipe Silva']}

Após confirmar que todas as faturas ids = [356, 341, 134] pertencem ao mesmo contribuinte id = 378, basta criar o boleto:


result = call('prefeitura.boleto', 'create', [{
    'contribuinte_id': 378,
    'move_ids': [356, 341, 134],
}])

print(result)

List<Object> result = Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.boleto", "create",
    Arrays.asList(Arrays.asList(
        new HashMap() {{
            put("contribuinte_id", 378);
            put("move_ids", Arrays.asList(356, 341, 134));
        }}
    ))
)));

System.out.println(result);

result = models.execute_kw(db, uid, password, 'prefeitura.boleto', 'create', [[
    {
        contribuinte_id: 378,
        'move_ids': [356, 341, 134],
    },
]])

puts result

Exemplo de resultado:


[495]

O boleto recém-criado estará no estado RASCUNHO. Nessa fase, o usuário pode realizar alterações e correções nos dados submetidos. Após tudo conferido, o usuário executa a ação de Emitir Boleto:


result = call('prefeitura.boleto', 'action_gerar_boleto', [495])

print(result)

Object result = (Object)models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.boleto", "action_gerar_boleto",
    Arrays.asList(Arrays.asList(495))
));

System.out.println(result);

result = models.execute_kw(db, uid, password, 'prefeitura.boleto',
                           'action_gerar_boleto', [[495]])
puts result

Exemplo de resultado:


True

Após emissão do boleto de cobrança id = 495, você poderá baixar o arquivo PDF utilizando a URL do arquivo:


url = 'https://riachao.teste.cecsystem.com.br'
path = call('prefeitura.boleto', 'action_baixar_arquivo_pdf', [495])['url']
print('{}/{}'.format(url, path))

String baseUrl = "https://riachao.teste.cecsystem.com.br";

String path = ((Map) models.execute("execute_kw", Arrays.asList(
    db, uid, password,
    "prefeitura.boleto", "action_baixar_arquivo_pdf",
    Arrays.asList(Arrays.asList(495))
))).get("url");

System.out.println(baseUrl + "/" + path);

url = 'https://riachao.teste.cecsystem.com.br'
path = models.execute_kw(db, uid, password, 'prefeitura.boleto',
                         'action_baixar_arquivo_pdf', [[495]])['url']
puts url + '/' + path

"https://riachao.teste.cecsystem.com.br/web/content/prefeitura.boleto/495/conteudo_pdf/boleto-495-felipe-silva-aberto-2023-04-10.pdf?download=true"