falta ordenar por paginas y revisar la busqueda de los selects

This commit is contained in:
2025-10-11 18:16:55 +02:00
parent a1359f37b0
commit 62dcff8869
11 changed files with 287 additions and 70 deletions

View File

@ -1,5 +1,6 @@
package com.imprimelibros.erp.presupuesto;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.domain.*;
@ -29,33 +30,38 @@ public class PresupuestoDatatableService {
/* ---------- API pública ---------- */
public DataTablesResponse<Map<String, Object>> datatableAnonimos(DataTablesRequest dt, Locale locale) {
public DataTablesResponse<Map<String, Object>> datatablePublicos(DataTablesRequest dt, Locale locale) {
String term = extractSearch(dt);
Pageable pageable = pageableFrom(dt);
EnumMatches matches = buildEnumMatches(term, locale);
Specification<Presupuesto> spec = baseSpec(term, matches, dt);
spec = spec.and((root, query, cb) -> cb.equal(root.get("origen"), "publico"));
Page<Presupuesto> page = repo.findAll(spec, pageable);
var rows = page.getContent().stream()
.map(p -> mapAnonimoRow(p, locale)) // 👈 mapper específico “anonimos”
.map(p -> mapPresupuestoPublico(p, locale))
.toList();
return new DataTablesResponse<>(dt.draw, repo.count(), page.getTotalElements(), rows);
}
public DataTablesResponse<Map<String, Object>> datatableNoAnonimos(DataTablesRequest dt, Locale locale) {
public DataTablesResponse<Map<String, Object>> datatablePrivados(DataTablesRequest dt, Locale locale) {
String term = extractSearch(dt);
Pageable pageable = pageableFrom(dt);
EnumMatches matches = buildEnumMatches(term, locale);
Specification<Presupuesto> spec = baseSpec(term, matches, dt);
spec = spec.and((root, query, cb) -> cb.equal(root.get("origen"), "privado"));
Page<Presupuesto> page = repo.findAll(spec, pageable);
var rows = page.getContent().stream()
.map(p -> mapNoAnonimoRow(p, locale)) // 👈 otro mapper con más/otros campos
.map(p -> mapPresupuestoPrivado(p, locale)) // 👈 otro mapper con más/otros campos
.toList();
return new DataTablesResponse<>(dt.draw, repo.count(), page.getTotalElements(), rows);
@ -67,6 +73,24 @@ public class PresupuestoDatatableService {
return (dt.search != null && dt.search.value != null) ? dt.search.value.trim().toLowerCase() : "";
}
private Map<String, String> extractColumnSearches(DataTablesRequest dt) {
Map<String, String> byColumn = new HashMap<>();
if (dt.columns == null)
return byColumn;
for (var col : dt.columns) {
// Importante: en el front usa columns[i][name] con el nombre del campo JPA
String field = col.name;
String value = (col.search != null && col.search.value != null)
? col.search.value.trim()
: "";
if (field != null && !field.isBlank() && value != null && !value.isBlank()) {
byColumn.put(field, value.toLowerCase());
}
}
return byColumn;
}
private Pageable pageableFrom(DataTablesRequest dt) {
int page = dt.length > 0 ? dt.start / dt.length : 0;
List<Sort.Order> orders = new ArrayList<>();
@ -115,10 +139,16 @@ public class PresupuestoDatatableService {
private Specification<Presupuesto> baseSpec(String term, EnumMatches m, DataTablesRequest dt) {
return (root, query, cb) -> {
List<Predicate> ors = new ArrayList<>();
List<Predicate> ands = new ArrayList<>(); // filtros por columna (AND)
Expression<Integer> totalPag = cb.sum(
cb.coalesce(root.get("paginasColor"), cb.literal(0)),
cb.coalesce(root.get("paginasNegro"), cb.literal(0)));
if (!term.isBlank()) {
String like = "%" + term + "%";
ors.add(cb.like(cb.lower(root.get("titulo")), like));
ors.add(cb.like(cb.lower(root.join("user").get("fullName")), like));
ors.add(cb.like(cb.lower(root.get("ciudad")), like));
ors.add(cb.like(cb.lower(root.get("region")), like));
ors.add(cb.like(cb.lower(root.get("pais")), like));
@ -132,6 +162,56 @@ public class PresupuestoDatatableService {
if (!m.est.isEmpty())
ors.add(root.get("estado").in(m.est));
Map<String, String> byCol = extractColumnSearches(dt);
for (var entry : byCol.entrySet()) {
String field = entry.getKey();
String value = entry.getValue();
if (value.isBlank() || field.isBlank()
|| field.contains("tipoEncuadernacion")
|| field.contains("tipoCubierta")
|| field.contains("tipoImpresion")
|| field.contains("estado")) {
continue;
}
// --- CASO ESPECIAL: filtro por nombre del usuario ---
if ("user".equals(field)) {
var userJoin = root.join("user");
var expr = cb.lower(userJoin.get("fullName"));
ands.add(cb.like(expr, "%" + value.toLowerCase() + "%"));
continue;
}
// --- CASO ESPECIAL: filtro por total de páginas ---
if ("paginas".equals(field)) {
try {
int paginas = Integer.parseInt(value);
ands.add(cb.equal(totalPag, paginas));
} catch (NumberFormatException nfe) {
var asString = cb.function("CONCAT", String.class, cb.literal(""), totalPag);
var safe = cb.function("COALESCE", String.class, asString, cb.literal(""));
var strExpr = cb.lower(safe);
ands.add(cb.like(strExpr, "%" + value.toLowerCase() + "%"));
}
continue;
}
// --- RESTO DE CAMPOS: acceso genérico ---
var path = root.get(field);
Class<?> type = path.getJavaType();
Expression<String> strExpr;
if (String.class.isAssignableFrom(type)) {
strExpr = cb.lower(path.as(String.class));
} else {
var asString = cb.function("CONCAT", String.class, cb.literal(""), path);
var safe = cb.function("COALESCE", String.class, asString, cb.literal(""));
strExpr = cb.lower(safe);
}
ands.add(cb.like(strExpr, "%" + value.toLowerCase() + "%"));
}
// ORDER BY especial si en columns[i][name] viene 'paginas' o 'estado'
if (query != null && !query.getOrderList().isEmpty()) {
var jpaOrders = new ArrayList<jakarta.persistence.criteria.Order>();
@ -139,26 +219,42 @@ public class PresupuestoDatatableService {
String prop = ob.getExpression().toString();
boolean asc = ob.isAscending();
if ("paginas".equals(prop)) {
var totalPag = cb.sum(cb.coalesce(root.get("paginasColor"), 0),
cb.coalesce(root.get("paginasNegro"), 0));
jpaOrders.add(asc ? cb.asc(totalPag) : cb.desc(totalPag));
var totalPagOrder = cb.sum(
cb.coalesce(root.get("paginasColor"), cb.literal(0)),
cb.coalesce(root.get("paginasNegro"), cb.literal(0)));
jpaOrders.add(asc ? cb.asc(totalPagOrder) : cb.desc(totalPagOrder));
} else if ("estado".equals(prop)) {
var estadoStr = cb.function("str", String.class, root.get("estado"));
jpaOrders.add(asc ? cb.asc(estadoStr) : cb.desc(estadoStr));
} else {
jpaOrders.add(asc ? cb.asc(root.get(prop)) : cb.desc(root.get(prop)));
try {
jpaOrders.add(asc ? cb.asc(root.get(prop)) : cb.desc(root.get(prop)));
} catch (IllegalArgumentException e) {
// El campo no existe (como 'paginas'), lo ignoramos
// Opcional: puedes loggear si quieres
// log.warn("Campo no encontrado para ORDER BY: {}", prop);
}
}
}
query.orderBy(jpaOrders);
}
return ors.isEmpty() ? cb.conjunction() : cb.or(ors.toArray(new Predicate[0]));
// === Compose final WHERE ===
Predicate where = ors.isEmpty()
? cb.conjunction()
: cb.or(ors.toArray(new Predicate[0]));
if (!ands.isEmpty()) {
where = cb.and(where, cb.and(ands.toArray(new Predicate[0])));
}
return where;
};
}
/* ---------- Mappers de filas (puedes tener tantos como vistas) ---------- */
private Map<String, Object> mapAnonimoRow(Presupuesto p, Locale locale) {
private Map<String, Object> mapPresupuestoPublico(Presupuesto p, Locale locale) {
int paginas = n(p.getPaginasColor()) + n(p.getPaginasNegro());
Map<String, Object> m = new HashMap<>();
m.put("id", p.getId());
@ -174,30 +270,55 @@ public class PresupuestoDatatableService {
m.put("region", p.getRegion());
m.put("ciudad", p.getCiudad());
m.put("updatedAt", formatDate(p.getUpdatedAt(), locale));
if(p.getEstado().equals(Presupuesto.Estado.borrador)){
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
+
"</div>");
}
else{
if (p.getEstado().equals(Presupuesto.Estado.borrador)) {
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
"</div>");
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-danger btn-delete-anonimo fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
+
"</div>");
} else {
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-anonimo fs-15\"><i class=\"ri-eye-line\"></i></a>" +
"</div>");
}
return m;
}
private Map<String, Object> mapNoAnonimoRow(Presupuesto p, Locale locale) {
Map<String, Object> m = mapAnonimoRow(p, locale); // base común
// añade/remueve campos específicos de “no anónimos”
// m.put("cliente", p.getCliente().getNombre()); // ejemplo
private Map<String, Object> mapPresupuestoPrivado(Presupuesto p, Locale locale) {
int paginas = n(p.getPaginasColor()) + n(p.getPaginasNegro());
Map<String, Object> m = new HashMap<>();
m.put("id", p.getId());
m.put("titulo", p.getTitulo());
m.put("user", p.getUser().getFullName());
m.put("tipoEncuadernacion", msg(p.getTipoEncuadernacion().getMessageKey(), locale));
m.put("tipoCubierta", msg(p.getTipoCubierta().getMessageKey(), locale));
m.put("tipoImpresion", msg(p.getTipoImpresion().getMessageKey(), locale));
m.put("tirada", p.getSelectedTirada());
m.put("paginas", paginas);
m.put("estado", msg(p.getEstado().getMessageKey(), locale));
m.put("totalConIva", formatCurrency(p.getTotalConIva(), locale));
m.put("updatedAt", formatDate(p.getUpdatedAt(), locale));
if (p.getEstado().equals(Presupuesto.Estado.borrador)) {
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-privado fs-15\"><i class=\"ri-pencil-line\"></i></a>" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-danger btn-delete-privado fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>"
+
"</div>");
} else {
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
"<a href=\"javascript:void(0);\" data-id=\"" + p.getId()
+ "\" class=\"link-success btn-edit-privado fs-15\"><i class=\"ri-pencil-line\"></i></a>" +
"</div>");
}
return m;
}