# Relatório Customizado

Para possibilitar que se criem relatórios customizados por Produto e por Cliente, será necessário converter as telas de relatórios existentes para o Zeedhi Next. Para esse fim, foi criado um template para definição de design visual padrão, além de agilizar o desenvolvimento.

Atenção!

  • Para utilizar essa rotina, é necessário que seu Produto e/ou Módulo esteja configurado devidamente com as rotinas do ACL. Caso ainda não tiver realizado esse processo, verifique a seção Engine (opens new window) antes de prosseguir.
  • Os dados são gravados na tabela CUSTOM_REPORTS. Apliquem o script 9152 (opens new window) em sua base de dados caso necessário.
# Funcionalidades da tela

  • Baixar arquivo fonte do BIRT (.rptdesign) para customização;
  • Criar um novo relatório customizado, realizando o upload e se baseando em algum relatório fixo selecionado;
    • O arquivo fonte do relatório será criado na pasta /var/www/teknisa/php7/customizations/reports/[NRORG]/[PRODUCT_ID]
  • Atualizar o relatório customizado existente;
  • Excluir relatórios customizados.
  • Gerar os relatórios para visualização no navegador (PDF) ou download (Excel).

# Atualizações

# composer.json

  • zeedhi/framework: 2.11.* (ou superior)
  • teknisa/libraries-next: 2.8.* (ou superior)
# package.json

  • @zeedhi/tek-lib: 2.8.*
  • @zeedhi/common: 1.95.*
  • @zeedhi/core: 1.95.*
  • @zeedhi/vue: 1.95.*
  • @zeedhi/vuetify: 1.95.*
  • @zeedhi/teknisa-components-common: 1.95.*
  • @zeedhi/teknisa-components-vuetify: 1.95.*

# Criando a nova tela (Frontend)

# Metadata (JSON)

Foi criado o templates/form-report para facilitar a construção. Ao utilizá-lo de forma básica, a tela já será renderizada pré configurada. Crie o json de sua tela com o fonte abaixo:

{
    "name": "example-report",
    "component": "ZdFramePage",
    "path": "",
    "type": "post",
    "params": {
        "jsonPath": "templates/form-report",
        "override": {
            "TITLE": "Exemplo de Formulário de Relatório",
            "WINDOW_NAME": "GER_123456"
        }
    }
}

Parâmetros básicos:

  • TITLE: Título da sua tela.
  • WINDOW_NAME: Identificador da sua tela. Será responsável por filtrar relatórios para que não apareçam em outras telas do seu produto.

Visão básica da tela com o metadata acima:

baseWindow

# Relatórios fixos

Para preencher os dados do campo Relatório com os dados fixos da sua tela, utilize a propriedade FIXED_REPORTS.

{
    "name": "example-report",
    "component": "ZdFramePage",
    "path": "",
    "type": "post",
    "params": {
        "jsonPath": "templates/form-report",
        "override": {
            "TITLE": "Exemplo de Formulário de Relatório",
            "WINDOW_NAME": "GER_123456",
            "REPORT_DEFAULT_VALUE": "GER80300",
            "FIXED_REPORTS": [
                {
                    "value": "GER80300",
                    "label": "Demonstrativo de Resultado Operacional"
                },
                {
                    "value": "GER80303",
                    "label": "Demonstrativo de Resultado Operacional Consolidado"
                },
                {
                    "value": "GERQR2",
                    "label": "Demonstrativo QR2",
                    "isQr2": true
                }
            ]
        }
    }
}
  • Adicione um DataSet possuindo value para controlar o valor do campo e label para controlar sua descrição.
  • Para os relatórios que forem QR2, informe a propriedade isQr2 como true.
  • Utilize o parâmetro REPORT_DEFAULT_VALUE para informar o valor padrão que deve vir preenchido em sua tela.

Atenção!

Relatórios QR2 não devem ser customizados. Lembre-se de informar a propriedade nesses casos!

reportList

# Campos adicionais

Para adicionar mais colunas em sua tela (filtrar o relatório, por exemplo), utilize a propriedade FORM. Adicione quantas colunas forem necessárias de acordo com suas necessidades, controlando a visibilidade posteriormente.

