Regra de aprovação de dias
A Regra de aprovação de dias é utilizada para realizar customizações na rotina de aprovação de dias, viabilizando a aprovação multinível por meio do BPM Senior.
Momentos de execução
A regra é executada nos seguintes momentos:
- Na aprovação de dia individual e coletiva, na tela Meus Acertos de Ponto;
- Na aprovação dos dias do colaborador, na tela Acertos da Minha Equipe.
Fluxo de exemplo da regra

Para habilitar o ponto de regra, é necessário que o campo Habilita Regra na Verificação do Dia (R000ASS.VerDia) esteja habilitado como “S - Sim”.
Exemplo de regra customizada
package custom.senior.regra.apuracao;
import java.util.List;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import com.senior.rule.Rule;
import com.senior.rh.Colaborador;
import com.senior.colaborador.BOColaboradorPonto;
import com.senior.util.config.Configuration;
import com.senior.entity.ISituacaoApurada;
import com.senior.entity.IApuracao;
import br.com.senior.gp.comum.HierarquiaGestoresDTO;
import br.com.senior.gp.comum.StatusVerificacao;
import br.com.senior.da.bo.BOAtualizacaoFluxoAprovacao;
import br.com.senior.gestaoponto.dao.statusverificacao.StatusVerificacaoDia;
import br.com.senior.gestaoponto.dao.statusverificacao.IDAOStatusVerificacaoDia;
import br.com.senior.gestaoponto.factorybean.FactoryBean;
import br.com.senior.rh.situacoes.Situacao;
import br.com.senior.situacao.ControleSituacoes;
import custom.senior.aprovacaodia.AprovacaoDia;
import custom.senior.aprovacaodia.ContextoAprovacaoDia;
import custom.senior.aprovacaodia.IApuracaoDia;
/**
* Regra de aprovacao do dia executada apos a apuracao do ponto.
*
* <p>Para cada dia apurado do colaborador:
* <ul>
* <li>Dias sem situacao de excecao sao aprovados automaticamente (VERIFICADO).</li>
* <li>Dias com situacao de excecao iniciam um fluxo BPM (startProcess) direcionado
* ao gestor de nivel 2 da hierarquia, ficando com status PENDENTE.</li>
* </ul>
*/
@Rule(description = "Regra de Aprovacao do Dia")
public class RegraAprovacaoDia extends AprovacaoDia {
private static final int PROCESS_ID = 1;
private static final int PROCESS_VERSION = 61;
private static final String COMENTARIO =
"Aprovado automaticamente pela regra: dia com apenas situacoes normais";
/**
* Ponto de entrada da regra. Obtem as apuracoes do colaborador, gera um unico token
* de autenticacao BPM (reutilizado para todos os dias, evitando rate limit) e processa
* cada dia apurado.
*/
@Override
public void execute() {
try {
ContextoAprovacaoDia contexto = getContainer().getContextoAprovacaoDia();
List<IApuracaoDia> apuracoes = contexto.getApuracoes();
if (apuracoes == null || apuracoes.isEmpty())
return;
IApuracaoDia primeira = apuracoes.get(0);
Colaborador colaborador = new Colaborador(
primeira.getNumEmp(), primeira.getTipCol(), primeira.getNumCad());
String token = obterToken();
for (IApuracaoDia apu : apuracoes)
processar(apu, colaborador, token);
} catch (Exception e) {
}
}
/**
* Processa um dia apurado: aprova automaticamente quando nao ha excecao ou inicia o
* fluxo BPM de aprovacao quando ha situacao de excecao.
*
* @param apu dia apurado a ser processado
* @param colaborador colaborador dono da apuracao
* @param token token de autenticacao BPM reutilizado para todos os dias
*/
private void processar(IApuracaoDia apu, Colaborador colaborador, String token) {
try {
ContextoAprovacaoDia contexto = getContainer().getContextoAprovacaoDia();
List<?> situacoes = contexto.getSituacoes(apu);
if (!possuiExcecao(situacoes)) {
aprovarAutomaticamente(apu);
return;
}
String gestor = buscarGestorNivel2(contexto, apu);
if (gestor == null || gestor.isEmpty()) {
return;
}
String nome = colaborador.getNomFun();
if (nome == null || nome.isEmpty())
nome = BOColaboradorPonto.getColaboradorPonto(
new Colaborador(apu.getNumEmp(), apu.getTipCol(), apu.getNumCad())).getNomFun();
nome = limpar(nome);
gestor = limpar(gestor);
String situacoesTexto = montarSituacoes(situacoes);
String businessData =
"{\"root\": {" +
"\"numemp\": \"" + apu.getNumEmp() + "\", " +
"\"tipcol\": \"" + apu.getTipCol() + "\", " +
"\"numcad\": \"" + apu.getNumCad() + "\", " +
"\"datapu\": \"" + apu.getDatApu() + "\", " +
"\"nomfun\": \"" + nome + "\", " +
"\"usuario\": \"" + gestor + "\", " +
"\"situacoes\": \"" + situacoesTexto +
"\"}}";
String bodyStartProcess =
"{\"processId\": " + PROCESS_ID +
", \"processVersion\": " + PROCESS_VERSION +
", \"generateRecord\": true" +
", \"requester\": \"admin\"" +
", \"title\": \"Aprovacao GPO " + nome + "\"" +
", \"businessData\": \"" + businessData + "\"" +
", \"flowExecutionData\": {\"actionToExecute\": \"Prosseguir\"" +
", \"nextSubject\": \"" + gestor + "\"}" + "}";
String resposta = post(
Configuration.get("br.com.senior.gp.bpm.start.process"),
bodyStartProcess, token);
String instanceId = extrairValorJson(resposta, "processInstanceID");
inserirRegistro(apu, instanceId.isEmpty() ? 0 : Integer.parseInt(instanceId));
} catch (Exception e) {
}
}
/**
* Verifica se o dia possui ao menos uma situacao de excecao (flag SitExc).
*
* @param situacoes situacoes apuradas no dia
* @return true se houver ao menos uma situacao de excecao
*/
private boolean possuiExcecao(List<?> situacoes) {
for (Object item : situacoes) {
Situacao sit = ControleSituacoes.getSituacao(((ISituacaoApurada) item).getCodSit());
if (sit != null && sit.getSitExc())
return true;
}
return false;
}
/**
* Busca o login (nomusu) do primeiro gestor do nivel 2 na hierarquia do colaborador.
*
* @param contexto contexto de aprovacao do dia
* @param apu dia apurado
* @return login do gestor nivel 2 ou string vazia se nao encontrado
*/
private String buscarGestorNivel2(ContextoAprovacaoDia contexto, IApuracaoDia apu) {
HierarquiaGestoresDTO hierarquia = contexto.getGestoresAcima(
apu.getNumEmp(), apu.getTipCol(), apu.getNumCad());
if (hierarquia == null)
return "";
for (HierarquiaGestoresDTO.NivelHierarquiaDTO nivel : hierarquia.getHierarquiaGestores()) {
if (nivel.getNivel() == 2 && !nivel.getGestores().isEmpty()) {
String nomusu = nivel.getGestores().get(0).getNomusu();
return nomusu;
}
}
return "";
}
/**
* Monta o texto descritivo das situacoes apuradas no formato "HH:MM - codigo - descricao",
* separadas por quebra de linha.
*
* @param situacoes situacoes apuradas no dia
* @return texto formatado das situacoes
*/
private String montarSituacoes(List<?> situacoes) {
StringBuilder texto = new StringBuilder();
for (Object item : situacoes) {
ISituacaoApurada apurada = (ISituacaoApurada) item;
Situacao sit = ControleSituacoes.getSituacao(apurada.getCodSit());
if (texto.length() > 0)
texto.append("\\n");
texto.append(String.format("%02d:%02d", apurada.getQtdHor() / 60, apurada.getQtdHor() % 60))
.append(" - ").append(apurada.getCodSit())
.append(" - ").append(sit == null ? "" : limpar(sit.getDesSit()));
}
return texto.toString();
}
/**
* Autentica na plataforma via loginWithKey e retorna o access_token.
*
* @return access_token para uso nas chamadas BPM
* @throws Exception em caso de falha na autenticacao
*/
private String obterToken() throws Exception {
String body = "{\"accessKey\": \"" + Configuration.get("br.com.senior.gp.bpm.access.key")
+ "\", \"secret\": \"" + Configuration.get("br.com.senior.gp.bpm.secret")
+ "\", \"tenantName\": \"" + Configuration.get("com.senior.portal.g7.tenant") + "\"}";
String resposta = post(
Configuration.get("br.com.senior.gp.auth.api.url"),
body, null);
return extrairValorJson(resposta, "access_token");
}
/**
* Aprova o dia automaticamente (sem passar pelo BPM): insere registro na R066STV com
* status VERIFICADO, atualiza o fluxo de aprovacao via BO e ajusta o StaVer na EntitySession.
*
* @param apu dia apurado a ser aprovado
*/
private void aprovarAutomaticamente(IApuracaoDia apu) {
IDAOStatusVerificacaoDia dao =
FactoryBean.getInstance(IDAOStatusVerificacaoDia.class);
StatusVerificacaoDia stv = new StatusVerificacaoDia();
stv.setNumEmp(apu.getNumEmp());
stv.setTipCol(apu.getTipCol());
stv.setNumCad(apu.getNumCad());
stv.setDatApu(apu.getDatApu());
stv.setTexRet(COMENTARIO);
stv.setProcId(0);
stv.setVerPro(0);
stv.setInstId(0);
stv.setBusDat("");
stv.setTipSta(StatusVerificacao.VERIFICADO.getCodigo());
dao.inserirRegistro(stv);
new BOAtualizacaoFluxoAprovacao().atualizarStatusVerificacao(
apu.getNumEmp(), apu.getTipCol(), apu.getNumCad(),
apu.getDatApu(), true, COMENTARIO);
((IApuracao) apu).setStaVer(StatusVerificacao.VERIFICADO.getCodigo());
}
/**
* Insere registro na R066STV com status PENDENTE apos o startProcess bem-sucedido.
*
* @param apu dia apurado
* @param instanceId id da instancia do processo BPM criado
*/
private void inserirRegistro(IApuracaoDia apu, int instanceId) {
IDAOStatusVerificacaoDia dao =
FactoryBean.getInstance(IDAOStatusVerificacaoDia.class);
StatusVerificacaoDia stv = new StatusVerificacaoDia();
stv.setNumEmp(apu.getNumEmp());
stv.setTipCol(apu.getTipCol());
stv.setNumCad(apu.getNumCad());
stv.setDatApu(apu.getDatApu());
stv.setTexRet("");
stv.setProcId(PROCESS_ID);
stv.setVerPro(PROCESS_VERSION);
stv.setInstId(instanceId);
stv.setBusDat("");
stv.setTipSta(StatusVerificacao.PENDENTE.getCodigo());
dao.inserirRegistro(stv);
}
/**
* Executa uma requisicao HTTP POST com corpo JSON. O token, quando informado, e enviado
* no header Authorization sem o prefixo "Bearer" (exigencia da API Senior).
*
* @param url endpoint de destino
* @param json corpo da requisicao
* @param token token de autenticacao (opcional)
* @return corpo da resposta
* @throws Exception em caso de erro de comunicacao ou HTTP fora do range 2xx
*/
private String post(String url, String json, String token) throws Exception {
HttpURLConnection c = (HttpURLConnection) new URL(url).openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Content-Type", "application/json");
if (token != null)
c.setRequestProperty("Authorization", token);
c.setDoOutput(true);
try (OutputStream o = c.getOutputStream()) {
o.write(json.getBytes(StandardCharsets.UTF_8));
}
int code = c.getResponseCode();
StringBuilder r = new StringBuilder();
try (BufferedReader b = new BufferedReader(new InputStreamReader(
code >= 200 && code < 300 ? c.getInputStream() : c.getErrorStream(),
StandardCharsets.UTF_8))) {
String l;
while ((l = b.readLine()) != null)
r.append(l);
}
c.disconnect();
if (code < 200 || code >= 300) {
throw new RuntimeException("HTTP " + code + ": " + r.toString());
}
return r.toString();
}
/**
* Extrai o valor de um campo JSON (string ou numerico), com fallback para JSON escapado.
*
* @param json conteudo JSON
* @param key nome do campo
* @return valor do campo ou string vazia se nao encontrado
*/
private String extrairValorJson(String json, String key) {
int idx = json.indexOf("\"" + key + "\"");
if (idx >= 0) {
int colon = json.indexOf(":", idx);
if (colon >= 0) {
int start = colon + 1;
while (start < json.length() && json.charAt(start) == ' ')
start++;
if (start < json.length()) {
if (json.charAt(start) == '"') {
int end = json.indexOf('"', start + 1);
if (end >= 0)
return json.substring(start + 1, end);
} else if (Character.isDigit(json.charAt(start)) || json.charAt(start) == '-') {
int end = start;
while (end < json.length() && "0123456789-.".indexOf(json.charAt(end)) >= 0)
end++;
return json.substring(start, end);
}
}
}
}
int esc = json.indexOf("\\\"" + key + "\\\"");
if (esc >= 0) {
int colon = json.indexOf(":", esc);
int start = json.indexOf("\\\"", colon + 1);
if (start >= 0) {
int end = json.indexOf("\\\"", start + 2);
if (end >= 0)
return json.substring(start + 2, end);
}
}
return "";
}
/**
* Remove aspas duplas e espacos das extremidades de um valor.
*
* @param valor valor a ser limpo
* @return valor sem aspas e sem espacos nas extremidades, ou string vazia se nulo
*/
private String limpar(String valor) {
return valor == null ? "" : valor.replace("\"", "").trim();
}
} Configuração do SeniorConfigCenter
Para utilizar a regra de aprovação de dias com o BPM Senior, garanta que as seguintes chaves estejam configuradas no SeniorConfigCenter:
Chave | Valor | Descrição |
|---|---|---|
br.com.senior.gp.bpm.start.process | URL da API startProcess do BPM. | |
br.com.senior.gp.auth.api.url | URL da API de autenticação ( loginWithKey ). | |
br.com.senior.gp.bpm.access.key | Gerar conforme os passos descritos em Configuração do Tenant e das credenciais . | Access Key utilizada para autenticação. |
br.com.senior.gp.bpm.secret | Gerar conforme os passos descritos em Configuração do Tenant e das credenciais . | Secret utilizada para autenticação. |
com.senior.portal.g7.tenant | Configurar conforme a documentação . | Tenant Name da plataforma. |
Configuração do Tenant e das credenciais
Na plataforma Senior X, acesse Tecnologia > Administração > Gerenciamento de aplicações.
-
Clique em Nova aplicação e informe um nome e uma descrição para a aplicação. O nome deve identificar a aplicação que está sendo configurada. Para o fluxo de aprovação, por exemplo, pode ser utilizado o nome fluxoaprovacao.
-
Clique em Salvar para retornar à lista de aplicações.
-
Copie as chaves de acesso geradas nos dois campos e informe os valores nas propriedades correspondentes:
Propriedade | Campo correspondente |
|---|---|
br.com.senior.gp.bpm.access.key | Chave de acesso (ApplicationManagement) |
br.com.senior.gp.bpm.secret | Chave secreta (ApplicationManagement) |
- Acesse Tecnologia > Administração > Autorização > Gestão dos papéis.
- Clique em Editar no papel Admin (administrador do sistema).
- Na guia Configurações, acesse Aplicações e habilite a aplicação criada anteriormente.
Fluxo de exemplo do BPM

