mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 08:58:48 +00:00
trabajando en añadir
This commit is contained in:
@ -146,11 +146,11 @@ public class MargenPresupuestoController {
|
||||
if (id != null) {
|
||||
var opt = repo.findById(id);
|
||||
if (opt.isEmpty()) {
|
||||
binding.reject("usuarios.error.noEncontrado",
|
||||
messageSource.getMessage("usuarios.error.noEncontrado", null, locale));
|
||||
binding.reject("margenes-presupuesto.error.noEncontrado",
|
||||
messageSource.getMessage("margenes-presupuesto.error.noEncontrado", null, locale));
|
||||
response.setStatus(404);
|
||||
model.addAttribute("action", "/users/" + id);
|
||||
return "imprimelibros/users/user-form :: userForm";
|
||||
model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
|
||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
||||
}
|
||||
|
||||
model.addAttribute("margenPresupuesto", opt.get());
|
||||
|
||||
225
src/main/java/com/imprimelibros/erp/direcciones/Direccion.java
Normal file
225
src/main/java/com/imprimelibros/erp/direcciones/Direccion.java
Normal file
@ -0,0 +1,225 @@
|
||||
package com.imprimelibros.erp.direcciones;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import com.imprimelibros.erp.common.jpa.AbstractAuditedEntity;
|
||||
import com.imprimelibros.erp.paises.Paises;
|
||||
import com.imprimelibros.erp.users.User;
|
||||
|
||||
@Entity
|
||||
@Table(name = "direcciones", indexes = {
|
||||
@Index(name = "idx_direcciones_user", columnList = "user_id"),
|
||||
@Index(name = "idx_direcciones_pais_code3", columnList = "pais_code3")
|
||||
})
|
||||
@SQLDelete(sql = "UPDATE direcciones SET deleted = 1, deleted_at = NOW(3) WHERE id = ?")
|
||||
@SQLRestriction("deleted = 0")
|
||||
public class Direccion extends AbstractAuditedEntity implements Serializable {
|
||||
|
||||
public enum TipoIdentificacionFiscal {
|
||||
DNI("direcciones.dni"),
|
||||
NIE("direcciones.nie"),
|
||||
CIF("direcciones.cif"),
|
||||
Pasaporte("direcciones.pasaporte"),
|
||||
VAT_ID("direcciones.vat_id");
|
||||
|
||||
private String key;
|
||||
|
||||
TipoIdentificacionFiscal(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
// --- FK a users(id)
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
@Column(name = "alias", length = 100, nullable = false)
|
||||
private String alias;
|
||||
|
||||
@Column(name = "att", length = 150, nullable = false)
|
||||
private String att;
|
||||
|
||||
@Column(name = "direccion", length = 255, nullable = false)
|
||||
private String direccion;
|
||||
|
||||
@Column(name = "cp", length = 20, nullable = false)
|
||||
private Integer cp;
|
||||
|
||||
@Column(name = "ciudad", length = 100, nullable = false)
|
||||
private String ciudad;
|
||||
|
||||
@Column(name = "provincia", length = 100, nullable = false)
|
||||
private String provincia;
|
||||
|
||||
// Usamos el code3 del país como FK lógica (String)
|
||||
@Column(name = "pais_code3", length = 3, nullable = false)
|
||||
private String paisCode3 = "esp";
|
||||
|
||||
@Column(name = "telefono", length = 30, nullable = false)
|
||||
private String telefono;
|
||||
|
||||
@Column(name = "instrucciones", length = 255)
|
||||
private String instrucciones;
|
||||
|
||||
@Column(name = "is_facturacion", nullable = false)
|
||||
private boolean direccionFacturacion = false;
|
||||
|
||||
@Column(name = "razon_social", length = 150)
|
||||
private String razonSocial;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "tipo_identificacion_fiscal", length = 20, nullable = false)
|
||||
private TipoIdentificacionFiscal tipoIdentificacionFiscal = TipoIdentificacionFiscal.DNI;
|
||||
|
||||
@Column(name = "identificacion_fiscal", length = 50)
|
||||
private String identificacionFiscal;
|
||||
|
||||
// --- Asociación opcional (read-only) a Pais por code3, si tienes la entidad
|
||||
// Pais
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "pais_code3", referencedColumnName = "code3", insertable = false, updatable = false)
|
||||
private Paises pais;
|
||||
|
||||
// --- Getters & Setters ---
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getAtt() {
|
||||
return att;
|
||||
}
|
||||
|
||||
public void setAtt(String att) {
|
||||
this.att = att;
|
||||
}
|
||||
|
||||
public String getDireccion() {
|
||||
return direccion;
|
||||
}
|
||||
|
||||
public void setDireccion(String direccion) {
|
||||
this.direccion = direccion;
|
||||
}
|
||||
|
||||
public Integer getCp() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public void setCp(Integer cp) {
|
||||
this.cp = cp;
|
||||
}
|
||||
|
||||
public String getCiudad() {
|
||||
return ciudad;
|
||||
}
|
||||
|
||||
public void setCiudad(String ciudad) {
|
||||
this.ciudad = ciudad;
|
||||
}
|
||||
|
||||
public String getProvincia() {
|
||||
return provincia;
|
||||
}
|
||||
|
||||
public void setProvincia(String provincia) {
|
||||
this.provincia = provincia;
|
||||
}
|
||||
|
||||
public String getPaisCode3() {
|
||||
return paisCode3;
|
||||
}
|
||||
|
||||
public void setPaisCode3(String paisCode3) {
|
||||
this.paisCode3 = paisCode3;
|
||||
}
|
||||
|
||||
public String getTelefono() {
|
||||
return telefono;
|
||||
}
|
||||
|
||||
public void setTelefono(String telefono) {
|
||||
this.telefono = telefono;
|
||||
}
|
||||
|
||||
public String getInstrucciones() {
|
||||
return instrucciones;
|
||||
}
|
||||
|
||||
public void setInstrucciones(String instrucciones) {
|
||||
this.instrucciones = instrucciones;
|
||||
}
|
||||
|
||||
public boolean isDireccionFacturacion() {
|
||||
return direccionFacturacion;
|
||||
}
|
||||
|
||||
public void setDireccionFacturacion(boolean direccionFacturacion) {
|
||||
this.direccionFacturacion = direccionFacturacion;
|
||||
}
|
||||
|
||||
public String getRazonSocial() {
|
||||
return razonSocial;
|
||||
}
|
||||
|
||||
public void setRazonSocial(String razonSocial) {
|
||||
this.razonSocial = razonSocial;
|
||||
}
|
||||
|
||||
public TipoIdentificacionFiscal getTipoIdentificacionFiscal() {
|
||||
return tipoIdentificacionFiscal;
|
||||
}
|
||||
|
||||
public void setTipoIdentificacionFiscal(TipoIdentificacionFiscal tipo) {
|
||||
this.tipoIdentificacionFiscal = tipo;
|
||||
}
|
||||
|
||||
public String getIdentificacionFiscal() {
|
||||
return identificacionFiscal;
|
||||
}
|
||||
|
||||
public void setIdentificacionFiscal(String identificacionFiscal) {
|
||||
this.identificacionFiscal = identificacionFiscal;
|
||||
}
|
||||
|
||||
public Paises getPais() {
|
||||
return pais;
|
||||
}
|
||||
|
||||
public void setPais(Paises pais) {
|
||||
this.pais = pais;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,317 @@
|
||||
package com.imprimelibros.erp.direcciones;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.imprimelibros.erp.datatables.DataTable;
|
||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||
import com.imprimelibros.erp.paises.PaisesService;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/direcciones")
|
||||
public class DireccionController {
|
||||
|
||||
protected final DireccionRepository repo;
|
||||
protected final PaisesService paisesService;
|
||||
protected final MessageSource messageSource;
|
||||
|
||||
public DireccionController(DireccionRepository repo, PaisesService paisesService, MessageSource messageSource) {
|
||||
this.repo = repo;
|
||||
this.paisesService = paisesService;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
@GetMapping()
|
||||
public String viewDirecciones(Model model, Authentication auth, Locale locale) {
|
||||
|
||||
boolean isUser = auth != null && auth.getAuthorities().stream()
|
||||
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"));
|
||||
model.addAttribute("isUser", isUser ? 1 : 0);
|
||||
|
||||
return "imprimelibros/direcciones/direccion-list";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/datatable", produces = "application/json")
|
||||
@ResponseBody
|
||||
public DataTablesResponse<Map<String, Object>> datatable(
|
||||
HttpServletRequest request,
|
||||
Authentication authentication,
|
||||
Locale locale) {
|
||||
|
||||
DataTablesRequest dt = DataTablesParser.from(request);
|
||||
|
||||
// Columnas visibles / lógicas para el DataTable en el frontend:
|
||||
// id, cliente (nombre de usuario), alias, att, direccion, cp, ciudad,
|
||||
// provincia, pais
|
||||
List<String> searchable = List.of(
|
||||
"id",
|
||||
"cliente", "alias",
|
||||
"att", "direccion", "cp", "ciudad", "provincia", "pais");
|
||||
|
||||
List<String> orderable = List.of(
|
||||
"id",
|
||||
"cliente", "alias",
|
||||
"att", "direccion", "cp", "ciudad", "provincia", "pais");
|
||||
|
||||
// Filtro base por rol (ROLE_USER solo ve sus direcciones)
|
||||
Specification<Direccion> base = (root, query, cb) -> {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (authentication != null && authentication.getAuthorities().stream()
|
||||
.anyMatch(a -> a.getAuthority().equals("ROLE_USER"))) {
|
||||
String username = authentication.getName();
|
||||
predicates.add(cb.equal(root.get("user").get("username"), username));
|
||||
}
|
||||
|
||||
return cb.and(predicates.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
long total = repo.count(); // total sin filtro global
|
||||
|
||||
// Construcción del datatable con entity + spec
|
||||
return DataTable
|
||||
.of(repo, Direccion.class, dt, searchable)
|
||||
.orderable(orderable)
|
||||
|
||||
// Columnas "crudas" (las que existen tal cual):
|
||||
.edit("id", d -> d.getId())
|
||||
.edit("alias", d -> d.getAlias())
|
||||
.edit("att", d -> d.getAtt())
|
||||
.edit("direccion", d -> d.getDireccion())
|
||||
.edit("cp", d -> d.getCp())
|
||||
.edit("ciudad", d -> d.getCiudad())
|
||||
.edit("provincia", d -> d.getProvincia())
|
||||
|
||||
// Columnas calculadas:
|
||||
|
||||
// cliente = nombre del usuario (o username si no tienes name)
|
||||
.add("cliente", d -> {
|
||||
var u = d.getUser();
|
||||
return (u != null && u.getFullName() != null && !u.getFullName().isBlank())
|
||||
? u.getFullName()
|
||||
: "";
|
||||
})
|
||||
|
||||
// pais = nombre localizado desde MessageSource usando el keyword del país
|
||||
.add("pais", d -> {
|
||||
// si tienes la relación read-only a Pais (d.getPais()) con .getKeyword()
|
||||
String keyword = (d.getPais() != null) ? d.getPais().getKeyword() : null;
|
||||
if (keyword == null || keyword.isBlank())
|
||||
return d.getPaisCode3();
|
||||
return messageSource.getMessage("paises." + keyword, null, keyword, locale);
|
||||
})
|
||||
|
||||
// Ejemplo de columna de acciones:
|
||||
.add("actions", d -> """
|
||||
<div class="hstack gap-3 flex-wrap">
|
||||
<a href="javascript:void(0);" data-id="%d" class="link-success btn-edit-direccion fs-15">
|
||||
<i class="ri-edit-2-line"></i>
|
||||
</a>
|
||||
<a href="javascript:void(0);" data-id="%d" class="link-danger btn-delete-direccion fs-15">
|
||||
<i class="ri-delete-bin-5-line"></i>
|
||||
</a>
|
||||
</div>
|
||||
""".formatted(d.getId(), d.getId()))
|
||||
|
||||
// WHERE dinámico (spec base)
|
||||
.where(base)
|
||||
|
||||
// Si tu DataTable helper soporta “join/alias” para buscar/ordenar por campos
|
||||
// relacionados:
|
||||
// .searchAlias("cliente", (root, cb) -> root.join("user").get("name"))
|
||||
// .orderAlias("cliente", (root) -> root.join("user").get("name"))
|
||||
// .searchAlias("pais", (root, cb) -> root.join("pais",
|
||||
// JoinType.LEFT).get("keyword"))
|
||||
// .orderAlias("pais", (root) -> root.join("pais",
|
||||
// JoinType.LEFT).get("keyword"))
|
||||
|
||||
.toJson(total);
|
||||
}
|
||||
|
||||
@GetMapping("form")
|
||||
public String getForm(@RequestParam(required = false) Long id,
|
||||
Direccion direccion,
|
||||
BindingResult binding,
|
||||
Model model,
|
||||
HttpServletResponse response,
|
||||
Locale locale) {
|
||||
|
||||
model.addAttribute("paises", paisesService.getForSelect("", "", locale).get("results"));
|
||||
|
||||
if (id != null) {
|
||||
var opt = repo.findByIdWithPaisAndUser(id);
|
||||
if (opt == null) {
|
||||
binding.reject("direcciones.error.noEncontrado",
|
||||
messageSource.getMessage("direcciones.error.noEncontrado", null, locale));
|
||||
response.setStatus(404);
|
||||
model.addAttribute("action", "/direcciones/" + id);
|
||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||
}
|
||||
|
||||
model.addAttribute("direccion", opt.get());
|
||||
model.addAttribute("action", "/direcciones/" + id);
|
||||
} else {
|
||||
|
||||
model.addAttribute("direccion", new Direccion());
|
||||
model.addAttribute("action", "/direcciones");
|
||||
}
|
||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String create(
|
||||
Direccion direccion,
|
||||
BindingResult binding,
|
||||
Model model,
|
||||
HttpServletResponse response,
|
||||
Locale locale) {
|
||||
|
||||
if (binding.hasErrors()) {
|
||||
response.setStatus(422);
|
||||
model.addAttribute("action", "/direcciones/");
|
||||
return "imprimelibros/direcciones/direccion-form :: direccionForm";
|
||||
}
|
||||
|
||||
var data = direccion;
|
||||
|
||||
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
/*
|
||||
@PutMapping("/{id}")
|
||||
public String edit(
|
||||
@PathVariable Long id,
|
||||
MargenPresupuesto form,
|
||||
BindingResult binding,
|
||||
Model model,
|
||||
HttpServletResponse response,
|
||||
Locale locale) {
|
||||
|
||||
var uOpt = repo.findById(id);
|
||||
if (uOpt.isEmpty()) {
|
||||
binding.reject("usuarios.error.noEncontrado",
|
||||
messageSource.getMessage("usuarios.error.noEncontrado", null, locale));
|
||||
}
|
||||
|
||||
if (binding.hasErrors()) {
|
||||
response.setStatus(422);
|
||||
model.addAttribute("action", "/configuracion/margenes-presupuesto/" + id);
|
||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
||||
}
|
||||
|
||||
var entity = uOpt.get();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@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))));
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package com.imprimelibros.erp.direcciones;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface DireccionRepository
|
||||
extends JpaRepository<Direccion, Long>,
|
||||
JpaSpecificationExecutor<Direccion> {
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
d.id AS id,
|
||||
d.alias AS alias,
|
||||
d.att AS att,
|
||||
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);
|
||||
|
||||
//findbyidwithPaisAndUser
|
||||
@Query("""
|
||||
SELECT
|
||||
d.id AS id,
|
||||
d.alias AS alias,
|
||||
d.att AS att,
|
||||
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 (d.id = :id)
|
||||
""")
|
||||
Optional<DireccionView> findByIdWithPaisAndUser(@Param("id") Long id);
|
||||
|
||||
|
||||
|
||||
@Query(value = "SELECT * FROM direcciones", nativeQuery = true)
|
||||
List<DireccionView> findAllWithDeleted();
|
||||
|
||||
// find by user_id
|
||||
List<Direccion> findByUserId(Long userId);
|
||||
|
||||
// find by user_id with deleted
|
||||
@Query(value = "SELECT * FROM direcciones WHERE user_id = :userId", nativeQuery = true)
|
||||
List<Direccion> findByUserIdWithDeleted(@Param("userId") Long userId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.imprimelibros.erp.direcciones;
|
||||
|
||||
public interface DireccionView {
|
||||
Long getId();
|
||||
String getAlias();
|
||||
String getAtt();
|
||||
String getDireccion();
|
||||
String getCp();
|
||||
String getCiudad();
|
||||
String getProvincia();
|
||||
String getPaisCode3();
|
||||
String getPaisKeyword();
|
||||
String getTelefono();
|
||||
Boolean getIsFacturacion();
|
||||
String getRazonSocial();
|
||||
String getTipoIdentificacionFiscal();
|
||||
String getIdentificacionFiscal();
|
||||
String getCliente();
|
||||
}
|
||||
@ -1,22 +1,17 @@
|
||||
package com.imprimelibros.erp.paises;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/paises")
|
||||
public class PaisesController {
|
||||
|
||||
private final PaisesRepository paisesRepository;
|
||||
private final MessageSource messageSource;
|
||||
private final PaisesService paisesService;
|
||||
|
||||
public PaisesController(PaisesRepository paisesRepository, MessageSource messageSource) {
|
||||
this.paisesRepository = paisesRepository;
|
||||
this.messageSource = messageSource;
|
||||
public PaisesController(PaisesService paisesService) {
|
||||
this.paisesService = paisesService;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,40 +29,8 @@ public class PaisesController {
|
||||
@RequestParam(value = "term", required = false) String q2,
|
||||
Locale locale) {
|
||||
|
||||
// Termino de búsqueda (Select2 usa 'q' o 'term' según versión/config)
|
||||
String search = Optional.ofNullable(q1).orElse(q2);
|
||||
if (search != null) {
|
||||
search = search.trim();
|
||||
}
|
||||
final String q = (search == null || search.isEmpty())
|
||||
? null
|
||||
: search.toLowerCase(locale);
|
||||
|
||||
List<Paises> all = paisesRepository.findAll();
|
||||
|
||||
// Mapear a opciones id/text con i18n y filtrar por búsqueda si llega
|
||||
List<Map<String, String>> options = all.stream()
|
||||
.map(cc -> {
|
||||
String key = cc.getKeyword();
|
||||
String text = messageSource.getMessage("paises." + key, null, key, locale);
|
||||
Map<String, String> m = new HashMap<>();
|
||||
m.put("id", key); // lo normal en Select2: id = valor que guardarás (keyword)
|
||||
m.put("text", text); // texto mostrado, i18n con fallback a keyword
|
||||
return m;
|
||||
})
|
||||
.filter(opt -> {
|
||||
if (q == null || q.isEmpty())
|
||||
return true;
|
||||
String text = opt.get("text").toLowerCase(locale);
|
||||
String id = opt.get("id").toLowerCase(locale);
|
||||
return text.contains(q) || id.contains(q);
|
||||
})
|
||||
.sorted(Comparator.comparing(m -> m.get("text"), Collator.getInstance(locale)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Estructura Select2
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("results", options);
|
||||
return resp;
|
||||
return paisesService.getForSelect(q1, q2, locale);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
package com.imprimelibros.erp.paises;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class PaisesService {
|
||||
|
||||
protected final PaisesRepository repo;
|
||||
protected final MessageSource messageSource;
|
||||
|
||||
public PaisesService(PaisesRepository repo, MessageSource messageSource) {
|
||||
this.repo = repo;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
public Map<String, Object> getForSelect(String q1, String q2, Locale locale) {
|
||||
|
||||
try {
|
||||
|
||||
// Termino de búsqueda (Select2 usa 'q' o 'term' según versión/config)
|
||||
String search = Optional.ofNullable(q1).orElse(q2);
|
||||
if (search != null) {
|
||||
search = search.trim();
|
||||
}
|
||||
final String q = (search == null || search.isEmpty())
|
||||
? null
|
||||
: search.toLowerCase(locale);
|
||||
|
||||
List<Paises> all = repo.findAll();
|
||||
|
||||
// Mapear a opciones id/text con i18n y filtrar por búsqueda si llega
|
||||
List<Map<String, String>> options = all.stream()
|
||||
.map(cc -> {
|
||||
String key = cc.getKeyword();
|
||||
String id = cc.getCode3();
|
||||
String text = messageSource.getMessage("paises." + key, null, key, locale);
|
||||
Map<String, String> m = new HashMap<>();
|
||||
m.put("id", id); // lo normal en Select2: id = valor que guardarás (code3)
|
||||
m.put("text", text); // texto mostrado, i18n con fallback a keyword
|
||||
return m;
|
||||
})
|
||||
.filter(opt -> {
|
||||
if (q == null || q.isEmpty())
|
||||
return true;
|
||||
String text = opt.get("text").toLowerCase(locale);
|
||||
String id = opt.get("id").toLowerCase(locale);
|
||||
return text.contains(q) || id.contains(q);
|
||||
})
|
||||
.sorted(Comparator.comparing(m -> m.get("text"), Collator.getInstance(locale)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Estructura Select2
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
resp.put("results", options);
|
||||
return resp;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Map.of("results", List.of());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user