Skip to content

Commit d45bbc8

Browse files
authored
Merge pull request #33 from EngStrategy/feature/relatorio
Feature/relatorio
2 parents 340cb94 + 86697f1 commit d45bbc8

File tree

12 files changed

+401
-2
lines changed

12 files changed

+401
-2
lines changed

pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@
124124
<scope>test</scope>
125125
</dependency>
126126

127+
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
128+
<dependency>
129+
<groupId>com.itextpdf</groupId>
130+
<artifactId>itextpdf</artifactId>
131+
<version>5.5.13.3</version>
132+
</dependency>
133+
134+
<!-- https://mvnrepository.com/artifact/jfree/jfreechart -->
135+
<dependency>
136+
<groupId>jfree</groupId>
137+
<artifactId>jfreechart</artifactId>
138+
<version>1.0.13</version>
139+
</dependency>
140+
141+
127142
</dependencies>
128143

129144
<build>

src/main/java/com/carvalhotechsolutions/mundoanimal/controllers/gerenciamento/MenuController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void initialize(URL url, ResourceBundle resourceBundle) {
6262
// Configure ações para cada botão
6363
configureButton(inicio_btn, ScreenEnum.INICIO);
6464
configureButton(historico_btn, ScreenEnum.HISTORICO);
65-
// configureButton(relatorio_btn, "relatorio.fxml");
65+
configureButton(relatorio_btn, ScreenEnum.RELATORIO);
6666
configureButton(agendamentos_btn, ScreenEnum.AGENDAMENTOS);
6767
configureButton(secretarios_btn, ScreenEnum.SECRETARIOS);
6868
configureButton(servicos_btn, ScreenEnum.SERVICOS);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.carvalhotechsolutions.mundoanimal.controllers.gerenciamento;
2+
3+
import com.carvalhotechsolutions.mundoanimal.model.Agendamento;
4+
import com.carvalhotechsolutions.mundoanimal.repositories.AgendamentoRepository;
5+
import com.carvalhotechsolutions.mundoanimal.utils.ModalManager;
6+
import com.carvalhotechsolutions.mundoanimal.utils.RelatorioManager;
7+
import com.itextpdf.text.*;
8+
import javafx.fxml.FXML;
9+
import javafx.scene.control.*;
10+
import javafx.stage.FileChooser;
11+
12+
import java.io.File;
13+
import java.io.IOException;
14+
import java.time.LocalDate;
15+
import java.util.List;
16+
17+
public class RelatorioController {
18+
19+
@FXML
20+
private Button actionButton;
21+
@FXML
22+
private DatePicker dataInicialPicker, dataFinalPicker;
23+
private final AgendamentoRepository agendamentoRepository = new AgendamentoRepository();
24+
private final RelatorioManager relatorioManager = new RelatorioManager();
25+
26+
27+
@FXML
28+
private void gerarRelatorio() {
29+
30+
LocalDate inicio = dataInicialPicker.getValue();
31+
LocalDate fim = dataFinalPicker.getValue();
32+
33+
if (inicio == null || fim == null) {
34+
mostrarAlerta("Erro", "Selecione um intervalo de datas válido.");
35+
return;
36+
}
37+
38+
if (inicio.isAfter(fim)) {
39+
mostrarAlerta("Erro", "A data inicial não pode ser maior que a data final.");
40+
return;
41+
}
42+
43+
List<Agendamento> agendamentos = agendamentoRepository.buscarAgendamentosPorIntervalo(inicio, fim);
44+
45+
if (agendamentos.isEmpty()) {
46+
mostrarAlerta("Aviso", "Nenhum agendamento encontrado neste período.");
47+
return;
48+
}
49+
50+
FileChooser fileChooser = new FileChooser();
51+
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PDF Files", "*.pdf"));
52+
fileChooser.setInitialFileName("Relatorio_" + inicio + "_" + fim + ".pdf");
53+
54+
File file = fileChooser.showSaveDialog(null);
55+
if (file == null) {
56+
return; // Usuário cancelou
57+
}
58+
59+
try {
60+
relatorioManager.gerarRelatorioPDF(agendamentos, inicio, fim, file);
61+
mostrarAlerta("Sucesso", "Relatório gerado com sucesso!");
62+
} catch (IOException | DocumentException e) {
63+
e.printStackTrace();
64+
mostrarAlerta("Erro", "Erro ao gerar o relatório.");
65+
}
66+
}
67+
68+
private void mostrarAlerta(String titulo, String mensagem) {
69+
ModalManager.mostrarModal(titulo, mensagem, "OK");
70+
}
71+
72+
}

src/main/java/com/carvalhotechsolutions/mundoanimal/enums/ScreenEnum.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public enum ScreenEnum {
1111
PETS("/fxml/gerenciamento/pets.fxml", "Pets", ScreenType.CONTENT),
1212
AGENDAMENTOS("/fxml/gerenciamento/agendamentos.fxml", "Agendamentos", ScreenType.CONTENT),
1313
HISTORICO("/fxml/gerenciamento/historico.fxml", "Historico", ScreenType.CONTENT),
14-
INICIO("/fxml/gerenciamento/inicio.fxml", "Inicio", ScreenType.CONTENT);
14+
INICIO("/fxml/gerenciamento/inicio.fxml", "Inicio", ScreenType.CONTENT),
15+
RELATORIO("/fxml/gerenciamento/relatorio.fxml", "Relatorio", ScreenType.CONTENT);
1516

1617
private final String fxmlPath;
1718
private final String title;

src/main/java/com/carvalhotechsolutions/mundoanimal/repositories/AgendamentoRepository.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,19 @@ public List<Agendamento> findFinalizadosUltimaSemana() {
131131
.getResultList();
132132
}
133133
}
134+
135+
public List<Agendamento> buscarAgendamentosPorIntervalo(LocalDate dataInicio, LocalDate dataFim) {
136+
try (EntityManager em = JPAutil.getEntityManager()) {
137+
String jpql = "SELECT a FROM Agendamento a " +
138+
"WHERE a.dataAgendamento BETWEEN :inicio AND :fim " +
139+
"AND a.status = :statusFinalizado";
140+
141+
return em.createQuery(jpql, Agendamento.class)
142+
.setParameter("inicio", dataInicio)
143+
.setParameter("fim", dataFim)
144+
.setParameter("statusFinalizado", StatusAgendamento.FINALIZADO)
145+
.getResultList();
146+
}
147+
}
148+
134149
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.carvalhotechsolutions.mundoanimal.utils;
2+
3+
import javafx.fxml.FXML;
4+
import javafx.scene.control.Button;
5+
import javafx.scene.control.Label;
6+
import javafx.stage.Stage;
7+
8+
public class AlertManager {
9+
10+
@FXML
11+
private Label modalMessage;
12+
13+
@FXML
14+
private Label modalTitle;
15+
16+
@FXML
17+
private Button btnModal;
18+
19+
public void setModalData(String titulo, String mensagem, String textoBotao) {
20+
modalTitle.setText(titulo);
21+
modalMessage.setText(mensagem);
22+
btnModal.setText(textoBotao);
23+
}
24+
25+
@FXML
26+
public void clicarNoBotao() {
27+
fecharModal();
28+
}
29+
30+
private void fecharModal() {
31+
Stage stage = (Stage) btnModal.getScene().getWindow();
32+
stage.close();
33+
}
34+
35+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.carvalhotechsolutions.mundoanimal.utils;
2+
3+
import javafx.fxml.FXMLLoader;
4+
import javafx.scene.Parent;
5+
import javafx.scene.Scene;
6+
import javafx.stage.Modality;
7+
import javafx.stage.Stage;
8+
9+
import java.io.IOException;
10+
11+
public class ModalManager {
12+
13+
public static void mostrarModal(String titulo, String mensagem, String textoBotao) {
14+
try {
15+
FXMLLoader loader = new FXMLLoader(ModalManager.class.getResource("/fxml/popup/genericAlert.fxml"));
16+
Parent root = loader.load();
17+
18+
AlertManager controller = loader.getController();
19+
controller.setModalData(titulo, mensagem, textoBotao);
20+
21+
Stage stage = new Stage();
22+
stage.initModality(Modality.APPLICATION_MODAL);
23+
stage.setTitle(titulo);
24+
stage.setScene(new Scene(root));
25+
stage.showAndWait();
26+
27+
} catch (IOException e) {
28+
e.printStackTrace();
29+
}
30+
}
31+
32+
33+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.carvalhotechsolutions.mundoanimal.utils;
2+
3+
import com.carvalhotechsolutions.mundoanimal.model.Agendamento;
4+
import com.itextpdf.text.*;
5+
import com.itextpdf.text.Font;
6+
import com.itextpdf.text.Image;
7+
import com.itextpdf.text.pdf.*;
8+
import org.jfree.chart.ChartFactory;
9+
import org.jfree.chart.ChartUtilities;
10+
import org.jfree.chart.JFreeChart;
11+
import org.jfree.data.general.DefaultPieDataset;
12+
import java.io.*;
13+
import java.time.LocalDate;
14+
import java.time.format.DateTimeFormatter;
15+
import java.util.HashMap;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
public class RelatorioManager {
20+
21+
public void gerarRelatorioPDF(List<Agendamento> agendamentos, LocalDate dataInicio, LocalDate dataFim, File file) throws IOException, DocumentException {
22+
Document document = new Document(PageSize.A4, 40, 40, 50, 50);
23+
PdfWriter.getInstance(document, new FileOutputStream(file));
24+
25+
document.open();
26+
27+
// Título
28+
Font titleFont = new Font(Font.FontFamily.TIMES_ROMAN, 18, Font.BOLD);
29+
Paragraph title = new Paragraph("Relatório de Agendamentos - Pet Shop Mundo Animal", titleFont);
30+
title.setAlignment(Element.ALIGN_CENTER);
31+
document.add(title);
32+
33+
// Período do relatório
34+
Font periodFont = new Font(Font.FontFamily.TIMES_ROMAN, 12, Font.ITALIC);
35+
Paragraph period = new Paragraph("Período: " + formatarData(dataInicio) + " - " + formatarData(dataFim), periodFont);
36+
period.setAlignment(Element.ALIGN_CENTER);
37+
document.add(period);
38+
document.add(new Paragraph("\n"));
39+
40+
// Tabela de agendamentos
41+
PdfPTable table = criarTabelaAgendamentos(agendamentos);
42+
document.add(table);
43+
44+
// Total arrecadado
45+
double total = calcularTotal(agendamentos);
46+
Font totalFont = new Font(Font.FontFamily.TIMES_ROMAN, 14, Font.BOLD);
47+
Paragraph totalParagraph = new Paragraph("Total Arrecadado No Período: R$ " + String.format("%.2f", total), totalFont);
48+
totalParagraph.setAlignment(Element.ALIGN_RIGHT);
49+
totalParagraph.setSpacingBefore(10f);
50+
document.add(totalParagraph);
51+
52+
// Gerar gráfico de pizza
53+
JFreeChart chart = criarGraficoPizza(agendamentos);
54+
Image chartImage = converterGraficoParaImagem(chart);
55+
document.add(chartImage);
56+
57+
document.close();
58+
}
59+
60+
private PdfPTable criarTabelaAgendamentos(List<Agendamento> agendamentos) {
61+
PdfPTable table = new PdfPTable(6);
62+
table.setWidthPercentage(100);
63+
table.setSpacingBefore(10f);
64+
table.setSpacingAfter(10f);
65+
66+
String[] headers = {"Serviço", "Data", "Pet", "Tutor", "Profissional", "Valor"};
67+
for (String header : headers) {
68+
PdfPCell cell = new PdfPCell(new Phrase(header, new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD, BaseColor.WHITE)));
69+
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
70+
cell.setBackgroundColor(BaseColor.DARK_GRAY);
71+
table.addCell(cell);
72+
}
73+
74+
for (Agendamento a : agendamentos) {
75+
table.addCell(a.getServico().getNomeServico());
76+
table.addCell(formatarData(a.getDataAgendamento()));
77+
table.addCell(a.getAnimal().getNome());
78+
table.addCell(a.getCliente().getNome());
79+
table.addCell(a.getResponsavelAtendimento());
80+
table.addCell(String.format("R$ %.2f", a.getServico().getValorServico()));
81+
}
82+
83+
return table;
84+
}
85+
86+
private double calcularTotal(List<Agendamento> agendamentos) {
87+
return agendamentos.stream().mapToDouble(a -> a.getServico().getValorServico().doubleValue()).sum();
88+
}
89+
90+
private JFreeChart criarGraficoPizza(List<Agendamento> agendamentos) {
91+
DefaultPieDataset dataset = new DefaultPieDataset();
92+
Map<String, Integer> servicoContagem = new HashMap<>();
93+
94+
// Contar quantos agendamentos tem por tipo de serviço
95+
for (Agendamento a : agendamentos) {
96+
servicoContagem.put(a.getServico().getNomeServico(), servicoContagem.getOrDefault(a.getServico().getNomeServico(), 0) + 1);
97+
}
98+
99+
// Adicionar ao dataset do gráfico
100+
for (Map.Entry<String, Integer> entry : servicoContagem.entrySet()) {
101+
dataset.setValue(entry.getKey(), entry.getValue());
102+
}
103+
104+
return ChartFactory.createPieChart(
105+
"Distribuição de Agendamentos por Serviço",
106+
dataset,
107+
true, // Legenda
108+
true, // Tooltips
109+
false // URLs
110+
);
111+
}
112+
113+
private Image converterGraficoParaImagem(JFreeChart chart) throws IOException, BadElementException {
114+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
115+
ChartUtilities.writeChartAsPNG(baos, chart, 500, 300);
116+
return Image.getInstance(baos.toByteArray());
117+
}
118+
119+
private String formatarData(LocalDate data) {
120+
return data.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
121+
}
122+
}
123+
124+

src/main/java/module-info.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
requires animatefx;
1111
requires fontawesomefx;
1212
requires java.desktop;
13+
requires itextpdf;
14+
requires jfreechart;
1315

1416
// Export and open the controllers package
1517

src/main/resources/css/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@ root {
307307
-fx-background-color: #1916a1;;
308308
}
309309

310+
.large_btn {
311+
-fx-font-size: 22px;
312+
}
313+
310314
.modal_btn:hover {
311315
-fx-opacity: 0.85;
312316
-fx-cursor: hand;

0 commit comments

Comments
 (0)