ya obtengo los datos de los anonimos, falta formatear esos datos y buscar

This commit is contained in:
2025-10-07 10:44:32 +02:00
parent 1e8f9cafb3
commit 389ac22b68
19 changed files with 205 additions and 93 deletions

View File

@ -9,8 +9,9 @@ import java.time.LocalDateTime;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.shared.validation.NoRangeOverlap;

View File

@ -28,8 +28,8 @@ import com.imprimelibros.erp.datatables.DataTablesParser;
import com.imprimelibros.erp.datatables.DataTablesRequest;
import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

View File

@ -5,8 +5,8 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
public interface MargenPresupuestoDao
extends JpaRepository<MargenPresupuesto, Long>, JpaSpecificationExecutor<MargenPresupuesto> {

View File

@ -6,8 +6,8 @@ import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
@Service
@Transactional

View File

@ -33,6 +33,12 @@ public class DataTable<T> {
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
private List<String> orderable = null;
private boolean onlyAdded = false;
public DataTable<T> onlyAddedColumns(){
this.onlyAdded = true;
return this;
}
private DataTable(JpaSpecificationExecutor<T> repo, Class<T> entityClass, DataTablesRequest dt,
List<String> searchable) {
this.repo = repo;
@ -148,7 +154,7 @@ public class DataTable<T> {
// Mapear entidad -> Map base (via Jackson) + add/edit
List<Map<String, Object>> data = new ArrayList<>();
for (T e : p.getContent()) {
Map<String, Object> row = om.convertValue(e, Map.class);
Map<String, Object> row = onlyAdded ? new HashMap<>() : om.convertValue(e, Map.class);
row.put("__entity", e); // para editores que necesiten la entidad
for (var ad : adders)
row.putAll(ad.apply(e));

View File

@ -13,8 +13,8 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuestoDao;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
import java.util.Map;
import java.util.HashMap;

View File

@ -4,9 +4,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoImpresion;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoImpresion;
import java.util.*;

Binary file not shown.

View File

@ -3,16 +3,22 @@ package com.imprimelibros.erp.presupuesto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import java.util.Arrays;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.validation.BindingResult;
@ -22,24 +28,21 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
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.RequestBody;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
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.datatables.*;
import com.imprimelibros.erp.externalApi.skApiClient;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoDTAnonimo;
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
import jakarta.servlet.http.HttpServletRequest;
@ -439,54 +442,87 @@ public class PresupuestoController {
return "imprimelibros/presupuestos/presupuesto-list";
}
@GetMapping(value = "/datatable/{tipo}", produces = "application/json")
@GetMapping(value = "/datatable/anonimos", produces = "application/json")
@ResponseBody
public DataTablesResponse<Map<String, Object>> datatable(@RequestParam("tipo") String tipo,
HttpServletRequest request, Authentication authentication,
public DataTablesResponse<Map<String, Object>> datatable(
HttpServletRequest request,
Authentication authentication,
Locale locale) {
DataTablesRequest dt = DataTablesParser.from(request);
List<String> searchable = List.of(
"id",
"titulo",
"cliente",
"tipoEncuadernacion",
"tipoCubierta",
"tipoImpresion",
"tirada",
"paginas",
"estado", "total", "pais", "region", "ciudad", "updatedAt");
// Filtros (opcional): p.ej. estado desde select extra
String fEstado = Optional.ofNullable(dt.raw.get("f_estado")).orElse(""); // '', 'borrador', ...
String term = (dt.search != null && dt.search.value != null) ? dt.search.value.trim() : "";
List<String> orderable = List.of(/*
* "id",
* "tipoEncuadernacion",
* "tipoCubierta",
* "tiradaMin",
* "tiradaMax",
* "margenMin",
* "margenMax"
*/);
// Pageable y Sort desde DataTables (usarás 'name' completos en el front)
int page = dt.length > 0 ? dt.start / dt.length : 0;
List<Sort.Order> orders = new ArrayList<>();
for (var o : dt.order) {
String field = dt.columns.get(o.column).name; // EJ: "cliente.nombre", "tipoImpresion", "pais"
// Whitelist de campos ordenables:
switch (field) {
case "id", "titulo",
"tipoEncuadernacion", "tipoCubierta", "tipoImpresion",
"selectedTirada", "paginas", "estado",
"totalConIva",
"pais", "region", "ciudad",
"updatedAt" ->
orders.add(new Sort.Order(
"desc".equalsIgnoreCase(o.dir) ? Sort.Direction.DESC : Sort.Direction.ASC,
field));
default -> {
/* ignora */ }
}
}
Sort sort = orders.isEmpty() ? Sort.by(Sort.Order.asc("id")) : Sort.by(orders);
Pageable pageable = dt.length > 0 ? PageRequest.of(page, dt.length, sort) : Pageable.unpaged();
Specification<MargenPresupuesto> base = (root, query, cb) -> cb.conjunction();
// Query DTO
Page<PresupuestoDTAnonimo> pageDTO = repo.datatableAnonimos(fEstado, term, pageable);
long total = repo.count();
return DataTable
.of(repo, MargenPresupuesto.class, dt, searchable) // 'searchable' en DataTable.java
// edita columnas "reales":
.orderable(orderable)
.add("actions", (presupuesto) -> {
return "<div class=\"hstack gap-3 flex-wrap\">\n" +
" <a href=\"javascript:void(0);\" data-id=\"" + presupuesto.getId()
+ "\" class=\"link-success btn-edit-" + tipo
+ " fs-15\"><i class=\"ri-edit-2-line\"></i></a>\n"
+ " <a href=\"javascript:void(0);\" data-id=\"" + presupuesto.getId()
+ "\" class=\"link-danger btn-delete-" + tipo
+ " fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>\n"
+ " </div>";
})
.where(base)
.toJson(total);
// para formatear la fecha
ZoneId zone = null;
if (locale != null && locale.getCountry() != null && !locale.getCountry().isEmpty()) {
zone = TimeZone.getTimeZone(locale.toLanguageTag()).toZoneId();
} else {
zone = ZoneId.systemDefault(); // fallback
}
DateTimeFormatter df = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm").withZone(zone);
// Mapear DTO -> Map para DataTables + acciones
List<Map<String, Object>> rows = pageDTO.getContent().stream().map(r -> {
Map<String, Object> m = new HashMap<>();
m.put("id", r.id());
m.put("titulo", r.titulo());
m.put("tipoEncuadernacion", r.tipoEncuadernacion().name()); // o etiqueta i18n si quieres
m.put("tipoCubierta", r.tipoCubierta().name());
m.put("tipoImpresion", r.tipoImpresion().name());
m.put("tirada", r.selectedTirada());
m.put("paginas", r.paginas());
m.put("estado", r.estado().name());
m.put("totalConIva", r.totalConIva());
m.put("pais", r.pais());
m.put("region", r.region());
m.put("ciudad", r.ciudad());
m.put("updatedAt", r.updatedAt() == null ? "" : df.format(r.updatedAt()));
// Mantén aquí tu “actions” como antes (puedes usar `tipo` en el css)
m.put("actions",
"<div class=\"hstack gap-3 flex-wrap\">" +
" <a href=\"javascript:void(0);\" data-id=\"" + r.id()
+ "\" class=\"link-success btn-edit-anonimo"
+ " fs-15\"><i class=\"ri-edit-2-line\"></i></a>" +
" <a href=\"javascript:void(0);\" data-id=\"" + r.id()
+ "\" class=\"link-danger btn-delete-anonimo"
+ " fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>" +
"</div>");
return m;
}).toList();
return new DataTablesResponse<>(dt.draw, total, pageDTO.getTotalElements(), rows);
}
}

View File

@ -5,13 +5,16 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoDTAnonimo;
import java.util.*;
@Repository
public interface PresupuestoRepository extends JpaRepository<Presupuesto, Long>, JpaSpecificationExecutor<MargenPresupuesto> {
public interface PresupuestoRepository extends JpaRepository<Presupuesto, Long>, JpaSpecificationExecutor<Presupuesto> {
Optional<Presupuesto> findFirstBySessionIdAndOrigenAndEstadoInOrderByUpdatedAtDesc(
String sessionId,
@ -26,4 +29,36 @@ public interface PresupuestoRepository extends JpaRepository<Presupuesto, Long>,
Optional<Presupuesto> findTopBySessionIdAndEstadoOrderByCreatedAtDesc(String sessionId, Presupuesto.Estado estado);
@Query("""
select new com.imprimelibros.erp.presupuesto.dto.PresupuestoDTAnonimo(
p.id,
p.titulo,
p.tipoEncuadernacion,
p.tipoCubierta,
p.tipoImpresion,
p.selectedTirada,
cast(coalesce(p.paginasColor,0) + coalesce(p.paginasNegro,0) as integer),
p.estado,
p.totalConIva,
p.pais,
p.region,
p.ciudad,
p.updatedAt
)
from Presupuesto p
where
(:estado = '' or str(p.estado) = :estado)
and (
:term = '' or
lower(p.titulo) like concat('%', lower(:term), '%') or
lower(p.ciudad) like concat('%', lower(:term), '%') or
lower(p.region) like concat('%', lower(:term), '%') or
lower(p.pais) like concat('%', lower(:term), '%')
)
""")
Page<PresupuestoDTAnonimo> datatableAnonimos(
@Param("estado") String estado, // '', 'borrador', 'aceptado', 'modificado'
@Param("term") String term,
Pageable pageable
);
}

View File

@ -23,7 +23,6 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
@ -32,6 +31,8 @@ import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepositor
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesRepository;
import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository;
import com.imprimelibros.erp.externalApi.skApiClient;

View File

@ -3,7 +3,8 @@ package com.imprimelibros.erp.presupuesto.classes;
import org.springframework.stereotype.Component;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.presupuesto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import org.springframework.context.MessageSource;
import java.util.Locale;

View File

@ -1,4 +1,4 @@
package com.imprimelibros.erp.presupuesto;
package com.imprimelibros.erp.presupuesto.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@ -188,6 +188,11 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
// ====== TUS CAMPOS ORIGINALES ======
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class)
@Enumerated(EnumType.STRING)
@Column(name = "tipo_encuadernacion")
@ -858,4 +863,15 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.altoFaja = altoFaja;
}
public Long getId(){
return id;
}
public void setId(Long id){
this.id = id;
}
}

View File

@ -0,0 +1,20 @@
package com.imprimelibros.erp.presupuesto.dto;
import java.math.BigDecimal;
import java.time.Instant;
public record PresupuestoDTAnonimo(
Long id,
String titulo, // c.nombre
Presupuesto.TipoEncuadernacion tipoEncuadernacion,
Presupuesto.TipoCubierta tipoCubierta,
Presupuesto.TipoImpresion tipoImpresion,
Integer selectedTirada,
Integer paginas,
Presupuesto.Estado estado,
BigDecimal totalConIva,
String pais,
String region,
String ciudad,
Instant updatedAt
) {}

View File

@ -1,7 +1,7 @@
package com.imprimelibros.erp.presupuesto.validation;
import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.presupuesto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

View File

@ -1,7 +1,7 @@
package com.imprimelibros.erp.presupuesto.validation;
import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.presupuesto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

View File

@ -52,24 +52,23 @@
},
order: [[0, 'asc']],
columns: [
{ data: 'id', name: 'id', orderable: true },
{ data: 'cliente', name: 'cliente', orderable: true },
{ data: 'tipoEncuadernacion', name: 'tipoEncuadernacion', orderable: true },
{ data: 'tipoCubierta', name: 'tipoCubierta', orderable: true },
{ data: 'tipoImpresion', name: 'tipoImpresion', orderable: true },
{ data: 'tirada', name: 'tirada', orderable: true },
{ data: 'paginas', name: 'paginas', orderable: true },
{ data: 'estado', name: 'estado', orderable: true },
{ data: 'total', name: 'total', orderable: true },
{ data: 'pais', name: 'pais', orderable: true },
{ data: 'region', name: 'region', orderable: true },
{ data: 'ciudad', name: 'ciudad', orderable: true },
{ data: 'updatedAt', name: 'updatedAt', orderable: true },
{ data: 'actions', name: 'actions' }
{ data: 'id', name: 'id', title: 'ID', orderable: true },
{ data: 'titulo', name: 'titulo', title: 'Título', orderable: true },
{ data: 'tipoEncuadernacion', name: 'tipoEncuadernacion', title: 'Encuadernación', orderable: true },
{ data: 'tipoCubierta', name: 'tipoCubierta', title: 'Cubierta', orderable: true },
{ data: 'tipoImpresion', name: 'tipoImpresion', title: 'Tipo de impresión', orderable: true },
{ data: 'tirada', name: 'selectedTirada', title: 'Tirada', orderable: true },
{ data: 'paginas', name: 'paginas', title: 'Páginas', orderable: true },
{ data: 'estado', name: 'estado', title: 'Estado', orderable: true },
{ data: 'totalConIva', name: 'totalConIva', title: 'Total con IVA', orderable: true },
{ data: 'pais', name: 'pais', title: 'País', orderable: true },
{ data: 'region', name: 'region', title: 'Región', orderable: true },
{ data: 'ciudad', name: 'ciudad', title: 'Ciudad', orderable: true },
{ data: 'updatedAt', name: 'updatedAt', title: 'Actualizado el', orderable: true },
{ data: 'actions', title: 'Acciones', orderable: false, searchable: false }
],
columnDefs: [{ targets: -1, orderable: false, searchable: false }]
});
})();

View File

@ -1,10 +1,9 @@
<div th:fragment="tabla-anonimos">
<table id="presupuesto-anonimos-datatable" class="table table-striped table-nowrap responsive w-100">
<table id="presupuestos-anonimos-datatable" class="table table-striped table-nowrap responsive w-100">
<thead>
<tr>
<th scope="col" th:text="#{presupuesto.tabla.id}">ID</th>
<th scope="col" th:text="#{presupuesto.tabla.titulo}">Título</th>
<th scope="col" th:text="#{presupuesto.tabla.cliente}">Cliente</th>
<th scope="col" th:text="#{presupuesto.tabla.encuadernacion}">Encuadernación</th>
<th scope="col" th:text="#{presupuesto.tabla.cubierta}">Cubierta</th>
<th scope="col" th:text="#{presupuesto.tabla.tipo-impresion}">Tipo de impresión</th>
@ -18,9 +17,7 @@
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
</tr>
<tr>
</tr>
</thead>
<tbody>
</tbody>

View File

@ -11,8 +11,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.imprimelibros.erp.externalApi.skApiClient;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
@SpringBootTest
class skApiClientTest {