{
    "name": "example-report",
    "component": "ZdFramePage",
    "path": "",
    "type": "post",
    "params": {
        "jsonPath": "templates/form-report",
        "override": {
            "TITLE": "Exemplo de Formulário de Relatório",
            "WINDOW_NAME": "GER_123456",
            "REPORT_DEFAULT_VALUE": "GER80300",
            "FIXED_REPORTS": [
                {
                    "value": "GER80300",
                    "label": "Demonstrativo de Resultado Operacional"
                },
                {
                    "value": "GER80303",
                    "label": "Demonstrativo de Resultado Operacional Consolidado"
                },
                {
                    "value": "GERQR2",
                    "label": "Demonstrativo QR2",
                    "isQr2": true
                }
            ],
            "FORM": [
                {
                    "name": "FILIAL",
                    "label": "Unidade",
                    "component": "ZdSelect",
                    "dataText": "NMFILIAL",
                    "dataValue": "CDFILIAL",
                    "isVisible": true,
                    "datasource": {
                        "route": "/filioper",
                        "type": "tek-rest",
                        "lazyLoad": false
                    },
                    "validations": {
						"required": {}
					},
					"grid": {
                        "cols": 12
                    }
                }
            ]
        }
    }
}

reportMoreColumns

Nota

  • Para verificar as definições dos demais campos que aparecem na imagem, verifique o modelo completo aqui (opens new window).
  • É recomendável que o campo relacionado ao tipo do relatório (PDF, XLS e afins) seja como o Abrir Como da imagem acima.
# Botões adicionais

Geralmente, não será preciso criar novos botões na tela. É recomendável que o layout seja o mesmo para facilitar o uso dos clientes. Mas, em casos específicos, será possível configurar a propriedade RIGHT_SLOT para botões ao lado de Gerar Relatório e LEFT_SLOT para botões ao lado de Upload Relatório Customizado. Exemplo de configuração abaixo:

{
    "name": "example-report",
    "component": "ZdFramePage",
    "path": "",
    "type": "post",
    "params": {
        "jsonPath": "templates/form-report",
        "override": {
            "TITLE": "Exemplo de Formulário de Relatório",
            "WINDOW_NAME": "GER_123456",
            "RIGHT_SLOT": [
                {
                    "name": "example-right-action",
                    "component": "ZdButton",
                    "label": "Exemplo de Botão Adicional Direta",
                    "events": {
                        "click": "{{ReportController.exampleRightButton}}"
                    }
                }
            ],
            "LEFT_SLOT": [
                {
                    "name": "example-left-action",
                    "component": "ZdButton",
                    "label": "Exemplo de Botão Adicional Esquerda",
                    "events": {
                        "click": "{{ReportController.exampleLeftButton}}"
                    }
                }
            ],
        }
    }
}

moreButtons

Atenção!

Não dificulte a vida dos usuários alterando o layout padrão! Apenas adicione botões extras se forem realmente necessários!

