Em todos os eventos do processo é possível obter informações da API de Workflow. Cada evento possui acesso ao handle da API de workflow pela variável global hAPI. Os seguintes métodos estão disponíveis através da hAPI:
Veja o exemplo em nosso repositório aqui.
Método | Especificação |
---|---|
getCardValue("nomeCampo") | Permite acessar o valor de um campo do formulário do processo, onde:
Dica Campos do tipo checkbox retornarem os valores on para marcado ou "" (vazio) para não marcado. Para trabalhar de uma maneira mais fácil, é possível transformá-los em booleanos. Exemplo: var campoCheckbox = hAPI.getCardValue("campoCheckbox") == "on" ? true : false; |
setCardValue("nomeCampo", "valor") | Permite definir o valor de um campo do formulário do processo, onde:
|
setAutomaticDecision(numAtiv, listaColab, "obs") | Atenção: Depreciado O método setAutomaticDecision encontra-se depreciado, não havendo mais suporte a partir da atualização 1.5.9 do fluig. Recomenda-se a utilização da atividade de Serviço ou Gateway Exclusivo. Permite definir o fluxo de saída de uma atividade de forma automática, onde:
function beforeStateEntry(sequenceId){ if(sequenceId == 4){ var users = new java.util.ArrayList(); //Caso a próxima atividade seja uma automática utilizar users.add("System:Auto"); users.add("adm"); hAPI.setAutomaticDecision(7, users, "Decisao tomada automaticamente pelo Fluig"); } } |
getActiveStates() | Retorna uma lista das atividades ativas do processo. |
getActualThread(numEmpresa, numProcesso, numAtiv) | Atenção: Depreciado O método getActualThread encontra-se depreciado, não havendo mais suporte a partir da atualização 1.8.0-221122 do Fluig. Recomenda-se a utilização dos parâmetros WKCurrentMovto e WKActualThread descritos na documentação Parâmetros. Retorna a thread da atividade que está ativa, lembrando que em caso de atividades paralelas, retorna 0, 1, 2 e assim sucessivamente.
Exemplo de uso para esta função: function afterTaskCreate(colleagueId) { var nrProxAtividade = getValue("WKNextState"); if (nrProxAtividade == "5"){ //atividade entre paralelas var data = new Date(); var numEmpresa = getValue("WKCompany"); //seta o dia, mês (Janeiro é 0) e ano data.setDate(20); data.setMonth(10); data.setFullYear(2010); // Recupera o numero da solicitação var numProcesso = getValue("WKNumProces"); // Seta o prazo para as 14:00 hAPI.setDueDate(numProcesso, hAPI.getActualThread(numEmpresa, numProcesso, nrProxAtividade), colleagueId, data, 50400); } } |
setDueDate(numProcesso, numThread, "userId", dataConclusao, tempoSeg) | Permite alterar o prazo de conclusão para uma determinada atividade do processo, onde:
|
transferTask(transferUsers, "obs", int numThread) | Transfere uma tarefa de um usuário para outro(s) usuário(s).
|
transferTask(transferUsers, "obs") | Transfere uma tarefa de um usuário para outro(s) usuário(s). Este método não pode ser usado em processos com atividades paralelas:
|
startProcess(processId, ativDest, listaColab, "obs", completarTarefa, valoresForm, modoGestor) | Inicia uma solicitação workflow, onde:
Retorna um mapa com informações da solicitação criada. Entre elas, o iProcess que é o número da solicitação criada. Exemplo de inicialização de uma solicitação pelo método hAPI.startProcess enviando a atividade para um papel: function beforeStateEntry(sequenceId){ if (sequenceId == 5) { //A tarefa destino tem o mecanismo de atribuição para um papel, cujo o código é papelUser var users = new java.util.ArrayList(); users.add("Pool:Role:papelUser"); var formData = new java.util.HashMap(); formData.put("Nome_do_Campo1", "Valor do Campo 1"); formData.put("Nome_do_Campo2", "Valor do Campo 2"); hAPI.startProcess("pool", 4, users, "Solicitação inicializada pela função hAPI", true, formData, false); } } |
setColleagueReplacement(userId) | Seta um usuário substituto, onde:
|
setTaskComments("userId", numProcesso, numThread, "obs") | Define uma observação para uma determinada tarefa do processo, onde:
Atenção É recomendado utilizar em eventos do tipo 'after', pois o comentário será criado no histórico da solicitação, então é necessário que já exista uma movimentação do processo para atribuir este comentário. |
getCardData(numProcesso) | Retorna um Mapa com todos os campos e valores do formulário da solicitação.
Para formulário pai e filho, os campos são identificados da seguinte forma: campo1___1, sendo campo1 o nome atribuído ao campo através da tag do campo HTML +___(3 underlines) + número sequencial do registro. Por exemplo, em um formulário de nota fiscal com os seguintes campos:
O retorno do mapa seria:
|
getAdvancedProperty("propriedade") | Retorna o valor da propriedade avançada de um processo.
|
calculateDeadLineHours(data, segundos, prazo, periodId) Importante! Este método retorna o horário com a formatação BRT, sendo necessário algumas adaptações em algumas funções do javascript, como exemplo: getFullYear(). | Calcula um prazo a partir de uma data com base no expediente e feriados cadastrados no produto passando o prazo em horas:
Retorno: Array de Objeto, onde a primeira posição do array é a data e a segunda a hora. Exemplo: function afterTaskCreate(colleagueId) { var data = new Date(); //Calcula o prazo var obj = hAPI.calculateDeadLineHours(data, 50000, 2, "Default"); var dt = obj[0]; var segundos = obj[1]; //Recupera o numero da solicitação var processo = getValue("WKNumProces"); //Altera o prazo do processo hAPI.setDueDate(processo,0,colleagueId, dt, segundos); } |
calculateDeadLineTime(data, segundos, prazo, periodId) | Calcula um prazo a partir de uma data com base no expediente e feriados cadastrados no produto passando o prazo em minutos:
Retorno: Array de Objeto, onde a primeira posição do array é a data e a segunda a hora. Exemplo: function afterTaskCreate(colleagueId) { var data = new Date(); //Calcula o prazo var obj = hAPI.calculateDeadLineTime(data, 50000, 120, "Default"); var dt = obj[0]; var segundos = obj[1]; //Recupera o numero da solicitação var processo = getValue("WKNumProces"); // Altera o prazo do processo hAPI.setDueDate(processo,0,colleagueId, dt, segundos); } |
getUserTaskLink(numAtiv) | Permite buscar o link para movimentação de uma determinada atividade, e utilizá-lo para enviar um e-mail com template personalizado, por exemplo.
Retorno: link para movimentação da solicitação. Atenção Este método não retorna link para atividades que ainda não foram criadas, ou seja, não pode ser utilizado em eventos como afterTaskComplete(colleagueId,nextSequenceId,userList) para obter o link da atividade com "nextSequenceId". Exemplo: function afterTaskCreate(colleagueId) { var sequenceId = getValue("WKCurrentState"); if (sequenceId == 2) { var destinatarios = new java.util.ArrayList(); destinatarios.add(colleagueId); var parametros = new java.util.HashMap(); parametros.put("WDK_CompanyId", getValue("WKCompany")); parametros.put("WDK_TaskLink", hAPI.getUserTaskLink(sequenceId)); notifier.notify(getValue("WKUser"), "tplCustomizado", parametros, destinatarios, "text/html"); } } |
createAdHocTasks(workflowProcessInstanceId, sequenceId, assunto, detalhamento, listatvidadesAhoc) | Permite a criação de atividades adhoc dentro dos eventos do fluig
function beforeStateEntry(sequenceId){ //lista das tarefas var adHocTasks = new Array(); // variavel com o código da solicitação do processo. var process = getValue("WKNumProces"); //criando uma tarefa var task = { name:"nome da tarefas", responsible:"adm", dueDate:"10/10/2014"}; //adicionando a tarefa criada para a lista de tarefas adHocTasks.push(task); // process = numero da solicitacao // sequenceId = codigo processstate da atividade que tem o processo ad-hoc // meeting = nome do assunto em questao // detalhe = detalhamento do assunto // adHocTasks lista te tarefas hAPI.createAdHocTasks(process, sequenceId, "Assunto das tarefas", "Detalhamento do assunto ", adHocTasks); } |
listAttachments() | Retorna a lista de anexos do processo. DocumentDto[ ] Veja aqui os paramêtros do objeto DocumentDto e aqui os métodos para acesso as variáveis. Atenção O formulário do processo não é retornado, apenas anexos do tipo GED e Workflow. Exemplo: function beforeTaskSave(colleagueId, nextSequenceId, userList) { var attachments = hAPI.listAttachments(); var hasAttachment = false; for (var i = 0; i < attachments.size(); i++) { var attachment = attachments.get(i); if (attachment.getDocumentDescription() == "fluig.pdf") { hasAttachment = true; } } if (!hasAttachment) { throw "Attachment not found!"; } } |
publishWorkflowAttachment(documento) | Permite publicar anexos workflow da solicitação no GED do fluig, onde:
Exemplo: function beforeStateEntry(sequenceId){ if (sequenceId == 4) { var calendar = java.util.Calendar.getInstance().getTime(); var docs = hAPI.listAttachments(); for (var i = 0; i < docs.size(); i++) { var doc = docs.get(i); if (doc.getDocumentType() != "7") { continue; } doc.setParentDocumentId(27); doc.setVersionDescription("Processo: " + getValue("WKNumProces")); doc.setExpires(false); doc.setCreateDate(calendar); doc.setInheritSecurity(true); doc.setTopicId(1); doc.setUserNotify(false); doc.setValidationStartDate(calendar); doc.setVersionOption("0"); doc.setUpdateIsoProperties(true); hAPI.publishWorkflowAttachment(doc); } } } |
attachDocument(documentId) | Permite anexar documentos do GED a solicitação workflow, onde:
Exemplo: function beforeStateEntry(sequenceId) { var docList = [44, 46, 135]; if (sequenceId == 2) { for (var i = 0; i < docList.length; i++) { var docId = docList[i]; hAPI.attachDocument(docId); }; } } Veja o exemplo (document-attach) no repositório do fluig clicando aqui. |
getAvailableStatesDetail(companyId, userId, processId, processInstanceId, threadSequence) | Retorna detalhes das atividades disponíveis para seleção. Parâmetros:
Retorno: ProcessStateDto[]. |
getChildrenInstances(processInstanceId) | Retorna uma lista com os números das solicitações filhas, onde:
Exemplo: function beforeStateEntry(sequenceId){ if (sequenceId == 5) { var numProcess = getValue("WKNumProces"); // Busca a Lista com o número da solicitação dos filhos var childrenProcess = hAPI.getChildrenInstances(numProcess); for (var i = 0; i < childrenProcess.size(); i++) { // Busca os dados do formulário da solicitação filha var childCardData = hAPI.getCardData(childrenProcess.get(i)); // Replica um dado do formulário da solicitação filha para o formulário da solicitação pai var obs = childCardData.get("obs"); hAPI.setCardValue("obs", obs ); } } } |
getParentInstance(processInstanceId) | Retorna o número da solicitação pai, onde:
Exemplo: function beforeStateEntry(sequenceId) { if (sequenceId == 2) { var numProcess = getValue("WKNumProces"); // Busca o número da solicitação pai var parentProcess = hAPI.getParentInstance(numProcess); // Busca os dados do formulário da solicitação pai var parentCardData = hAPI.getCardData(parentProcess); // Replica um dado do formulário da solicitação pai para o formulário da solicitação filha var cnpj = parentCardData.get("cnpj"); hAPI.setCardValue("cnpj", cnpj); } } |
addCardChild(tableName, cardData) | Adiciona um filho no formulário pai e filho do processo, onde:
Exemplo: function beforeStateEntry(sequenceId) { if (sequenceId == 4) { var childData = new java.util.HashMap(); childData.put("matricula", "0041"); childData.put("nome", "João Silva"); childData.put("cpf", "44455889987"); hAPI.addCardChild("funcionarios", childData); } } |
removeCardChild | Este método estará disponível a partir da atualização 1.8.1 do TOTVS Fluig Plataforma. Remove uma linha de uma tabela Pai X Filho. Utilize o método removeCardChild, passando o nome da tabela (valor do atributo name) e o número da linha que deseja deletar. Exemplo: removeCardChild(tableName, 1); Observação Caso queira excluir mais de uma linha e chamar o método mais de uma vez (utilizando um laço de repetição), se atente ao detalhe de que cada chamada nesse método ele irá resetar os index dos filhos da tabela por exemplo: removeCardChild(tableName, 1); Nesse caso ele estaria excluindo a linha A e linha C, pois quando chamou o primeiro método para excluir a linha 1(A) ele resetou os index da tabela e a linha 2(B) passou a ser 1 e a 3(C) passou a ser 2 e assim por diante. Temos duas alternativas para esse cenário: 1 - Excluir primeiro os últimos registros Exemplo: removeCardChild(tableName, 2); 2 - Antes de excluir utilizar um for para percorrer a linha de forma decrescente. Assim você garante estar excluindo sempre a linha que deseja sem interferir na próxima, por exemplo: var indexes = hAPI.getChildrenIndexes("tableName"); for (var i = indexes.length-1; i >= 0; i--) { hAPI.removeCardChild("tableName", indexes[i]); } Ou este outro exemplo: var indexes = hAPI.getChildrenIndexes("tableName"); for (var i = indexes.length-1; i >= 0; i--) { if (indexes[i] == 1) \{ hAPI.removeCardChild("tableName", indexes[i]); } Onde a variável indexes[i] possui os valor do número da linha. |
Nos eventos existe a possibilidade de integração com serviços de dados. Tais serviços podem ser WebServices, AppServer Progress® e Dataset.
O acesso a WebServices ou AppServer Progress® deve ser previamente configurado no cadastro de Serviços. Para mais detalhes consulte em Integração com aplicativos externos, no capítulo "Acessando WebServices a partir do fluig".
Abaixo um exemplo de como executar o WebService de Colleague para criar um usuário no fluig após executar uma tarefa:
function afterTaskComplete(colleagueId, nextSequenceId, userList) { if (nextSequenceId == 2) { //Busca o webservices de Colaborador //Servico "<url_fluig>/webdesk/ECMColleagueService?wsdl" cadastrado com o código "Colleague" var colleagueServiceProvider = ServiceManager.getServiceInstance("Colleague"); var colleagueServiceLocator = colleagueServiceProvider.instantiate("com.totvs.technology.ecm.foundation.ws.ECMColleagueServiceService"); var colleagueService = colleagueServiceLocator.getColleagueServicePort(); //Cria o ColleagueDto – Verificar a lista de métodos na visualização do serviço var colleagueDto = colleagueServiceProvider.instantiate("com.totvs.technology.ecm.foundation.ws.ColleagueDto"); colleagueDto.setCompanyId(1); colleagueDto.setColleagueId("teste"); colleagueDto.setColleagueName("Usuario Teste"); colleagueDto.setActive(true); colleagueDto.setVolumeId("Default"); colleagueDto.setLogin("teste"); colleagueDto.setMail("[email protected]"); colleagueDto.setPasswd("teste"); colleagueDto.setAdminUser(false); colleagueDto.setEmailHtml(true); colleagueDto.setDialectId("pt_BR"); //Cria o colleagueDtoArray e adiciona var colleagueDtoArray = colleagueServiceProvider.instantiate("com.totvs.technology.ecm.foundation.ws.ColleagueDtoArray"); colleagueDtoArray.getItem().add(colleagueDto); var result = colleagueService.createColleague("adm", "adm", 1, colleagueDtoArray); log.info("Result: " + result); } }
Abaixo um outro exemplo utilizando o WebService ECMCardService para alterar o valor do campo de um registro de formulário após a entrada em uma nova atividade:
function afterStateEntry(sequenceId){ if (sequenceId == 2) { //Servico "<url_fluig>/webdesk/ECMCardService?wsdl"cadastrado com o código "CardService" var cardServiceProvider = ServiceManager.getServiceInstance("01"); var cardServiceLocator = cardServiceProvider.instantiate("com.totvs.technology.ecm.dm.ws.ECMCardServiceService"); var cardService = cardServiceLocator.getCardServicePort(); var cardFieldDtoArray = cardServiceProvider.instantiate("com.totvs.technology.ecm.dm.ws.CardFieldDtoArray"); var cardField = cardServiceProvider.instantiate("com.totvs.technology.ecm.dm.ws.CardFieldDto"); //Seta valor no campo com name = 'nome' cardField.setField("nome"); cardField.setValue("Valor alterado via WS dentro de um evento workflow"); var vetCardFields = new Array(); vetCardFields.push(cardField); cardFieldDtoArray.getItem().addAll(vetCardFields); //Altera o(s) campo(s) do registro de formulário. //updateCardData(tenantId, login, senha, codRegistroForm, cardFieldDtoArray); cardService.updateCardData(1, "adm", "adm", 8, cardFieldDtoArray); } }
Os seguintes eventos são disparados pela API de Workflow:
Evento | Descrição | Parâmetros | |
---|---|---|---|
afterCancelProcess | Ocorre após o cancelamento da solicitação. É recomendado não disparar exceções neste método, pois o cancelamento já foi realizado. |
| |
afterProcessCreate | Ocorre logo após a criação de um novo processo. |
| |
afterProcessFinish | Ocorre após finalizada a solicitação. |
| |
afterReleaseVersion | Ocorre após a liberação de uma versão do processo. |
| |
afterStateEntry | Ocorre após a entrada em uma nova atividade. Este evento não retorna erros para a tela naturalmente. Caso ocorra um erro durante a execução do afterStateEntry, ele será ignorado. Se o mesmo erro resultar no encerramento da transação (por exemplo, causando o timeout de transação ou uma exceção que force Rollback) a mensagem de transação interrompida será apresentada em tela ao invés do erro esperado. |
| |
afterStateLeave | Ocorre após a saída de uma atividade. |
| |
afterTaskComplete | Ocorre após o usuário completar uma tarefa, porém as informações de próxima tarefa e usuários destino já foram salvas. |
| |
afterTaskCreate | Ocorre após o usuário receber uma tarefa. |
| |
afterTaskSave | Ocorre após salvar as informações selecionadas pelo usuário. |
| |
beforeCancelProcess | Ocorre antes do cancelamento da solicitação. |
| |
beforeStateEntry | Ocorre antes da entrada em uma nova atividade. Dica Utilize este evento para realizar validações que ocasionalmente possam resultar no bloqueio da movimentação do processo. Para suspender a movimentação e exibir uma mensagem de alerta para o usuário, utilize o comando 'throw', conforme exemplo abaixo:
|
| |
beforeStateLeave | Ocorre antes da saída de uma atividade. |
| |
beforeTaskComplete | Ocorre antes que o usuário complete uma tarefa, porém as informações de próxima tarefa e usuários destino já foram salvas. Observação Diferente dos demais eventos do tipo before, este evento não dispara suas validações ao servidor, fazendo com que o sistema não pare a movimentação da atividade quando o mesmo lança uma exceção. |
| |
beforeTaskCreate | Ocorre antes que o usuário receba uma tarefa. |
| |
beforeTaskSave | Ocorre antes de salvar as informações selecionadas pelo usuário. |
| |
calculateAgreement | Ocorre após o cálculo do consenso (somente para atividades conjuntas) e permite alterar os dados do consenso de uma atividade. Exemplo: function calculateAgreement(currentState, agreementData) { log.info("Consenso Atual: " + agreementData.get("currentPercentage")); log.info("Atividade Destino Atual: " + agreementData.get("currentDestState")); log.info("Usuario Destino Atual: " + agreementData.get("currentDestUsers")); //Altera o consenso agreementData.put("currentPercentage", 100); agreementData.put("currentDestState", 2); agreementData.put("currentDestUsers", "marcos"); } |
| |
onNotify | Se refere a um evento global que ocorre após a movimentação da solicitação e antes de enviar as notificações. | Para mais detalhes consulte a página Desenvolvimento de eventos - On Notify. | |
subProcessCreated | Ocorre quando um sub-processo é criado. |
| |
validateAvailableStates | Ocorre após montada a lista de tarefas disponíveis para o usuário a partir da tarefa atual. Exemplo: function validateAvailableStates(iCurrentState, stateList) { // Código: 1 - Descrição: Atividade inicial // Código: 2 - Descrição: Atividade ordem 3 // Código: 3 - Descrição: Atividade ordem 2 // Código: 4 - Descrição: Atividade ordem 1 // stateList atual: [2,3,4] var stateArray = new Array(); if (iCurrentState == 1) { stateList.clear(); stateArray.push(4,3,2); } stateArray.forEach(function(code) { stateList.add(new java.lang.Integer(code)); }); // stateList reordenado: [4,3,2] return stateList; } |
|
- Não é necessário criar todos os eventos do processo – apenas aqueles nos quais se tem interesse.
- Todos os eventos são executados de forma persistente.
Importante!
Não é possível capturar (hAPI.getCardValue) ou adicionar (hAPI.setCardValue) dados no formulário na inicialização do processo, sendo possível somente a partir da segunda tarefa.
Para isso pode ser utilizada a seguinte lógica:
function beforeStateEntry(sequenceId) { if (sequenceId != "NUMERO_DA_ATIVIDADE_INICIAL") { var campo = hAPI.getCardValue("campo1"); } }