Árvore de páginas

CONTEÚDO

  1. Cadastrando um novo Monitor exclusivo
  2. Criando uma nova Api de negócio
    1. Implementando procedure de modo Gráfico
    2. 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ção

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).


Lembre-se disso

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.


Exemplo 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ção

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.


Exemplo 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.

Exemplo 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:

Exemplo Monitor Exclusivo.zip

Para mais exemplos, consulte a página Exemplos adicionais de monitores.


  • Sem rótulos