# Eventos da tela e métodos dos botões

  • É necessário configurar os eventos EVENT_ON_CREATED e EVENT_ON_BEFORE_DESTROY para armazenar e remover, respectivamente, na memória o modal para upload do arquivo customizado.
  • Métodos a configurar:
    • METHOD_CHANGE_REPORT: Realizar os controles ao alterar o valor do campo Relatório. É aqui onde serão feitos os tratamentos para alteração de visibilidade de campos, por exemplo. Utilize a função TekLibReport.validateReportOrParent para validar se é o relatório fixo selecionado ou algum customizado criado a partir dele, para fins de realizar os mesmo tratamentos visuais na tela.
    • METHOD_GENERATE_REPORT (Botão Gerar Relatório): Realizar os controles para geração do relatório. É aqui onde serão feitos os tratamentos na row para envio dos dados para o backend e chamar função da LIB para gerar e abrir em nova aba.
    • METHOD_MOUNTED_REPORT: Carregar os relatórios customizados e adicioná-los juntamente com os demais fixo. Caso não possua nada específico, basta apenas chamar a função da LIB.
    • METHOD_DOWNLOAD_REPORT (Botão Baixar Arquivo do Relatório): Baixar o arquivo .rptdesign do relatório selecionado. Caso não possua nada específico, basta apenas chamar a função da LIB.
    • METHOD_DELETE_REPORT (Botão Excluir Relatório Customizado): Excluir o arquivo .rptdesign do relatório selecionado. Apenas relatórios customizados poderão ser excluídos. Caso não possua nada específico, basta apenas chamar a função da LIB.
    • METHOD_OPEN_UPLOAD_MODAL (Botão Upload Relatório Customizado): Abrir a popup para upload do arquivo .rptdesign customizado. Caso não possua nada específico, basta apenas chamar a função da LIB. Necessário enviar a instância criada do modal como parâmetro.
{
    "name": "example-report",
    "component": "ZdFramePage",
    "path": "",
    "type": "post",
    "params": {
        "jsonPath": "templates/form-report",
        "override": {
            "TITLE": "Exemplo de Formulário de Relatório",
            "WINDOW_NAME": "GER_123456",
            "REPORT_DEFAULT_VALUE": "GER80300",
            "EVENT_ON_BEFORE_DESTROY": "{{ReportController.destroyModal}}",
            "EVENT_ON_CREATED": "{{ReportController.createModal}}",
            "METHOD_CHANGE_REPORT": "{{ReportController.changeReport}}",
            "METHOD_MOUNTED_REPORT": "{{ReportController.onMountedReportSelect}}",
            "METHOD_DOWNLOAD_REPORT": "{{ReportController.onClickDownloadReport}}",
            "METHOD_DELETE_REPORT": "{{ReportController.onClickDeleteReport}}",
            "METHOD_GENERATE_REPORT": "{{ReportController.generateReport}}",
            "METHOD_OPEN_UPLOAD_MODAL": "{{ReportController.openUploadModal}}"
        }
    }
}
# Métodos do modal upload

  • METHOD_CHANGE_REPORT_MODAL: Realizar os controles ao alterar o valor do campo Relatório na popup de upload. Caso não possua nada específico, basta apenas chamar a função da LIB.
  • METHOD_CLOSE_UPLOAD_MODAL: Fechar a popup de upload.
  • METHOD_UPLOAD_MODAL (Botão Upload): Realizar o upload do arquivo .rptdesign do relatório customizado. Caso não possua nada específico, basta apenas chamar a função da LIB. Necessário enviar a instância criada do modal como parâmetro.

Exemplo de JSON do modal e da tela renderizada:

{
    "name": "modal-report",
    "children": [
        {
            "name": "modal-report-template",
            "component": "ZdFrame",
            "type": "post",
            "params": {
                "jsonPath": "templates/modal-report",
                "override": {
                    "NAME": "modal-report",
                    "TITLE": "Upload do Relatório Customizado",
                    "METHOD_CHANGE_REPORT_MODAL": "{{ReportController.changeReportModal}}",
                    "METHOD_CLOSE_UPLOAD_MODAL": "{{ReportController.closeModal}}",
                    "METHOD_UPLOAD_MODAL": "{{ReportController.uploadModal}}"
                }
            }
        }
    ]
}

modalUpload

Exemplo básico das funções do controller TypeScript:

    import { Modal, ModalService } from '@zeedhi/common';
    import { TekLibReport } from '@zeedhi/tek-lib';

    private modal!: Modal;

    public async createModal() {
        this.modal = await ModalService.createFromJsonPost('', { jsonPath: 'example/modal-report' });
    }

    public destroyModal() {
        this.modal?.destroy();
    }

    public closeModal() {
        this.modal.hide();
    }

    public async openUploadModal() {
        TekLibReport.setModalForm(this.modal);
    }

    public async uploadModal() {
        TekLibReport.uploadModal(this.modal);
    }

    public async onMountedReportSelect() {
        TekLibReport.loadCustomReports();
    }

    public changeReportModal() {
        TekLibReport.changeReportModal();
    }

    public onClickDownloadReport() {
        TekLibReport.downloadReport();
    }

    public onClickDeleteReport() {
        TekLibReport.deleteReport();
    }
# Exemplos completos para consulta

# Criando a nova tela (Backend)


Esse passo é quase idêntico ao Zeedhi Angular, visto que o backend é quase o mesmo. Será necessário apenas pequenos passos para utilizar os relatórios customizados.

# Configurações XML

