From b2f3ef042e4eebb49cec196bbdc58655778ab72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Jim=C3=A9nez?= Date: Sun, 5 Oct 2025 17:47:42 +0200 Subject: [PATCH] se guardan los presupuestos publicos --- .../erp/config/JpaAuditConfig.java | 27 ++-- .../erp/config/SecurityConfig.java | 2 +- .../erp/presupuesto/Presupuesto.java | 17 +-- .../erp/presupuesto/PresupuestoService.java | 140 ++++++++++++------ src/main/resources/application.properties | 6 + .../presupuestador/presupuestador.js | 4 +- 6 files changed, 122 insertions(+), 74 deletions(-) diff --git a/src/main/java/com/imprimelibros/erp/config/JpaAuditConfig.java b/src/main/java/com/imprimelibros/erp/config/JpaAuditConfig.java index 5729aaf..0c2e545 100644 --- a/src/main/java/com/imprimelibros/erp/config/JpaAuditConfig.java +++ b/src/main/java/com/imprimelibros/erp/config/JpaAuditConfig.java @@ -1,35 +1,42 @@ +// JpaAuditConfig.java package com.imprimelibros.erp.config; +import java.util.Optional; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import com.imprimelibros.erp.users.User; - -import java.util.Optional; +import com.imprimelibros.erp.users.UserDao; @Configuration @EnableJpaAuditing(auditorAwareRef = "auditorAware") public class JpaAuditConfig { @Bean - public AuditorAware auditorAware() { + public AuditorAware auditorAware(UserDao userDao) { return () -> { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - if (auth == null || !auth.isAuthenticated()) return Optional.empty(); + var auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null || !auth.isAuthenticated()) + return Optional.empty(); Object principal = auth.getPrincipal(); - if (principal instanceof User u) return Optional.of(u); + + if (principal instanceof User u) + return Optional.of(u); if (principal instanceof UserDetails ud) { - // Si tu principal es UserDetails y no la entidad User, - // aquí podrías cargar User por username si lo necesitas. - return Optional.empty(); + return userDao.findByUserNameIgnoreCase(ud.getUsername()); } + + if (principal instanceof String username && !"anonymousUser".equals(username)) { + return userDao.findByUserNameIgnoreCase(username); + } + return Optional.empty(); }; } diff --git a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java index e348160..20e5161 100644 --- a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java +++ b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java @@ -85,7 +85,7 @@ public class SecurityConfig { .authenticationProvider(provider) .sessionManagement(session -> session - .invalidSessionUrl("/login?expired") + //.invalidSessionUrl("/login?expired") .maximumSessions(1)) // Ignora CSRF para tu recurso público (sin Ant/Mvc matchers) diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java b/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java index 3a6b04b..00dbb63 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java @@ -17,10 +17,11 @@ import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.math.BigDecimal; -import java.time.LocalDateTime; +import java.time.Instant; import com.imprimelibros.erp.users.User; @@ -336,19 +337,6 @@ public class Presupuesto extends AbstractAuditedSoftDeleteEntity implements Clon @Column(name = "alto_faja") private Integer altoFaja = 0; - // ====== AUDIT ====== - - @CreatedDate - @Column(name = "created_at", updatable = false) - private LocalDateTime createdAt; - - @CreatedBy - @Column(name = "created_by", updatable = false) // BIGINT o VARCHAR: ajusta el tipo del campo - private Long createdBy; // o String si tu columna es texto - - - - // ====== MÉTODOS AUX ====== public String resumenPresupuesto() { @@ -874,5 +862,4 @@ public class Presupuesto extends AbstractAuditedSoftDeleteEntity implements Clon this.altoFaja = altoFaja; } - } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java index d71706a..4bb77bc 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java @@ -540,35 +540,6 @@ public class PresupuestoService { return response; } - private Double obtenerPrototipo(Presupuesto presupuesto) { - // Obtenemos el precio de 1 unidad para el ejemplar de prueba - HashMap price = new HashMap<>(); - Presupuesto presupuestoTemp = presupuesto.clone(); - presupuestoTemp.setTirada1(1); - presupuestoTemp.setTirada2(null); - presupuestoTemp.setTirada3(null); - presupuestoTemp.setTirada4(null); - if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.color) { - presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.colorhq); - } else if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.negro) { - presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.negrohq); - } - String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuestoTemp), - presupuestoTemp.getTipoEncuadernacion(), presupuestoTemp.getTipoCubierta()); - Double price_prototipo = 0.0; - try { - price = new ObjectMapper().readValue(priceStr, new TypeReference<>() { - }); - price_prototipo = ((List) ((Map) price.get("data")).get("precios")).get(0); - if (price_prototipo < 25) { - price_prototipo = 25.0; - } - } catch (JsonProcessingException e) { - } catch (Exception exception) { - } - return price_prototipo; - } - public HashMap getPrecioMaquetacion(PresupuestoMaquetacion presupuestoMaquetacion, Locale locale) { try { List lista = maquetacionPreciosRepository.findAll(); @@ -903,7 +874,8 @@ public class PresupuestoService { // IP: guarda hash y trunc (si tienes campos). Si no, guarda tal cual en // ip_trunc/ip_hash según tu modelo. - presupuesto.setIpTrunc(ip); + String ipTrunc = anonymizeIp(ip); + presupuesto.setIpTrunc(ipTrunc); presupuesto.setIpHash(Integer.toHexString(ip.hashCode())); // ubicación (si tienes un servicio GeoIP disponible; si no, omite estas tres @@ -957,6 +929,33 @@ public class PresupuestoService { return resumen; } + /** + * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. + */ + @Transactional + public Presupuesto guardarPrivado(Presupuesto presupuesto) { + presupuesto.setEstado(Presupuesto.Estado.borrador); + return presupuestoRepository.saveAndFlush(presupuesto); + } + + public HashMap calcularPresupuesto(Presupuesto presupuesto, Locale locale) { + HashMap price = new HashMap<>(); + String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuesto), presupuesto.getTipoEncuadernacion(), + presupuesto.getTipoCubierta()); + + try { + price = new ObjectMapper().readValue(priceStr, new TypeReference<>() { + }); + } catch (JsonProcessingException e) { + price = new HashMap<>(); + price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale)); + } + return price; + } + + // ======================================================================= + // Métodos privados + // ======================================================================= /** * Copia de campos "actualizables" para no machacar otros (created_at, etc.) */ @@ -1016,27 +1015,76 @@ public class PresupuestoService { return target; } - /** - * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. - */ - @Transactional - public Presupuesto guardarPrivado(Presupuesto presupuesto) { - presupuesto.setEstado(Presupuesto.Estado.borrador); - return presupuestoRepository.saveAndFlush(presupuesto); - } - - public HashMap calcularPresupuesto(Presupuesto presupuesto, Locale locale) { + private Double obtenerPrototipo(Presupuesto presupuesto) { + // Obtenemos el precio de 1 unidad para el ejemplar de prueba HashMap price = new HashMap<>(); - String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuesto), presupuesto.getTipoEncuadernacion(), - presupuesto.getTipoCubierta()); - + Presupuesto presupuestoTemp = presupuesto.clone(); + presupuestoTemp.setTirada1(1); + presupuestoTemp.setTirada2(null); + presupuestoTemp.setTirada3(null); + presupuestoTemp.setTirada4(null); + if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.color) { + presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.colorhq); + } else if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.negro) { + presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.negrohq); + } + String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuestoTemp), + presupuestoTemp.getTipoEncuadernacion(), presupuestoTemp.getTipoCubierta()); + Double price_prototipo = 0.0; try { price = new ObjectMapper().readValue(priceStr, new TypeReference<>() { }); + price_prototipo = ((List) ((Map) price.get("data")).get("precios")).get(0); + if (price_prototipo < 25) { + price_prototipo = 25.0; + } } catch (JsonProcessingException e) { - price = new HashMap<>(); - price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale)); + } catch (Exception exception) { } - return price; + return price_prototipo; } + + // Utilidad local (puedes moverla a una clase Utils si quieres) + private static String anonymizeIp(String ip) { + if (ip == null) + return null; + // IPv4 + if (ip.contains(".") && !ip.contains(":")) { + String[] p = ip.split("\\."); + if (p.length == 4) { + return p[0] + "." + p[1] + "." + p[2] + ".0"; + } + } + // IPv6: quedarnos con /64 -> primera mitad y rellenar + if (ip.contains(":")) { + String[] parts = ip.split(":", -1); + // expand no estricta, nos quedamos con primeros 4 bloques y completamos + int blocks = Math.min(parts.length, 4); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < blocks; i++) { + if (i > 0) + sb.append(":"); + sb.append(parts[i].isEmpty() ? "0" : parts[i]); + } + // completar a /64 con ceros + for (int i = blocks; i < 8; i++) + sb.append(":0"); + return sb.toString(); + } + return ip; + } + + private static String sha256Hex(String input) { + try { + var md = java.security.MessageDigest.getInstance("SHA-256"); + byte[] digest = md.digest(input.getBytes(java.nio.charset.StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(digest.length * 2); + for (byte b : digest) + sb.append(String.format("%02x", b)); + return sb.toString(); + } catch (Exception e) { + return null; + } + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d1aa346..2721042 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -81,3 +81,9 @@ server.servlet.session.persistent=false geoip.enabled=true geoip.maxmind.enabled=true geoip.http.enabled=true + + +# +# Hibernate Timezone +# +spring.jpa.properties.hibernate.jdbc.time_zone=UTC diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js index aace3a7..a02cdca 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js @@ -1418,8 +1418,8 @@ class PresupuestoCliente { data: JSON.stringify(body) }).then((data) => { $('#resumen-titulo').text(data.titulo); - if (resumen.presupuesto_id) { - window.PRESUPUESTO_ID = resumen.presupuesto_id; + if (data.presupuesto_id) { + window.PRESUPUESTO_ID = data.presupuesto_id; } body.presupuesto.id = window.PRESUPUESTO_ID || body.presupuesto.id || null;