Para que o ponto de regra funcione e crie a pendência de aprovação, é necessário que já exista um fluxo customizado criado no BPM.
API de Atualização do Fluxo de Aprovação
Para permitir que o BPM atualize o status dos dias durante o fluxo de aprovação, está disponível a API api/verificacao/atualiza-fluxo-aprovacao.
Parâmetros de entrada
A API recebe os seguintes parâmetros de entrada:
Parâmetro | Tipo | Descrição |
|---|---|---|
processInstanceId | Inteiro | Identificador da instância do processo BPM. |
comentario | Texto | Comentário informado pelo gestor durante a aprovação ou reprovação. |
aprovado | Booleano | Indica se o dia foi aprovado ou reprovado pelo BPM. |
numEmp | Inteiro | Código da empresa. |
tipCol | Inteiro | Tipo de colaborador. |
numCad | Inteiro | Cadastro do colaborador. |
datApu | Data | Data de apuração do dia. |
Atualização do status do dia
Com base nos parâmetros recebidos, a API realiza a atualização do status do dia conforme a decisão definida pelo BPM.
Para isso, são atualizados os seguintes campos:
Campo | Descrição |
|---|---|
R066APU.STAVER | Atualiza o status de verificação do dia. |
R066STV.TIPSTA | Atualiza o status da situação. |
R066STV.TEXRET | Armazena o comentário informado pelo gestor. |
Dessa forma, o dia fica registrado no Gestão do Ponto com o status definido pelo fluxo do BPM, mantendo também o comentário realizado pelo gestor durante o processo de aprovação ou reprovação.
API de Gestores da Hierarquia de Postos ou Chefia
Para facilitar a customização do fluxo de aprovação, está disponível uma API responsável por retornar o gestor de determinado colaborador, considerando a hierarquia de postos ou chefia.
Parâmetros de entrada
A API recebe os seguintes parâmetros de entrada:
Parâmetro | Tipo | Descrição |
|---|---|---|
numEmp | Inteiro | Código da empresa. |
tipCol | Inteiro | Tipo de colaborador. |
numCad | Inteiro | Cadastro do colaborador. |
nivel | Inteiro | Nível hierárquico do gestor a ser consultado. |
Retorno
Com base nos parâmetros recebidos, a API identifica o gestor correspondente na hierarquia e retorna o campo nomusu, que representa o nome do usuário vinculado à ficha cadastral do gestor.
Dessa forma, a API permite que a regra de customização identifique o usuário responsável pela aprovação de determinado colaborador, de acordo com o nível hierárquico informado.
- Assinalamento Utiliza Posto de Trabalho (R000ASS.USAPOS):
- A API respeita o assinalamento Utiliza Posto de Trabalho para determinar a origem da hierarquia utilizada na identificação do gestor.
- Quando ele estiver definido como “S- Sim”, o gestor será identificado por meio da hierarquia de postos de trabalho.
- Já quando estiver definido como “N - Não”, o gestor será identificado por meio do organograma de locais.
- Caso o gestor identificado não possua um usuário vinculado à ficha cadastral, a API retornará um erro.
- Caso exista mais de um gestor no mesmo posto, será necessário realizar um tratamento na regra de customização para identificar o gestor correto.
- Nessa situação, a regra deverá informar o nível hierárquico (nivel) específico e enviá-lo ao StartProcess, conforme a necessidade do fluxo de aprovação.
English
Español

English
Español

