mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-12 16:38:48 +00:00
Merge branch 'mod/margenes-tirada' into 'main'
modificado los margenes por precio en lugar de por tirada y tipos See merge request jjimenez/erp-imprimelibros!11
This commit is contained in:
@ -5,18 +5,12 @@ import jakarta.transaction.Transactional;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
import com.imprimelibros.erp.common.Utils;
|
||||
|
||||
@ -8,6 +8,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -15,11 +17,17 @@ import org.springframework.stereotype.Component;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatrices;
|
||||
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Path;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class Utils {
|
||||
|
||||
@ -42,6 +50,55 @@ public class Utils {
|
||||
return currencyFormatter.format(amount);
|
||||
}
|
||||
|
||||
public static String formatNumber(BigDecimal amount, Locale locale) {
|
||||
NumberFormat numberFormatter = NumberFormat.getNumberInstance(locale);
|
||||
return numberFormatter.format(amount);
|
||||
}
|
||||
|
||||
public static String formatNumber(Double amount, Locale locale) {
|
||||
NumberFormat numberFormatter = NumberFormat.getNumberInstance(locale);
|
||||
return numberFormatter.format(amount);
|
||||
}
|
||||
|
||||
public static Optional<BiFunction<Path<BigDecimal>, CriteriaBuilder, Predicate>> parseNumericFilter(
|
||||
DataTablesRequest dt, String colName, Locale locale) {
|
||||
String raw = dt.getColumnSearch(colName); // usa el "name" del DataTable (snake_case)
|
||||
if (raw == null || raw.isBlank())
|
||||
return Optional.empty();
|
||||
|
||||
String s = raw.trim();
|
||||
// normaliza número con coma o punto
|
||||
Function<String, BigDecimal> toBig = x -> {
|
||||
String t = x.replace(".", "").replace(",", "."); // 1.234,56 -> 1234.56
|
||||
return new BigDecimal(t);
|
||||
};
|
||||
|
||||
try {
|
||||
if (s.matches("(?i)^>=?\\s*[-\\d.,]+$")) {
|
||||
BigDecimal v = toBig.apply(s.replace(">=", "").replace(">", "").trim());
|
||||
return Optional.of((path, cb) -> cb.greaterThanOrEqualTo(path, v));
|
||||
}
|
||||
if (s.matches("(?i)^<=?\\s*[-\\d.,]+$")) {
|
||||
BigDecimal v = toBig.apply(s.replace("<=", "").replace("<", "").trim());
|
||||
return Optional.of((path, cb) -> cb.lessThanOrEqualTo(path, v));
|
||||
}
|
||||
if (s.contains("-")) { // rango "a-b"
|
||||
String[] p = s.split("-");
|
||||
if (p.length == 2) {
|
||||
BigDecimal a = toBig.apply(p[0].trim());
|
||||
BigDecimal b = toBig.apply(p[1].trim());
|
||||
BigDecimal min = a.min(b), max = a.max(b);
|
||||
return Optional.of((path, cb) -> cb.between(path, min, max));
|
||||
}
|
||||
}
|
||||
// exacto/like numérico
|
||||
BigDecimal v = toBig.apply(s);
|
||||
return Optional.of((path, cb) -> cb.equal(path, v));
|
||||
} catch (Exception ignore) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getTextoPresupuesto(Presupuesto presupuesto, Locale locale) {
|
||||
|
||||
Map<String, Object> resumen = new HashMap<>();
|
||||
|
||||
@ -1,182 +1,126 @@
|
||||
package com.imprimelibros.erp.configuracion.margenes_presupuestos;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
|
||||
import com.imprimelibros.erp.shared.validation.NoRangeOverlap;
|
||||
|
||||
|
||||
@Entity
|
||||
@Table(name = "margenes_presupuesto")
|
||||
@SQLDelete(sql = "UPDATE margenes_presupuesto SET deleted = true WHERE id=?")
|
||||
@SQLRestriction("deleted = false")
|
||||
@NoRangeOverlap(
|
||||
min = "tiradaMin",
|
||||
max = "tiradaMax",
|
||||
id = "id",
|
||||
partitionBy = {"tipoEncuadernacion","tipoCubierta"},
|
||||
partitionBy = {},
|
||||
deletedFlag = "deleted", // <- si usas soft delete
|
||||
deletedActiveValue = false, // activo cuando deleted == false
|
||||
message = "{validation.range.overlaps}",
|
||||
invalidRangeMessage = "{validation.range.invalid}"
|
||||
)
|
||||
@Entity
|
||||
@Table(name = "margenes_presupuesto")
|
||||
@SQLDelete(sql = "UPDATE margenes_presupuesto SET deleted = TRUE, deleted_at = NOW() WHERE id = ?")
|
||||
@SQLRestriction("deleted = false")
|
||||
|
||||
public class MargenPresupuesto {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name="tipo_encuadernacion", nullable = false, length = 50)
|
||||
@Column(name="importe_min", nullable=false, precision=12, scale=2)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TipoEncuadernacion tipoEncuadernacion;
|
||||
private BigDecimal importeMin;
|
||||
|
||||
@Column(name="tipo_cubierta", nullable = false, length = 50)
|
||||
@Column(name="importe_max", nullable=false, precision=12, scale=2)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TipoCubierta tipoCubierta;
|
||||
private BigDecimal importeMax;
|
||||
|
||||
@Column(name="tirada_min", nullable = false)
|
||||
@Column(name="margen_min", nullable=false, precision=6, scale=2)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Min(value=1, message="{validation.min}")
|
||||
private Integer tiradaMin;
|
||||
private BigDecimal margenMin;
|
||||
|
||||
@Column(name="tirada_max", nullable = false)
|
||||
@Column(name="margen_max", nullable=false, precision=6, scale=2)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Min(value=1, message="{validation.min}")
|
||||
private Integer tiradaMax;
|
||||
private BigDecimal margenMax;
|
||||
|
||||
@Column(name="margen_max", nullable = false)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Min(value = 0, message="{validation.min}")
|
||||
@Max(value = 200, message="{validation.max}")
|
||||
private Integer margenMax;
|
||||
@Column(nullable=false)
|
||||
private boolean deleted = false;
|
||||
|
||||
@Column(name = "margen_min", nullable = false)
|
||||
@NotNull(message="{validation.required}")
|
||||
@Min(value = 0, message="{validation.min}")
|
||||
@Max(value = 200, message="{validation.max}")
|
||||
private Integer margenMin;
|
||||
|
||||
@Column(name="created_at", nullable = false, updatable = false)
|
||||
@Column(name="created_at", nullable=false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name="updated_at")
|
||||
@Column(name="updated_at", nullable=false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean deleted = false;
|
||||
|
||||
@Column(name="deleted_at")
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
@PrePersist
|
||||
void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = createdAt;
|
||||
}
|
||||
@PreUpdate
|
||||
void onUpdate() {
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public TipoEncuadernacion getTipoEncuadernacion() {
|
||||
return tipoEncuadernacion;
|
||||
public BigDecimal getImporteMin() {
|
||||
return importeMin;
|
||||
}
|
||||
|
||||
public void setTipoEncuadernacion(TipoEncuadernacion tipoEncuadernacion) {
|
||||
this.tipoEncuadernacion = tipoEncuadernacion;
|
||||
public void setImporteMin(BigDecimal importeMin) {
|
||||
this.importeMin = importeMin;
|
||||
}
|
||||
|
||||
public TipoCubierta getTipoCubierta() {
|
||||
return tipoCubierta;
|
||||
public BigDecimal getImporteMax() {
|
||||
return importeMax;
|
||||
}
|
||||
|
||||
public void setTipoCubierta(TipoCubierta tipoCubierta) {
|
||||
this.tipoCubierta = tipoCubierta;
|
||||
public void setImporteMax(BigDecimal importeMax) {
|
||||
this.importeMax = importeMax;
|
||||
}
|
||||
|
||||
public Integer getTiradaMin() {
|
||||
return tiradaMin;
|
||||
}
|
||||
|
||||
public void setTiradaMin(Integer tiradaMin) {
|
||||
this.tiradaMin = tiradaMin;
|
||||
}
|
||||
|
||||
public Integer getTiradaMax() {
|
||||
return tiradaMax;
|
||||
}
|
||||
|
||||
public void setTiradaMax(Integer tiradaMax) {
|
||||
this.tiradaMax = tiradaMax;
|
||||
}
|
||||
|
||||
public Integer getMargenMax() {
|
||||
return margenMax;
|
||||
}
|
||||
|
||||
public void setMargenMax(Integer margenMax) {
|
||||
this.margenMax = margenMax;
|
||||
}
|
||||
|
||||
public Integer getMargenMin() {
|
||||
public BigDecimal getMargenMin() {
|
||||
return margenMin;
|
||||
}
|
||||
|
||||
public void setMargenMin(Integer margenMin) {
|
||||
public void setMargenMin(BigDecimal margenMin) {
|
||||
this.margenMin = margenMin;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
public BigDecimal getMargenMax() {
|
||||
return margenMax;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
public void setMargenMax(BigDecimal margenMax) {
|
||||
this.margenMax = margenMax;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
@PrePersist
|
||||
void onCreate() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = this.createdAt;
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
void onUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
package com.imprimelibros.erp.configuracion.margenes_presupuestos;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
@ -23,13 +22,13 @@ 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.common.Utils;
|
||||
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.i18n.TranslationService;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -83,25 +82,46 @@ public class MargenPresupuestoController {
|
||||
|
||||
List<String> searchable = List.of(
|
||||
"id",
|
||||
"tiradaMin", "tiradaMax",
|
||||
"importeMin", "importeMax",
|
||||
"margenMin", "margenMax");
|
||||
|
||||
List<String> orderable = List.of(
|
||||
"id",
|
||||
"tipoEncuadernacion",
|
||||
"tipoCubierta",
|
||||
"tiradaMin",
|
||||
"tiradaMax",
|
||||
"importeMin",
|
||||
"importeMax",
|
||||
"margenMin",
|
||||
"margenMax");
|
||||
|
||||
Specification<MargenPresupuesto> base = (root, query, cb) -> cb.conjunction();
|
||||
|
||||
Specification<MargenPresupuesto> filtros = (root, query, cb) -> {
|
||||
List<Predicate> ps = new ArrayList<>();
|
||||
|
||||
Utils.parseNumericFilter(dt, "importe_min", locale)
|
||||
.ifPresent(f -> ps.add(f.apply(root.get("importeMin"), cb)));
|
||||
|
||||
Utils.parseNumericFilter(dt, "importe_max", locale)
|
||||
.ifPresent(f -> ps.add(f.apply(root.get("importeMax"), cb)));
|
||||
|
||||
Utils.parseNumericFilter(dt, "margen_min", locale)
|
||||
.ifPresent(f -> ps.add(f.apply(root.get("margenMin"), cb)));
|
||||
|
||||
Utils.parseNumericFilter(dt, "margen_max", locale)
|
||||
.ifPresent(f -> ps.add(f.apply(root.get("margenMax"), cb)));
|
||||
|
||||
return ps.isEmpty() ? cb.conjunction() : cb.and(ps.toArray(new Predicate[0]));
|
||||
};
|
||||
|
||||
long total = repo.count();
|
||||
|
||||
return DataTable
|
||||
.of(repo, MargenPresupuesto.class, dt, searchable) // 'searchable' en DataTable.java
|
||||
// edita columnas "reales":
|
||||
.orderable(orderable)
|
||||
.edit("importeMin", (margen) -> Utils.formatCurrency(margen.getImporteMin(), locale))
|
||||
.edit("importeMax", (margen) -> Utils.formatCurrency(margen.getImporteMax(), locale))
|
||||
.edit("margenMin", (margen) -> Utils.formatNumber(margen.getMargenMin(), locale))
|
||||
.edit("margenMax", (margen) -> Utils.formatNumber(margen.getMargenMax(), locale))
|
||||
.add("actions", (margen) -> {
|
||||
return "<div class=\"hstack gap-3 flex-wrap\">\n" +
|
||||
" <a href=\"javascript:void(0);\" data-id=\"" + margen.getId()
|
||||
@ -110,58 +130,8 @@ public class MargenPresupuestoController {
|
||||
+ "\" class=\"link-danger btn-delete-margen fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>\n"
|
||||
+ " </div>";
|
||||
})
|
||||
.edit("tipoEncuadernacion", (margen) -> {
|
||||
return messageSource.getMessage("presupuesto." + margen.getTipoEncuadernacion().name(), null,
|
||||
locale);
|
||||
})
|
||||
.edit("tipoCubierta", (margen) -> {
|
||||
return messageSource.getMessage("presupuesto." + margen.getTipoCubierta().name(), null, locale);
|
||||
})
|
||||
.where(base)
|
||||
// Filtros custom:
|
||||
.filter((builder, req) -> {
|
||||
String fEncuadernacion = Optional.ofNullable(req.raw.get("f_encuadernacion")).orElse("").trim();
|
||||
if (!fEncuadernacion.isEmpty()) {
|
||||
boolean added = false;
|
||||
// 1) Si llega el nombre del enum (p.ej. "fresado", "cosido", ...)
|
||||
try {
|
||||
var encEnum = TipoEncuadernacion.valueOf(fEncuadernacion);
|
||||
builder.add((root, q, cb) -> cb.equal(root.get("tipoEncuadernacion"), encEnum));
|
||||
added = true;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
// 2) Si llega la clave i18n (p.ej. "presupuesto.fresado", ...)
|
||||
if (!added) {
|
||||
Arrays.stream(TipoEncuadernacion.values())
|
||||
.filter(e -> e.getMessageKey().equals(fEncuadernacion))
|
||||
.findFirst()
|
||||
.ifPresent(encEnum -> builder
|
||||
.add((root, q, cb) -> cb.equal(root.get("tipoEncuadernacion"), encEnum)));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Cubierta ---
|
||||
String fCubierta = Optional.ofNullable(req.raw.get("f_cubierta")).orElse("").trim();
|
||||
if (!fCubierta.isEmpty()) {
|
||||
boolean added = false;
|
||||
// 1) Si llega el nombre del enum (p.ej. "tapaBlanda", "tapaDura",
|
||||
// "tapaDuraLomoRedondo")
|
||||
try {
|
||||
var cubEnum = TipoCubierta.valueOf(fCubierta);
|
||||
builder.add((root, q, cb) -> cb.equal(root.get("tipoCubierta"), cubEnum));
|
||||
added = true;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
// 2) Si llega la clave i18n (p.ej. "presupuesto.tapa-blanda", ...)
|
||||
if (!added) {
|
||||
Arrays.stream(TipoCubierta.values())
|
||||
.filter(e -> e.getMessageKey().equals(fCubierta))
|
||||
.findFirst()
|
||||
.ifPresent(cubEnum -> builder
|
||||
.add((root, q, cb) -> cb.equal(root.get("tipoCubierta"), cubEnum)));
|
||||
}
|
||||
}
|
||||
})
|
||||
.toJson(total);
|
||||
}
|
||||
|
||||
@ -202,16 +172,14 @@ public class MargenPresupuestoController {
|
||||
Locale locale) {
|
||||
|
||||
if (binding.hasErrors()) {
|
||||
response.setStatus(422);
|
||||
response.setStatus(422);
|
||||
model.addAttribute("action", "/configuracion/margenes-presupuesto");
|
||||
return "imprimelibros/configuracion/margenes-presupuesto/margenes-presupuesto-form :: margenesPresupuestoForm";
|
||||
}
|
||||
|
||||
MargenPresupuesto data = new MargenPresupuesto();
|
||||
data.setTipoEncuadernacion(margenPresupuesto.getTipoEncuadernacion());
|
||||
data.setTipoCubierta(margenPresupuesto.getTipoCubierta());
|
||||
data.setTiradaMin(margenPresupuesto.getTiradaMin());
|
||||
data.setTiradaMax(margenPresupuesto.getTiradaMax());
|
||||
data.setImporteMin(margenPresupuesto.getImporteMin());
|
||||
data.setImporteMax(margenPresupuesto.getImporteMax());
|
||||
data.setMargenMax(margenPresupuesto.getMargenMax());
|
||||
data.setMargenMin(margenPresupuesto.getMargenMin());
|
||||
|
||||
@ -243,7 +211,6 @@ public class MargenPresupuestoController {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public String edit(
|
||||
@PathVariable Long id,
|
||||
@ -268,10 +235,8 @@ public class MargenPresupuestoController {
|
||||
var entity = uOpt.get();
|
||||
|
||||
// 3) Copiar solamente campos editables
|
||||
entity.setTipoEncuadernacion(form.getTipoEncuadernacion());
|
||||
entity.setTipoCubierta(form.getTipoCubierta());
|
||||
entity.setTiradaMin(form.getTiradaMin());
|
||||
entity.setTiradaMax(form.getTiradaMax());
|
||||
entity.setImporteMin(form.getImporteMin());
|
||||
entity.setImporteMax(form.getImporteMax());
|
||||
entity.setMargenMax(form.getMargenMax());
|
||||
entity.setMargenMin(form.getMargenMin());
|
||||
|
||||
@ -317,21 +282,23 @@ public class MargenPresupuestoController {
|
||||
@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)));
|
||||
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))));
|
||||
.body(Map.of("message",
|
||||
messageSource.getMessage("margenes-presupuesto.error.not-found", null, locale))));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,40 +1,36 @@
|
||||
package com.imprimelibros.erp.configuracion.margenes_presupuestos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
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 com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
|
||||
|
||||
public interface MargenPresupuestoDao
|
||||
extends JpaRepository<MargenPresupuesto, Long>, JpaSpecificationExecutor<MargenPresupuesto> {
|
||||
|
||||
@Query("""
|
||||
SELECT COUNT(m) FROM MargenPresupuesto m
|
||||
WHERE m.deleted = false
|
||||
AND m.tipoEncuadernacion = :enc
|
||||
AND m.tipoCubierta = :cub
|
||||
AND (:id IS NULL OR m.id <> :id)
|
||||
AND NOT (m.tiradaMax < :min OR m.tiradaMin > :max)
|
||||
""")
|
||||
SELECT COUNT(m) FROM MargenPresupuesto m
|
||||
WHERE m.deleted = false
|
||||
AND ( ( :min BETWEEN m.importeMin AND m.importeMax )
|
||||
OR ( :max BETWEEN m.importeMin AND m.importeMax )
|
||||
OR ( m.importeMin BETWEEN :min AND :max )
|
||||
OR ( m.importeMax BETWEEN :min AND :max ) )
|
||||
AND ( :excludeId IS NULL OR m.id <> :excludeId )
|
||||
""")
|
||||
long countOverlaps(
|
||||
@Param("enc") TipoEncuadernacion enc,
|
||||
@Param("cub") TipoCubierta cub,
|
||||
@Param("min") Integer min,
|
||||
@Param("max") Integer max,
|
||||
@Param("id") Long id);
|
||||
@Param("min") BigDecimal min,
|
||||
@Param("max") BigDecimal max,
|
||||
@Param("excludeId") Long excludeId);
|
||||
|
||||
@Query("""
|
||||
SELECT m FROM MargenPresupuesto m
|
||||
WHERE m.deleted = false
|
||||
AND m.tipoEncuadernacion = :enc
|
||||
AND m.tipoCubierta = :cub
|
||||
AND :tirada BETWEEN m.tiradaMin AND m.tiradaMax
|
||||
""")
|
||||
MargenPresupuesto findByTipoAndTirada(
|
||||
@Param("enc") TipoEncuadernacion enc,
|
||||
@Param("cub") TipoCubierta cub,
|
||||
@Param("tirada") Integer tirada);
|
||||
SELECT m FROM MargenPresupuesto m
|
||||
WHERE m.deleted = false
|
||||
AND :importe BETWEEN m.importeMin AND m.importeMax
|
||||
""")
|
||||
Optional<MargenPresupuesto> findByImporte(@Param("importe") BigDecimal importe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
package com.imprimelibros.erp.configuracion.margenes_presupuestos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class MargenPresupuestoService {
|
||||
@ -27,17 +25,19 @@ public class MargenPresupuestoService {
|
||||
return dao.findById(id);
|
||||
}
|
||||
|
||||
public MargenPresupuesto save(MargenPresupuesto entity) {
|
||||
return dao.save(entity);
|
||||
public Optional<MargenPresupuesto> findByImporte(BigDecimal importe){
|
||||
return dao.findByImporte(importe);
|
||||
}
|
||||
|
||||
public MargenPresupuesto save(MargenPresupuesto e) {
|
||||
return dao.save(e);
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
dao.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasOverlap(TipoEncuadernacion enc, TipoCubierta cub, Integer min, Integer max, Long excludeId) {
|
||||
long count = dao.countOverlaps(enc, cub, min, max, excludeId);
|
||||
return count > 0;
|
||||
public boolean hasOverlap(BigDecimal min, BigDecimal max, Long excludeId) {
|
||||
return dao.countOverlaps(min, max, excludeId) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,15 +9,34 @@ public class DataTablesRequest {
|
||||
public Search search = new Search();
|
||||
public List<Order> order = new ArrayList<>();
|
||||
public List<Column> columns = new ArrayList<>();
|
||||
public Map<String,String> raw = new HashMap<>(); // <- params extra
|
||||
public Map<String, String> raw = new HashMap<>(); // <- params extra
|
||||
|
||||
public static class Search {
|
||||
public String value = "";
|
||||
public boolean regex;
|
||||
}
|
||||
|
||||
public static class Order {
|
||||
public int column;
|
||||
public String dir;
|
||||
}
|
||||
|
||||
public static class Search { public String value=""; public boolean regex; }
|
||||
public static class Order { public int column; public String dir; }
|
||||
public static class Column {
|
||||
public String data;
|
||||
public String name;
|
||||
public boolean searchable=true;
|
||||
public boolean orderable=true;
|
||||
public Search search=new Search();
|
||||
public boolean searchable = true;
|
||||
public boolean orderable = true;
|
||||
public Search search = new Search();
|
||||
}
|
||||
|
||||
public String getColumnSearch(String columnName) {
|
||||
if (columnName == null || columns == null)
|
||||
return null;
|
||||
for (Column col : columns) {
|
||||
if (col != null && col.name != null && col.name.equalsIgnoreCase(columnName)) {
|
||||
return col.search != null ? col.search.value : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,8 @@ import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto.TipoEncuadernacion;
|
||||
|
||||
import java.util.Map;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
@ -34,7 +36,8 @@ public class skApiClient {
|
||||
private final MargenPresupuestoDao margenPresupuestoDao;
|
||||
private final MessageSource messageSource;
|
||||
|
||||
public skApiClient(AuthService authService, MargenPresupuestoDao margenPresupuestoDao, MessageSource messageSource) {
|
||||
public skApiClient(AuthService authService, MargenPresupuestoDao margenPresupuestoDao,
|
||||
MessageSource messageSource) {
|
||||
this.authService = authService;
|
||||
this.restTemplate = new RestTemplate();
|
||||
this.margenPresupuestoDao = margenPresupuestoDao;
|
||||
@ -80,23 +83,28 @@ public class skApiClient {
|
||||
data.get("precios"), new TypeReference<List<Double>>() {
|
||||
});
|
||||
|
||||
for (int i = 0; i < tiradas.size(); i++) {
|
||||
int tirada = tiradas.get(i);
|
||||
for (int i = 0; i < precios.size(); i++) {
|
||||
BigDecimal importe = new BigDecimal(precios.get(i));
|
||||
|
||||
MargenPresupuesto margen = margenPresupuestoDao.findByTipoAndTirada(
|
||||
tipoEncuadernacion, tipoCubierta, tirada);
|
||||
BigDecimal importeTotal = importe.multiply(BigDecimal.valueOf(tiradas.get(i)));
|
||||
|
||||
MargenPresupuesto margen = margenPresupuestoDao
|
||||
.findByImporte(importeTotal).orElse(null);
|
||||
|
||||
if (margen != null) {
|
||||
double margenValue = calcularMargen(
|
||||
tirada,
|
||||
margen.getTiradaMin(),
|
||||
margen.getTiradaMax(),
|
||||
BigDecimal margenValue = calcularMargen(
|
||||
importeTotal,
|
||||
margen.getImporteMin(),
|
||||
margen.getImporteMax(),
|
||||
margen.getMargenMax(),
|
||||
margen.getMargenMin());
|
||||
double nuevoPrecio = precios.get(i) * (1 + margenValue / 100.0);
|
||||
precios.set(i, Math.round(nuevoPrecio * 10000.0) / 10000.0); // redondear a 2 decimales
|
||||
BigDecimal nuevoPrecio = new BigDecimal(precios.get(i)).multiply(BigDecimal.ONE
|
||||
.add(margenValue.divide(BigDecimal.valueOf(100), RoundingMode.HALF_UP)));
|
||||
precios.set(i, nuevoPrecio.setScale(4, RoundingMode.HALF_UP).doubleValue()); // redondear
|
||||
// a 4
|
||||
// decimales
|
||||
} else {
|
||||
System.out.println("No se encontró margen para tirada " + tirada);
|
||||
System.out.println("No se encontró margen para importe " + importe);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +162,8 @@ public class skApiClient {
|
||||
JsonNode root = mapper.readTree(jsonResponse);
|
||||
|
||||
if (root.get("data") == null || !root.get("data").isInt()) {
|
||||
throw new RuntimeException(messageSource.getMessage("presupuesto.errores.error-interior", new Object[]{1} , locale));
|
||||
throw new RuntimeException(
|
||||
messageSource.getMessage("presupuesto.errores.error-interior", new Object[] { 1 }, locale));
|
||||
}
|
||||
|
||||
return root.get("data").asInt();
|
||||
@ -227,13 +236,15 @@ public class skApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
private static double calcularMargen(
|
||||
int tirada, int tiradaMin, int tiradaMax,
|
||||
double margenMax, double margenMin) {
|
||||
if (tirada <= tiradaMin)
|
||||
private static BigDecimal calcularMargen(
|
||||
BigDecimal importe, BigDecimal importeMin, BigDecimal importeMax,
|
||||
BigDecimal margenMax, BigDecimal margenMin) {
|
||||
if (importe.compareTo(importeMin) <= 0)
|
||||
return margenMax;
|
||||
if (tirada >= tiradaMax)
|
||||
if (importe.compareTo(importeMax) >= 0)
|
||||
return margenMin;
|
||||
return margenMax - ((double) (tirada - tiradaMin) / (tiradaMax - tiradaMin)) * (margenMax - margenMin);
|
||||
return margenMax.subtract(margenMax.subtract(margenMin)
|
||||
.multiply(importe.subtract(importeMin)
|
||||
.divide(importeMax.subtract(importeMin), RoundingMode.HALF_UP)));
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ public class TamanioValidator implements ConstraintValidator<Tamanio, Presupuest
|
||||
Integer max = variableService.getValorEntero("ancho_alto_max");
|
||||
|
||||
|
||||
if (presupuesto.getAncho() <= min || presupuesto.getAncho() >= max) {
|
||||
if (presupuesto.getAncho() < min || presupuesto.getAncho() > max) {
|
||||
|
||||
String mensajeInterpolado = messageSource.getMessage(
|
||||
"presupuesto.errores.ancho.min_max", // clave del mensaje
|
||||
|
||||
@ -6,18 +6,14 @@ margenes-presupuesto.editar=Editar margen
|
||||
margenes-presupuesto.eliminar=Eliminar
|
||||
|
||||
margenes-presupuesto.tabla.id=ID
|
||||
margenes-presupuesto.tabla.tipo_encuadernacion=Tipo encuadernación
|
||||
margenes-presupuesto.tabla.tipo_cubierta=Tipo cubierta
|
||||
margenes-presupuesto.tabla.tirada_minima=Tirada Mín.
|
||||
margenes-presupuesto.tabla.tirada_maxima=Tirada Máx.
|
||||
margenes-presupuesto.tabla.importe_minimo=Importe Mín.
|
||||
margenes-presupuesto.tabla.importe_maximo=Importe Máx.
|
||||
margenes-presupuesto.tabla.margen_minimo=Margen Mín.
|
||||
margenes-presupuesto.tabla.margen_maximo=Margen Máx.
|
||||
margenes-presupuesto.tabla.acciones=Acciones
|
||||
|
||||
margenes-presupuesto.form.tipo_encuadernacion=Tipo de encuadernación
|
||||
margenes-presupuesto.form.tipo_cubierta=Tipo de cubierta
|
||||
margenes-presupuesto.form.tirada_minima=Tirada mínima
|
||||
margenes-presupuesto.form.tirada_maxima=Tirada máxima
|
||||
margenes-presupuesto.form.importe_minimo=Importe mínimo
|
||||
margenes-presupuesto.form.importe_maximo=Importe máximo
|
||||
margenes-presupuesto.form.margen_minimo=Margen mínimo (%)
|
||||
margenes-presupuesto.form.margen_maximo=Margen máximo (%)
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import {normalizeNumericFilter} from '../../utils.js';
|
||||
|
||||
(() => {
|
||||
// si jQuery está cargado, añade CSRF a AJAX
|
||||
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||
@ -22,7 +24,6 @@
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
orderCellsTop: true,
|
||||
stateSave: true,
|
||||
pageLength: 50,
|
||||
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||
responsive: true,
|
||||
@ -45,18 +46,12 @@
|
||||
ajax: {
|
||||
url: '/configuracion/margenes-presupuesto/datatable',
|
||||
method: 'GET',
|
||||
data: function (d) {
|
||||
d.f_encuadernacion = $('#search-encuadernacion').val() || ''; // 'USER' | 'ADMIN' | 'SUPERADMIN' | ''
|
||||
d.f_cubierta = $('#search-cubierta').val() || ''; // 'true' | 'false' | ''
|
||||
}
|
||||
},
|
||||
order: [[0, 'asc']],
|
||||
columns: [
|
||||
{ data: 'id', name: 'id', orderable: true },
|
||||
{ data: 'tipoEncuadernacion', name: 'tipoEncuadernacion', orderable: true },
|
||||
{ data: 'tipoCubierta', name: 'tipoCubierta', orderable: true },
|
||||
{ data: 'tiradaMin', name: 'tiradaMin', orderable: true },
|
||||
{ data: 'tiradaMax', name: 'tiradaMax', orderable: true },
|
||||
{ data: 'importeMin', name: 'importeMin', orderable: true },
|
||||
{ data: 'importeMax', name: 'importeMax', orderable: true },
|
||||
{ data: 'margenMax', name: 'margenMax', orderable: true },
|
||||
{ data: 'margenMin', name: 'margenMin', orderable: true },
|
||||
{ data: 'actions', name: 'actions' }
|
||||
@ -69,7 +64,7 @@
|
||||
const colIndex = table.settings()[0].aoColumns.findIndex(c => c.name === colName);
|
||||
|
||||
if (colIndex >= 0) {
|
||||
table.column(colIndex).search(this.value).draw();
|
||||
table.column(colIndex).search(normalizeNumericFilter(this.value)).draw();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -64,4 +64,17 @@ export function bracketPrefix(obj, prefix) {
|
||||
out[`${prefix}[${k}]`] = v;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
export function normalizeNumericFilter(input) {
|
||||
if (!input) return input;
|
||||
// Convierte todos los números del string:
|
||||
// - Quita separadores de miles con punto
|
||||
// - Cambia coma decimal por punto
|
||||
// Mantiene operadores (>=, <=, <, >) y rangos con '-'
|
||||
return input.replace(
|
||||
/\d{1,3}(?:\.\d{3})*(?:,\d+)?|\d+(?:,\d+)?/g,
|
||||
(num) => num.replace(/\./g, '').replace(',', '.')
|
||||
);
|
||||
}
|
||||
@ -9,41 +9,17 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label th:text="#{margenes-presupuesto.form.tipo_encuadernacion}" for="tipo_encuadernacion">Tipo de Encuadernación</label>
|
||||
<select class="form-control" id="tipo_encuadernacion" th:field="*{tipoEncuadernacion}" required
|
||||
th:classappend="${#fields.hasErrors('tipoEncuadernacion')} ? ' is-invalid'">
|
||||
<option value="fresado" th:text="#{presupuesto.fresado}" selected>Fresado</option>
|
||||
<option value="cosido" th:text="#{presupuesto.cosido}">Cosido</option>
|
||||
<option value="espiral" th:text="#{presupuesto.espiral}">Espiral</option>
|
||||
<option value="wireo" th:text="#{presupuesto.wireo}">Wire-O</option>
|
||||
<option value="grapado" th:text="#{presupuesto.grapado}">Grapado</option>
|
||||
</select>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('tipoEncuadernacion')}" th:errors="*{tipoEncuadernacion}">Error</div>
|
||||
<label th:text="#{margenes-presupuesto.form.importe_minimo}" for="importe_minimo">Importe Mínimo</label>
|
||||
<input type="number" class="form-control" id="importe_minimo" th:field="*{importeMin}" min="1"
|
||||
th:classappend="${#fields.hasErrors('importeMin')} ? ' is-invalid'" required>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('importeMin')}" th:errors="*{importeMin}">Error</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label th:text="#{margenes-presupuesto.form.tipo_cubierta}" for="tipo_cubierta">Tipo de Cubierta</label>
|
||||
<select class="form-control" id="tipo_cubierta" th:field="*{tipoCubierta}" required
|
||||
th:classappend="${#fields.hasErrors('tipoCubierta')} ? ' is-invalid'">
|
||||
<option value="tapaBlanda" th:text="#{presupuesto.tapaBlanda}" selected>Tapa Blanda</option>
|
||||
<option value="tapaDura" th:text="#{presupuesto.tapaDura}">Tapa Dura</option>
|
||||
<option value="tapaDuraLomoRedondo" th:text="#{presupuesto.tapaDuraLomoRedondo}">Tapa Dura Lomo Redondo</option>
|
||||
</select>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('tipoCubierta')}" th:errors="*{tipoCubierta}">Error</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label th:text="#{margenes-presupuesto.form.tirada_minima}" for="tirada_minima">Tirada Mínima</label>
|
||||
<input type="number" class="form-control" id="tirada_minima" th:field="*{tiradaMin}" min="1"
|
||||
th:classappend="${#fields.hasErrors('tiradaMin')} ? ' is-invalid'" required>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('tiradaMin')}" th:errors="*{tiradaMin}">Error</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label th:text="#{margenes-presupuesto.form.tirada_maxima}" for="tirada_maxima">Tirada Máxima</label>
|
||||
<input type="number" class="form-control" id="tirada_maxima" th:field="*{tiradaMax}" min="1"
|
||||
th:classappend="${#fields.hasErrors('tiradaMax')} ? ' is-invalid'" required>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('tiradaMax')}" th:errors="*{tiradaMax}">Error</div>
|
||||
<label th:text="#{margenes-presupuesto.form.importe_maximo}" for="importe_maximo">Importe Máximo</label>
|
||||
<input type="number" class="form-control" id="importe_maximo" th:field="*{importeMax}" min="1"
|
||||
th:classappend="${#fields.hasErrors('importeMax')} ? ' is-invalid'" required>
|
||||
<div class="invalid-feedback" th:if="${#fields.hasErrors('importeMax')}" th:errors="*{importeMax}">Error</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
@ -45,11 +45,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.id}">ID</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tipo_encuadernacion}">Tipo
|
||||
encuadernación</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tipo_cubierta}">Tipo cubierta</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tirada_minima}">Tirada Mín.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tirada_maxima}">Tirada Máx.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.importe_minimo}">Importe Mín.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.importe_maximo}">Importe Máx.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.margen_maximo}">Margen Máx.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.margen_minimo}">Margen Mín.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.acciones}">Acciones</th>
|
||||
@ -58,33 +55,12 @@
|
||||
<th><input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="id" /></th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter"
|
||||
id="search-encuadernacion">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="fresado" th:text="#{presupuesto.fresado}">Fresado</option>
|
||||
<option value="cosido" th:text="#{presupuesto.cosido}">Cosido</option>
|
||||
<option value="espiral" th:text="#{presupuesto.espiral}">Espiral</option>
|
||||
<option value="wireo" th:text="#{presupuesto.wireo}">Wireo</option>
|
||||
<option value="grapado" th:text="#{presupuesto.grapado}">Grapado</option>
|
||||
</select>
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter"
|
||||
id="search-cubierta">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="tapaBlanda" th:text="#{presupuesto.tapa-blanda}"></option>
|
||||
<option value="tapaDura" th:text="#{presupuesto.tapa-dura}"></option>
|
||||
<option value="tapaDuraLomoRedondo" th:text="#{presupuesto.tapa-dura-lomo-redondo}">
|
||||
</option>
|
||||
</select>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="importeMin" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="tiradaMin" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="tiradaMax" />
|
||||
data-col="importeMax" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
@ -122,7 +98,7 @@
|
||||
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/configuracion/margenes-presupuesto/list.js}"></script>
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/configuracion/margenes-presupuesto/list.js}"></script>
|
||||
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user