|
Inicio → WINDEV 25 → Manual Completo: Relatórios Programados no WX (Windev/Webdev/Windev Mobile) |
Manual Completo: Relatórios Programados no WX (Windev/Webdev/Windev Mobile) |
Iniciado por Boller, 02,jul. 2025 15:07 - 8 respuestas |
| |
| | | |
|
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:07 |
# Manual Completo: Relatórios Programados no WX (Windev/Webdev/Windev Mobile)
## Introdução
Este manual aborda a técnica de criação de relatórios totalmente programados usando WLanguage, onde o preenchimento dos dados é controlado inteiramente via código, proporcionando máxima flexibilidade e controle sobre a geração de relatórios.
## 1. Configuração Inicial do Relatório
### 1.1 Criação do Relatório
1. Crie um novo relatório no editor 1. **IMPORTANTE**: Configure a fonte de dados como “**Preenchimento por programação**” 1. Adicione os controles necessários (campos de texto, imagens, etc.) 1. Nomeie os controles de forma clara (ex: STC_NOME, STC_VALOR, etc.)
### 1.2 Estrutura Base do Código
```wlanguage // Variáveis globais dsMinhaConsulta is Data Source sSQL is string bPrimeiroRegistro is boolean = True
// Configuração da consulta SQL sSQL = [ SELECT campo1, campo2, campo3 FROM minha_tabela WHERE condicao = 'valor' ORDER BY campo1 ] ```
## 2. Implementação dos Blocos de Código
### 2.1 Bloco BEFORE PRINTING REPORT
```wlanguage // Executar a consulta SQL IF NOT HExecuteSQLQuery(dsMinhaConsulta, MyConnection, hQueryWithoutCorrection, sSQL) THEN Info(HErrorInfo(hErrMessage)) gsERRO is string = HErrorInfo(hErrFullDetails) RETURN False ELSE HReadFirst(dsMinhaConsulta) END ```
### 2.2 Bloco BEFORE PRINTING BODY (Mais Importante)
```wlanguage // Verificar se há dados para processar IF HOut(dsMinhaConsulta) THEN RESULT NOT_HOut(dsMinhaConsulta) END
// Preencher os campos do relatório com dados do Data Source STC_CAMPO1 = dsMinhaConsulta.campo1 STC_CAMPO2 = dsMinhaConsulta.campo2 STC_CAMPO3 = dsMinhaConsulta.campo3
// Tratamento de valores nulos IF dsMinhaConsulta.valor = Null THEN STC_VALOR = "0,00" ELSE STC_VALOR = NumToString(dsMinhaConsulta.valor, "99999.99") END
// Cálculos e formatações específicas IF dsMinhaConsulta.tipo = "ENTRADA" THEN STC_TIPO.Color = DarkGreen ELSE STC_TIPO.Color = DarkRed END ```
### 2.3 Bloco AFTER PRINTING BODY
```wlanguage // Avançar para o próximo registro HReadNext(dsMinhaConsulta) ```
### 2.4 Bloco AFTER PRINTING REPORT
```wlanguage // Liberar a memória da consulta HFreeQuery(dsMinhaConsulta) ```
## 3. Exemplo Completo: Relatório de Movimentação de Estoque
### 3.1 Código Completo do Relatório
## 4. Técnicas Avançadas
### 4.1 Tratamento de Erros
```wlanguage // Verificação de erros na execução da consulta IF NOT HExecuteSQLQuery(dsConsulta, MinhaConexao, hQueryWithoutCorrection, sSQL) THEN sErro is string = HErrorInfo(hErrFullDetails) // Log do erro Trace("Erro SQL: " + sErro) // Notificar usuário Error("Erro ao gerar relatório. Contate o suporte.") RETURN False END ```
### 4.2 Otimização de Performance
```wlanguage // Usar índices nas consultas sSQL = [ SELECT /*+ INDEX(tabela, indice_data) */ campos FROM tabela WHERE data_campo BETWEEN %1 AND %2 ]
// Limitar resultados se necessário sSQL += " LIMIT 1000" ```
### 4.3 Formatação Condicional Avançada
```wlanguage // No BEFORE PRINTING BODY SWITCH dsConsulta.status CASE "ATIVO" STC_STATUS.BackgroundColor = LightGreen STC_STATUS.Font.Bold = True CASE "INATIVO" STC_STATUS.BackgroundColor = LightRed STC_STATUS.Font.Strikethrough = True CASE "PENDENTE" STC_STATUS.BackgroundColor = LightYellow STC_STATUS.Font.Italic = True END ```
## 5. Boas Práticas
### 5.1 Nomenclatura
- Use prefixos claros: `STC_` para campos de texto, `IMG_` para imagens - Nomeie Data Sources com prefixo `ds`: `dsRelatorioVendas` - Use nomes descritivos: `dsMovimentacaoEstoque` ao invés de `ds1`
### 5.2 Gerenciamento de Memória
```wlanguage // SEMPRE usar HFreeQuery ao final HFreeQuery(dsConsulta)
// Para múltiplas consultas HFreeQuery(dsConsulta1) HFreeQuery(dsConsulta2) HFreeQuery(dsConsulta3) ```
### 5.3 Validações
```wlanguage // Validar dados antes de processar IF HOut(dsConsulta) THEN Info("Nenhum registro encontrado.") RETURN False END
// Validar campos críticos IF dsConsulta.valor = Null OR dsConsulta.valor < 0 THEN STC_VALOR = "R$ 0,00" STC_VALOR.Color = Red END ```
## 6. Troubleshooting
### 6.1 Problemas Comuns
**Problema**: Relatório não exibe dados **Solução**: Verificar se `HReadFirst()` foi chamado no `BEFORE PRINTING REPORT`
**Problema**: Erro de memória **Solução**: Sempre usar `HFreeQuery()` no `AFTER PRINTING REPORT`
**Problema**: Campos não preenchidos **Solução**: Verificar se o preenchimento está no `BEFORE PRINTING BODY`
**Problema**: Loop infinito **Solução**: Certificar que `HReadNext()` está no `AFTER PRINTING BODY`
### 6.2 Debug
```wlanguage // Adicionar traces para debug Trace("Registro atual: " + dsConsulta.id) Trace("Valor: " + dsConsulta.valor)
// Verificar posição do cursor Trace("HOut: " + HOut(dsConsulta)) Trace("Número do registro: " + HRecNum(dsConsulta)) ```
## 7. Conclusão
A técnica de relatórios programados oferece controle total sobre a apresentação dos dados, permitindo:
- **Flexibilidade máxima** na formatação - **Cálculos complexos** em tempo real - **Formatação condicional** avançada - **Controle total** sobre a fonte de dados - **Performance otimizada** com consultas específicas
**Lembre-se sempre**: Configure como “Preenchimento por programação”, use `HExecuteSQLQuery` no início, preencha os campos no `BEFORE PRINTING BODY`, avance com `HReadNext` no `AFTER PRINTING BODY`, e finalize com `HFreeQuery` no `AFTER PRINTING REPORT`.
Esta técnica é essencial para relatórios corporativos complexos e deve fazer parte do arsenal de todo desenvolvedor WX.
Link exemplo https://repository.windev.com/resource.awp…
Relatórios do PEDROSAO
Help.Windev.com Para maiores detalhes
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:12 |
Aqui está um manual detalhado e aprimorado, com foco em relatórios condicionados e programados no WX (WinDev/WebDev/Windev Mobile), integrando técnicas avançadas do help.windev.com:
⸻
1. Criação Inicial do Relatório
1.1 Passo a passo no Assistente de Relatórios 1. No editor, clique em Criar → Relatório. 2. Escolha o tipo (tabela, formulário, rótulo, etc.). 3. Em “Fonte de dados”, selecione Outro (programming, …) 4. Marque “Programo a leitura dos dados” . 5. Defina parâmetros específicos ao tipo: • Tabela: número de colunas, cabeçalhos, tipos. • Formulário: imagem, informações de visualização. • Rótulo: formato e quantidade de cópias. 6. Configure o layout da página (A4, paisagem, margens). 7. Escolha um theme ou skin. 8. Dê nome/título ao .WDE e finalize. 9. Ajuste layout se colunas “vazarem” .
⸻
2. Eventos e Seqüência de Execução
Use o help.windev.com para entender a ordem dos eventos:
Evento Bloco? Objetivo principal Report Opening — HReadFirst(dto) () Block Initializing Header/Body… Inicializações gerais () Data Query / Reading Data — HOut → parar ou continuar () Before Printing Body Body Atribuir valores, formatações, HReadNext () After Printing Body Body Quebra de página, reset de variáveis () After Report — HFreeQuery para liberar memória ()
Fluxo completo resumido (): 1. Report Opening: leitura da primeira linha. 2. Reading Data: verifica HOut → RESULT True/False. 3. Before Printing Body: preenche controles e lê o próximo. 4. After Printing Body: aplica iPageEnd se ainda houver dados. 5. After Report: uso de HFreeQuery.
⸻
3. Exemplo Avançado (Movimentação Estoque)
3.1 Variáveis iniciais
dsMovStock is Data Source sSQL is string
3.2 Report Opening
sSQL = [ SELECT id, produto, tipo, quantidade, data FROM Movimentacao WHERE condicao = 'X' ORDER BY data ] IF NOT HExecuteSQLQuery(dsMovStock, HConnection, hQueryWithoutCorrection, sSQL) THEN Error("Erro SQL: "+HErrorInfo(hErrFullDetails)) RESULT False END HReadFirst(dsMovStock)
3.3 Reading Data
RESULT NOT HOut(dsMovStock)
3.4 Before Printing Body (condições e formatação)
STC_ID = dsMovStock.id STC_PROD = dsMovStock.produto STC_DATA = DateToString(dsMovStock.data, "DD/MM/YYYY")
IF dsMovStock.quantidade = Null THEN STC_QTD = "0" ELSE STC_QTD = NumToString(dsMovStock.quantidade, "###,##0") END
IF dsMovStock.tipo = "ENTRADA" THEN STC_TIPO.Color = DarkGreen ELSE STC_TIPO.Color = DarkRed END
HReadNext(dsMovStock)
3.5 After Printing Body
IF NOT HOut(dsMovStock) THEN iPageEnd() END
3.6 After Report
HFreeQuery(dsMovStock)
⸻
4. Técnicas Especiais e Recursos
4.1 Gráficos e Planilhas • Table/Spreadsheet: podem ser populados por código e ajustados em eventos “Before printing cell”  . • Gráficos: controle Chart pode ser preenchido via WLanguage automaticamente.
4.2 Formatação Condicional e controles • Use SWITCH ou IF em Before Printing Body para cores, tipos de fonte, estilos (riscar, negrito, itálico) . • Controle de blocos: visibilidade, substituições com iSubstBlock, imprimir blocos iterativos com iPrintBlock .
4.3 Funções de impressão úteis
É possível usar funções como iPageEnd, iCloseReport, iParameterPDF, iMargin, entre outras, conforme documentação .
⸻
5. Otimização e Boas Práticas • Erros SQL: log com Trace, exibir mensagens claras ao usuário. • Performance: usar índices e limitar linhas com LIMIT (). • Memória: sempre HFreeQuery ao final. • Nomenclatura: STC_, IMG_, dsNome, qryNome para clareza. • Null e validação: tratar em corpo do relatório para evitar campos em branco ou erros.
⸻
6. Exemplo Completo em WLanguage
PROCEDURE RelMovEstoque() REPORT_OPENING ... // Report Opening conforme §3.2
REPORT_READING RESULT NOT HOut(dsMovStock)
BODY_BEFORE_PRINTING ... // Corpo conforme §3.4
BODY_AFTER_PRINTING ... // Quebra de página conforme §3.5
REPORT_AFTER HFreeQuery(dsMovStock) END
⸻
✅ Resumo Final • Crie com fonte “Preenchimento por programação”  • Use HExecuteSQLQuery e HReadFirst em Report Opening • No Reading Data, use RESULT NOT HOut(...) • Em Body Before, preencha controles, formate e leia próximo • Após corpo, use iPageEnd se houver mais registros • Finalize com HFreeQuery
⸻
Este manual integra o exemplo básico inicial com as boas práticas do help.windev.com, detalhando eventos, funções avançadas e formatação condicional programada — ideal para relatórios dinâmicos e robustos no WX. Se quiser ampliar com planilhas, gráficos ou relatórios aninhados, posso detalhar em seguida!
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:17 |
Novo Exemplo: Relatório de Vendas por Cliente (WLanguage)
Objetivo
Criar um relatório que exiba as vendas de cada cliente, incluindo nome, total de vendas, data da última compra e status (ativo/inativo). O relatório terá formatação condicional (cores baseadas no status) e tratamento de valores nulos, ideal para um desenvolvedor júnior. Passo a Passo para um Desenvolvedor Júnior 1. Criando o Relatório no Editor 1 Abra o WinDev e clique em Arquivo → Novo → Relatório. 2 Escolha o tipo Tabela (para listar vendas). 3 Na tela de fonte de dados, selecione Outro (programação, …) e marque “Programo a leitura dos dados”. 4 Adicione controles no layout: ◦ STC_NOME_CLIENTE: Campo de texto para o nome do cliente. ◦ STC_TOTAL_VENDAS: Campo de texto para o total de vendas (formato monetário). ◦ STC_ULTIMA_COMPRA: Campo de texto para a data da última compra. ◦ STC_STATUS: Campo de texto para o status (ativo/inativo). 5 Configure o layout (ex.: A4, margens de 1 cm, tema moderno). 6 Salve o relatório com o nome REL_VendasPorCliente. 2. Estrutura do Banco de Dados Para este exemplo, assumimos uma tabela simples no banco de dados chamada Vendas com a seguinte estrutura: CREATE TABLE Vendas ( id_venda INT PRIMARY KEY, nome_cliente VARCHAR(100), total_vendas DECIMAL(10,2), data_ultima_compra DATE, status VARCHAR(20) -- 'ATIVO' ou 'INATIVO' ); 3. Código WLanguage Completo Abaixo está o código comentado e organizado para gerar o relatório programado. Cada bloco é explicado para facilitar a compreensão. // Procedimento principal do relatório PROCEDURE REL_VendasPorCliente()
// === Declaração de Variáveis === dsVendas is Data Source // Fonte de dados para a consulta sSQL is string // String para a query SQL bErro is boolean = False // Controle de erro
// === Bloco: REPORT_OPENING (Início do relatório) === // Configura e executa a consulta SQL sSQL = [ SELECT id_venda, nome_cliente, total_vendas, data_ultima_compra, status FROM Vendas WHERE data_ultima_compra >= %1 ORDER BY nome_cliente ]
// Substitui %1 por uma data inicial (ex.: últimos 30 dias) sSQL = StringBuild(sSQL, DateToString(DateSys() - 30, "YYYYMMDD"))
// Executa a consulta IF NOT HExecuteSQLQuery(dsVendas, hDefaultConnection, hQueryWithoutCorrection, sSQL) THEN Error("Erro ao consultar dados: " + HErrorInfo(hErrMessage)) Trace("Detalhes do erro: " + HErrorInfo(hErrFullDetails)) // Log para debug bErro = True RETURN False END
// Lê o primeiro registro HReadFirst(dsVendas)
// === Bloco: READING_DATA (Verifica se há dados) === // Retorna False se não houver registros IF HOut(dsVendas) THEN Info("Nenhum registro de vendas encontrado.") RETURN False END RESULT True
// === Bloco: BEFORE_PRINTING_BODY (Preenche os campos do relatório) === // Preenche os controles com os dados do registro atual STC_NOME_CLIENTE = dsVendas.nome_cliente
// Trata valores nulos para o total de vendas IF dsVendas.total_vendas = Null THEN STC_TOTAL_VENDAS = "R$ 0,00" ELSE STC_TOTAL_VENDAS = "R$ " + NumToString(dsVendas.total_vendas, "#,##0.00") END
// Formata a data da última compra IF dsVendas.data_ultima_compra = Null THEN STC_ULTIMA_COMPRA = "Sem compras" ELSE STC_ULTIMA_COMPRA = DateToString(dsVendas.data_ultima_compra, "DD/MM/YYYY") END
// Formatação condicional para o status SWITCH dsVendas.status CASE "ATIVO" STC_STATUS = "Ativo" STC_STATUS.BackgroundColor = LightGreen STC_STATUS.Font.Bold = True CASE "INATIVO" STC_STATUS = "Inativo" STC_STATUS.BackgroundColor = LightRed STC_STATUS.Font.Strikethrough = True OTHER CASE STC_STATUS = "Desconhecido" STC_STATUS.BackgroundColor = LightYellow END
// Avança para o próximo registro HReadNext(dsVendas)
// === Bloco: AFTER_PRINTING_BODY (Controle de página) === // Se ainda houver registros, continua imprimindo IF NOT HOut(dsVendas) THEN iPageEnd() // Força a impressão do registro atual e continua END
// === Bloco: AFTER_REPORT (Finalização) === // Libera a memória da consulta HFreeQuery(dsVendas)
// Se houve erro, não imprime o relatório IF bErro THEN iCloseReport() END 4. Explicação dos Blocos • REPORT_OPENING: Configura a consulta SQL para buscar vendas dos últimos 30 dias, executa-a e lê o primeiro registro. Inclui tratamento de erro com mensagens claras. • READING_DATA: Verifica se há registros. Se não houver, exibe uma mensagem amigável e interrompe o relatório. • BEFORE_PRINTING_BODY: Preenche os controles do relatório com os dados do registro atual, trata valores nulos e aplica formatação condicional (cores e estilos de fonte). • AFTER_PRINTING_BODY: Avança para o próximo registro e usa iPageEnd() para garantir que o relatório continue imprimindo enquanto houver dados. • AFTER_REPORT: Libera a memória com HFreeQuery e fecha o relatório se houve erro. 5. Recursos Inovadores Adicionados 1 Filtro Dinâmico: A consulta usa StringBuild para incluir um parâmetro de data (últimos 30 dias), tornando o relatório reutilizável. 2 Formatação Condicional Moderna: Usa SWITCH para aplicar cores e estilos de fonte (negrito, riscado) com base no status do cliente. 3 Mensagens Amigáveis: Inclui mensagens de erro claras para o usuário e logs detalhados com Trace para depuração. 4 Tratamento de Nulos: Garante que valores nulos sejam exibidos de forma legível (ex.: “R$ 0,00” ou “Sem compras”). 5 Controle de Erro Global: A variável bErro evita que o relatório seja impresso se ocorrer um erro na consulta. 6. Boas Práticas Aplicadas • Nomenclatura Clara: Controles com prefixo STC_ e fonte de dados com ds. • Gerenciamento de Memória: Uso de HFreeQuery para liberar recursos. • Validações: Tratamento de nulos e verificação de HOut para evitar erros. • Performance: Consulta com ORDER BY para organizar os dados e filtro de data para limitar resultados. • Debug: Uso de Trace para facilitar a identificação de problemas. 7. Como Testar 1 Crie a tabela Vendas no seu banco de dados e insira alguns registros de teste. 2 Crie o relatório no editor do WinDev conforme descrito. 3 Copie e cole o código acima nos eventos correspondentes do relatório (clique com o botão direito no relatório → “Código”). 4 Execute o relatório com iPrintReport(REL_VendasPorCliente) em uma janela ou procedimento. 5 Verifique o resultado em PDF ou na tela. Use a função Trace para depurar se necessário. 8. Dicas para o Desenvolvedor Júnior • Teste Incrementalmente: Comece com uma consulta simples e adicione formatações aos poucos. • Use o Help: Consulte o help.windev.com para funções como iPrintBlock ou iParameterPDF se quiser personalizar a saída. • Depure com Trace: Adicione Trace em pontos críticos para entender o fluxo do relatório. • Valide o Layout: Certifique-se de que os controles não “vazam” para fora da página no editor. • Experimente: Adicione gráficos ou planilhas (ex.: controle Chart) para tornar o relatório mais visual. 9. Exemplo de Dados de Teste Insira no banco para testar: INSERT INTO Vendas (id_venda, nome_cliente, total_vendas, data_ultima_compra, status) VALUES (1, 'João Silva', 1500.50, '2025-06-15', 'ATIVO'), (2, 'Maria Oliveira', NULL, NULL, 'INATIVO'), (3, 'Pedro Santos', 750.25, '2025-07-01', 'ATIVO'); 10. Resultado Esperado O relatório exibirá uma tabela com: • Nome do cliente (ex.: “João Silva”). • Total de vendas formatado (ex.: “R$ 1.500,50” ou “R$ 0,00” para nulos). • Data da última compra (ex.: “15/06/2025” ou “Sem compras”). • Status com cores (verde para “Ativo”, vermelho para “Inativo”, amarelo para “Desconhecido”).
Por que este Exemplo é Inovador? 1 Simplicidade para Juniores: O código é comentado e estruturado em etapas claras, com explicações para cada bloco. 2 Funcionalidades Modernas: Inclui formatação condicional, tratamento de nulos e filtros dinâmicos, que são comuns em relatórios corporativos. 3 Robusto e Reutilizável: Pode ser facilmente adaptado para outros tipos de relatórios (ex.: estoque, financeiro) alterando a consulta SQL e os controles. 4 Foco em Boas Práticas: Segue as recomendações do manual original, como nomenclatura, gerenciamento de memória e tratamento de erros.
Ok
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:24 |
// ============================================== // RELATÓRIO: RPT_DASHBOARD_EXECUTIVO // TIPO: Dashboard com múltiplos gráficos programados // CONTROLES: CHART_VENDAS_MES, CHART_PRODUTOS_TOP, CHART_REGIOES, TABLE_RESUMO // ==============================================
// ============================================== // DECLARAÇÕES GLOBAIS // ============================================== dsVendasMes is Data Source dsProdutosTop is Data Source dsRegioes is Data Source dsResumoGeral is Data Source
nTotalVendas is numeric = 0 nMetaMensal is numeric = 500000 arrCoresGrafico is array of 12 strings = [”#FF6B6B”, “#4ECDC4”, “#45B7D1”, “#96CEB4”, “#FFEAA7”, “#DDA0DD”, “#98D8C8”, “#F06292”, “#AED581”, “#FFB74D”, “#A1887F”, “#90A4AE”]
// ============================================== // BEFORE PRINTING REPORT // ==============================================
// ===== CONSULTA 1: Vendas por Mês ===== sSQL_VendasMes is string = [ SELECT EXTRACT(MONTH FROM data_venda) as mes, EXTRACT(YEAR FROM data_venda) as ano, SUM(valor_total) as total_vendas, COUNT(*) as qtd_vendas, AVG(valor_total) as ticket_medio FROM vendas WHERE data_venda >= CURRENT_DATE - INTERVAL ‘12 months’ AND status_venda = ‘FINALIZADA’ GROUP BY EXTRACT(YEAR FROM data_venda), EXTRACT(MONTH FROM data_venda) ORDER BY ano DESC, mes DESC LIMIT 12 ]
IF NOT HExecuteSQLQuery(dsVendasMes, MyConnection, hQueryWithoutCorrection, sSQL_VendasMes) THEN Error(“Erro ao carregar dados de vendas por mês: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 2: Top 10 Produtos ===== sSQL_ProdutosTop is string = [ SELECT p.nome_produto, p.codigo_produto, SUM(iv.quantidade) as qtd_vendida, SUM(iv.valor_total) as valor_total, AVG(iv.valor_unitario) as preco_medio, COUNT(DISTINCT v.id_venda) as num_vendas FROM produtos p INNER JOIN itens_venda iv ON p.id_produto = iv.id_produto INNER JOIN vendas v ON iv.id_venda = v.id_venda WHERE v.data_venda >= CURRENT_DATE - INTERVAL ‘3 months’ AND v.status_venda = ‘FINALIZADA’ GROUP BY p.id_produto, p.nome_produto, p.codigo_produto ORDER BY valor_total DESC LIMIT 10 ]
IF NOT HExecuteSQLQuery(dsProdutosTop, MyConnection, hQueryWithoutCorrection, sSQL_ProdutosTop) THEN Error(“Erro ao carregar top produtos: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 3: Vendas por Região ===== sSQL_Regioes is string = [ SELECT r.nome_regiao, r.sigla_regiao, COUNT(v.id_venda) as qtd_vendas, SUM(v.valor_total) as total_vendas, AVG(v.valor_total) as ticket_medio, COUNT(DISTINCT c.id_cliente) as clientes_ativos FROM regioes r INNER JOIN clientes c ON r.id_regiao = c.id_regiao INNER JOIN vendas v ON c.id_cliente = v.id_cliente WHERE v.data_venda >= CURRENT_DATE - INTERVAL ‘1 month’ AND v.status_venda = ‘FINALIZADA’ GROUP BY r.id_regiao, r.nome_regiao, r.sigla_regiao ORDER BY total_vendas DESC ]
IF NOT HExecuteSQLQuery(dsRegioes, MyConnection, hQueryWithoutCorrection, sSQL_Regioes) THEN Error(“Erro ao carregar dados por região: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 4: Resumo Geral ===== sSQL_Resumo is string = [ SELECT ‘VENDAS_HOJE’ as indicador, COALESCE(SUM(valor_total), 0) as valor, COUNT(*) as quantidade FROM vendas WHERE DATE(data_venda) = CURRENT_DATE AND status_venda = ‘FINALIZADA’ UNION ALL SELECT ‘VENDAS_MES’ as indicador, COALESCE(SUM(valor_total), 0) as valor, COUNT(*) as quantidade FROM vendas WHERE EXTRACT(MONTH FROM data_venda) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM data_venda) = EXTRACT(YEAR FROM CURRENT_DATE) AND status_venda = ‘FINALIZADA’ UNION ALL SELECT ‘CLIENTES_NOVOS’ as indicador, COUNT(*) as valor, 0 as quantidade FROM clientes WHERE DATE(data_cadastro) >= CURRENT_DATE - INTERVAL ‘30 days’ UNION ALL SELECT ‘PRODUTOS_ESTOQUE_BAIXO’ as indicador, COUNT(*) as valor, 0 as quantidade FROM produtos WHERE estoque_atual <= estoque_minimo ]
IF NOT HExecuteSQLQuery(dsResumoGeral, MyConnection, hQueryWithoutCorrection, sSQL_Resumo) THEN Error(“Erro ao carregar resumo geral: “ + HErrorInfo(hErrMessage)) RETURN False END
// ============================================== // BEFORE PRINTING HEADER // ==============================================
// Configurar título dinâmico com período STC_TITULO = “Dashboard Executivo - “ + DateToString(DateSys(), “MMMM YYYY”) STC_PERIODO = “Período de Análise: “ + DateToString(DateSys() - 90, “DD/MM/YYYY”) + “ a “ + DateToString(DateSys(), “DD/MM/YYYY”) STC_DATA_GERACAO = “Gerado em: “ + DateTimeToString(Now(), “DD/MM/YYYY às HH:MM”)
// ============================================== // BEFORE PRINTING BODY // ==============================================
// ===== CONFIGURAR GRÁFICO DE VENDAS POR MÊS ===== // Limpar gráfico grDeleteAll(CHART_VENDAS_MES)
// Configurar propriedades do gráfico grType(CHART_VENDAS_MES, grColumn3D) grTitle(CHART_VENDAS_MES, “Evolução de Vendas - Últimos 12 Meses”) grAxisTitle(CHART_VENDAS_MES, grXCoordinate, “Meses”) grAxisTitle(CHART_VENDAS_MES, grYCoordinate, “Valor em R$”) grColor(CHART_VENDAS_MES, 1, RGB(70, 130, 180)) grGradient(CHART_VENDAS_MES, 1, RGB(135, 206, 250), RGB(70, 130, 180), 45)
// Adicionar dados do gráfico HReadFirst(dsVendasMes) nContadorMes is int = 1 WHILE NOT HOut(dsVendasMes) sMesAno is string = Right(“0” + dsVendasMes.mes, 2) + “/” + Right(dsVendasMes.ano, 2) grAddData(CHART_VENDAS_MES, 1, dsVendasMes.total_vendas) grCategoryLabel(CHART_VENDAS_MES, nContadorMes, sMesAno)
``` // Adicionar meta como linha grAddData(CHART_VENDAS_MES, 2, nMetaMensal)
nTotalVendas += dsVendasMes.total_vendas nContadorMes++ HReadNext(dsVendasMes) ```
END
// Configurar segunda série (meta) grSeriesLabel(CHART_VENDAS_MES, 1, “Vendas Realizadas”) grSeriesLabel(CHART_VENDAS_MES, 2, “Meta Mensal”) grSeriesType(CHART_VENDAS_MES, 2, grLine) grColor(CHART_VENDAS_MES, 2, RGB(255, 99, 71))
// ===== CONFIGURAR GRÁFICO DE TOP PRODUTOS ===== grDeleteAll(CHART_PRODUTOS_TOP) grType(CHART_PRODUTOS_TOP, grPie) grTitle(CHART_PRODUTOS_TOP, “Top 10 Produtos por Faturamento”) grLegend(CHART_PRODUTOS_TOP, grAtBottom)
HReadFirst(dsProdutosTop) nContadorProd is int = 1 WHILE NOT HOut(dsProdutosTop) AND nContadorProd <= 10 sNomeProduto is string = Left(dsProdutosTop.nome_produto, 20) nValorProduto is numeric = dsProdutosTop.valor_total
``` grAddData(CHART_PRODUTOS_TOP, 1, nValorProduto) grCategoryLabel(CHART_PRODUTOS_TOP, nContadorProd, sNomeProduto)
// Usar cores do array IF nContadorProd <= Dimension(arrCoresGrafico) THEN grColor(CHART_PRODUTOS_TOP, nContadorProd, HTML(arrCoresGrafico[nContadorProd])) END
nContadorProd++ HReadNext(dsProdutosTop) ```
END
// ===== CONFIGURAR GRÁFICO DE REGIÕES ===== grDeleteAll(CHART_REGIOES) grType(CHART_REGIOES, grBarHorizontal) grTitle(CHART_REGIOES, “Vendas por Região - Último Mês”) grAxisTitle(CHART_REGIOES, grXCoordinate, “Valor em R$”) grAxisTitle(CHART_REGIOES, grYCoordinate, “Regiões”)
HReadFirst(dsRegioes) nContadorReg is int = 1 WHILE NOT HOut(dsRegioes) grAddData(CHART_REGIOES, 1, dsRegioes.total_vendas) grCategoryLabel(CHART_REGIOES, nContadorReg, dsRegioes.nome_regiao)
``` // Gradiente baseado no valor IF dsRegioes.total_vendas > 100000 THEN grColor(CHART_REGIOES, nContadorReg, RGB(34, 139, 34)) // Verde forte ELSE IF dsRegioes.total_vendas > 50000 THEN grColor(CHART_REGIOES, nContadorReg, RGB(255, 165, 0)) // Laranja ELSE grColor(CHART_REGIOES, nContadorReg, RGB(220, 20, 60)) // Vermelho END
nContadorReg++ HReadNext(dsRegioes) ```
END
// ===== PREENCHER TABELA DE RESUMO ===== TableDeleteAll(TABLE_RESUMO)
HReadFirst(dsResumoGeral) WHILE NOT HOut(dsResumoGeral) nLinha is int = TableAddLine(TABLE_RESUMO)
``` SWITCH dsResumoGeral.indicador CASE "VENDAS_HOJE" TABLE_RESUMO[nLinha][1] = "Vendas Hoje" TABLE_RESUMO[nLinha][2] = NumToString(dsResumoGeral.valor, "R$ 999,999.99") TABLE_RESUMO[nLinha][3] = dsResumoGeral.quantidade + " vendas" // Colorir baseado na performance IF dsResumoGeral.valor > 10000 THEN TABLE_RESUMO[nLinha].BackgroundColor = LightGreen ELSE IF dsResumoGeral.valor > 5000 THEN TABLE_RESUMO[nLinha].BackgroundColor = LightYellow ELSE TABLE_RESUMO[nLinha].BackgroundColor = LightRed END CASE "VENDAS_MES" TABLE_RESUMO[nLinha][1] = "Vendas do Mês" TABLE_RESUMO[nLinha][2] = NumToString(dsResumoGeral.valor, "R$ 999,999.99") TABLE_RESUMO[nLinha][3] = dsResumoGeral.quantidade + " vendas" // Comparar com meta nPercentualMeta is numeric = (dsResumoGeral.valor / nMetaMensal) * 100 TABLE_RESUMO[nLinha][4] = NumToString(nPercentualMeta, "999.9") + "% da meta" CASE "CLIENTES_NOVOS" TABLE_RESUMO[nLinha][1] = "Novos Clientes (30d)" TABLE_RESUMO[nLinha][2] = dsResumoGeral.valor + " clientes" TABLE_RESUMO[nLinha][3] = "Crescimento da base" CASE "PRODUTOS_ESTOQUE_BAIXO" TABLE_RESUMO[nLinha][1] = "Estoque Baixo" TABLE_RESUMO[nLinha][2] = dsResumoGeral.valor + " produtos" TABLE_RESUMO[nLinha][3] = "Requer atenção" IF dsResumoGeral.valor > 50 THEN TABLE_RESUMO[nLinha].Color = Red TABLE_RESUMO[nLinha].Font.Bold = True END END
HReadNext(dsResumoGeral) ```
END
// ===== INDICADORES VISUAIS (CARDS) ===== // Card de Performance Geral nPerformanceGeral is numeric = (nTotalVendas / (nMetaMensal * 12)) * 100
IF nPerformanceGeral >= 100 THEN STC_PERFORMANCE.BackgroundColor = RGB(76, 175, 80) // Verde STC_PERFORMANCE.Color = White STC_PERFORMANCE_ICON = “✓” ELSE IF nPerformanceGeral >= 80 THEN STC_PERFORMANCE.BackgroundColor = RGB(255, 193, 7) // Amarelo STC_PERFORMANCE.Color = Black STC_PERFORMANCE_ICON = “⚠” ELSE STC_PERFORMANCE.BackgroundColor = RGB(244, 67, 54) // Vermelho STC_PERFORMANCE.Color = White STC_PERFORMANCE_ICON = “✗” END
STC_PERFORMANCE = NumToString(nPerformanceGeral, “999.9”) + “%”
// Redimensionar gráficos dinamicamente grSaveBMP(CHART_VENDAS_MES, CompleteDir(fDataDir()) + “temp_vendas_mes.bmp”) grSaveBMP(CHART_PRODUTOS_TOP, CompleteDir(fDataDir()) + “temp_produtos_top.bmp”) grSaveBMP(CHART_REGIOES, CompleteDir(fDataDir()) + “temp_regioes.bmp”)
// ============================================== // AFTER PRINTING BODY // ============================================== // Não há loop de registros neste relatório, é um relatório único
// ============================================== // BEFORE PRINTING FOOTER // ==============================================
// Configurar informações de rodapé STC_TOTAL_VENDAS_FOOTER = “Total Geral: “ + NumToString(nTotalVendas, “R$ 999,999,999.99”) STC_META_FOOTER = “Meta Anual: “ + NumToString(nMetaMensal * 12, “R$ 999,999,999.99”) STC_USUARIO_FOOTER = “Gerado por: “ + gsUsuarioLogado STC_SISTEMA_FOOTER = “Sistema: “ + gsNomeSistema + “ v” + gsVersaoSistema
// ============================================== // AFTER PRINTING REPORT // ==============================================
// Liberar todas as consultas HFreeQuery(dsVendasMes) HFreeQuery(dsProdutosTop) HFreeQuery(dsRegioes) HFreeQuery(dsResumoGeral)
// Limpar arquivos temporários fDelete(CompleteDir(fDataDir()) + “temp_vendas_mes.bmp”) fDelete(CompleteDir(fDataDir()) + “temp_produtos_top.bmp”) fDelete(CompleteDir(fDataDir()) + “temp_regioes.bmp”)
// Resetar variáveis nTotalVendas = 0
Trace(“Dashboard executivo gerado com sucesso!”)
// ============================================== // PROCEDIMENTOS AUXILIARES // ==============================================
// Procedimento para configurar cores dinâmicas do gráfico PROCEDURE ConfigurarCoresGrafico(LOCAL nomeGrafico is string, LOCAL nNumSeries is int)
arrCoresPadrao is array of strings = [ “#FF6B6B”, “#4ECDC4”, “#45B7D1”, “#96CEB4”, “#FFEAA7”, “#DDA0DD”, “#98D8C8”, “#F06292”, “#AED581”, “#FFB74D” ]
FOR i = 1 TO nNumSeries IF i <= Dimension(arrCoresPadrao) THEN grColor(nomeGrafico, i, HTML(arrCoresPadrao[i])) END END
// Procedimento para adicionar efeitos 3D nos gráficos PROCEDURE ConfigurarEfeitos3D(LOCAL nomeGrafico is string)
grParameter(nomeGrafico, grElevation, 30) grParameter(nomeGrafico, grRotation, 45) grParameter(nomeGrafico, grPerspective, 50) grSmoothing(nomeGrafico, True)
// ============================================== // EXEMPLO DE CHAMADA // ============================================== /* // Configurar parâmetros globais gsUsuarioLogado = “João Silva” gsNomeSistema = “ERP Corporativo” gsVersaoSistema = “2.1.5”
// Configurar destino iDestination(iViewer)
// Gerar o relatório iPrintReport(RPT_DASHBOARD_EXECUTIVO) */
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:28 |
// ============================================== // RELATÓRIO: RPT_MOVIMENTACAO_ESTOQUE // FONTE DE DADOS: Preenchimento por programação // ==============================================
// ============================================== // DECLARAÇÕES GLOBAIS // ============================================== dsMovimentoEstoque is Data Source sSQL is string nTotalEntradas is numeric = 0 nTotalSaidas is numeric = 0 nTotalGeral is numeric = 0
// ============================================== // BEFORE PRINTING REPORT // ============================================== // Construir a consulta SQL sSQL = [ SELECT mov.id_movimento, mov.data_movimento, mov.tipo_movimento, mov.quantidade, mov.valor_unitario, mov.valor_total, prod.codigo_produto, prod.descricao_produto, prod.unidade_medida, est.estoque_anterior, est.estoque_atual FROM movimentos_estoque mov INNER JOIN produtos prod ON mov.id_produto = prod.id_produto INNER JOIN estoque est ON mov.id_estoque = est.id_estoque WHERE mov.data_movimento BETWEEN ‘%1’ AND ‘%2’ AND mov.ativo = 1 ORDER BY mov.data_movimento DESC, prod.descricao_produto ]
// Substituir parâmetros da consulta sSQL = StringBuild(sSQL, DateToString(gdDataInicio, “YYYY-MM-DD”), DateToString(gdDataFim, “YYYY-MM-DD”))
// Executar a consulta IF NOT HExecuteSQLQuery(dsMovimentoEstoque, MyConnection_Postgres, hQueryWithoutCorrection, sSQL) THEN Info(“Erro ao executar consulta: “ + HErrorInfo(hErrMessage)) gsERRO is string = HErrorInfo(hErrFullDetails) Error(“Detalhes do erro:”, gsERRO) RETURN False ELSE // Posicionar no primeiro registro HReadFirst(dsMovimentoEstoque)
``` // Verificar se existem dados IF HOut(dsMovimentoEstoque) THEN Info("Nenhum dado encontrado para o período selecionado.") RETURN False END ```
END
// ============================================== // BEFORE PRINTING BODY // ============================================== // Verificar se ainda há registros para processar IF HOut(dsMovimentoEstoque) THEN RESULT NOT_HOut(dsMovimentoEstoque) END
// PREENCHER OS CAMPOS DO RELATÓRIO // Dados do movimento STC_ID = dsMovimentoEstoque.id_movimento STC_DATA = DateToString(dsMovimentoEstoque.data_movimento, “DD/MM/YYYY”) STC_TIPO = Upper(dsMovimentoEstoque.tipo_movimento)
// Dados do produto STC_CODIGO = dsMovimentoEstoque.codigo_produto STC_DESCRICAO = dsMovimentoEstoque.descricao_produto STC_UNIDADE = dsMovimentoEstoque.unidade_medida
// Tratamento de valores nulos e formatação // Quantidade IF dsMovimentoEstoque.quantidade = Null OR dsMovimentoEstoque.quantidade = 0 THEN STC_QUANTIDADE = “0” ELSE STC_QUANTIDADE = NumToString(dsMovimentoEstoque.quantidade, “999999”) END
// Valor unitário IF dsMovimentoEstoque.valor_unitario = Null THEN STC_VALOR_UNITARIO = “R$ 0,00” ELSE STC_VALOR_UNITARIO = “R$ “ + NumToString(dsMovimentoEstoque.valor_unitario, “999999.99”) END
// Valor total IF dsMovimentoEstoque.valor_total = Null THEN STC_VALOR_TOTAL = “R$ 0,00” ELSE STC_VALOR_TOTAL = “R$ “ + NumToString(dsMovimentoEstoque.valor_total, “999999.99”) END
// Estoque anterior e atual STC_ESTOQUE_ANTERIOR = NumToString(dsMovimentoEstoque.estoque_anterior, “999999”) STC_ESTOQUE_ATUAL = NumToString(dsMovimentoEstoque.estoque_atual, “999999”)
// FORMATAÇÃO CONDICIONAL // Colorir conforme o tipo de movimento SWITCH dsMovimentoEstoque.tipo_movimento CASE “ENTRADA” STC_TIPO.Color = DarkGreen STC_QUANTIDADE.Color = DarkGreen STC_VALOR_TOTAL.Color = DarkGreen
``` // Acumular totais nTotalEntradas += dsMovimentoEstoque.valor_total CASE "SAIDA" STC_TIPO.Color = DarkRed STC_QUANTIDADE.Color = DarkRed STC_VALOR_TOTAL.Color = DarkRed // Acumular totais nTotalSaidas += dsMovimentoEstoque.valor_total OTHER CASE STC_TIPO.Color = Black STC_QUANTIDADE.Color = Black STC_VALOR_TOTAL.Color = Black ```
END
// Destacar produtos com estoque baixo IF dsMovimentoEstoque.estoque_atual < 10 THEN STC_ESTOQUE_ATUAL.Color = Orange STC_ESTOQUE_ATUAL.Font.Bold = True END
// Calcular giro de estoque nGiroEstoque is numeric IF dsMovimentoEstoque.estoque_anterior > 0 THEN nGiroEstoque = (dsMovimentoEstoque.quantidade / dsMovimentoEstoque.estoque_anterior) * 100 IF nGiroEstoque > 50 THEN STC_GIRO_ESTOQUE = NumToString(nGiroEstoque, “999.9”) + “%” STC_GIRO_ESTOQUE.Color = Red ELSE STC_GIRO_ESTOQUE = NumToString(nGiroEstoque, “999.9”) + “%” STC_GIRO_ESTOQUE.Color = Black END ELSE STC_GIRO_ESTOQUE = “N/A” END
// Calcular porcentagem do movimento nPercentual is numeric IF nTotalEntradas + nTotalSaidas > 0 THEN nPercentual = (dsMovimentoEstoque.valor_total / (nTotalEntradas + nTotalSaidas)) * 100 STC_PORCENTAGEM = NumToString(nPercentual, “999.99”) + “%” ELSE STC_PORCENTAGEM = “0,00%” END
// ============================================== // AFTER PRINTING BODY // ============================================== // Avançar para o próximo registro HReadNext(dsMovimentoEstoque)
// ============================================== // BEFORE PRINTING END_OF_DOCUMENT // ============================================== // Calcular totais finais nTotalGeral = nTotalEntradas - nTotalSaidas
// Preencher campos de totais STC_TOTAL_ENTRADAS = “R$ “ + NumToString(nTotalEntradas, “999,999.99”) STC_TOTAL_SAIDAS = “R$ “ + NumToString(nTotalSaidas, “999,999.99”) STC_TOTAL_GERAL = “R$ “ + NumToString(nTotalGeral, “999,999.99”)
// Colorir total geral conforme saldo IF nTotalGeral >= 0 THEN STC_TOTAL_GERAL.Color = DarkGreen ELSE STC_TOTAL_GERAL.Color = DarkRed END
// Informações do rodapé STC_DATA_GERACAO = “Relatório gerado em: “ + DateTimeToString(Now(), “DD/MM/YYYY às HH:MM:SS”) STC_USUARIO = “Usuário: “ + gsNomeUsuario STC_PERIODO = “Período: “ + DateToString(gdDataInicio, “DD/MM/YYYY”) + “ a “ + DateToString(gdDataFim, “DD/MM/YYYY”)
// ============================================== // AFTER PRINTING REPORT // ============================================== // IMPORTANTE: Liberar a memória da consulta HFreeQuery(dsMovimentoEstoque)
// Resetar variáveis globais nTotalEntradas = 0 nTotalSaidas = 0 nTotalGeral = 0
// Log de finalização Info(“Relatório gerado com sucesso!”)
// ============================================== // PROCEDIMENTOS AUXILIARES // ==============================================
// Procedimento para validar dados antes da impressão PROCEDURE ValidarDadosRelatorio()
// Verificar se as datas são válidas IF gdDataInicio = InvalidDate OR gdDataFim = InvalidDate THEN Error(“Datas inválidas selecionadas!”) RETURN False END
// Verificar se a data inicial não é maior que a final IF gdDataInicio > gdDataFim THEN Error(“A data inicial não pode ser maior que a data final!”) RETURN False END
// Verificar conexão com banco IF NOT HConnectionStatusPostgres(MyConnection_Postgres) THEN Error(“Erro de conexão com o banco de dados!”) RETURN False END
RETURN True
// Procedimento para configurar parâmetros do relatório PROCEDURE ConfigurarParametrosRelatorio(LOCAL dDataIni is Date, LOCAL dDataFim is Date, LOCAL sUsuario is string)
gdDataInicio = dDataIni gdDataFim = dDataFim gsNomeUsuario = sUsuario
// ============================================== // EXEMPLO DE CHAMADA DO RELATÓRIO // ============================================== /* // No código que chama o relatório:
// Configurar parâmetros ConfigurarParametrosRelatorio(EDT_DATA_INICIO, EDT_DATA_FIM, gsUsuarioLogado)
// Validar dados IF NOT ValidarDadosRelatorio() THEN RETURN END
// Imprimir relatório iDestination(iViewer) iPrintReport(RPT_MOVIMENTACAO_ESTOQUE) */
# Manual Completo: Relatórios Programados no WX (Windev/Webdev/Windev Mobile)
## Introdução
Este manual aborda a técnica de criação de relatórios totalmente programados usando WLanguage, onde o preenchimento dos dados é controlado inteiramente via código, proporcionando máxima flexibilidade e controle sobre a geração de relatórios.
## 1. Configuração Inicial do Relatório
### 1.1 Criação do Relatório
1. Crie um novo relatório no editor 1. **IMPORTANTE**: Configure a fonte de dados como “**Preenchimento por programação**” 1. Adicione os controles necessários (campos de texto, imagens, etc.) 1. Nomeie os controles de forma clara (ex: STC_NOME, STC_VALOR, etc.)
### 1.2 Estrutura Base do Código
```wlanguage // Variáveis globais dsMinhaConsulta is Data Source sSQL is string bPrimeiroRegistro is boolean = True
// Configuração da consulta SQL sSQL = [ SELECT campo1, campo2, campo3 FROM minha_tabela WHERE condicao = 'valor' ORDER BY campo1 ] ```
## 2. Implementação dos Blocos de Código
### 2.1 Bloco BEFORE PRINTING REPORT
```wlanguage // Executar a consulta SQL IF NOT HExecuteSQLQuery(dsMinhaConsulta, MyConnection, hQueryWithoutCorrection, sSQL) THEN Info(HErrorInfo(hErrMessage)) gsERRO is string = HErrorInfo(hErrFullDetails) RETURN False ELSE HReadFirst(dsMinhaConsulta) END ```
### 2.2 Bloco BEFORE PRINTING BODY (Mais Importante)
```wlanguage // Verificar se há dados para processar IF HOut(dsMinhaConsulta) THEN RESULT NOT_HOut(dsMinhaConsulta) END
// Preencher os campos do relatório com dados do Data Source STC_CAMPO1 = dsMinhaConsulta.campo1 STC_CAMPO2 = dsMinhaConsulta.campo2 STC_CAMPO3 = dsMinhaConsulta.campo3
// Tratamento de valores nulos IF dsMinhaConsulta.valor = Null THEN STC_VALOR = "0,00" ELSE STC_VALOR = NumToString(dsMinhaConsulta.valor, "99999.99") END
// Cálculos e formatações específicas IF dsMinhaConsulta.tipo = "ENTRADA" THEN STC_TIPO.Color = DarkGreen ELSE STC_TIPO.Color = DarkRed END ```
### 2.3 Bloco AFTER PRINTING BODY
```wlanguage // Avançar para o próximo registro HReadNext(dsMinhaConsulta) ```
### 2.4 Bloco AFTER PRINTING REPORT
```wlanguage // Liberar a memória da consulta HFreeQuery(dsMinhaConsulta) ```
## 3. Exemplo Completo: Relatório de Movimentação de Estoque
### 3.1 Código Completo do Relatório
## 4. Técnicas Avançadas
### 4.1 Tratamento de Erros
```wlanguage // Verificação de erros na execução da consulta IF NOT HExecuteSQLQuery(dsConsulta, MinhaConexao, hQueryWithoutCorrection, sSQL) THEN sErro is string = HErrorInfo(hErrFullDetails) // Log do erro Trace("Erro SQL: " + sErro) // Notificar usuário Error("Erro ao gerar relatório. Contate o suporte.") RETURN False END ```
### 4.2 Otimização de Performance
```wlanguage // Usar índices nas consultas sSQL = [ SELECT /*+ INDEX(tabela, indice_data) */ campos FROM tabela WHERE data_campo BETWEEN %1 AND %2 ]
// Limitar resultados se necessário sSQL += " LIMIT 1000" ```
### 4.3 Formatação Condicional Avançada
```wlanguage // No BEFORE PRINTING BODY SWITCH dsConsulta.status CASE "ATIVO" STC_STATUS.BackgroundColor = LightGreen STC_STATUS.Font.Bold = True CASE "INATIVO" STC_STATUS.BackgroundColor = LightRed STC_STATUS.Font.Strikethrough = True CASE "PENDENTE" STC_STATUS.BackgroundColor = LightYellow STC_STATUS.Font.Italic = True END ```
## 5. Boas Práticas
### 5.1 Nomenclatura
- Use prefixos claros: `STC_` para campos de texto, `IMG_` para imagens - Nomeie Data Sources com prefixo `ds`: `dsRelatorioVendas` - Use nomes descritivos: `dsMovimentacaoEstoque` ao invés de `ds1`
### 5.2 Gerenciamento de Memória
```wlanguage // SEMPRE usar HFreeQuery ao final HFreeQuery(dsConsulta)
// Para múltiplas consultas HFreeQuery(dsConsulta1) HFreeQuery(dsConsulta2) HFreeQuery(dsConsulta3) ```
### 5.3 Validações
```wlanguage // Validar dados antes de processar IF HOut(dsConsulta) THEN Info("Nenhum registro encontrado.") RETURN False END
// Validar campos críticos IF dsConsulta.valor = Null OR dsConsulta.valor < 0 THEN STC_VALOR = "R$ 0,00" STC_VALOR.Color = Red END ```
## 6. Troubleshooting
### 6.1 Problemas Comuns
**Problema**: Relatório não exibe dados **Solução**: Verificar se `HReadFirst()` foi chamado no `BEFORE PRINTING REPORT`
**Problema**: Erro de memória **Solução**: Sempre usar `HFreeQuery()` no `AFTER PRINTING REPORT`
**Problema**: Campos não preenchidos **Solução**: Verificar se o preenchimento está no `BEFORE PRINTING BODY`
**Problema**: Loop infinito **Solução**: Certificar que `HReadNext()` está no `AFTER PRINTING BODY`
### 6.2 Debug
```wlanguage // Adicionar traces para debug Trace("Registro atual: " + dsConsulta.id) Trace("Valor: " + dsConsulta.valor)
// Verificar posição do cursor Trace("HOut: " + HOut(dsConsulta)) Trace("Número do registro: " + HRecNum(dsConsulta)) ```
## 7. Conclusão
A técnica de relatórios programados oferece controle total sobre a apresentação dos dados, permitindo:
- **Flexibilidade máxima** na formatação - **Cálculos complexos** em tempo real - **Formatação condicional** avançada - **Controle total** sobre a fonte de dados - **Performance otimizada** com consultas específicas
**Lembre-se sempre**: Configure como “Preenchimento por programação”, use `HExecuteSQLQuery` no início, preencha os campos no `BEFORE PRINTING BODY`, avance com `HReadNext` no `AFTER PRINTING BODY`, e finalize com `HFreeQuery` no `AFTER PRINTING REPORT`.
Esta técnica é essencial para relatórios corporativos complexos e deve fazer parte do arsenal de todo desenvolvedor WX.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:29 |
Manual Avançado: Técnicas Especiais de Relatórios Programados WX 1. Relatório com Gráficos Dinâmicos Integrados 1.1 Relatório de Dashboard Executivo com Múltiplos Gráficos
// ============================================== // RELATÓRIO: RPT_DASHBOARD_EXECUTIVO // TIPO: Dashboard com múltiplos gráficos programados // CONTROLES: CHART_VENDAS_MES, CHART_PRODUTOS_TOP, CHART_REGIOES, TABLE_RESUMO // ==============================================
// ============================================== // DECLARAÇÕES GLOBAIS // ============================================== dsVendasMes is Data Source dsProdutosTop is Data Source dsRegioes is Data Source dsResumoGeral is Data Source
nTotalVendas is numeric = 0 nMetaMensal is numeric = 500000 arrCoresGrafico is array of 12 strings = [”#FF6B6B”, “#4ECDC4”, “#45B7D1”, “#96CEB4”, “#FFEAA7”, “#DDA0DD”, “#98D8C8”, “#F06292”, “#AED581”, “#FFB74D”, “#A1887F”, “#90A4AE”]
// ============================================== // BEFORE PRINTING REPORT // ==============================================
// ===== CONSULTA 1: Vendas por Mês ===== sSQL_VendasMes is string = [ SELECT EXTRACT(MONTH FROM data_venda) as mes, EXTRACT(YEAR FROM data_venda) as ano, SUM(valor_total) as total_vendas, COUNT(*) as qtd_vendas, AVG(valor_total) as ticket_medio FROM vendas WHERE data_venda >= CURRENT_DATE - INTERVAL ‘12 months’ AND status_venda = ‘FINALIZADA’ GROUP BY EXTRACT(YEAR FROM data_venda), EXTRACT(MONTH FROM data_venda) ORDER BY ano DESC, mes DESC LIMIT 12 ]
IF NOT HExecuteSQLQuery(dsVendasMes, MyConnection, hQueryWithoutCorrection, sSQL_VendasMes) THEN Error(“Erro ao carregar dados de vendas por mês: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 2: Top 10 Produtos ===== sSQL_ProdutosTop is string = [ SELECT p.nome_produto, p.codigo_produto, SUM(iv.quantidade) as qtd_vendida, SUM(iv.valor_total) as valor_total, AVG(iv.valor_unitario) as preco_medio, COUNT(DISTINCT v.id_venda) as num_vendas FROM produtos p INNER JOIN itens_venda iv ON p.id_produto = iv.id_produto INNER JOIN vendas v ON iv.id_venda = v.id_venda WHERE v.data_venda >= CURRENT_DATE - INTERVAL ‘3 months’ AND v.status_venda = ‘FINALIZADA’ GROUP BY p.id_produto, p.nome_produto, p.codigo_produto ORDER BY valor_total DESC LIMIT 10 ]
IF NOT HExecuteSQLQuery(dsProdutosTop, MyConnection, hQueryWithoutCorrection, sSQL_ProdutosTop) THEN Error(“Erro ao carregar top produtos: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 3: Vendas por Região ===== sSQL_Regioes is string = [ SELECT r.nome_regiao, r.sigla_regiao, COUNT(v.id_venda) as qtd_vendas, SUM(v.valor_total) as total_vendas, AVG(v.valor_total) as ticket_medio, COUNT(DISTINCT c.id_cliente) as clientes_ativos FROM regioes r INNER JOIN clientes c ON r.id_regiao = c.id_regiao INNER JOIN vendas v ON c.id_cliente = v.id_cliente WHERE v.data_venda >= CURRENT_DATE - INTERVAL ‘1 month’ AND v.status_venda = ‘FINALIZADA’ GROUP BY r.id_regiao, r.nome_regiao, r.sigla_regiao ORDER BY total_vendas DESC ]
IF NOT HExecuteSQLQuery(dsRegioes, MyConnection, hQueryWithoutCorrection, sSQL_Regioes) THEN Error(“Erro ao carregar dados por região: “ + HErrorInfo(hErrMessage)) RETURN False END
// ===== CONSULTA 4: Resumo Geral ===== sSQL_Resumo is string = [ SELECT ‘VENDAS_HOJE’ as indicador, COALESCE(SUM(valor_total), 0) as valor, COUNT(*) as quantidade FROM vendas WHERE DATE(data_venda) = CURRENT_DATE AND status_venda = ‘FINALIZADA’ UNION ALL SELECT ‘VENDAS_MES’ as indicador, COALESCE(SUM(valor_total), 0) as valor, COUNT(*) as quantidade FROM vendas WHERE EXTRACT(MONTH FROM data_venda) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM data_venda) = EXTRACT(YEAR FROM CURRENT_DATE) AND status_venda = ‘FINALIZADA’ UNION ALL SELECT ‘CLIENTES_NOVOS’ as indicador, COUNT(*) as valor, 0 as quantidade FROM clientes WHERE DATE(data_cadastro) >= CURRENT_DATE - INTERVAL ‘30 days’ UNION ALL SELECT ‘PRODUTOS_ESTOQUE_BAIXO’ as indicador, COUNT(*) as valor, 0 as quantidade FROM produtos WHERE estoque_atual <= estoque_minimo ]
IF NOT HExecuteSQLQuery(dsResumoGeral, MyConnection, hQueryWithoutCorrection, sSQL_Resumo) THEN Error(“Erro ao carregar resumo geral: “ + HErrorInfo(hErrMessage)) RETURN False END
// ============================================== // BEFORE PRINTING HEADER // ==============================================
// Configurar título dinâmico com período STC_TITULO = “Dashboard Executivo - “ + DateToString(DateSys(), “MMMM YYYY”) STC_PERIODO = “Período de Análise: “ + DateToString(DateSys() - 90, “DD/MM/YYYY”) + “ a “ + DateToString(DateSys(), “DD/MM/YYYY”) STC_DATA_GERACAO = “Gerado em: “ + DateTimeToString(Now(), “DD/MM/YYYY às HH:MM”)
// ============================================== // BEFORE PRINTING BODY // ==============================================
// ===== CONFIGURAR GRÁFICO DE VENDAS POR MÊS ===== // Limpar gráfico grDeleteAll(CHART_VENDAS_MES)
// Configurar propriedades do gráfico grType(CHART_VENDAS_MES, grColumn3D) grTitle(CHART_VENDAS_MES, “Evolução de Vendas - Últimos 12 Meses”) grAxisTitle(CHART_VENDAS_MES, grXCoordinate, “Meses”) grAxisTitle(CHART_VENDAS_MES, grYCoordinate, “Valor em R$”) grColor(CHART_VENDAS_MES, 1, RGB(70, 130, 180)) grGradient(CHART_VENDAS_MES, 1, RGB(135, 206, 250), RGB(70, 130, 180), 45)
// Adicionar dados do gráfico HReadFirst(dsVendasMes) nContadorMes is int = 1 WHILE NOT HOut(dsVendasMes) sMesAno is string = Right(“0” + dsVendasMes.mes, 2) + “/” + Right(dsVendasMes.ano, 2) grAddData(CHART_VENDAS_MES, 1, dsVendasMes.total_vendas) grCategoryLabel(CHART_VENDAS_MES, nContadorMes, sMesAno)
``` // Adicionar meta como linha grAddData(CHART_VENDAS_MES, 2, nMetaMensal)
nTotalVendas += dsVendasMes.total_vendas nContadorMes++ HReadNext(dsVendasMes) ```
END
// Configurar segunda série (meta) grSeriesLabel(CHART_VENDAS_MES, 1, “Vendas Realizadas”) grSeriesLabel(CHART_VENDAS_MES, 2, “Meta Mensal”) grSeriesType(CHART_VENDAS_MES, 2, grLine) grColor(CHART_VENDAS_MES, 2, RGB(255, 99, 71))
// ===== CONFIGURAR GRÁFICO DE TOP PRODUTOS ===== grDeleteAll(CHART_PRODUTOS_TOP) grType(CHART_PRODUTOS_TOP, grPie) grTitle(CHART_PRODUTOS_TOP, “Top 10 Produtos por Faturamento”) grLegend(CHART_PRODUTOS_TOP, grAtBottom)
HReadFirst(dsProdutosTop) nContadorProd is int = 1 WHILE NOT HOut(dsProdutosTop) AND nContadorProd <= 10 sNomeProduto is string = Left(dsProdutosTop.nome_produto, 20) nValorProduto is numeric = dsProdutosTop.valor_total
``` grAddData(CHART_PRODUTOS_TOP, 1, nValorProduto) grCategoryLabel(CHART_PRODUTOS_TOP, nContadorProd, sNomeProduto)
// Usar cores do array IF nContadorProd <= Dimension(arrCoresGrafico) THEN grColor(CHART_PRODUTOS_TOP, nContadorProd, HTML(arrCoresGrafico[nContadorProd])) END
nContadorProd++ HReadNext(dsProdutosTop) ```
END
// ===== CONFIGURAR GRÁFICO DE REGIÕES ===== grDeleteAll(CHART_REGIOES) grType(CHART_REGIOES, grBarHorizontal) grTitle(CHART_REGIOES, “Vendas por Região - Último Mês”) grAxisTitle(CHART_REGIOES, grXCoordinate, “Valor em R$”) grAxisTitle(CHART_REGIOES, grYCoordinate, “Regiões”)
HReadFirst(dsRegioes) nContadorReg is int = 1 WHILE NOT HOut(dsRegioes) grAddData(CHART_REGIOES, 1, dsRegioes.total_vendas) grCategoryLabel(CHART_REGIOES, nContadorReg, dsRegioes.nome_regiao)
``` // Gradiente baseado no valor IF dsRegioes.total_vendas > 100000 THEN grColor(CHART_REGIOES, nContadorReg, RGB(34, 139, 34)) // Verde forte ELSE IF dsRegioes.total_vendas > 50000 THEN grColor(CHART_REGIOES, nContadorReg, RGB(255, 165, 0)) // Laranja ELSE grColor(CHART_REGIOES, nContadorReg, RGB(220, 20, 60)) // Vermelho END
nContadorReg++ HReadNext(dsRegioes) ```
END
// ===== PREENCHER TABELA DE RESUMO ===== TableDeleteAll(TABLE_RESUMO)
HReadFirst(dsResumoGeral) WHILE NOT HOut(dsResumoGeral) nLinha is int = TableAddLine(TABLE_RESUMO)
``` SWITCH dsResumoGeral.indicador CASE "VENDAS_HOJE" TABLE_RESUMO[nLinha][1] = "Vendas Hoje" TABLE_RESUMO[nLinha][2] = NumToString(dsResumoGeral.valor, "R$ 999,999.99") TABLE_RESUMO[nLinha][3] = dsResumoGeral.quantidade + " vendas" // Colorir baseado na performance IF dsResumoGeral.valor > 10000 THEN TABLE_RESUMO[nLinha].BackgroundColor = LightGreen ELSE IF dsResumoGeral.valor > 5000 THEN TABLE_RESUMO[nLinha].BackgroundColor = LightYellow ELSE TABLE_RESUMO[nLinha].BackgroundColor = LightRed END CASE "VENDAS_MES" TABLE_RESUMO[nLinha][1] = "Vendas do Mês" TABLE_RESUMO[nLinha][2] = NumToString(dsResumoGeral.valor, "R$ 999,999.99") TABLE_RESUMO[nLinha][3] = dsResumoGeral.quantidade + " vendas" // Comparar com meta nPercentualMeta is numeric = (dsResumoGeral.valor / nMetaMensal) * 100 TABLE_RESUMO[nLinha][4] = NumToString(nPercentualMeta, "999.9") + "% da meta" CASE "CLIENTES_NOVOS" TABLE_RESUMO[nLinha][1] = "Novos Clientes (30d)" TABLE_RESUMO[nLinha][2] = dsResumoGeral.valor + " clientes" TABLE_RESUMO[nLinha][3] = "Crescimento da base" CASE "PRODUTOS_ESTOQUE_BAIXO" TABLE_RESUMO[nLinha][1] = "Estoque Baixo" TABLE_RESUMO[nLinha][2] = dsResumoGeral.valor + " produtos" TABLE_RESUMO[nLinha][3] = "Requer atenção" IF dsResumoGeral.valor > 50 THEN TABLE_RESUMO[nLinha].Color = Red TABLE_RESUMO[nLinha].Font.Bold = True END END
HReadNext(dsResumoGeral) ```
END
// ===== INDICADORES VISUAIS (CARDS) ===== // Card de Performance Geral nPerformanceGeral is numeric = (nTotalVendas / (nMetaMensal * 12)) * 100
IF nPerformanceGeral >= 100 THEN STC_PERFORMANCE.BackgroundColor = RGB(76, 175, 80) // Verde STC_PERFORMANCE.Color = White STC_PERFORMANCE_ICON = “✓” ELSE IF nPerformanceGeral >= 80 THEN STC_PERFORMANCE.BackgroundColor = RGB(255, 193, 7) // Amarelo STC_PERFORMANCE.Color = Black STC_PERFORMANCE_ICON = “⚠” ELSE STC_PERFORMANCE.BackgroundColor = RGB(244, 67, 54) // Vermelho STC_PERFORMANCE.Color = White STC_PERFORMANCE_ICON = “✗” END
STC_PERFORMANCE = NumToString(nPerformanceGeral, “999.9”) + “%”
// Redimensionar gráficos dinamicamente grSaveBMP(CHART_VENDAS_MES, CompleteDir(fDataDir()) + “temp_vendas_mes.bmp”) grSaveBMP(CHART_PRODUTOS_TOP, CompleteDir(fDataDir()) + “temp_produtos_top.bmp”) grSaveBMP(CHART_REGIOES, CompleteDir(fDataDir()) + “temp_regioes.bmp”)
// ============================================== // AFTER PRINTING BODY // ============================================== // Não há loop de registros neste relatório, é um relatório único
// ============================================== // BEFORE PRINTING FOOTER // ==============================================
// Configurar informações de rodapé STC_TOTAL_VENDAS_FOOTER = “Total Geral: “ + NumToString(nTotalVendas, “R$ 999,999,999.99”) STC_META_FOOTER = “Meta Anual: “ + NumToString(nMetaMensal * 12, “R$ 999,999,999.99”) STC_USUARIO_FOOTER = “Gerado por: “ + gsUsuarioLogado STC_SISTEMA_FOOTER = “Sistema: “ + gsNomeSistema + “ v” + gsVersaoSistema
// ============================================== // AFTER PRINTING REPORT // ==============================================
// Liberar todas as consultas HFreeQuery(dsVendasMes) HFreeQuery(dsProdutosTop) HFreeQuery(dsRegioes) HFreeQuery(dsResumoGeral)
// Limpar arquivos temporários fDelete(CompleteDir(fDataDir()) + “temp_vendas_mes.bmp”) fDelete(CompleteDir(fDataDir()) + “temp_produtos_top.bmp”) fDelete(CompleteDir(fDataDir()) + “temp_regioes.bmp”)
// Resetar variáveis nTotalVendas = 0
Trace(“Dashboard executivo gerado com sucesso!”)
// ============================================== // PROCEDIMENTOS AUXILIARES // ==============================================
// Procedimento para configurar cores dinâmicas do gráfico PROCEDURE ConfigurarCoresGrafico(LOCAL nomeGrafico is string, LOCAL nNumSeries is int)
arrCoresPadrao is array of strings = [ “#FF6B6B”, “#4ECDC4”, “#45B7D1”, “#96CEB4”, “#FFEAA7”, “#DDA0DD”, “#98D8C8”, “#F06292”, “#AED581”, “#FFB74D” ]
FOR i = 1 TO nNumSeries IF i <= Dimension(arrCoresPadrao) THEN grColor(nomeGrafico, i, HTML(arrCoresPadrao[i])) END END
// Procedimento para adicionar efeitos 3D nos gráficos PROCEDURE ConfigurarEfeitos3D(LOCAL nomeGrafico is string)
grParameter(nomeGrafico, grElevation, 30) grParameter(nomeGrafico, grRotation, 45) grParameter(nomeGrafico, grPerspective, 50) grSmoothing(nomeGrafico, True)
// ============================================== // EXEMPLO DE CHAMADA // ============================================== /* // Configurar parâmetros globais gsUsuarioLogado = “João Silva” gsNomeSistema = “ERP Corporativo” gsVersaoSistema = “2.1.5”
// Configurar destino iDestination(iViewer)
// Gerar o relatório iPrintReport(RPT_DASHBOARD_EXECUTIVO) */
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:30 |
2. Sistema de Etiquetas Inteligentes 2.1 Gerador de Etiquetas de Produtos com Códigos de Barras
// ============================================== // RELATÓRIO: RPT_ETIQUETAS_PRODUTOS // TIPO: Etiquetas com código de barras e QR Code // FORMATO: 4 etiquetas por linha, papel A4 // CONTROLES: IMG_BARCODE, IMG_QRCODE, STC_PRODUTO, STC_PRECO, etc. // ==============================================
// ============================================== // DECLARAÇÕES GLOBAIS // ============================================== dsEtiquetas is Data Source nEtiquetaAtual is int = 0 nEtiquetasPorLinha is int = 4 nLinhasProcessadas is int = 0 arrProdutosSelecionados is array of strings nQuantidadePorProduto is associative array of int // ID_PRODUTO -> QUANTIDADE
// Configurações de formatação REAL_WIDTH is real = 50 // mm REAL_HEIGHT is real = 25 // mm
// ============================================== // BEFORE PRINTING REPORT // ==============================================
// Construir lista de produtos baseada na seleção sListaProdutos is string = “” FOR EACH sProdutoID OF arrProdutosSelecionados IF sListaProdutos <> “” THEN sListaProdutos += “,” sListaProdutos += “’” + sProdutoID + “’” END
IF sListaProdutos = “” THEN Error(“Nenhum produto selecionado para gerar etiquetas!”) RETURN False END
// ===== CONSULTA PRINCIPAL ===== sSQL is string = [ SELECT p.id_produto, p.codigo_barras, p.codigo_interno, p.nome_produto, p.nome_resumido, p.marca, p.categoria, p.unidade_medida, p.peso_liquido, p.peso_bruto, p.data_validade, p.lote_fabricacao, COALESCE(pv.preco_venda, 0) as preco_venda, COALESCE(pv.preco_promocional, 0) as preco_promocional, CASE WHEN pv.preco_promocional > 0 AND pv.preco_promocional < pv.preco_venda THEN pv.preco_promocional ELSE pv.preco_venda END as preco_final, CASE WHEN pv.preco_promocional > 0 AND pv.preco_promocional < pv.preco_venda THEN ‘S’ ELSE ‘N’ END as em_promocao, f.nome_fornecedor, f.cnpj_fornecedor, p.origem_produto, p.ncm_produto, p.observacoes FROM produtos p LEFT JOIN precos_venda pv ON p.id_produto = pv.id_produto AND pv.data_inicio <= CURRENT_DATE AND (pv.data_fim IS NULL OR pv.data_fim >= CURRENT_DATE) LEFT JOIN fornecedores f ON p.id_fornecedor_principal = f.id_fornecedor WHERE p.id_produto IN (%1) AND p.ativo = ‘S’ AND p.gerar_etiqueta = ‘S’ ORDER BY p.nome_produto ]
sSQL = StringBuild(sSQL, sListaProdutos)
IF NOT HExecuteSQLQuery(dsEtiquetas, MyConnection, hQueryWithoutCorrection, sSQL) THEN Error(“Erro ao carregar dados dos produtos: “ + HErrorInfo(hErrMessage)) gsErroDetalhado is string = HErrorInfo(hErrFullDetails) Trace(“Erro SQL detalhado: “ + gsErroDetalhado) RETURN False END
// Verificar se encontrou produtos HReadFirst(dsEtiquetas) IF HOut(dsEtiquetas) THEN Info(“Nenhum produto válido encontrado para gerar etiquetas!”) RETURN False END
// Expandir dataset baseado na quantidade de cada produto dsEtiquetasExpandido is Data Source CriarDataSourceExpandido()
// ============================================== // BEFORE PRINTING BODY // ==============================================
// Verificar se há mais etiquetas para processar IF HOut(dsEtiquetasExpandido) THEN RESULT NOT_HOut(dsEtiquetasExpandido) END
// Determinar posição da etiqueta na página (1-4) nPosicaoEtiqueta is int = (nEtiquetaAtual MOD nEtiquetasPorLinha) + 1
// ===== CONFIGURAR CONTROLES DA ETIQUETA =====
// Determinar qual set de controles usar baseado na posição sPrefixoControle is string = “ETQ” + nPosicaoEtiqueta + “_”
// Nome do produto (truncado se necessário) sNomeProduto is string = dsEtiquetasExpandido.nome_produto IF Length(sNomeProduto) > 35 THEN sNomeProduto = Left(dsEtiquetasExpandido.nome_resumido, 32) + “…” END
{sPrefixoControle + “STC_NOME”} = Upper(sNomeProduto)
// Código do produto {sPrefixoControle + “STC_CODIGO”} = dsEtiquetasExpandido.codigo_interno
// Marca e categoria {sPrefixoControle + “STC_MARCA”} = dsEtiquetasExpandido.marca {sPrefixoControle + “STC_CATEGORIA”} = dsEtiquetasExpandido.categoria
// ===== FORMATAÇÃO DE PREÇOS ===== nPrecoFinal is numeric = dsEtiquetasExpandido.preco_final nPrecoVenda is numeric = dsEtiquetasExpandido.preco_venda
IF dsEtiquetasExpandido.em_promocao = “S” THEN // Produto em promoção {sPrefixoControle + “STC_PRECO_NORMAL”} = “De: R$ “ + NumToString(nPrecoVenda, “999.99”) {sPrefixoControle + “STC_PRECO_NORMAL”}.Font.Strikethrough = True {sPrefixoControle + “STC_PRECO_NORMAL”}.Color = RGB(128, 128, 128)
``` {sPrefixoControle + "STC_PRECO_PROMOCIONAL"} = "R$ " + NumToString(nPrecoFinal, "999.99") {sPrefixoControle + "STC_PRECO_PROMOCIONAL"}.Color = RGB(255, 0, 0) {sPrefixoControle + "STC_PRECO_PROMOCIONAL"}.Font.Bold = True {sPrefixoControle + "STC_PRECO_PROMOCIONAL"}.Font.Size = 14
// Badge de promoção {sPrefixoControle + "STC_PROMOCAO"} = "OFERTA" {sPrefixoControle + "STC_PROMOCAO"}.BackgroundColor = RGB(255, 0, 0) {sPrefixoControle + "STC_PROMOCAO"}.Color = White {sPrefixoControle + "STC_PROMOCAO"}.Visible = True
// Calcular desconto nDesconto is numeric = ((nPrecoVenda - nPrecoFinal) / nPrecoVenda) * 100 {sPrefixoControle + "STC_DESCONTO"} = NumToString(nDesconto, "99") + "% OFF" ```
ELSE // Produto com preço normal {sPrefixoControle + “STC_PRECO_NORMAL”} = “R$ “ + NumToString(nPrecoFinal, “999.99”) {sPrefixoControle + “STC_PRECO_NORMAL”}.Font.Strikethrough = False {sPrefixoControle + “STC_PRECO_NORMAL”}.Color = Black {sPrefixoControle + “STC_PRECO_NORMAL”}.Font.Bold = True {sPrefixoControle + “STC_PRECO_NORMAL”}.Font.Size = 12
``` {sPrefixoControle + "STC_PRECO_PROMOCIONAL"}.Visible = False {sPrefixoControle + "STC_PROMOCAO"}.Visible = False {sPrefixoControle + "STC_DESCONTO"}.Visible = False ```
END
// ===== CÓDIGO DE BARRAS ===== IF dsEtiquetasExpandido.codigo_barras <> “” THEN // Gerar código de barras usando a função nativa do WinDev sCodigoBarras is string = dsEtiquetasExpandido.codigo_barras
``` // Configurar imagem do código de barras {sPrefixoControle + "IMG_BARCODE"} = iBarCode(sCodigoBarras, BC_EAN13, bcCheckDigit + bcText) {sPrefixoControle + "IMG_BARCODE"}.Width = 120 {sPrefixoControle + "IMG_BARCODE"}.Height = 40
// Texto do código de barras {sPrefixoControle + "STC_CODIGO_BARRAS"} = sCodigoBarras {sPrefixoControle + "STC_CODIGO_BARRAS"}.Font.Size = 8 ```
ELSE {sPrefixoControle + “IMG_BARCODE”}.Visible = False {sPrefixoControle + “STC_CODIGO_BARRAS”}.Visible = False END
// ===== QR CODE ===== // Criar dados para QR Code (JSON com informações do produto) sQRData is string = [ { “id”: “%1”, “codigo”: “%2”, “nome”: “%3”, “preco”: %4, “promocao”: “%5”, “validade”: “%6”, “lote”: “%7”, “origem”: “%8” } ]
sQRData = StringBuild(sQRData, dsEtiquetasExpandido.id_produto, dsEtiquetasExpandido.codigo_interno, dsEtiquetasExpandido.nome_produto, nPrecoFinal, dsEtiquetasExpandido.em_promocao, DateToString(dsEtiquetasExpandido.data_validade, “DD/MM/YYYY”), dsEtiquetasExpandido.lote_fabricacao, dsEtiquetasExpandido.origem_produto )
// Gerar QR Code {sPrefixoControle + “IMG_QRCODE”} = iQRCode(sQRData, qrLow) {sPrefixoControle + “IMG_QRCODE”}.Width = 30 {sPrefixoControle + “IMG_QRCODE”}.Height = 30
// ===== INFORMAÇÕES ADICIONAIS =====
// Unidade de medida e peso IF dsEtiquetasExpandido.peso_liquido > 0 THEN {sPrefixoControle + “STC_PESO”} = NumToString(dsEtiquetasExpandido.peso_liquido, “999.999”) + “ “ + dsEtiquetasExpandido.unidade_medida ELSE {sPrefixoControle + “STC_PESO”} = dsEtiquetasExpandido.unidade_medida END
// Data de validade (se aplicável) IF dsEtiquetasExpandido.data_validade <> InvalidDate THEN {sPrefixoControle + “STC_VALIDADE”} = “Val: “ + DateToString(dsEtiquetasExpandido.data_validade, “DD/MM/YY”)
``` // Alertar se próximo do vencimento nDiasVencimento is int = dsEtiquetasExpandido.data_validade - DateSys() IF nDiasVencimento <= 30 THEN {sPrefixoControle + "STC_VALIDADE"}.Color = RGB(255, 0, 0) {sPrefixoControle + "STC_VALIDADE"}.Font.Bold = True END ```
ELSE {sPrefixoControle + “STC_VALIDADE”}.Visible = False END
// Lote de fabricação IF dsEtiquetasExpandido.lote_fabricacao <> “” THEN {sPrefixoControle + “STC_LOTE”} = “Lote: “ + dsEtiquetasExpandido.lote_fabricacao ELSE {sPrefixoControle + “STC_LOTE”}.Visible = False END
// Origem do produto IF dsEtiquetasExpandido.origem_produto <> “” THEN {sPrefixoControle + “STC_ORIGEM”} = “Origem: “ + dsEtiquetasExpandido.origem_produto {sPrefixoControle + “STC_ORIGEM”}.Font.Size = 7 END
// Data de impressão {sPrefixoControle + “STC_DATA_IMPRESSAO”} = DateToString(DateSys(), “DD/MM/YY”) {sPrefixoControle + “STC_DATA_IMPRESSAO”}.Font.Size = 6
// ===== CONTROLE DE VISIBILIDADE ===== // Mostrar apenas os controles da posição atual FOR nPos = 1 TO nEtiquetasPorLinha bVisible is boolean = (nPos = nPosicaoEtiqueta)
``` ConfigurarVisibilidadeEtiqueta(nPos, bVisible) ```
END
// Incrementar contador nEtiquetaAtual++
// ===== QUEBRA DE PÁGINA ===== // Se completou uma linha de 4 etiquetas, preparar para próxima linha IF nPosicaoEtiqueta = nEtiquetasPorLinha THEN nLinhasProcessadas++
``` // Quebrar página a cada 10 linhas (40 etiquetas por página) IF nLinhasProcessadas MOD 10 = 0 THEN iPageEnd() END ```
END
// ============================================== // AFTER PRINTING BODY // ==============================================
// Avançar para próxima etiqueta HReadNext(dsEtiquetasExpandido)
// ============================================== // AFTER PRINTING REPORT // ==============================================
// Liberar recursos HFreeQuery(dsEtiquetas) HFreeQuery(dsEtiquetasExpandido)
// Limpar arrays ArrayDeleteAll(arrProdutosSelecionados) AssociativeArrayDeleteAll(nQuantidadePorProduto)
// Reset contadores nEtiquetaAtual = 0 nLinhasProcessadas = 0
Info(“Etiquetas geradas com sucesso! Total: “ + nEtiquetaAtual)
// ============================================== // PROCEDIMENTOS AUXILIARES // ==============================================
// Criar dataset expandido baseado na quantidade PROCEDURE CriarDataSourceExpandido()
// Primeiro, criar um array temporário com todos os registros expandidos arrRegistrosExpandidos is array of structures id_produto is string quantidade_etiqueta is int registro_original is buffer END
HReadFirst(dsEtiquetas) WHILE NOT HOut(dsEtiquetas) nQtdEtiquetas is int = nQuantidadePorProduto[dsEtiquetas.id_produto] IF nQtdEtiquetas <= 0 THEN nQtdEtiquetas = 1 // Mínimo 1 etiqueta
``` FOR i = 1 TO nQtdEtiquetas stRegistro is ST_RegistroExpandido stRegistro.id_produto = dsEtiquetas.id_produto stRegistro.quantidade_etiqueta = i stRegistro.registro_original = HRecordToString(dsEtiquetas) Add(arrRegistrosExpandidos, stRegistro) END
HReadNext(dsEtiquetas) ```
END
// Criar o datasource expandido sCreateQuery is string = “CREATE TABLE temp_etiquetas_expandidas AS “ + HRecordToString(dsEtiquetas, “”, hRecordHeader)
// Inserir registros expandidos FOR EACH stReg OF arrRegistrosExpandidos HStringToRecord(dsEtiquetasExpandido, stReg.registro_original) // Adicionar campo de sequência dsEtiquetasExpandido.sequencia_etiqueta = stReg.quantidade_etiqueta HAdd(dsEtiquetasExpandido) END
HReadFirst(dsEtiquetasExpandido)
// Configurar visibilidade dos controles de uma etiqueta PROCEDURE ConfigurarVisibilidadeEtiqueta(LOCAL nPosicao is int, LOCAL bVisivel is boolean)
sPrefixo is string = “ETQ” + nPosicao + “_”
// Lista de controles de cada etiqueta arrControles is array of strings = [ “STC_NOME”, “STC_CODIGO”, “STC_MARCA”, “STC_CATEGORIA”, “STC_PRECO_NORMAL”, “STC_PRECO_PROMOCIONAL”, “STC_PROMOCAO”, “STC_DESCONTO”, “IMG_BARCODE”, “STC_CODIGO_BARRAS”, “IMG_QRCODE”, “STC_PESO”, “STC_VALIDADE”, “STC_LOTE”, “STC_ORIGEM”, “STC_DATA_IMPRESSAO” ]
FOR EACH sControle OF arrControles {sPrefixo + sControle}.Visible = bVisivel END
// Configurar dados para teste sem impressora PROCEDURE ConfigurarDadosTeste()
// Adicionar produtos para teste Add(arrProdutosSelecionados, “1”) Add(arrProdutosSelecionados, “2”) Add(arrProdutosSelecionados, “3”)
nQuantidadePorProduto[“1”] = 5 nQuantidadePorProduto[“2”] = 3 nQuantidadePorProduto[“3”] = 2
// ============================================== // EXEMPLO DE CHAMADA // ============================================== /* // Configurar produtos para impressão ArrayDeleteAll(arrProdutosSelecionados) AssociativeArrayDeleteAll(nQuantidadePorProduto)
// Adicionar produtos selecionados Add(arrProdutosSelecionados, “123”) // ID do produto Add(arrProdutosSelecionados, “456”) Add(arrProdutosSelecionados, “789”)
// Definir quantidade de etiquetas por produto nQuantidadePorProduto[“123”] = 10 nQuantidadePorProduto[“456”] = 5 nQuantidadePorProduto[“789”] = 3
// Configurar impressora para etiquetas iParameter(iDestination, “ETIQUETAS_ZEBRA”) iParameter(iPaperSize, iPaper_A4) iParameter(iMargin, 5, 5, 5, 5)
// Imprimir iPrintReport(RPT_ETIQUETAS_PRODUTOS) */
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 15:38 |
Vamos criar um exemplo totalmente diferente e completo de impressão ZPL para uma etiqueta de rastreamento logístico, com código 100% pronto para rodar no WinDev/WebDev/Windev Mobile (WX), seguindo os padrões de geração de relatório programado para impressoras Zebra.
⸻
📦 Cenário do Exemplo:
Rastreamento logístico de encomendas com etiqueta contendo: • Nome do destinatário • Endereço completo • Código de rastreamento • Peso e dimensões • Código de barras (rastreamento) • QR Code com JSON do pacote
⸻
✅ Estrutura Geral do Projeto
Relatório: RPT_ETIQUETA_LOGISTICA Impressora: Zebra_GK420t Modo: ZPL via iPrint() Fonte de dados: Preenchimento por programação
⸻
🔧 Código Completo
// ============================================== // RELATÓRIO PROGRAMADO - ETIQUETA LOGÍSTICA // IMPRESSÃO ZPL - ZEBRA // ==============================================
dsPacoteLogistico is Data Source sImpressoraZebra is string = "Zebra_GK420t" nLarguraZPL is int = 406 nAlturaZPL is int = 203
// ============================================== // BEFORE PRINTING REPORT // ==============================================
sSQL is string = [ SELECT id_pacote, nome_destinatario, endereco, cidade, cep, peso_kg, dimensao_cm, codigo_rastreamento FROM pacote_logistico WHERE id_pacote = %1 ]
sSQL = StringBuild(sSQL, gnPacoteSelecionado)
IF NOT HExecuteSQLQuery(dsPacoteLogistico, MyConnection, hQueryWithoutCorrection, sSQL) THEN Error("Erro ao carregar pacote: " + HErrorInfo(hErrMessage)) RETURN False END
HReadFirst(dsPacoteLogistico)
IF HOut(dsPacoteLogistico) THEN RETURN False END
// Configurar impressora IF NOT PrinterExist(sImpressoraZebra) THEN Error("Impressora Zebra não encontrada.") RETURN False END
iParameter(iDestination, sImpressoraZebra) iParameter(iMode, iModeText)
RETURN True
⸻
🧩 BEFORE PRINTING BODY
// Geração do ZPL logístico sZPL is string = GerarZPLLogistico() iPrint(sZPL)
⸻
🧯 AFTER PRINTING BODY
HReadNext(dsPacoteLogistico)
⸻
🧹 AFTER PRINTING REPORT
HFreeQuery(dsPacoteLogistico) iParameter(iDestination, iViewer)
⸻
🧠 Função: GerarZPLLogistico
FUNCTION GerarZPLLogistico() : string
sNome is string = dsPacoteLogistico.nome_destinatario sEndereco is string = dsPacoteLogistico.endereco + ", " + dsPacoteLogistico.cidade + " - CEP: " + dsPacoteLogistico.cep sPeso is string = NumToString(dsPacoteLogistico.peso_kg, "###0.0") + " kg" sDimensao is string = dsPacoteLogistico.dimensao_cm + " cm" sCodigo is string = dsPacoteLogistico.codigo_rastreamento sData is string = DateToString(DateSys(), "DD/MM/YYYY")
// JSON resumido para QR Code sQRData is string = [ {"destinatario":"%1","codigo":"%2","peso":"%3"} ] sQRData = StringBuild(sQRData, sNome, sCodigo, sPeso)
sZPL is string = [ ^XA ^PW%1 ^LL%2 ^CF0,30 ^FO20,20^FDDESTINATARIO:^FS ^CF0,25 ^FO20,55^FD%3^FS ^CF0,20 ^FO20,90^FD%4^FS ^FO20,115^FDPeso: %5 - Dim: %6^FS ^FO20,140^FDData: %7^FS
^FO20,170^BCN,80,Y,N,N ^FD%8^FS
^CF0,15 ^FO20,260^FDRastreamento: %8^FS
^FO300,30^BQ,2,5 ^FDQA,%9^FS ^XZ ]
sZPL = StringBuild(sZPL, nLarguraZPL, // %1 nAlturaZPL, // %2 sNome, // %3 sEndereco, // %4 sPeso, // %5 sDimensao, // %6 sData, // %7 sCodigo, // %8 sQRData // %9 )
RETURN sZPL
⸻
🧪 Teste Simulado
// Definir o pacote que será impresso gnPacoteSelecionado = 15
// Chamar o relatório iPrintReport(RPT_ETIQUETA_LOGISTICA)
⸻
✅ Vantagens do Exemplo • Suporta QRCode nativo ZPL (^BQ) • Imprime código de barras rastreável (^BC) • Mostra dados úteis ao operador logístico (peso, dimensões, CEP) • Baseado em SQL dinâmico • Adaptável para WebDev, WinDev Mobile e WinDev
⸻ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.520 mensajes |
|
Publicado el 02,julio 2025 - 18:42 |
| |
| |
| | | |
|
| | | | |
| | |
|