package com.imprimelibros.erp.datatables; import org.springframework.data.jpa.domain.Specification; import jakarta.persistence.criteria.*; import java.util.ArrayList; import java.util.List; public class DataTablesSpecification { /** * Crea una Specification con búsqueda global y por columna (LIKE * case-insensitive) * * @param dt request de datatables * @param searchableFields campos del entity para el buscador global */ public static Specification build(DataTablesRequest dt, List searchableFields) { return (root, query, cb) -> { List ands = new ArrayList<>(); // Filtro por columna (si lo usas en el cliente) for (int i = 0; i < dt.columns.size(); i++) { DataTablesRequest.Column col = dt.columns.get(i); if (col.searchable && col.search != null && col.search.value != null && !col.search.value.isEmpty()) { try { Path path = root; String[] parts = col.name.split("\\."); for (String part : parts) { path = path.get(part); } ands.add(like(cb, path, col.search.value)); } catch (IllegalArgumentException ex) { // columna no mapeada o relación: la ignoramos //System.out.println("[DT] columna no mapeada o relación: " + col.name); } } } // Búsqueda global if (dt.search != null && dt.search.value != null && !dt.search.value.isEmpty() && !searchableFields.isEmpty()) { String term = "%" + dt.search.value.trim().toLowerCase() + "%"; List ors = new ArrayList<>(); for (String f : searchableFields) { try { ors.add(cb.like(cb.lower(root.get(f).as(String.class)), term)); } catch (IllegalArgumentException ex) { // campo no simple: lo saltamos } } if (!ors.isEmpty()) ands.add(cb.or(ors.toArray(new Predicate[0]))); } return ands.isEmpty() ? cb.conjunction() : cb.and(ands.toArray(new Predicate[0])); }; } private static Predicate like(CriteriaBuilder cb, Path path, String value) { return cb.like(cb.lower(path.as(String.class)), "%" + value.trim().toLowerCase() + "%"); } }