Browse Source

Se corrigio el problema de que no se filtraban bien los tickets por fecha desde y hasta, asi como tambien se cambio a una forma mas vizual de cuando esta generando el excel con lo tickes en el perdiodo indicado.

main
mramirezg 7 days ago
parent
commit
cf6544619c
4 changed files with 188 additions and 110 deletions
  1. +2
    -0
      src/main/java/mx/gob/jumapacelaya/Application.java
  2. +32
    -10
      src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java
  3. +153
    -99
      src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java
  4. +1
    -1
      src/main/resources/application.properties

+ 2
- 0
src/main/java/mx/gob/jumapacelaya/Application.java View File

@ -1,6 +1,7 @@
package mx.gob.jumapacelaya;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.theme.Theme;
import mx.gob.jumapacelaya.services.DatabaseService;
@ -17,6 +18,7 @@ import org.springframework.boot.autoconfigure.validation.ValidationAutoConfigura
*
*/
@SpringBootApplication
@Push
@Theme(value = "sistema-mantenimiento")
@PWA(name = "Aplicacion de Mantenimiento de Equipo de Computo", shortName = "Mantenimiento de Computo", iconPath = "icons/icon.png")
public class Application implements AppShellConfigurator {


+ 32
- 10
src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java View File

@ -51,16 +51,29 @@ public class RedmineClient {
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&offset=" + offset + "&limit=" + limit);
url.append("&sort=created_on:desc");
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
if (fechaDesde != null || fechaHasta != null) {
url.append("&created_on=");
if (fechaDesde != null && fechaHasta != null) {
// En lugar de >< usamos %3E%3C y en lugar de | usamos %7C
url.append("%3E%3C")
.append(fechaDesde.toString())
.append("%7C")
.append(fechaHasta.toString());
} else if (fechaDesde != null) {
url.append("%3E%3D").append(fechaDesde.toString());
} else {
url.append("%3C%3D").append(fechaHasta.toString());
}
}
//log.info("URL: " + url.toString());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
@ -95,11 +108,20 @@ public class RedmineClient {
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
if (fechaDesde != null || fechaHasta != null) {
url.append("&created_on=");
if (fechaDesde != null && fechaHasta != null) {
// En lugar de >< usamos %3E%3C y en lugar de | usamos %7C
url.append("%3E%3C")
.append(fechaDesde.toString())
.append("%7C")
.append(fechaHasta.toString());
} else if (fechaDesde != null) {
url.append("%3E%3D").append(fechaDesde.toString());
} else {
url.append("%3C%3D").append(fechaHasta.toString());
}
}
HttpRequest request = HttpRequest.newBuilder()


+ 153
- 99
src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java View File

@ -22,8 +22,10 @@ import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.component.popover.Popover;
import com.vaadin.flow.component.popover.PopoverPosition;
import com.vaadin.flow.component.progressbar.ProgressBar;
import com.vaadin.flow.component.richtexteditor.RichTextEditor;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.function.SerializableBiConsumer;
@ -91,7 +93,8 @@ public class ActDiariaView extends VerticalLayout {
// Layout de opciones
opcionesLyt = new HorizontalLayout();
opcionesLyt.setWidthFull();
opcionesLyt.setMargin(false);
opcionesLyt.setAlignItems(Alignment.BASELINE);
//opcionesLyt.setMargin(false);
opcionesLyt.getStyle()
.set("box-shadow", "0 4px 8px rgba(0,0,0,0.2)")
.set("border-radius", "10px")
@ -103,18 +106,20 @@ public class ActDiariaView extends VerticalLayout {
chkSoloAbiertos = new Checkbox("Ver solo abiertos");
chkSoloAbiertos.setValue(false); // inicial: mostrar cerrados
fechaDesde = new DatePicker("Fecha desde:");
fechaHasta = new DatePicker("Fecha hasta:");
btnBuscar = new Button("Buscar");
fechaDesde = new DatePicker();
fechaDesde.setPlaceholder("Fecha desde:");
fechaHasta = new DatePicker();
fechaHasta.setPlaceholder("Fecha hasta:");
btnBuscar = new Button("Buscar", VaadinIcon.SEARCH.create());
// Listener del checkbox
chkSoloAbiertos.addValueChangeListener(e -> {
boolean soloAbiertos = e.getValue(); // marcado = abiertos
loadTickets(soloAbiertos);
fechaDesde.setEnabled(!soloAbiertos);
fechaHasta.setEnabled(!soloAbiertos);
btnBuscar.setEnabled(!soloAbiertos);
grid.getDataProvider().refreshAll();
});
// Estado inicial
@ -128,6 +133,30 @@ public class ActDiariaView extends VerticalLayout {
opcionesLyt.add(chkSoloAbiertos, fechaDesde, fechaHasta, btnBuscar);
grid.setItems(
query -> {
try {
return redmineClient.getTickets(
userService.getRedmineUser(),
chkSoloAbiertos.getValue(),
query.getOffset(),
query.getLimit(),
fechaDesde.getValue(),
fechaHasta.getValue()
).stream();
} catch (Exception e) {
log.error("Error al cargar tickets", e);
return Stream.empty();
}
},
query -> redmineClient.getTotalTickets(
userService.getRedmineUser(),
chkSoloAbiertos.getValue(),
fechaDesde.getValue(),
fechaHasta.getValue()
)
);
// Configuración de columnas del grid
grid.addColumn(Ticket::getId).setHeader("No.")
.setAutoWidth(true)
@ -148,6 +177,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha creación")
.setAutoWidth(true)
.setSortable(true)
.setKey("fechaCreacion");
grid.addColumn(ticket -> {
@ -155,6 +185,7 @@ public class ActDiariaView extends VerticalLayout {
return fecha != null ? fecha.format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")) : "";
}).setHeader("Fecha cierre")
.setAutoWidth(true)
.setSortable(true)
.setKey("fechaCierre");
grid.addColumn(ticket -> {
@ -198,7 +229,7 @@ public class ActDiariaView extends VerticalLayout {
} else {
return "";
}
}).setHeader("Duración").setAutoWidth(true).setKey("duracion");
}).setHeader("Duración").setAutoWidth(true).setSortable(true).setKey("duracion");
grid.addColumn(ticket ->
@ -229,6 +260,7 @@ public class ActDiariaView extends VerticalLayout {
btnColumns = new Button(VaadinIcon.GRID_H.create());
// Botón para exportar
btnExport = new Button("Exportar", LineAwesomeIcon.FILE_EXCEL.create());
btnExport.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
btnExport.addClickListener(e -> exportTicketsCerrados());
showColumnsLyt = new HorizontalLayout(btnExport, btnColumns);
showColumnsLyt.setJustifyContentMode(JustifyContentMode.END);
@ -281,30 +313,6 @@ public class ActDiariaView extends VerticalLayout {
// Invertir parámetro para que coincida con RedmineClient
boolean paramCliente = !soloAbiertos;
grid.setItems(
query -> {
try {
return redmineClient.getTickets(
user,
chkSoloAbiertos.getValue(),
query.getOffset(),
query.getLimit(),
fechaDesde.getValue(),
fechaHasta.getValue()
).stream();
} catch (Exception e) {
e.printStackTrace();
return Stream.empty();
}
},
query -> redmineClient.getTotalTickets(
user,
chkSoloAbiertos.getValue(),
fechaDesde.getValue(),
fechaHasta.getValue()
)
);
}
@ -399,85 +407,131 @@ public class ActDiariaView extends VerticalLayout {
// Metodo para exportar los tickets cerrados a un archivo de Excel
private void exportTicketsCerrados() {
try {
RedmineUser user = userService.getRedmineUser();
// Traer TODOS los tickets cerrados, respetando fechas si están seleccionadas
List<Ticket> ticketsCerrados = redmineClient.getAllTickets(
user,
false, // false = cerrados
fechaDesde.getValue(),
fechaHasta.getValue()
);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Tickets cerrados");
String[] headers = {"ID","Tipo","Tiempo estimado","Fecha creación","Fecha cierre",
"Fecha actualización","Situación","Estado","Duración","Autor","Asunto"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
final UI ui = UI.getCurrent();
final RedmineUser user = userService.getRedmineUser();
final LocalDate desde = fechaDesde.getValue();
final LocalDate hasta = fechaHasta.getValue();
int rowNum = 1;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
for (Ticket ticket : ticketsCerrados) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(ticket.getId());
row.createCell(1).setCellValue(ticket.getType());
row.createCell(2).setCellValue(ticket.getEstimatedHrs() + " horas");
row.createCell(3).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(4).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(5).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getStatus());
row.createCell(9).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(10).setCellValue(ticket.getSubject());
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long dias = ChronoUnit.DAYS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(8).setCellValue(dias + " días");
} else {
row.createCell(8).setCellValue("");
}
if (ui == null) return;
if (ticket.getEstimatedHrs() != null && ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long horasReales = ChronoUnit.HOURS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(6).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
} else {
row.createCell(6).setCellValue("Sin estimación");
Dialog loadingDialog = createLoadingDialog("Generando archivo por favor espere...");
loadingDialog.open();
new Thread(() -> {
try {
// Traer TODOS los tickets cerrados, respetando fechas si están seleccionadas
List<Ticket> ticketsCerrados = redmineClient.getAllTickets(
user,
false, // false = cerrados
desde,
hasta
);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Tickets cerrados");
String[] headers = {"ID","Tipo","Tiempo estimado","Fecha creación","Fecha cierre",
"Fecha actualización","Situación","Estado","Duración","Autor","Asunto"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
}
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
int rowNum = 1;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
for (Ticket ticket : ticketsCerrados) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(ticket.getId());
row.createCell(1).setCellValue(ticket.getType());
row.createCell(2).setCellValue(ticket.getEstimatedHrs() + " horas");
row.createCell(3).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(4).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(5).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getStatus());
row.createCell(9).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(10).setCellValue(ticket.getSubject());
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long dias = ChronoUnit.DAYS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(8).setCellValue(dias + " días");
} else {
row.createCell(8).setCellValue("");
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
if (ticket.getEstimatedHrs() != null && ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long horasReales = ChronoUnit.HOURS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(6).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
} else {
row.createCell(6).setCellValue("Sin estimación");
}
}
StreamResource resource = new StreamResource("tickets_cerrados.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
StreamRegistration registration = UI.getCurrent().getSession()
.getResourceRegistry().registerResource(resource);
UI.getCurrent().getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
} catch (Exception e) {
log.error("Error exportando tickets", e);
Notification.show("Error al exportar los tickets", 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
}
ui.access(() -> {
try {
StreamResource resource = new StreamResource("rptGeneralDeTicketsMsaAyda.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
StreamRegistration registration = ui.getSession()
.getResourceRegistry().registerResource(resource);
ui.getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
} catch (Exception ex) {
log.error("Error al registrar recurso", ex);
} finally {
loadingDialog.close();
}
});
} catch (Exception e) {
log.error("Error exportando tickets", e);
ui.access(() -> {
loadingDialog.close();
Notification.show("Error al generar el reporte", 5000, Notification.Position.MIDDLE)
.addThemeVariants(NotificationVariant.LUMO_ERROR);
});
}
}).start();
}
private void filtrarTickets() {
boolean soloAbiertos = chkSoloAbiertos.getValue();
loadTickets(soloAbiertos);
grid.getDataProvider().refreshAll();
}
private Dialog createLoadingDialog(String mensaje) {
Dialog dialog = new Dialog();
dialog.setCloseOnEsc(false);
dialog.setCloseOnOutsideClick(false);
VerticalLayout layout = new VerticalLayout();
layout.setAlignItems(Alignment.CENTER);
layout.setPadding(true);
layout.setSpacing(true);
ProgressBar progressBar = new ProgressBar();
progressBar.setIndeterminate(true);
progressBar.setWidth("15rem");
Span texto = new Span(mensaje);
texto.getStyle().set("font-weight", "bold");
texto.getStyle().set("color", "var(--lumo-primary-text-color)");
layout.add(texto, progressBar);
dialog.add(layout);
return dialog;
}
}

+ 1
- 1
src/main/resources/application.properties View File

@ -18,7 +18,7 @@ spring.ldap.username=administrator
spring.ldap.password=Dr3na$134%4guA
###################PRODUCTIVO####################
redmine.url=https://proyman.jumapacelaya.gob.mx/
redmine.url=https://proyman.jumapacelaya.gob.mx
redmine.api_key=69be2a5df9bacce02722f566fdf0731d728a1b86


Loading…
Cancel
Save