Registre os serviços do BIRT em algum xml. É recomendável criar um arquivo report.xml.template, como o exemplo abaixo, e gerar o arquivo oficial a partir do Grunt.

<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="BirtStrategy" class="Zeedhi\Framework\Report\Strategy\BirtStrategy">
            <argument><>BIRT_LOGO_PATH<></argument>
            <argument><>BIRT_REPORTS_PATH<></argument>
            <argument><>BIRT_CONF_PATH<></argument>
            <argument><>BIRT_URL<></argument>
            <argument><>BIRT_TYPE<></argument>
            <argument><>BIRT_FORMAT<></argument>
            <argument><>BIRT_VIEW_MODE<></argument>
        </service>

        <service id="reportService" class="Zeedhi\Framework\Report\ReportService">
            <argument type="collection">
                <argument type="service" id="BirtStrategy"/>
            </argument>
        </service>

        <service id="reportController" class="Zeedhi\Framework\Controller\Report" abstract="true">
            <argument type="service" id="reportService"/>
        </service>
    </services>
</container>

Atenção!

  • Caso crie um novo arquivo xml, lembre-se de carregá-lo no bootstrap.php!

Nota

  • Caso possua relatórios QR2, configure sua rotinas também.
# Rota

Configure em seu arquivo de rotas do backend a rota passada para a função TekLibReport.openReport em seu frontend. A propriedade methods deve ser POST e a requestType deve ser Row, como no exemplo abaixo:

{
    "uri": "/generateReport",
    "controller": "localReportController",
    "controllerMethod": "createRemoteReport",
    "methods": ["POST"],
    "requestType": "Row"
}
# Controller

  • Crie sua classe controller extendendo o \Zeedhi\Framework\Controller\Report do framework;
  • Faça o mapeamento do strategy e do parameterMapping de cada relatório na variável $reportMapping;
  • Crie a função beforeProcessReport para tratar os dados de sua row. Aqui entra a diferença com a rotina anterior feita para o Zeedhi Angular:
    • Crie a função setCustomReportProperties, como no exemplo abaixo, para realizar os tratamentos referentes aos relatórios customizados. Utilize as funções da LIB para realizar os tratamentos necessários.
<?php

namespace Controllers;

use Util\InstanceProvider;
use Zeedhi\Framework\DTO;
use Zeedhi\Framework\Report\ReportService;
use Zeedhi\Framework\Report\Strategy\BirtStrategy;
use Teknisa\Libs\Util\Environment;
use Teknisa\Libs\Service\Api as LibServiceApi;

class Report extends \Zeedhi\Framework\Controller\Report
{
    /** @var ReportService $reportService */
    protected $reportService;
    /** @var BirtStrategy $birtStrategy */
    protected $birtStrategy;
    /** @var Environment $environment */
    protected $environment;

    protected $reportMapping = array (
        'GER80300' => array (
            'strategy' => 'BIRT',
            'parameterMapping' => array (
                "FILIAL"  => "P_CDFILIAL",
                "DATAINI" => "P_DATAINI",
                "DATAFIM" => "P_DATAFIN",
                "NRORG"   => "P_NRORG"
            )
        ),
    );

    public function __construct(ReportService $reportService, BirtStrategy $birtStrategy) {
        $this->reportService = $reportService;
        $this->birtStrategy = $birtStrategy;
        $this->environment = InstanceProvider::getEnvironment();
    }

    public function beforeProcessReport(DTO\Request\Row $request, DTO\Response $response) {
        $row = $request->getRow();
        
        $this->setCustomReportProperties($row);

        // Handle the row values here!
        $row['NRORG'] = $this->environment->getNrorg();

        $this->birtStrategy->setReportFormat($row["FORMAT"]);
        $this->birtStrategy->setReportLocale($row["LOCALE"]);
        $this->birtStrategy->setReportTimezone($row["TIMEZONE"]);
    }

    protected function setCustomReportProperties($row) {
        if(!empty($row['isCustom'])) {
            LibServiceApi::copyConnectionJs();
            $this->birtStrategy->setReportPath(LibServiceApi::getCustomReportsPath());
            $this->setParentReport($row['parentReport']);
        }
    }
}
# Exemplos completos para consulta