# 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]
- O arquivo fonte do relatório será criado na pasta
- 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:
# 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 elabel
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!
# 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
}
}
]
}
}
}
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}}"
}
}
],
}
}
}
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
eEVENT_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}}"
}
}
}
]
}
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
- Metadata da tela (opens new window);
- Metadata do modal (opens new window);
- Controller Type Script (opens new window).
# 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 doparameterMapping
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.
- Crie a função
<?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']);
}
}
}