añadida validacion en el backend para datos generales

This commit is contained in:
Jaime Jiménez
2025-07-28 13:03:34 +02:00
parent 14f6633b83
commit 8b34d6dca9
44 changed files with 1138 additions and 308 deletions

View File

@ -4,6 +4,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
public class SecurityConfig {
@ -11,15 +12,24 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/assets/**", "/css/**", "/js/**", "/images/**", "/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(login -> login
//.loginPage("/login") añadir cuando se tenga login personalizado
.permitAll()
)
.logout(logout -> logout.permitAll());
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/",
"/assets/**",
"/css/**",
"/js/**",
"/images/**",
"/public/**",
"/error",
"/presupuesto/validar/**")
.permitAll()
.anyRequest().authenticated())
.csrf(csrf -> csrf
.ignoringRequestMatchers("/presupuesto/validar/**"))
.formLogin(login -> login
// .loginPage("/login") añadir cuando se tenga login personalizado
.permitAll())
.logout(logout -> logout.permitAll());
return http.build();
}

View File

@ -0,0 +1,16 @@
package com.imprimelibros.erp.config.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = ConsistentTiradasValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConsistentTiradas {
String message() default "Las tiradas deben ser todas mayores o todas menores al valor POD";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,52 @@
package com.imprimelibros.erp.config.validation;
import com.imprimelibros.erp.entity.Presupuesto;
import com.imprimelibros.erp.service.VariableService;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
public class ConsistentTiradasValidator implements ConstraintValidator<ConsistentTiradas, Presupuesto> {
@Autowired
private VariableService variableService;
@Autowired
private MessageSource messageSource;
@Override
public boolean isValid(Presupuesto presupuesto, ConstraintValidatorContext context) {
if (presupuesto == null)
return true;
Integer[] tiradas = presupuesto.getTiradas();
Integer podValue = variableService.getValorEntero("POD");
boolean allAbove = true;
boolean allBelow = true;
for (Integer t : tiradas) {
if (t == null)
continue;
if (t <= podValue)
allAbove = false;
if (t >= podValue)
allBelow = false;
}
if (!(allAbove || allBelow)) {
String mensajeInterpolado = messageSource.getMessage(
"presupuesto.errores.tiradas.consistentes", // clave del mensaje
new Object[] { podValue }, // parámetros para {0}
LocaleContextHolder.getLocale() // respeta el idioma actual
);
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(mensajeInterpolado)
.addConstraintViolation();
return false;
}
return true;
}
}

View File

@ -0,0 +1,16 @@
package com.imprimelibros.erp.config.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = ParValidator.class)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface Par {
String message() default "El valor debe ser un número par";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,14 @@
package com.imprimelibros.erp.config.validation;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class ParValidator implements ConstraintValidator<Par, Integer> {
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (value == null) return true; // se permite null, usa @NotNull aparte si lo necesitas
return value % 2 == 0;
}
}

View File

@ -0,0 +1,9 @@
package com.imprimelibros.erp.config.validation;
public class PresupuestoValidationGroups {
public interface DatosGenerales {}
public interface Interior {}
public interface Cubierta {}
}

View File

@ -1,24 +1,46 @@
package com.imprimelibros.erp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.service.VariableService;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@Controller
public class HomeController {
@Autowired
private MessageSource messageSource;
private TranslationService translationService;
@Autowired
private VariableService variableService;
@GetMapping("/")
public String index(Model model, Locale locale) {
//model.addAttribute("title", messageSource.getMessage("t-home", null, locale));
model.addAttribute("title", "Inicio");
return "imprimelibros/home";
public String index(Model model, Authentication authentication, Locale locale) {
boolean isAuthenticated = authentication != null && authentication.isAuthenticated()
&& !(authentication instanceof AnonymousAuthenticationToken);
if (!isAuthenticated) {
List<String> keys = List.of(
"presupuesto.plantilla-cubierta",
"presupuesto.plantilla-cubierta-text",
"presupuesto.impresion-cubierta",
"presupuesto.impresion-cubierta-help");
Map<String, String> translations = translationService.getTranslations(locale, keys);
model.addAttribute("languageBundle", translations);
model.addAttribute("pod", variableService.getValorEntero("POD"));
}
return "imprimelibros/home";
}
}

View File

@ -0,0 +1,53 @@
package com.imprimelibros.erp.controller;
import org.springframework.web.bind.annotation.RestController;
import com.imprimelibros.erp.service.PresupuestoService;
import java.util.HashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import jakarta.validation.Valid; // o javax.validation.Valid según tu versión
import java.util.Map;
import com.imprimelibros.erp.config.validation.PresupuestoValidationGroups;
import com.imprimelibros.erp.entity.Presupuesto;
@RestController
@RequestMapping("/presupuesto")
public class PresupuestoController {
@Autowired
protected PresupuestoService presupuestoService;
@PostMapping("/validar/datos-generales")
public ResponseEntity<?> validarDatosGenerales(
@Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
BindingResult result) {
Map<String, String> errores = new HashMap<>();
// errores de campos individuales
result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
// errores globales (como tu @ConsistentTiradas)
result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
if (!errores.isEmpty()) {
return ResponseEntity.badRequest().body(errores);
}
return ResponseEntity.ok().build();
}
}

View File

@ -0,0 +1,153 @@
package com.imprimelibros.erp.entity;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.persistence.*;
import com.imprimelibros.erp.config.validation.PresupuestoValidationGroups;
import com.imprimelibros.erp.config.validation.ConsistentTiradas;
import com.imprimelibros.erp.config.validation.Par;
@ConsistentTiradas(groups = PresupuestoValidationGroups.DatosGenerales.class)
@Entity
@Table(name = "presupuesto")
public class Presupuesto {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Otros campos del presupuesto
@NotBlank(message = "{presupuesto.errores.titulo}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Column(name = "titulo")
private String titulo;
@Column(name = "autor")
private String autor;
@Column(name = "isbn")
private String isbn;
@NotNull(message = "{presupuesto.errores.tirada1}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Column(name = "tirada1")
private Integer tirada1;
@Column(name = "tirada2")
private Integer tirada2;
@Column(name = "tirada3")
private Integer tirada3;
@Column(name = "tirada4")
private Integer tirada4;
@Par(message = "{presupuesto.errores.paginasNegro.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@NotNull(message = "{presupuesto.errores.paginasNegro.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Column(name = "paginas_negro")
private Integer paginasNegro;
@Par(message = "{presupuesto.errores.paginasColor.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@NotNull(message = "{presupuesto.errores.paginasColor.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Column(name = "paginas_color")
private Integer paginasColor;
@NotNull(message = "El papel interior no puede estar vacío", groups = PresupuestoValidationGroups.Interior.class)
@Column(name = "papel_interior")
private Integer papelInterior;
// Getters y Setters
public String getAutor() {
return autor;
}
public void setAutor(String autor) {
this.autor = autor;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public Integer getTirada1() {
return tirada1;
}
public void setTirada1(Integer tirada1) {
this.tirada1 = tirada1;
}
public Integer getTirada2() {
return tirada2;
}
public void setTirada2(Integer tirada2) {
this.tirada2 = tirada2;
}
public Integer getTirada3() {
return tirada3;
}
public void setTirada3(Integer tirada3) {
this.tirada3 = tirada3;
}
public Integer getTirada4() {
return tirada4;
}
public Integer[] getTiradas() {
return new Integer[]{tirada1, tirada2, tirada3, tirada4};
}
public void setTirada4(Integer tirada4) {
this.tirada4 = tirada4;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getPaginasNegro() {
return paginasNegro;
}
public void setPaginasNegro(Integer paginasNegro) {
this.paginasNegro = paginasNegro;
}
public Integer getPaginasColor() {
return paginasColor;
}
public void setPaginasColor(Integer paginasColor) {
this.paginasColor = paginasColor;
}
public Integer getPapelInterior() {
return papelInterior;
}
public void setPapelInterior(Integer papelInterior) {
this.papelInterior = papelInterior;
}
}

View File

@ -0,0 +1,30 @@
package com.imprimelibros.erp.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "variables")
public class Variable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String clave;
private String valor;
// Getters y setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getClave() { return clave; }
public void setClave(String clave) { this.clave = clave; }
public String getValor() { return valor; }
public void setValor(String valor) { this.valor = valor; }
}

View File

@ -0,0 +1,22 @@
package com.imprimelibros.erp.i18n;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class TranslationService {
@Autowired
private MessageSource messageSource;
public Map<String, String> getTranslations(Locale locale, List<String> keys) {
Map<String, String> translations = new HashMap<>();
for (String key : keys) {
translations.put(key, messageSource.getMessage(key, null, locale));
}
return translations;
}
}

View File

@ -0,0 +1,10 @@
package com.imprimelibros.erp.repository;
import com.imprimelibros.erp.entity.Variable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface VariableRepository extends JpaRepository<Variable, Long> {
Optional<Variable> findByClave(String clave);
}

View File

@ -0,0 +1,18 @@
package com.imprimelibros.erp.service;
import org.springframework.stereotype.Service;
@Service
public class PresupuestoService {
public boolean validateDatosGenerales(int[] tiradas) {
// Implement the logic to validate the tiradas
// For example, check if all tiradas are positive integers
for (int tirada : tiradas) {
if (tirada <= 0) {
return false; // Invalid tirada found
}
}
return true; // All tiradas are valid
}
}

View File

@ -0,0 +1,20 @@
package com.imprimelibros.erp.service;
import com.imprimelibros.erp.repository.VariableRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class VariableService {
@Autowired
private VariableRepository variableRepository;
public Integer getValorEntero(String clave) {
return variableRepository.findByClave(clave)
.<Integer>map(v -> Integer.parseInt(v.getValor()))
.orElseThrow(
() -> new IllegalArgumentException("No se encontró la variable con clave '" + clave + "'"));
}
}