mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 00:48:49 +00:00
falta borrar direcciones e implementar una vista de cliente
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
package com.imprimelibros.erp.config;
|
package com.imprimelibros.erp.config;
|
||||||
|
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
@ -10,13 +11,16 @@ import jakarta.validation.ValidatorFactory;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class BeanValidationConfig {
|
public class BeanValidationConfig {
|
||||||
|
|
||||||
// Asegura que usamos la factory de Spring (con SpringConstraintValidatorFactory)
|
// Usa el MessageSource (messages*.properties) para resolver {códigos}
|
||||||
@Bean
|
@Bean
|
||||||
public LocalValidatorFactoryBean validator() {
|
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
|
||||||
return new LocalValidatorFactoryBean();
|
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
|
||||||
|
bean.setValidationMessageSource(messageSource); // <-- CLAVE
|
||||||
|
|
||||||
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserta esa factory en Hibernate/JPA
|
// Inserta esa factory en Hibernate/JPA (opcional pero correcto)
|
||||||
@Bean
|
@Bean
|
||||||
public HibernatePropertiesCustomizer hibernateValidationCustomizer(ValidatorFactory vf) {
|
public HibernatePropertiesCustomizer hibernateValidationCustomizer(ValidatorFactory vf) {
|
||||||
return props -> props.put("jakarta.persistence.validation.factory", vf);
|
return props -> props.put("jakarta.persistence.validation.factory", vf);
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import com.imprimelibros.erp.common.jpa.AbstractAuditedEntity;
|
|||||||
import com.imprimelibros.erp.paises.Paises;
|
import com.imprimelibros.erp.paises.Paises;
|
||||||
import com.imprimelibros.erp.users.User;
|
import com.imprimelibros.erp.users.User;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "direcciones", indexes = {
|
@Table(name = "direcciones", indexes = {
|
||||||
@Index(name = "idx_direcciones_user", columnList = "user_id"),
|
@Index(name = "idx_direcciones_user", columnList = "user_id"),
|
||||||
@ -42,32 +45,41 @@ public class Direccion extends AbstractAuditedEntity implements Serializable {
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
// --- FK a users(id)
|
// --- FK a users(id)
|
||||||
|
@NotNull(message = "{direcciones.form.error.required}")
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
@JoinColumn(name = "user_id", nullable = false)
|
@JoinColumn(name = "user_id", nullable = false)
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "alias", length = 100, nullable = false)
|
@Column(name = "alias", length = 100, nullable = false)
|
||||||
private String alias;
|
private String alias;
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "att", length = 150, nullable = false)
|
@Column(name = "att", length = 150, nullable = false)
|
||||||
private String att;
|
private String att;
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "direccion", length = 255, nullable = false)
|
@Column(name = "direccion", length = 255, nullable = false)
|
||||||
private String direccion;
|
private String direccion;
|
||||||
|
|
||||||
|
@NotNull(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "cp", length = 20, nullable = false)
|
@Column(name = "cp", length = 20, nullable = false)
|
||||||
private Integer cp;
|
private Integer cp;
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "ciudad", length = 100, nullable = false)
|
@Column(name = "ciudad", length = 100, nullable = false)
|
||||||
private String ciudad;
|
private String ciudad;
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "provincia", length = 100, nullable = false)
|
@Column(name = "provincia", length = 100, nullable = false)
|
||||||
private String provincia;
|
private String provincia;
|
||||||
|
|
||||||
// Usamos el code3 del país como FK lógica (String)
|
// Usamos el code3 del país como FK lógica (String)
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "pais_code3", length = 3, nullable = false)
|
@Column(name = "pais_code3", length = 3, nullable = false)
|
||||||
private String paisCode3 = "esp";
|
private String paisCode3 = "esp";
|
||||||
|
|
||||||
|
@NotBlank(message = "{direcciones.form.error.required}")
|
||||||
@Column(name = "telefono", length = 30, nullable = false)
|
@Column(name = "telefono", length = 30, nullable = false)
|
||||||
private String telefono;
|
private String telefono;
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -21,11 +23,15 @@ import com.imprimelibros.erp.datatables.DataTable;
|
|||||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||||
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||||
|
import com.imprimelibros.erp.i18n.TranslationService;
|
||||||
import com.imprimelibros.erp.paises.PaisesService;
|
import com.imprimelibros.erp.paises.PaisesService;
|
||||||
|
import com.imprimelibros.erp.users.UserDao;
|
||||||
|
import com.imprimelibros.erp.users.UserDetailsImpl;
|
||||||
|
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/direcciones")
|
@RequestMapping("/direcciones")
|
||||||
@ -34,11 +40,16 @@ public class DireccionController {
|
|||||||
protected final DireccionRepository repo;
|
protected final DireccionRepository repo;
|
||||||
protected final PaisesService paisesService;
|
protected final PaisesService paisesService;
|
||||||
protected final MessageSource messageSource;
|
protected final MessageSource messageSource;
|
||||||
|
protected final UserDao userRepo;
|
||||||
|
protected final TranslationService translationService;
|
||||||
|
|
||||||
public DireccionController(DireccionRepository repo, PaisesService paisesService, MessageSource messageSource) {
|
public DireccionController(DireccionRepository repo, PaisesService paisesService,
|
||||||
|
MessageSource messageSource, UserDao userRepo, TranslationService translationService) {
|
||||||
this.repo = repo;
|
this.repo = repo;
|
||||||
this.paisesService = paisesService;
|
this.paisesService = paisesService;
|
||||||
this.messageSource = messageSource;
|
this.messageSource = messageSource;
|
||||||
|
this.userRepo = userRepo;
|
||||||
|
this.translationService = translationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
@ -48,6 +59,19 @@ public class DireccionController {
|
|||||||
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"));
|
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"));
|
||||||
model.addAttribute("isUser", isUser ? 1 : 0);
|
model.addAttribute("isUser", isUser ? 1 : 0);
|
||||||
|
|
||||||
|
List<String> keys = List.of(
|
||||||
|
"margenes-presupuesto.delete.title",
|
||||||
|
"margenes-presupuesto.delete.text",
|
||||||
|
"margenes-presupuesto.eliminar",
|
||||||
|
"margenes-presupuesto.delete.button",
|
||||||
|
"app.yes",
|
||||||
|
"app.cancelar",
|
||||||
|
"margenes-presupuesto.delete.ok.title",
|
||||||
|
"margenes-presupuesto.delete.ok.text");
|
||||||
|
|
||||||
|
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||||
|
model.addAttribute("languageBundle", translations);
|
||||||
|
|
||||||
return "imprimelibros/direcciones/direccion-list";
|
return "imprimelibros/direcciones/direccion-list";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,13 +104,13 @@ public class DireccionController {
|
|||||||
if (authentication != null && authentication.getAuthorities().stream()
|
if (authentication != null && authentication.getAuthorities().stream()
|
||||||
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"))) {
|
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"))) {
|
||||||
String username = authentication.getName();
|
String username = authentication.getName();
|
||||||
predicates.add(cb.equal(root.get("user").get("username"), username));
|
predicates.add(cb.equal(root.get("user").get("userName"), username));
|
||||||
}
|
}
|
||||||
|
|
||||||
return cb.and(predicates.toArray(new Predicate[0]));
|
return cb.and(predicates.toArray(new Predicate[0]));
|
||||||
};
|
};
|
||||||
|
|
||||||
long total = repo.count(); // total sin filtro global
|
long total = repo.count(base);
|
||||||
|
|
||||||
// Construcción del datatable con entity + spec
|
// Construcción del datatable con entity + spec
|
||||||
return DataTable
|
return DataTable
|
||||||
@ -168,11 +192,11 @@ public class DireccionController {
|
|||||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("direccion", opt.get());
|
model.addAttribute("dirForm", opt.get());
|
||||||
model.addAttribute("action", "/direcciones/" + id);
|
model.addAttribute("action", "/direcciones/" + id);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
model.addAttribute("direccion", new Direccion());
|
model.addAttribute("dirForm", new Direccion());
|
||||||
model.addAttribute("action", "/direcciones");
|
model.addAttribute("action", "/direcciones");
|
||||||
}
|
}
|
||||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
@ -180,7 +204,7 @@ public class DireccionController {
|
|||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public String create(
|
public String create(
|
||||||
Direccion direccion,
|
@Valid @ModelAttribute("dirForm") Direccion direccion,
|
||||||
BindingResult binding,
|
BindingResult binding,
|
||||||
Model model,
|
Model model,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
@ -188,130 +212,110 @@ public class DireccionController {
|
|||||||
|
|
||||||
if (binding.hasErrors()) {
|
if (binding.hasErrors()) {
|
||||||
response.setStatus(422);
|
response.setStatus(422);
|
||||||
model.addAttribute("action", "/direcciones/");
|
model.addAttribute("paises", paisesService.getForSelect("", "", locale).get("results"));
|
||||||
|
model.addAttribute("action", "/direcciones");
|
||||||
|
model.addAttribute("dirForm", direccion);
|
||||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = direccion;
|
var data = direccion;
|
||||||
|
|
||||||
|
repo.save(data);
|
||||||
try {
|
|
||||||
repo.save(data);
|
|
||||||
} catch (jakarta.validation.ConstraintViolationException vex) {
|
|
||||||
// Errores de Bean Validation disparados al flush (incluye tu @NoRangeOverlap)
|
|
||||||
vex.getConstraintViolations().forEach(v -> {
|
|
||||||
// intenta asignar al campo si existe, si no, error global
|
|
||||||
String path = v.getPropertyPath() != null ? v.getPropertyPath().toString() : null;
|
|
||||||
String code = v.getMessage() != null ? v.getMessage().trim() : "";
|
|
||||||
|
|
||||||
if (code.startsWith("{") && code.endsWith("}")) {
|
|
||||||
code = code.substring(1, code.length() - 1); // -> "validation.required"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path != null && binding.getFieldError(path) == null) {
|
|
||||||
|
|
||||||
binding.rejectValue(path, "validation", messageSource.getMessage(code, null, locale));
|
|
||||||
} else {
|
|
||||||
binding.reject("validation", messageSource.getMessage(code, null, locale));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response.setStatus(422);
|
|
||||||
model.addAttribute("action", "/direcciones/");
|
|
||||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
|
||||||
}
|
|
||||||
response.setStatus(201);
|
response.setStatus(201);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
@PutMapping("/{id}")
|
@PostMapping("/{id}")
|
||||||
public String edit(
|
public String update(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
MargenPresupuesto form,
|
@Valid @ModelAttribute("dirForm") Direccion direccion, // <- nombre distinto
|
||||||
BindingResult binding,
|
BindingResult binding,
|
||||||
Model model,
|
Model model,
|
||||||
|
Authentication auth,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
Locale locale) {
|
Locale locale) {
|
||||||
|
|
||||||
var uOpt = repo.findById(id);
|
var opt = repo.findById(id);
|
||||||
if (uOpt.isEmpty()) {
|
if (opt.isEmpty()) {
|
||||||
binding.reject("usuarios.error.noEncontrado",
|
binding.reject("direcciones.error.noEncontrado",
|
||||||
messageSource.getMessage("usuarios.error.noEncontrado", null, locale));
|
messageSource.getMessage("direcciones.error.noEncontrado", null, locale));
|
||||||
|
response.setStatus(404);
|
||||||
|
model.addAttribute("dirForm", direccion); // por si re-renderiza
|
||||||
|
model.addAttribute("action", "/direcciones/" + id);
|
||||||
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
|
}
|
||||||
|
|
||||||
|
Long ownerId = opt.get().getUser() != null ? opt.get().getUser().getId() : null;
|
||||||
|
if (!isOwnerOrAdmin(auth, ownerId)) {
|
||||||
|
binding.reject("direcciones.error.sinPermiso",
|
||||||
|
messageSource.getMessage("direcciones.error.sinPermiso", null, locale));
|
||||||
|
response.setStatus(403);
|
||||||
|
model.addAttribute("dirForm", direccion); // por si re-renderiza
|
||||||
|
model.addAttribute("action", "/direcciones/" + id);
|
||||||
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binding.hasErrors()) {
|
if (binding.hasErrors()) {
|
||||||
response.setStatus(422);
|
response.setStatus(422);
|
||||||
model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
|
model.addAttribute("dirForm", direccion); // <- importante
|
||||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
model.addAttribute("paises", paisesService.getForSelect("", "", locale).get("results"));
|
||||||
|
model.addAttribute("action", "/direcciones/" + id);
|
||||||
|
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
var entity = uOpt.get();
|
repo.save(direccion);
|
||||||
|
response.setStatus(200);
|
||||||
// 3) Copiar solamente campos editables
|
|
||||||
entity.setImporteMin(form.getImporteMin());
|
|
||||||
entity.setImporteMax(form.getImporteMax());
|
|
||||||
entity.setMargenMax(form.getMargenMax());
|
|
||||||
entity.setMargenMin(form.getMargenMin());
|
|
||||||
|
|
||||||
try {
|
|
||||||
repo.saveAndFlush(entity);
|
|
||||||
|
|
||||||
} catch (jakarta.validation.ConstraintViolationException vex) {
|
|
||||||
// Errores de Bean Validation disparados al flush (incluye tu @NoRangeOverlap)
|
|
||||||
vex.getConstraintViolations().forEach(v -> {
|
|
||||||
// intenta asignar al campo si existe, si no, error global
|
|
||||||
String path = v.getPropertyPath() != null ? v.getPropertyPath().toString() : null;
|
|
||||||
String code = v.getMessage() != null ? v.getMessage().trim() : "";
|
|
||||||
|
|
||||||
if (code.startsWith("{") && code.endsWith("}")) {
|
|
||||||
code = code.substring(1, code.length() - 1); // -> "validation.required"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path != null && binding.getFieldError(path) == null) {
|
|
||||||
|
|
||||||
binding.rejectValue(path, "validation", messageSource.getMessage(code, null, locale));
|
|
||||||
} else {
|
|
||||||
binding.reject("validation", messageSource.getMessage(code, null, locale));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response.setStatus(422);
|
|
||||||
model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
|
|
||||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
|
||||||
|
|
||||||
} catch (org.springframework.dao.DataIntegrityViolationException dex) {
|
|
||||||
// Uniques, FKs, checks… mensajes de la BD
|
|
||||||
String msg = dex.getMostSpecificCause() != null ? dex.getMostSpecificCause().getMessage()
|
|
||||||
: dex.getMessage();
|
|
||||||
binding.reject("db.error", messageSource.getMessage(msg, null, locale));
|
|
||||||
response.setStatus(422);
|
|
||||||
model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
|
|
||||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
|
||||||
}
|
|
||||||
|
|
||||||
response.setStatus(204);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
/*
|
||||||
@Transactional
|
*
|
||||||
public ResponseEntity<?> delete(@PathVariable Long id, Authentication auth, Locale locale) {
|
* @DeleteMapping("/{id}")
|
||||||
|
*
|
||||||
|
* @Transactional
|
||||||
|
* public ResponseEntity<?> delete(@PathVariable Long id, Authentication auth,
|
||||||
|
* Locale locale) {
|
||||||
|
*
|
||||||
|
* return repo.findById(id).map(u -> {
|
||||||
|
* try {
|
||||||
|
* u.setDeleted(true);
|
||||||
|
* u.setDeletedAt(LocalDateTime.now());
|
||||||
|
*
|
||||||
|
* repo.save(u); // ← NO delete(); guardamos el soft delete con deleted_by
|
||||||
|
* relleno
|
||||||
|
* return ResponseEntity.ok(Map.of("message",
|
||||||
|
* messageSource.getMessage("margenes-presupuesto.exito.eliminado", null,
|
||||||
|
* locale)));
|
||||||
|
* } catch (Exception ex) {
|
||||||
|
* return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
* .body(Map.of("message",
|
||||||
|
* messageSource.getMessage("margenes-presupuesto.error.delete-internal-error",
|
||||||
|
* null,
|
||||||
|
* locale)));
|
||||||
|
* }
|
||||||
|
* }).orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||||
|
* .body(Map.of("message",
|
||||||
|
* messageSource.getMessage("margenes-presupuesto.error.not-found", null,
|
||||||
|
* locale))));
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
return repo.findById(id).map(u -> {
|
private boolean isOwnerOrAdmin(Authentication auth, Long ownerId) {
|
||||||
try {
|
if (auth == null) {
|
||||||
u.setDeleted(true);
|
return false;
|
||||||
u.setDeletedAt(LocalDateTime.now());
|
}
|
||||||
|
boolean isAdmin = auth.getAuthorities().stream()
|
||||||
repo.save(u); // ← NO delete(); guardamos el soft delete con deleted_by relleno
|
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
|
||||||
return ResponseEntity.ok(Map.of("message",
|
if (isAdmin) {
|
||||||
messageSource.getMessage("margenes-presupuesto.exito.eliminado", null, locale)));
|
return true;
|
||||||
} catch (Exception ex) {
|
}
|
||||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
// Aquí deberías obtener el ID del usuario actual desde tu servicio de usuarios
|
||||||
.body(Map.of("message",
|
Long currentUserId = null;
|
||||||
messageSource.getMessage("margenes-presupuesto.error.delete-internal-error", null,
|
if (auth != null && auth.getPrincipal() instanceof UserDetailsImpl udi) {
|
||||||
locale)));
|
currentUserId = udi.getId();
|
||||||
}
|
} else if (auth != null) {
|
||||||
}).orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND)
|
currentUserId = userRepo.findIdByUserNameIgnoreCase(auth.getName()).orElse(null);
|
||||||
.body(Map.of("message",
|
}
|
||||||
messageSource.getMessage("margenes-presupuesto.error.not-found", null, locale))));
|
return currentUserId != null && currentUserId.equals(ownerId);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,54 +13,22 @@ public interface DireccionRepository
|
|||||||
extends JpaRepository<Direccion, Long>,
|
extends JpaRepository<Direccion, Long>,
|
||||||
JpaSpecificationExecutor<Direccion> {
|
JpaSpecificationExecutor<Direccion> {
|
||||||
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
select d from Direccion d
|
||||||
d.id AS id,
|
left join fetch d.user
|
||||||
d.alias AS alias,
|
left join fetch d.pais
|
||||||
d.att AS att,
|
where d.user.id = :id
|
||||||
d.direccion AS direccion,
|
""")
|
||||||
d.cp AS cp,
|
|
||||||
d.ciudad AS ciudad,
|
|
||||||
d.provincia AS provincia,
|
|
||||||
d.paisCode3 AS paisCode3,
|
|
||||||
p.keyword AS paisKeyword,
|
|
||||||
d.telefono AS telefono,
|
|
||||||
d.direccionFacturacion AS direccionFacturacion,
|
|
||||||
d.razonSocial AS razonSocial,
|
|
||||||
d.tipoIdentificacionFiscal AS tipoIdentificacionFiscal,
|
|
||||||
d.identificacionFiscal AS identificacionFiscal,
|
|
||||||
u.fullName AS cliente
|
|
||||||
FROM Direccion d
|
|
||||||
JOIN d.user u
|
|
||||||
LEFT JOIN Paises p ON d.paisCode3 = p.code3
|
|
||||||
WHERE (:userId IS NULL OR u.id = :userId)
|
|
||||||
""")
|
|
||||||
List<DireccionView> findAllWithPaisAndUser(@Param("userId") Long userId);
|
List<DireccionView> findAllWithPaisAndUser(@Param("userId") Long userId);
|
||||||
|
|
||||||
//findbyidwithPaisAndUser
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT
|
select d from Direccion d
|
||||||
d.id AS id,
|
left join fetch d.user
|
||||||
d.alias AS alias,
|
left join fetch d.pais
|
||||||
d.att AS att,
|
where d.id = :id
|
||||||
d.direccion AS direccion,
|
""")
|
||||||
d.cp AS cp,
|
Optional<Direccion> findByIdWithPaisAndUser(@Param("id") Long id);
|
||||||
d.ciudad AS ciudad,
|
|
||||||
d.provincia AS provincia,
|
|
||||||
d.paisCode3 AS paisCode3,
|
|
||||||
p.keyword AS paisKeyword,
|
|
||||||
d.telefono AS telefono,
|
|
||||||
d.direccionFacturacion AS direccionFacturacion,
|
|
||||||
d.razonSocial AS razonSocial,
|
|
||||||
d.tipoIdentificacionFiscal AS tipoIdentificacionFiscal,
|
|
||||||
d.identificacionFiscal AS identificacionFiscal,
|
|
||||||
u.fullName AS cliente
|
|
||||||
FROM Direccion d
|
|
||||||
JOIN d.user u
|
|
||||||
LEFT JOIN Paises p ON d.paisCode3 = p.code3
|
|
||||||
WHERE (d.id = :id)
|
|
||||||
""")
|
|
||||||
Optional<DireccionView> findByIdWithPaisAndUser(@Param("id") Long id);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,14 @@ package com.imprimelibros.erp.direcciones;
|
|||||||
|
|
||||||
public interface DireccionView {
|
public interface DireccionView {
|
||||||
Long getId();
|
Long getId();
|
||||||
|
UserView getUser();
|
||||||
String getAlias();
|
String getAlias();
|
||||||
String getAtt();
|
String getAtt();
|
||||||
String getDireccion();
|
String getDireccion();
|
||||||
String getCp();
|
String getCp();
|
||||||
String getCiudad();
|
String getCiudad();
|
||||||
String getProvincia();
|
String getProvincia();
|
||||||
String getPaisCode3();
|
PaisView getPais();
|
||||||
String getPaisKeyword();
|
String getPaisKeyword();
|
||||||
String getTelefono();
|
String getTelefono();
|
||||||
Boolean getIsFacturacion();
|
Boolean getIsFacturacion();
|
||||||
@ -16,4 +17,12 @@ public interface DireccionView {
|
|||||||
String getTipoIdentificacionFiscal();
|
String getTipoIdentificacionFiscal();
|
||||||
String getIdentificacionFiscal();
|
String getIdentificacionFiscal();
|
||||||
String getCliente();
|
String getCliente();
|
||||||
|
interface UserView {
|
||||||
|
Long getId();
|
||||||
|
String getFullName();
|
||||||
|
}
|
||||||
|
interface PaisView {
|
||||||
|
String getCode3();
|
||||||
|
String getKeyword();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,7 +85,6 @@ geoip.http.enabled=true
|
|||||||
# Hibernate Timezone
|
# Hibernate Timezone
|
||||||
#
|
#
|
||||||
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
|
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
|
||||||
|
|
||||||
#
|
#
|
||||||
# PDF Templates
|
# PDF Templates
|
||||||
#
|
#
|
||||||
|
|||||||
@ -4,6 +4,8 @@ direcciones.editar=Editar dirección
|
|||||||
direcciones.breadcrumb=Direcciones
|
direcciones.breadcrumb=Direcciones
|
||||||
direcciones.add=Añadir dirección
|
direcciones.add=Añadir dirección
|
||||||
direcciones.edit=Editar dirección
|
direcciones.edit=Editar dirección
|
||||||
|
direcciones.save=Guardar dirección
|
||||||
|
direcciones.user=Cliente
|
||||||
direcciones.alias=Alias
|
direcciones.alias=Alias
|
||||||
direcciones.alias-descripcion=Nombre descriptivo para identificar la dirección.
|
direcciones.alias-descripcion=Nombre descriptivo para identificar la dirección.
|
||||||
direcciones.nombre=Nombre y Apellidos
|
direcciones.nombre=Nombre y Apellidos
|
||||||
@ -21,6 +23,7 @@ direcciones.tipo_identificacion_fiscal=Tipo de identificación fiscal
|
|||||||
direcciones.identificacion_fiscal=Número de identificación fiscal
|
direcciones.identificacion_fiscal=Número de identificación fiscal
|
||||||
|
|
||||||
direcciones.tabla.id=ID
|
direcciones.tabla.id=ID
|
||||||
|
direcciones.tabla.att=Att.
|
||||||
direcciones.tabla.cliente=Cliente
|
direcciones.tabla.cliente=Cliente
|
||||||
direcciones.tabla.acciones=Acciones
|
direcciones.tabla.acciones=Acciones
|
||||||
|
|
||||||
@ -30,5 +33,15 @@ direcciones.pasaporte=Pasaporte
|
|||||||
direcciones.cif=C.I.F.
|
direcciones.cif=C.I.F.
|
||||||
direcciones.vat_id=VAT ID
|
direcciones.vat_id=VAT ID
|
||||||
|
|
||||||
direcciones.error.noEncontrado=Dirección no encontrada.
|
direcciones.delete.title=Eliminar dirección
|
||||||
|
direcciones.delete.button=Si, ELIMINAR
|
||||||
|
direcciones.delete.text=¿Está seguro de que desea eliminar esta dirección?<br>Esta acción no se puede deshacer.
|
||||||
|
direcciones.delete.ok.title=Dirección eliminada
|
||||||
|
direcciones.delete.ok.text=La dirección ha sido eliminada con éxito.
|
||||||
|
|
||||||
|
|
||||||
|
direcciones.error.noEncontrado=Dirección no encontrada.
|
||||||
|
direcciones.error.sinPermiso=No tienes permiso para realizar esta acción.
|
||||||
|
|
||||||
|
direcciones.form.error.required=Campo obligatorio.
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
pageLength: 50,
|
pageLength: 50,
|
||||||
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||||
responsive: true,
|
responsive: true,
|
||||||
dom: 'lrBtip',
|
dom: $('#isUser').val() == 1 ? 'lrtip' : 'lrBtip',
|
||||||
buttons: {
|
buttons: {
|
||||||
dom: {
|
dom: {
|
||||||
button: {
|
button: {
|
||||||
@ -49,10 +49,10 @@
|
|||||||
},
|
},
|
||||||
order: [[0, 'asc']],
|
order: [[0, 'asc']],
|
||||||
columns: [
|
columns: [
|
||||||
{ data: 'id', name: 'id', orderable: true, visible: $('#isUser').val() },
|
{ data: 'id', name: 'id', orderable: true, visible: $('#isUser').val() == 1 ? false : true },
|
||||||
{ data: 'cliente', name: 'cliente', orderable: true },
|
{ data: 'cliente', name: 'cliente', orderable: true, visible: $('#isUser').val() == 1 ? false : true },
|
||||||
{ data: 'alias', name: 'alias', orderable: true },
|
{ data: 'alias', name: 'alias', orderable: true },
|
||||||
{ data: 'nombre', name: 'nombre', orderable: true },
|
{ data: 'att', name: 'att', orderable: true },
|
||||||
{ data: 'direccion', name: 'direccion', orderable: true },
|
{ data: 'direccion', name: 'direccion', orderable: true },
|
||||||
{ data: 'cp', name: 'cp', orderable: true },
|
{ data: 'cp', name: 'cp', orderable: true },
|
||||||
{ data: 'ciudad', name: 'ciudad', orderable: true },
|
{ data: 'ciudad', name: 'ciudad', orderable: true },
|
||||||
@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
$(document).on("change", ".direccionFacturacion", function () {
|
$(document).on("change", ".direccionFacturacion", function () {
|
||||||
const isChecked = $(this).is(':checked');
|
const isChecked = $(this).is(':checked');
|
||||||
if(isChecked) {
|
if (isChecked) {
|
||||||
$('.direccionFacturacionItems').removeClass('d-none');
|
$('.direccionFacturacionItems').removeClass('d-none');
|
||||||
} else {
|
} else {
|
||||||
$('.direccionFacturacionItems').addClass('d-none');
|
$('.direccionFacturacionItems').addClass('d-none');
|
||||||
@ -95,24 +95,50 @@
|
|||||||
const title = $('#direccionFormModalBody #direccionForm').data('add');
|
const title = $('#direccionFormModalBody #direccionForm').data('add');
|
||||||
$('#direccionFormModal .modal-title').text(title);
|
$('#direccionFormModal .modal-title').text(title);
|
||||||
modal.show();
|
modal.show();
|
||||||
|
initSelect2Cliente(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function initSelect2Cliente(initialize = false) {
|
||||||
|
|
||||||
|
if ($('#isUser').val() == 0) {
|
||||||
|
const $sel = $('#user_id').select2({
|
||||||
|
dropdownParent: modalEl,
|
||||||
|
width: '100%',
|
||||||
|
ajax: {
|
||||||
|
url: 'users/api/get-users',
|
||||||
|
dataType: 'json',
|
||||||
|
delay: 250,
|
||||||
|
},
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
if (initialize) {
|
||||||
|
const id = $sel.data('init-id');
|
||||||
|
const text = $sel.data('init-name');
|
||||||
|
const option = new Option(text, id, true, true);
|
||||||
|
$('#user_id').append(option).trigger('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Abrir "Editar"
|
// Abrir "Editar"
|
||||||
$(document).on('click', '.btn-edit-direccion', function (e) {
|
$(document).on('click', '.btn-edit-direccion', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const id = $(this).data('id');
|
const id = $(this).data('id');
|
||||||
/*$.get('/configuracion/margenes-presupuesto/form', { id }, function (html) {
|
e.preventDefault();
|
||||||
$('#margenesPresupuestoModalBody').html(html);
|
$.get('/direcciones/form', { id }, function (html) {
|
||||||
const title = $('#margenesPresupuestoModalBody #margenesPresupuestoForm').data('edit');
|
$('#direccionFormModalBody').html(html);
|
||||||
$('#margenesPresupuestoModal .modal-title').text(title);
|
const title = $('#direccionFormModalBody #direccionForm').data('edit');
|
||||||
modal.show();*/
|
$('#direccionFormModal .modal-title').text(title);
|
||||||
|
modal.show();
|
||||||
|
initSelect2Cliente(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Botón "Eliminar"
|
// Botón "Eliminar"
|
||||||
$(document).on('click', '.btn-delete-margen', function (e) {
|
$(document).on('click', '.btn-delete-direccion', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const id = $(this).data('id');
|
const id = $(this).data('id');
|
||||||
|
|
||||||
@ -132,12 +158,12 @@
|
|||||||
if (!result.isConfirmed) return;
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/configuracion/margenes-presupuesto/' + id,
|
url: '/direcciones/' + id,
|
||||||
type: 'DELETE',
|
type: 'DELETE',
|
||||||
success: function () {
|
success: function () {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: 'success', title: window.languageBundle.get(['margenes-presupuesto.delete.ok.title']) || 'Eliminado',
|
icon: 'success', title: window.languageBundle.get(['direcciones.delete.ok.title']) || 'Eliminado',
|
||||||
text: window.languageBundle.get(['margenes-presupuesto.delete.ok.text']) || 'El margen ha sido eliminado con éxito.',
|
text: window.languageBundle.get(['direcciones.delete.ok.text']) || 'La dirección ha sido eliminada con éxito.',
|
||||||
showConfirmButton: true,
|
showConfirmButton: true,
|
||||||
customClass: {
|
customClass: {
|
||||||
confirmButton: 'btn btn-secondary w-xs mt-2',
|
confirmButton: 'btn btn-secondary w-xs mt-2',
|
||||||
@ -148,7 +174,7 @@
|
|||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
// usa el mensaje del backend; fallback genérico por si no llega JSON
|
// usa el mensaje del backend; fallback genérico por si no llega JSON
|
||||||
const msg = (xhr.responseJSON && xhr.responseJSON.message)
|
const msg = (xhr.responseJSON && xhr.responseJSON.message)
|
||||||
|| 'Error al eliminar el usuario.';
|
|| 'Error al eliminar la direccion.';
|
||||||
Swal.fire({ icon: 'error', title: 'No se pudo eliminar', text: msg });
|
Swal.fire({ icon: 'error', title: 'No se pudo eliminar', text: msg });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -156,7 +182,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Submit del form en el modal
|
// Submit del form en el modal
|
||||||
$(document).on('submit', '#margenesPresupuestoForm', function (e) {
|
$(document).on('submit', '#direccionForm', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const $form = $(this);
|
const $form = $(this);
|
||||||
|
|
||||||
@ -167,11 +193,11 @@
|
|||||||
dataType: 'html',
|
dataType: 'html',
|
||||||
success: function (html) {
|
success: function (html) {
|
||||||
// Si por cualquier motivo llega 200 con fragmento, lo insertamos igual
|
// Si por cualquier motivo llega 200 con fragmento, lo insertamos igual
|
||||||
if (typeof html === 'string' && html.indexOf('id="margenesPresupuestoForm"') !== -1 && html.indexOf('<html') === -1) {
|
if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) {
|
||||||
$('#margenesPresupuestoModalBody').html(html);
|
$('#direccionFormModalBody').html(html);
|
||||||
const isEdit = $('#margenesPresupuestoModalBody #margenesPresupuestoForm input[name="_method"][value="PUT"]').length > 0;
|
const isEdit = $('#direccionFormModalBody #direccionForm input[name="_method"][value="PUT"]').length > 0;
|
||||||
const title = $('#margenesPresupuestoModalBody #margenesPresupuestoForm').data(isEdit ? 'edit' : 'add');
|
const title = $('#direccionFormModalBody #direccionForm').data(isEdit ? 'edit' : 'add');
|
||||||
$('#margenesPresupuestoModal .modal-title').text(title);
|
$('#direccionModal .modal-title').text(title);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Éxito real: cerrar y recargar tabla
|
// Éxito real: cerrar y recargar tabla
|
||||||
@ -181,14 +207,15 @@
|
|||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
// Con 422 devolvemos el fragmento con errores aquí
|
// Con 422 devolvemos el fragmento con errores aquí
|
||||||
if (xhr.status === 422 && xhr.responseText) {
|
if (xhr.status === 422 && xhr.responseText) {
|
||||||
$('#margenesPresupuestoModalBody').html(xhr.responseText);
|
$('#direccionFormModalBody').html(xhr.responseText);
|
||||||
const isEdit = $('#margenesPresupuestoModalBody #margenesPresupuestoForm input[name="_method"][value="PUT"]').length > 0;
|
const isEdit = $('#direccionFormModalBody #direccionForm input[name="_method"][value="PUT"]').length > 0;
|
||||||
const title = $('#margenesPresupuestoModalBody #margenesPresupuestoForm').data(isEdit ? 'edit' : 'add');
|
const title = $('#direccionFormModalBody #direccionForm').data(isEdit ? 'edit' : 'add');
|
||||||
$('#margenesPresupuestoModal .modal-title').text(title);
|
$('#direccionModal .modal-title').text(title);
|
||||||
|
initSelect2Cliente(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Fallback
|
// Fallback
|
||||||
$('#margenesPresupuestoModalBody').html('<div class="p-3 text-danger">Error inesperado.</div>');
|
$('#direccionFormModalBody').html('<div class="p-3 text-danger">Error inesperado.</div>');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,44 +1,66 @@
|
|||||||
<div th:fragment="direccionForm">
|
<div th:fragment="direccionForm">
|
||||||
<form id="direccionForm" novalidate th:action="${action}" th:object="${direccion}" method="post"
|
<form id="direccionForm" novalidate th:action="${action}" th:object="${dirForm}" method="post"
|
||||||
th:data-add="#{direcciones.add}" th:data-edit="#{direcciones.editar}">
|
th:data-add="#{direcciones.add}" th:data-edit="#{direcciones.editar}">
|
||||||
<div class="form-group">
|
|
||||||
|
<div class="alert alert-danger" th:if="${#fields.hasGlobalErrors()}" th:each="err : ${#fields.globalErrors()}">
|
||||||
|
<span th:text="${err}">Error</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div sec:authorize="hasAnyRole('SUPERADMIN','ADMIN')" class="form-group">
|
||||||
|
<label for="user_id">
|
||||||
|
<span th:text="#{direcciones.user}">Cliente</span>
|
||||||
|
<span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<select class="form-control select2 direccion-item" id="user_id" th:field="*{user}" th:attr="data-init-id=*{user?.id},
|
||||||
|
data-init-name=*{user?.fullName}" th:classappend="${#fields.hasErrors('user')} ? ' is-invalid'">
|
||||||
|
</select>
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('user')}" th:errors="*{user}"></div>
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('user.id')}" th:errors="*{user.id}"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input sec:authorize="hasAnyRole('USER')" type="hidden" th:field="*{user.id}" th:value="*{user.id}" />
|
||||||
|
|
||||||
|
<div class="form-group mt-2">
|
||||||
<label for="alias">
|
<label for="alias">
|
||||||
<span th:text="#{direcciones.alias}">Alias</span>
|
<span th:text="#{direcciones.alias}">Alias</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="alias" th:field="*{alias}" maxlength="100" required>
|
<input class="form-control direccion-item" id="alias" th:field="*{alias}" maxlength="100" required
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('alias')} ? ' is-invalid'">
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('alias')}" th:errors="*{alias}"></div>
|
||||||
<label th:text="#{direcciones.alias-descripcion}" class="form-text text-muted"></label>
|
<label th:text="#{direcciones.alias-descripcion}" class="form-text text-muted"></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group mt-2">
|
||||||
<label for="att">
|
<label for="att">
|
||||||
<span th:text="#{direcciones.nombre}">Nombre y Apellidos</span>
|
<span th:text="#{direcciones.nombre}">Nombre y Apellidos</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="att" th:field="*{att}" maxlength="150" required>
|
<input class="form-control direccion-item" id="att" th:field="*{att}" maxlength="150" required
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('att')} ? ' is-invalid'">
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('att')}" th:errors="*{att}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group mt-2">
|
||||||
<label for="direccion">
|
<label for="direccion">
|
||||||
<span th:text="#{direcciones.direccion}">Dirección</span>
|
<span th:text="#{direcciones.direccion}">Dirección</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea class="form-control direccion-item" id="direccion" th:field="*{direccion}" maxlength="255"
|
<textarea class="form-control direccion-item" id="direccion" th:field="*{direccion}" maxlength="255"
|
||||||
required style="max-height: 125px;"></textarea>
|
required style="max-height: 125px;"
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('direccion')} ? ' is-invalid'"></textarea>
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('direccion')}" th:errors="*{direccion}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row mt-2">
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
||||||
<label for="cp">
|
<label for="cp">
|
||||||
<span th:text="#{direcciones.cp}">Código Postal</span>
|
<span th:text="#{direcciones.cp}">Código Postal</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input type="number" class="form-control direccion-item" id="cp" th:field="*{cp}" min="1" max="99999"
|
<input type="number" class="form-control direccion-item" id="cp" th:field="*{cp}" min="1" max="99999"
|
||||||
required>
|
required th:classappend="${#fields.hasErrors('cp')} ? ' is-invalid'">
|
||||||
<div class="invalid-feedback"></div>
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('cp')}" th:errors="*{cp}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 mr-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 mr-0">
|
||||||
@ -46,21 +68,21 @@
|
|||||||
<span th:text="#{direcciones.ciudad}">Ciudad</span>
|
<span th:text="#{direcciones.ciudad}">Ciudad</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="ciudad" th:field="*{ciudad}" maxlength="100" required>
|
<input class="form-control direccion-item" id="ciudad" th:field="*{ciudad}" maxlength="100" required
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('ciudad')} ? ' is-invalid'">
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('ciudad')}" th:errors="*{ciudad}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row mt-2">
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
||||||
<label for="provincia">
|
<label for="provincia">
|
||||||
<span th:text="#{direcciones.provincia}">Provincia</span>
|
<span th:text="#{direcciones.provincia}">Provincia</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="provincia" th:field="*{provincia}" maxlength="100"
|
<input class="form-control direccion-item" id="provincia" th:field="*{provincia}" maxlength="100"
|
||||||
required>
|
required th:classappend="${#fields.hasErrors('provincia')} ? ' is-invalid'">
|
||||||
<div class="invalid-feedback"></div>
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('provincia')}" th:errors="*{provincia}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 mr-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 mr-0">
|
||||||
@ -68,67 +90,74 @@
|
|||||||
<span th:text="#{direcciones.pais}">País</span>
|
<span th:text="#{direcciones.pais}">País</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<select class="form-control select2 direccion-item" id="pais" th:field="*{paisCode3}">
|
<select class="form-control select2 direccion-item" id="paisCode3" th:field="*{paisCode3}"
|
||||||
|
th:classappend="${#fields.hasErrors('paisCode3')} ? ' is-invalid'">
|
||||||
<option th:each="pais : ${paises}" th:value="${pais.id}" th:text="${pais.text}"
|
<option th:each="pais : ${paises}" th:value="${pais.id}" th:text="${pais.text}"
|
||||||
th:selected="${pais.id} == ${direccion.paisCode3}">
|
th:selected="${pais.id} == ${dirForm.paisCode3}">
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback"></div>
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('paisCode3')}" th:errors="*{paisCode3}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group mt-2">
|
||||||
<label for="telefono">
|
<label for="telefono">
|
||||||
<span th:text="#{direcciones.telefono}">Teléfono</span>
|
<span th:text="#{direcciones.telefono}">Teléfono</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="telefono" th:field="*{telefono}" maxlength="50">
|
<input class="form-control direccion-item" id="telefono" th:field="*{telefono}" maxlength="50"
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('telefono')} ? ' is-invalid'">
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('telefono')}" th:errors="*{telefono}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group mt-2">
|
||||||
<label for="instrucciones">
|
<label for="instrucciones">
|
||||||
<span th:text="#{direcciones.instrucciones}">Instrucciones</span>
|
<span th:text="#{direcciones.instrucciones}">Instrucciones</span>
|
||||||
</label>
|
</label>
|
||||||
<textarea class="form-control direccion-item" id="instrucciones" th:field="*{instrucciones}" maxlength="255"
|
<textarea class="form-control direccion-item" id="instrucciones" th:field="*{instrucciones}" maxlength="255"
|
||||||
required style="max-height: 125px;"></textarea>
|
style="max-height: 125px;"
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('instrucciones')} ? ' is-invalid'"></textarea>
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('instrucciones')}" th:errors="*{instrucciones}">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check form-switch form-switch-custom my-2">
|
<div class="form-check form-switch form-switch-custom my-2">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
class="form-check-input form-switch-custom-primary direccion-item direccionFacturacion"
|
class="form-check-input form-switch-custom-primary direccion-item direccionFacturacion"
|
||||||
id="direccionFacturacion" name="direccionFacturacion" th:field="*{direccionFacturacion}">
|
id="direccionFacturacion" th:field="*{direccionFacturacion}">
|
||||||
<label for="direccionFacturacion" class="form-check-label" th:text="#{direcciones.isFacturacion}">Usar
|
<label for="direccionFacturacion" class="form-check-label" th:text="#{direcciones.isFacturacion}">
|
||||||
también como
|
Usar también como dirección de facturación
|
||||||
dirección de facturación</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
th:class="'form-group direccionFacturacionItems' + (${direccion != null and direccion.direccionFacturacion} ? '' : ' d-none')">
|
th:class="'form-group direccionFacturacionItems' + (${direccion != null and direccion.direccionFacturacion} ? '' : ' d-none')">
|
||||||
<label for="razon_social">
|
<label for="razonSocial">
|
||||||
<span th:text="#{direcciones.razon_social}">Razón Social</span>
|
<span th:text="#{direcciones.razon_social}">Razón Social</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="razonSocial" th:field="*{razonSocial}" maxlength="150">
|
<input class="form-control direccion-item" id="razonSocial" th:field="*{razonSocial}" maxlength="150"
|
||||||
<div class="invalid-feedback"></div>
|
th:classappend="${#fields.hasErrors('razonSocial')} ? ' is-invalid'">
|
||||||
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('razonSocial')}" th:errors="*{razonSocial}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
th:class="'row direccionFacturacionItems' + (${direccion != null and direccion.direccionFacturacion} ? '' : ' d-none')">
|
th:class="'row mt-2 direccionFacturacionItems' + (${direccion != null and direccion.direccionFacturacion} ? '' : ' d-none')">
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
||||||
<label for="tipoIdentificacionFiscal">
|
<label for="tipoIdentificacionFiscal">
|
||||||
<span th:text="#{direcciones.tipo_identificacion_fiscal}">Tipo de identificación fiscal</span>
|
<span th:text="#{direcciones.tipo_identificacion_fiscal}">Tipo de identificación fiscal</span>
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<select class="form-control select2 direccion-item" id="tipoIdentificacionFiscal"
|
<select class="form-control select2 direccion-item" id="tipoIdentificacionFiscal"
|
||||||
th:field="*{tipoIdentificacionFiscal}">
|
th:field="*{tipoIdentificacionFiscal}"
|
||||||
|
th:classappend="${#fields.hasErrors('tipoIdentificacionFiscal')} ? ' is-invalid'">
|
||||||
<option th:value="DNI" th:text="#{direcciones.dni}">DNI</option>
|
<option th:value="DNI" th:text="#{direcciones.dni}">DNI</option>
|
||||||
<option th:value="NIE" th:text="#{direcciones.nie}">NIE</option>
|
<option th:value="NIE" th:text="#{direcciones.nie}">NIE</option>
|
||||||
<option th:value="Pasaporte" th:text="#{direcciones.pasaporte}">Pasaporte</option>
|
<option th:value="Pasaporte" th:text="#{direcciones.pasaporte}">Pasaporte</option>
|
||||||
<option th:value="CIF" th:text="#{direcciones.cif}">CIF</option>
|
<option th:value="CIF" th:text="#{direcciones.cif}">CIF</option>
|
||||||
<option th:value="VAT_ID" th:text="#{direcciones.vat_id}">VAT ID</option>
|
<option th:value="VAT_ID" th:text="#{direcciones.vat_id}">VAT ID</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="invalid-feedback"></div>
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('tipoIdentificacionFiscal')}"
|
||||||
|
th:errors="*{tipoIdentificacionFiscal}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
<div class="form-group col-lg-6 col-md-6 col-sm-12 ml-0">
|
||||||
@ -137,13 +166,14 @@
|
|||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control direccion-item" id="identificacionFiscal" th:field="*{identificacionFiscal}"
|
<input class="form-control direccion-item" id="identificacionFiscal" th:field="*{identificacionFiscal}"
|
||||||
maxlength="50">
|
maxlength="50" th:classappend="${#fields.hasErrors('identificacionFiscal')} ? ' is-invalid'">
|
||||||
<div class="invalid-feedback"></div>
|
<div class="invalid-feedback" th:if="${#fields.hasErrors('identificacionFiscal')}"
|
||||||
|
th:errors="*{identificacionFiscal}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex align-items-center justify-content-center">
|
<div class="d-flex align-items-center justify-content-center">
|
||||||
<button type="submit" class="btn btn-secondary mt-3" th:text="#{direcciones.add}"></button>
|
<button type="submit" class="btn btn-secondary mt-3" th:text="#{direcciones.save}"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
<th scope="col" th:text="#{direcciones.tabla.id}">ID</th>
|
<th scope="col" th:text="#{direcciones.tabla.id}">ID</th>
|
||||||
<th scope="col" th:text="#{direcciones.tabla.cliente}">Cliente</th>
|
<th scope="col" th:text="#{direcciones.tabla.cliente}">Cliente</th>
|
||||||
<th scope="col" th:text="#{direcciones.alias}">Alias</th>
|
<th scope="col" th:text="#{direcciones.alias}">Alias</th>
|
||||||
<th scope="col" th:text="#{direcciones.nombre}">Nombre y Apellidos</th>
|
<th scope="col" th:text="#{direcciones.tabla.att}">Att.</th>
|
||||||
<th scope="col" th:text="#{direcciones.direccion}">Dirección</th>
|
<th scope="col" th:text="#{direcciones.direccion}">Dirección</th>
|
||||||
<th scope="col" th:text="#{direcciones.cp}">Código Postal</th>
|
<th scope="col" th:text="#{direcciones.cp}">Código Postal</th>
|
||||||
<th scope="col" th:text="#{direcciones.ciudad}">Ciudad</th>
|
<th scope="col" th:text="#{direcciones.ciudad}">Ciudad</th>
|
||||||
|
|||||||
Reference in New Issue
Block a user