Histórico da Página
CONTEÚDO
- Cadastrando um novo Monitor exclusivo
- Criando uma nova Api de negócio
- Implementando procedure de modo Gráfico
- Implementando procedure de modo Detalhe
01. CADASTRANDO UM NOVO MONITOR EXCLUSIVO
O primeiro passo para criar o seu Monitor Exclusivo é acessar no menu do ERP Datasul o programa Gestão à Vista - Monitores Exclusivos (ou pelo código html.supply.Monitor.Custom).
Nesta rotina é possível visualizar os monitores Exclusivos já cadastrados e realizar o cadastro de um novo monitor:
Ao clicar em Adicionar, será apresentado um formulário contendo os campos abaixo:
Para o exemplo deste guia, iremos criar um monitor exclusivo para mostrar a quantidade de Ordens de Produção pela sua situação em um determinado período.
Seguindo o cadastro do Monitor, ao informar os campos conforme acima e clicar em Salvar, será habilitado a possibilidade de cadastrar Filtros para o Monitor. Os filtros são importantes pois irão garantir a possibilidade da parametrização individual deste monitor quando ele for ser adicionado em uma Visão por um usuário.
Informações | ||
---|---|---|
| ||
Pode-se notar que indicamos como nossa Api de Negócio o programa cpp/exclusivo/statusOrdem.p. Esse programa ainda não existe (iremos construí-lo em breve), porém precisamos informar seu futuro caminho no momento da criação do monitor. Se porventura o nome da API for alterado ou ela seja movida para um novo diretório, não se esqueça de atualizar esse cadastro com o novo caminho/nome. |
No nosso exemplo, iremos cadastrar apenas dois filtros, Estabelecimento e Desde quando (que irá representar o número de dias no passado que iremos buscar as ordens de produção de acordo com a sua data de criação).
Nota | ||
---|---|---|
| ||
Note que os filtros possuem como código de propriedade os valores cod-estabel e qtd-dias-atras respectivamente, isso será importante para o momento em que criaremos a Api de negócio. |
02. CRIANDO UMA NOVA API DE NEGÓCIO
Para criar a Api de negócio o primeiro passo é realizar a importação de classes do Progress que permitem a utilização de objetos do tipo JSON, além disso também é importante definir as includes disponibilizadas pelo Gestão à Vista.
Além da importação dos objetos Progress e definição de includes, também sugerimos criar uma função para verificar se existem RowErrors, essa função será útil para reaproveitamento de código posteriormente.
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
BLOCK-LEVEL ON ERROR UNDO, THROW. USING PROGRESS.json.*. USING PROGRESS.json.ObjectModel.*. USING cdp.services.gestaoavista.*. //A classe ChartBuilder está definida aqui. {method/dbotterr.i} {cdp/services/gestaoavista/builder-utils.i} {cdp/services/gestaoavista/monitor-utils.i} FUNCTION fn-has-row-errors RETURNS LOGICAL (): FOR EACH RowErrors WHERE UPPER(RowErrors.ErrorType) = 'INTERNAL':U: DELETE RowErrors. END. RETURN CAN-FIND(FIRST RowErrors WHERE UPPER(RowErrors.ErrorSubType) = 'ERROR':U). END FUNCTION. |
Após essa etapa, será necessário definir as procedures de acordo com a forma que o seu monitor exclusivo foi cadastrado, por exemplo:
Monitores do tipo gráfico, devem implementar a procedure pi-get-monitor-data-chart.
Monitores do tipo texto, devem implementar a procedure pi-get-monitor-data-info.
Ambos os tipos de monitores suportam o modo de detalhe das informações através da procedure pi-get-monitor-detail.
Informações | ||
---|---|---|
| ||
Neste guia vamos considerar dar continuidade com a construção de um monitor do tipo Gráfico com modo Detalhe. |
02.a. IMPLEMENTANDO A PROCEDURE DE MODO GRÁFICO
No bloco de código abaixo, iremos criar a procedure pi-get-monitor-data-chart (maiores detalhes sobre ela podem ser consultados aqui), para o nosso exemplo neste guia, iremos realizar uma query dinâmica na tabela ord-prod para realizar a contagem de Ordens x Situação aplicando os filtros de estabelecimento e desde quando que é calculado em número de dias retroativos a hoje.
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
PROCEDURE pi-get-monitor-data-chart: DEFINE INPUT PARAMETER TABLE FOR ttVisaoMonitor. DEFINE OUTPUT PARAMETER monitorJsonOutput AS JsonObject. DEFINE OUTPUT PARAMETER TABLE FOR RowErrors. DEFINE VARIABLE cFiltroEstab AS CHARACTER NO-UNDO. DEFINE VARIABLE dFiltroData AS DATE NO-UNDO. DEFINE VARIABLE iStatusAberto AS INTEGER NO-UNDO. DEFINE VARIABLE iStatusAndamento AS INTEGER NO-UNDO. DEFINE VARIABLE iStatusEncerrado AS INTEGER NO-UNDO. DEFINE VARIABLE ChartBuilder AS ChartBuilder NO-UNDO. // Classe utilitária que ajudará na montagem do gráfico FIND FIRST ttVisaoMonitor. EMPTY TEMP-TABLE RowErrors. fn-validate-properties(). // Método interno que valida automaticamente se todos os filtros marcados como obrigatórios foram preenchidos. IF fn-has-row-errors() THEN RETURN "NOK":U. /* Instanciar a classe passando como parâmetro a ttVisaoMonitor, que contém as informações do monitor e visão que estão sendo processados nesse instante */ ChartBuilder = NEW ChartBuilder(INPUT TABLE ttVisaoMonitor). /** Pega os valores que o usuário digitou nos filtros do monitor. O valor é sempre gravado como STRING, portanto deve-se fazer a conversão dos dados caso exista necessidade. Importante: Os valores passados como parâmetro devem ser os mesmos informados no campo "Propriedade" no cadastro de cada filtro **/ ASSIGN cFiltroEstab = fn-get-valor-propriedade(INPUT "cod-estabel") dFiltroData = TODAY - INTEGER(fn-get-valor-propriedade(INPUT "qtd-dias-atras")). FOR EACH ord-prod NO-LOCK WHERE ord-prod.cod-estabel >= cFiltroEstab AND ord-prod.dt-emissao >= dFiltroData: IF ord-prod.estado < 3 THEN ASSIGN iStatusAberto = iStatusAberto + 1. ELSE IF ord-prod.estado <= 6 THEN ASSIGN iStatusAndamento = iStatusAndamento + 1. ELSE ASSIGN iStatusEncerrado = iStatusEncerrado + 1. END. CREATE ttSeries. ASSIGN ttSeries.titulo = "Pendentes" ttSeries.valor = STRING(iStatusAberto) ttSeries.cor = "#A0B9BF". CREATE ttSeries. ASSIGN ttSeries.titulo = "Em Andamento" ttSeries.valor = STRING(iStatusAndamento) ttSeries.cor = "#007acc". CREATE ttSeries. ASSIGN ttSeries.titulo = "Concluídas" ttSeries.valor = STRING(iStatusEncerrado) ttSeries.cor = "#26BA41". CREATE ttTags. ASSIGN ttTags.valor = "Tag de Exemplo" ttTags.icone = "po-icon-calendar" ttTags.cor-texto = "#f5f5f5" ttTags.cor-tag = "#080707". CREATE ttTags. ASSIGN ttTags.valor = "Tag de Exemplo 2" ttTags.icone = "po-icon-manufacture" ttTags.cor-texto = "#f5f5f5" ttTags.cor-tag = "#080707". /* Depois que todas as entidades estão criadas, basta setá-las no objeto ChartBuilder */ ChartBuilder:setTags(INPUT TABLE ttTags). ChartBuilder:setSeries(INPUT TABLE ttSeries). /* Chama o método para criar e devolver o gráfico completo e guarda o resultado na variável monitorJsonOutput */ monitorJsonOutput = ChartBuilder:createChart(). DELETE OBJECT ChartBuilder. /* Exibe o resultado no Log do AppServer, se estiver ativo */ RUN displayJsonObject(monitorJsonOutput). CATCH eSysError AS Progress.Lang.Error: CREATE RowErrors. ASSIGN RowErrors.ErrorNumber = 17006 RowErrors.ErrorDescription = eSysError:getMessage(1) RowErrors.ErrorSubType = "ERROR". END. FINALLY: IF fn-has-row-errors() THEN DO: UNDO, RETURN 'NOK':U. END. END FINALLY. END. |
Após a inclusão dessa procedure na api, já é possível adicionar esse monitor exclusivo em uma Visão e observar o resultado espero conforme abaixo:
Exemplos de outros tipos de gráficos podem ser consultados em: Exemplos adicionais de monitores.
02.b. IMPLEMENTANDO A PROCEDURE DE MODO DETALHE
No bloco de código abaixo, iremos criar a procedure pi-get-monitor-data-detail (maiores detalhes sobre ela podem ser consultados aqui), através da definição dessa procedure é possível fazer o retorno do schema e dados da modal de detalhe para detalhar as informações que estão sendo mostradas no monitor (por exemplo, lista de registros que foram considerados para montar o gráfico do monitor).
No exemplo de código abaixo, ao clicar em uma parte do gráfico, será aberto uma modal com a listagem de Ordens de Produção com a situação escolhida, ou ao acionar a opção Detalhar nas configurações do monitor, será listada todas as ordens de produção que compõe o gráfico.
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
PROCEDURE pi-get-monitor-data-detail: /* O nome da procedure de detalhe sempre precisará ser esse. */ DEFINE INPUT PARAM TABLE FOR ttVisaoMonitor. DEFINE INPUT PARAM iPage AS INTEGER NO-UNDO. DEFINE INPUT PARAM cSerie AS CHARACTER NO-UNDO. DEFINE INPUT PARAM cCategory AS CHARACTER NO-UNDO. DEFINE OUTPUT PARAM detailJsonOutput AS JsonObject NO-UNDO. DEFINE OUTPUT PARAM TABLE FOR RowErrors. DEFINE VARIABLE DetailBuilder AS DetailBuilder NO-UNDO. DEFINE VARIABLE lHasNext AS LOGICAL INITIAL FALSE NO-UNDO. DetailBuilder = NEW DetailBuilder(). // Classe utilitária que ajudará na montagem do objeto de detalhe FIND FIRST ttVisaoMonitor. /* Procedure local que cria a temp-table das colunas que serão apresentadas na janela de detalhe */ RUN pi-get-colunas-detalhe(OUTPUT TABLE ttColunaDetalhe). /* Opcionalmente, também podemos montar cabeçalhos (headers) para a janela de detalhe */ RUN pi-get-headers-detalhe(INPUT cSerie, OUTPUT TABLE ttHeadersDetalhe). /** Executar o método que irá retornar a temp-table contendo os dados a serem apresentados no detalhe. Dessa vez não serão os totais, e sim os registros individuais. Pode ser aproveitado o mesmo método de query que gera o gráfico do monitor, porém ele precisará ser ajustado para levar em consideração qual fatia/coluna do gráfico o usuário clicou (variáveis cSerie e cCategory) e também precisará gerar a tabela com os registros individuais, e não somente calcular os totais **/ RUN pi-get-itens-detalhe(INPUT iPage, INPUT cSerie, OUTPUT lHasNext, OUTPUT TABLE ttOrdemProducao). CREATE ttTags. ASSIGN ttTags.valor = "Tag de Exemplo" ttTags.icone = "po-icon-calendar" ttTags.cor-texto = "#f5f5f5" ttTags.cor-tag = "#080707". CREATE ttTags. ASSIGN ttTags.valor = "Tag de Exemplo 2" ttTags.icone = "po-icon-manufacture" ttTags.cor-texto = "#f5f5f5" ttTags.cor-tag = "#080707". // Setamos as colunas, tags, headers e itens que serão exibidos DetailBuilder:setTags(INPUT TABLE ttTags). DetailBuilder:setColumns(INPUT TABLE ttColunaDetalhe). DetailBuilder:setHeaders(INPUT TABLE ttHeadersDetalhe). DetailBuilder:setItems(JsonAPIUtils:convertTempTableToJsonArray(TEMP-TABLE ttOrdemProducao:HANDLE, FALSE)). //O método convertTempTableToJsonArray transforma nossa temp-table em um Array, contendo os campos conforme os SERIALIZE-NAME definidos /** Se a consulta for paginada e tiver mais resultados além dos que estão sendo retornados, podemos setar a variável 'hasNext' como TRUE. Desse modo o botão 'Carregar mais resultados' ficará habilitado na janela de detalhe para que o usuário possa consultar os registros da próxima página. Recomendamos utilizar paginação caso exista a possibilidade da consulta geral demorar mais que um minuto **/ DetailBuilder:setHasNext(lHasNext). DetailBuilder:setCanExportXLS(TRUE). // Determina se o botão de exportação para planilha ficará habilitado (TRUE) ou não (FALSE) DetailBuilder:setModalMaxWidth("1440px"). // Tamanho máximo que a janela terá em tela ASSIGN detailJsonOutput = DetailBuilder:createDetail(). //Gera o objeto de detalhe /** A temp-table RowErrors pode ser utilizada para retornar mensagens de erro, caso necessário: CREATE RowErrors. ASSIGN RowErrors.ErrorNumber = 17006 RowErrors.ErrorDescription = "ERRO DE EXEMPLO" RowErrors.ErrorSubType = "ERROR". **/ END PROCEDURE. PROCEDURE pi-get-headers-detalhe: DEFINE INPUT PARAM cSerie AS CHARACTER NO-UNDO. DEFINE OUTPUT PARAMETER TABLE FOR ttHeadersDetalhe. CREATE ttHeadersDetalhe. ASSIGN ttHeadersDetalhe.texto-header = "Listando Ordens com Situação:" ttHeadersDetalhe.classe-header = "po-sm-12 po-font-subtitle blue-text" //Classes de tipografia do PO-UI estão disponíveis em: https://po-ui.io/guides/typography ttHeadersDetalhe.estilo-header = "". CREATE ttHeadersDetalhe. ASSIGN ttHeadersDetalhe.texto-header = IF cSerie <> "" THEN cSerie ELSE "Todos" ttHeadersDetalhe.classe-header = "po-sm-12 po-font-text-large-bold" //Classes de tipografia do PO-UI estão disponíveis em: https://po-ui.io/guides/typography ttHeadersDetalhe.estilo-header = "". END PROCEDURE. PROCEDURE pi-get-colunas-detalhe: DEFINE OUTPUT PARAMETER TABLE FOR ttColunaDetalhe. /* Atenção! O campo 'propriedade' da ttColunaDetalhe deve conter o mesmo nome que consta no SERIALIZE-NAME do campo que será apresentado, conforme definição da temp-table. Estamos utilizando a temp-table ttOrdemProducao neste exemplo, veja que estamos usando os nomes em inglês que foram definidos para cada propriedade: */ CREATE ttColunaDetalhe. ASSIGN ttColunaDetalhe.cod-label = "Ordem" ttColunaDetalhe.propriedade = "productionOrderNumber" //productionOrderNumber equivale ao campo nr-ord-produ na temp-table ttOrdemProducao ttColunaDetalhe.formato = "1.0-0" ttColunaDetalhe.tipo = "number" ttColunaDetalhe.largura = "180px". CREATE ttColunaDetalhe. ASSIGN ttColunaDetalhe.cod-label = "Item" ttColunaDetalhe.propriedade = "itemCode". //itemCode equivale ao campo it-codigo na temp-table ttOrdemProducao CREATE ttColunaDetalhe. ASSIGN ttColunaDetalhe.cod-label = "Data de Emissão" ttColunaDetalhe.propriedade = "creationDate" //creationDate equivale ao campo dt-emissao na temp-table ttOrdemProducao ttColunaDetalhe.tipo = "date". CREATE ttColunaDetalhe. ASSIGN ttColunaDetalhe.cod-label = "Quantidade" ttColunaDetalhe.propriedade = "quantity" //quantity equivale ao campo qt-ordem na temp-table ttOrdemProducao ttColunaDetalhe.tipo = "number" ttColunaDetalhe.formato = "1.4-4". END PROCEDURE. PROCEDURE pi-get-itens-detalhe: DEFINE INPUT PARAM iPage AS INTEGER NO-UNDO. DEFINE INPUT PARAM cSerie AS CHARACTER NO-UNDO. DEFINE OUTPUT PARAM lHasNext AS LOGICAL NO-UNDO. DEFINE OUTPUT PARAMETER TABLE FOR ttOrdemProducao. DEFINE VARIABLE cQuery AS CHARACTER NO-UNDO. DEFINE VARIABLE iCount AS INTEGER NO-UNDO. DEFINE VARIABLE cFiltroEstab AS CHARACTER NO-UNDO. DEFINE VARIABLE dFiltroData AS DATE NO-UNDO. ASSIGN cFiltroEstab = fn-get-valor-propriedade(INPUT "cod-estabel") dFiltroData = TODAY - INTEGER(fn-get-valor-propriedade(INPUT "qtd-dias-atras")). ASSIGN cQuery = 'FOR EACH ord-prod NO-LOCK':U. ASSIGN cQuery = cQuery + ' WHERE ord-prod.cod-estabel = "' + cFiltroEstab + '"'. ASSIGN cQuery = cQuery + ' AND ord-prod.dt-emissao >= ' + STRING(dFiltroData) + ''. /* Caso a cSerie esteja preenchido, irá significar que a tela de detalhes foi acionada através de uma interação com o gráfico e deverá ser filtrado os dados de acordo com a série clicada */ IF cSerie <> "" THEN DO: IF cSerie = "Em Aberto" THEN ASSIGN cQuery = cQuery + ' AND ord-prod.estado < 3'. ELSE IF cSerie = "Em Andamento" THEN ASSIGN cQuery = cQuery + ' AND ord-prod.estado >= 3 AND ord-prod.estado <= 6'. ELSE IF cSerie = "Concluídas" THEN ASSIGN cQuery = cQuery + ' AND ord-prod.estado >= 7'. END. DEFINE QUERY findQuery FOR ord-prod SCROLLING. QUERY findQuery:QUERY-PREPARE(cQuery). QUERY findQuery:QUERY-OPEN(). QUERY findQuery:REPOSITION-TO-ROW(iPage). REPEAT: GET NEXT findQuery. IF QUERY findQuery:QUERY-OFF-END THEN LEAVE. IF iCount >= 50 THEN DO: ASSIGN lHasNext = TRUE. LEAVE. END. CREATE ttOrdemProducao. TEMP-TABLE ttOrdemProducao:HANDLE:DEFAULT-BUFFER-HANDLE:BUFFER-COPY( BUFFER ord-prod:HANDLE ). FOR FIRST item FIELDS(desc-item) WHERE item.it-codigo = ttOrdemProducao.it-codigo NO-LOCK: END. ASSIGN ttOrdemProducao.desc-item = IF AVAIL item THEN item.desc-item ELSE ''. ASSIGN iCount = iCount + 1. END. END PROCEDURE. |
Com o código exemplificado acima teremos o resultado abaixo ao clicar no gráfico:
Em anexo também estamos disponibilizando o código completo utilizado neste passo a passo:
View file | ||||
---|---|---|---|---|
|
Para mais exemplos, consulte a página Exemplos adicionais de monitores.