From 42fa3478295f1814f6a74057e73aae3a73b07066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Jim=C3=A9nez?= Date: Sun, 21 Sep 2025 14:16:00 +0200 Subject: [PATCH] terminado --- pom.xml | 6 +++ .../erp/common/HtmlStripConverter.java | 20 ++++++++ .../erp/presupuesto/Presupuesto.java | 5 ++ .../resources/i18n/presupuesto_es.properties | 8 +++- .../presupuestador/presupuestador.js | 48 +++++++++++++++++-- .../presupuestador/presupuesto-maquetacion.js | 6 ++- .../presupuesto-marcapaginas.js | 11 +++-- .../presupuestador/service-option-card.js | 4 +- .../presupuestador/tirada-price-card.js | 17 +++---- .../assets/js/pages/imprimelibros/utils.js | 46 +++++++++++------- .../_datos-generales.html | 6 +-- .../presupuestador-items/_extras.html | 4 +- .../presupuestador-items/_resumen.html | 24 ++++++++++ .../presupuestos/presupuestador.html | 16 +++++++ 14 files changed, 177 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/imprimelibros/erp/common/HtmlStripConverter.java create mode 100644 src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen.html diff --git a/pom.xml b/pom.xml index 5f641c6..2463cb2 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,12 @@ test + + org.jsoup + jsoup + 1.17.2 + + diff --git a/src/main/java/com/imprimelibros/erp/common/HtmlStripConverter.java b/src/main/java/com/imprimelibros/erp/common/HtmlStripConverter.java new file mode 100644 index 0000000..b7b364b --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/common/HtmlStripConverter.java @@ -0,0 +1,20 @@ +package com.imprimelibros.erp.common; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import org.jsoup.Jsoup; +import org.jsoup.safety.Safelist; + +@Converter(autoApply = false) +public class HtmlStripConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(String attribute) { + return attribute == null ? null : Jsoup.clean(attribute, Safelist.none()); + } + + @Override + public String convertToEntityAttribute(String dbData) { + return dbData; // tal cual + } +} diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java b/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java index 83cb397..d768ab2 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/Presupuesto.java @@ -8,6 +8,8 @@ import com.imprimelibros.erp.presupuesto.validation.Par; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; import com.imprimelibros.erp.presupuesto.validation.Tamanio; +import com.imprimelibros.erp.common.HtmlStripConverter; + import jakarta.persistence.*; @ConsistentTiradas(groups = PresupuestoValidationGroups.DatosGenerales.class) @@ -45,13 +47,16 @@ public class Presupuesto implements Cloneable{ @Column(name = "tipo_encuadernacion") private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado; + @Convert(converter = HtmlStripConverter.class) @NotBlank(message = "{presupuesto.errores.titulo}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "titulo") private String titulo; + @Convert(converter = HtmlStripConverter.class) @Column(name = "autor") private String autor; + @Convert(converter = HtmlStripConverter.class) @Column(name = "isbn") private String isbn; diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties index 222b3c6..13a8e98 100644 --- a/src/main/resources/i18n/presupuesto_es.properties +++ b/src/main/resources/i18n/presupuesto_es.properties @@ -1,9 +1,11 @@ presupuesto.datos-generales=Datos Generales presupuesto.interior=Interior presupuesto.cubierta=Cubierta -presupuesto.seleccion-tirada=Seleccion de tirada +presupuesto.seleccion-tirada=Selección tirada presupuesto.extras=Extras +presupuesto.resumen=Resumen presupuesto.add-to-presupuesto=Añadir al presupuesto +presupuesto.calcular=Calcular # Pestaña datos generales de presupuesto presupuesto.informacion-libro=Información del libro @@ -153,7 +155,9 @@ presupuesto.volver-cubierta=Volver a diseño cubierta presupuesto.finalizar=Finalizar presupuesto presupuesto.calcular-presupuesto=Calcular presupuesto presupuesto.consultar-soporte=Consultar con soporte -presupuesto.calcular=Calcular + +# Pestaña resumen del presupuesto +presupuesto.volver-extras=Volver a extras # Resumen del presupuesto presupuesto.resumen-presupuesto=Resumen presupuesto diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js index 6748a20..303654b 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js @@ -70,7 +70,7 @@ class PresupuestoCliente { acabado_marcapaginas: 'ninguno', resultado: { precio_unitario: 0, - precio_total: 0 + precio: 0 } }, datosMaquetacion: { @@ -184,6 +184,7 @@ class PresupuestoCliente { this.#initInterior(); this.#initSeleccionTirada(); this.#initExtras(); + this.#initResumen(); $(document).on('change', 'input[min][max]', function () { const $input = $(this); @@ -1387,14 +1388,25 @@ class PresupuestoCliente { this.#changeTab('pills-seleccion-tirada'); this.summaryTableExtras.addClass('d-none'); } else { - //this.#changeTab('pills-finalizar'); + this.#changeTab('pills-resumen'); } }); // Eventos para el resumen $(document).on('change', '.service-checkbox', (e) => { - Summary.updateExtras(); + const $target = $(e.currentTarget); + + if ($target.prop('checked')) { + this.formData.servicios.servicios.push($target.val()); + } else { + const index = this.formData.servicios.servicios.indexOf($target.val()); + if (index > -1) { + this.formData.servicios.servicios.splice(index, 1); + } + } + Summary.updateExtras(); + this.#cacheFormData(); }); } @@ -1406,14 +1418,44 @@ class PresupuestoCliente { this.divExtras.addClass('animate-fadeInUpBounce'); for (const extra of servicios) { + if (this.formData.servicios.servicios.includes(extra.id) && !extra.checked) { + extra.checked = true; + if (extra.id === "marcapaginas" || extra.id === "maquetacion") { + extra.price = extra.id === "marcapaginas" ? + this.formData.servicios.datosMarcapaginas.resultado.precio : + this.formData.servicios.datosMaquetacion.resultado.precio; + extra.priceUnit = this.divExtras.data('currency'); + } + } const item = new ServiceOptionCard(extra); this.divExtras.append(item.render()); + if (item.checked) { + if (!this.formData.servicios.servicios.includes(extra.id)) { + this.formData.servicios.servicios.push(extra.id); + } + } } + this.#cacheFormData(); Summary.updateExtras(); } + /****************************** + * END EXTRAS + ******************************/ + + /****************************** + * EXTRAS + ******************************/ + #initResumen() { + $(document).on('click', '.btn-change-tab-resumen', (e) => { + const id = e.currentTarget.id; + if (id === 'btn-prev-resumen') { + this.#changeTab('pills-extras'); + } + }); + } /****************************** * END EXTRAS ******************************/ diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js index eaaa0da..6b7e8db 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-maquetacion.js @@ -113,9 +113,11 @@ $(document).on("submit", "#maquetacionForm", function (e) { $(document).on('hidden.bs.modal', '#maquetacionModal', function () { const calcularStr = $('#div-extras').data('language-calcular'); - $('#maquetacion').prop('checked', false); $('#maquetacion').data('price', calcularStr); $('label[for="maquetacion"] .service-price').text(calcularStr); + $('#maquetacion').prop('checked', false).trigger('change'); + + Summary.updateExtras(); }); @@ -138,6 +140,8 @@ function loadMaquetacionData() { $('#texto-mecanografiado').prop('checked', stored.texto_mecanografiado); $('#disenio-portada').prop('checked', stored.disenio_portada); $('#epub').prop('checked', stored.epub); + + Summary.updateExtras(); } diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js index 458f393..34e4a3b 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuesto-marcapaginas.js @@ -1,5 +1,5 @@ import * as Summary from "./summary.js"; -import { formateaMoneda, formateaMoneda6Decimales } from "../utils.js"; +import { formateaMoneda } from "../utils.js"; $(document).on('change', '#marcapaginas', function (e) { e.preventDefault(); @@ -10,7 +10,7 @@ $(document).on('change', '#marcapaginas', function (e) { loadMarcapaginasData(); // init marcapaginas form - filtrarAcabados(); + filtrarAcabados(); $("#marcapaginasModal").modal("show"); }); @@ -42,7 +42,7 @@ $(document).on("submit", "#marcapaginasForm", function (e) { const resumenHtml = `
-

${json.language.precio_unidad || 'Precio por unidad'}: ${formateaMoneda6Decimales(json.precio_unitario) || "-"}

+

${json.language.precio_unidad || 'Precio por unidad'}: ${formateaMoneda(json.precio_unitario, 6) || "-"}

${json.language.precio_total || 'Precio total'}: ${formateaMoneda(json.precio_total) || "-"}

`; @@ -72,7 +72,7 @@ $(document).on("submit", "#marcapaginasForm", function (e) { // guardamos los datos del formulario en sessionStorage const stored = JSON.parse(sessionStorage.getItem("formData")); stored.servicios.datosMarcapaginas.resultado.precio_unitario = json.precio_unitario; - stored.servicios.datosMarcapaginas.resultado.precio_total = json.precio_total; + stored.servicios.datosMarcapaginas.resultado.precio = json.precio_total; sessionStorage.setItem("formData", JSON.stringify(stored)); } else { @@ -117,9 +117,10 @@ $(document).on("change", "#caras-impresion", function (e) { $(document).on('hidden.bs.modal', '#marcapaginasModal', function () { const calcularStr = $('#div-extras').data('language-calcular'); - $('#marcapaginas').prop('checked', false); $('#marcapaginas').data('price', calcularStr); $('label[for="marcapaginas"] .service-price').text(calcularStr); + $('#marcapaginas').prop('checked', false).trigger('change'); + Summary.updateExtras(); }); diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/service-option-card.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/service-option-card.js index d9416f0..2e0835f 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/service-option-card.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/service-option-card.js @@ -1,3 +1,5 @@ +import {formateaMoneda, isNumber} from '../utils.js'; + class ServiceOptionCard { constructor({ id, title, description = '', price = '', priceUnit = '', checked = false, allowChange = true, ribbonText }) { @@ -23,7 +25,7 @@ class ServiceOptionCard { ${ribbonHtml}
${this.title}

${this.description}

-

${this.price} ${this.priceUnit}

+

${isNumber(this.price) ? formateaMoneda(this.price, 2, this.locale) : this.price}

`); diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/tirada-price-card.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/tirada-price-card.js index 0acf65f..7acc726 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/tirada-price-card.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/tirada-price-card.js @@ -1,6 +1,8 @@ +import { formateaMoneda, formateaNumero } from "../utils.js"; + // ===== Clase ===== class TiradaCard { - constructor({ id, titulo, unidades, precioUnidad, precioTotal, selected = false, moneda = '€', name = 'tirada', + constructor({ id, titulo, unidades, precioUnidad, precioTotal, selected = false, name = 'tirada', labels = {}, locale = (document.documentElement.lang || navigator.language || 'es-ES') }) { this.id = id; @@ -9,7 +11,6 @@ class TiradaCard { this.precioUnidad = precioUnidad; this.precioTotal = precioTotal; this.selected = selected; - this.moneda = moneda; this.name = name; this.locale = locale; this.labels = Object.assign({ @@ -22,16 +23,10 @@ class TiradaCard { this.$container = null; // se establece al renderizar } - #formatMoneyES(value, decimals = 2) { - return new Intl.NumberFormat(this.locale, { - minimumFractionDigits: decimals, - maximumFractionDigits: decimals - }).format(Number(value)); - } #title() { if (this.titulo) return this.titulo; - if (this.unidades != null) return `${this.unidades} ${this.labels.units}`; + if (this.unidades != null) return `${formateaNumero({valor: this.unidades, locale: this.locale, digits: 0})} ${this.labels.units}`; return ''; } @@ -46,11 +41,11 @@ class TiradaCard {
${this.#title()}
${this.labels.perUnit}
-
${this.#formatMoneyES(this.precioUnidad, 4)} ${this.moneda}
+
${formateaMoneda(this.precioUnidad, 4, this.locale)}
${this.labels.total}
-
${this.#formatMoneyES(this.precioTotal, 2)} ${this.moneda}
+
${formateaMoneda(this.precioTotal, 2, this.locale)}
diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/utils.js b/src/main/resources/static/assets/js/pages/imprimelibros/utils.js index 4872496..15a4a3a 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/utils.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/utils.js @@ -1,26 +1,40 @@ -function formateaMoneda(valor) { +function formateaMoneda(valor, digits = 2, locale = 'es-ES', currency = 'EUR') { try { - return new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR' }).format(valor); + return new Intl.NumberFormat(locale, { style: 'currency', currency, minimumFractionDigits: digits, useGrouping: true }).format(valor); } catch { return valor; } } export { formateaMoneda }; -function formateaMoneda4Decimales(valor) { - try { - return new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR', minimumFractionDigits: 4 }).format(valor); - } catch { - return valor; - } -} -export { formateaMoneda4Decimales }; -function formateaMoneda6Decimales(valor) { - try { - return new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR', minimumFractionDigits: 6 }).format(valor); - } catch { - return valor; +function formateaNumero({ + valor, + digits = 2, + style = 'decimal', + locale = 'es-ES', + currency = 'EUR' +}) { + const n = Number(valor); + if (!Number.isFinite(n)) return valor; + + const opts = { + useGrouping: true, + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }; + + if (style === 'currency') { + opts.style = 'currency'; + opts.currency = currency; } + + return new Intl.NumberFormat(locale, opts).format(n); } -export { formateaMoneda6Decimales }; \ No newline at end of file +export { formateaNumero }; + + +function isNumber(value) { + return !isNaN(Number(value)) && value.trim() !== ''; +} +export { isNumber }; \ No newline at end of file diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_datos-generales.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_datos-generales.html index f0b25e0..a8666b6 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_datos-generales.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_datos-generales.html @@ -24,7 +24,7 @@ + th:value="${presupuesto?.titulo} ?: ''"> @@ -33,13 +33,13 @@
- +
- +
diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_extras.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_extras.html index c16830e..ecb6a86 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_extras.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_extras.html @@ -10,7 +10,7 @@
-
+
@@ -24,7 +24,7 @@ diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen.html new file mode 100644 index 0000000..50330b6 --- /dev/null +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen.html @@ -0,0 +1,24 @@ +
+ + +
+
+
Resumen +
+
+ +
+
+
+
+
+ + +
+ +
+
\ No newline at end of file diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html index 5f2e48b..07f0245 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador.html @@ -61,6 +61,15 @@ + @@ -106,6 +115,13 @@ +
+ +
+
+ +