diff --git a/src/main/java/com/imprimelibros/erp/cart/CartController.java b/src/main/java/com/imprimelibros/erp/cart/CartController.java index 1bd4e7a..4bb4f78 100644 --- a/src/main/java/com/imprimelibros/erp/cart/CartController.java +++ b/src/main/java/com/imprimelibros/erp/cart/CartController.java @@ -94,7 +94,8 @@ public class CartController { } /** Eliminar línea por presupuesto_id (opcional) */ - @DeleteMapping("/remove/presupuesto/{presupuestoId}") + @DeleteMapping("/delete/item/{presupuestoId}") + @ResponseBody public String removeByPresupuesto(@PathVariable Long presupuestoId, Principal principal) { service.removeByPresupuesto(currentUserId(principal), presupuestoId); return "redirect:/cart"; diff --git a/src/main/java/com/imprimelibros/erp/cart/CartService.java b/src/main/java/com/imprimelibros/erp/cart/CartService.java index afe4215..cc4400e 100644 --- a/src/main/java/com/imprimelibros/erp/cart/CartService.java +++ b/src/main/java/com/imprimelibros/erp/cart/CartService.java @@ -19,6 +19,7 @@ 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; import com.imprimelibros.erp.presupuesto.PresupuestoRepository; @@ -30,15 +31,17 @@ public class CartService { private final MessageSource messageSource; private final PresupuestoFormatter presupuestoFormatter; private final PresupuestoRepository presupuestoRepo; + private final Utils utils; public CartService(CartRepository cartRepo, CartItemRepository itemRepo, MessageSource messageSource, PresupuestoFormatter presupuestoFormatter, - PresupuestoRepository presupuestoRepo) { + PresupuestoRepository presupuestoRepo, Utils utils) { this.cartRepo = cartRepo; this.itemRepo = itemRepo; this.messageSource = messageSource; this.presupuestoFormatter = presupuestoFormatter; this.presupuestoRepo = presupuestoRepo; + this.utils = utils; } /** Devuelve el carrito activo o lo crea si no existe. */ @@ -137,65 +140,14 @@ public class CartService { resumen.put("presupuestoId", presupuesto.getId()); - ObjectMapper mapper = new ObjectMapper(); - List> servicios = new ArrayList<>(); - if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isBlank()) - try{ - servicios = mapper.readValue(presupuesto.getServiciosJson(), new TypeReference<>() { - }); - } catch (JsonProcessingException e) { - // Manejar la excepción - } + Map detalles = utils.getTextoPresupuesto(presupuesto, locale); - boolean hayDepositoLegal = servicios != null && servicios.stream() - .map(m -> java.util.Objects.toString(m.get("id"), "")) - .map(String::trim) - .anyMatch("deposito-legal"::equals); + resumen.put("baseTotal", Utils.formatCurrency(presupuesto.getBaseImponible(), locale)); + resumen.put("base", presupuesto.getBaseImponible()); + resumen.put("iva4", presupuesto.getIvaImporte4()); + resumen.put("iva21", presupuesto.getIvaImporte21()); - List> lineas = new ArrayList<>(); - HashMap linea = new HashMap<>(); - Double precio_unitario = 0.0; - Double precio_total = 0.0; - BigDecimal total = BigDecimal.ZERO; - linea.put("descripcion", presupuestoFormatter.resumen(presupuesto, servicios, locale)); - linea.put("cantidad", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0); - precio_unitario = (presupuesto.getPrecioUnitario() != null ? presupuesto.getPrecioUnitario().doubleValue() : 0.0); - precio_total = (presupuesto.getPrecioTotalTirada() != null ? presupuesto.getPrecioTotalTirada().doubleValue() : 0.0); - linea.put("precio_unitario", precio_unitario); - linea.put("precio_total", BigDecimal.valueOf(precio_total).setScale(2, RoundingMode.HALF_UP)); - total = total.add(BigDecimal.valueOf(precio_total)); - lineas.add(linea); - - if (hayDepositoLegal) { - linea = new HashMap<>(); - linea.put("descripcion", messageSource.getMessage("presupuesto.resumen-deposito-legal", null, locale)); - linea.put("cantidad", 4); - linea.put("precio_unitario", precio_unitario); - linea.put("precio_total", BigDecimal.valueOf(precio_unitario * 4).setScale(2, RoundingMode.HALF_UP)); - total = total.add(BigDecimal.valueOf(precio_unitario * 4)); - lineas.add(linea); - } - - List> serviciosExtras = new ArrayList<>(); - if (servicios != null) { - for (Map servicio : servicios) { - HashMap servicioData = new HashMap<>(); - servicioData.put("id", servicio.get("id")); - servicioData.put("descripcion", servicio.get("label")); - servicioData.put("precio", servicio.get("id").equals("marcapaginas") - ? Double.parseDouble(servicio.get("price").toString()) - / Double.parseDouble(servicio.get("units").toString()) - : servicio.get("price")); - total = total.add(BigDecimal.valueOf(Double.parseDouble(servicioData.get("precio").toString()))); - servicioData.put("unidades", servicio.get("units")); - serviciosExtras.add(servicioData); - } - } - NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale); - String formattedString = currencyFormat.format(total.setScale(2, RoundingMode.HALF_UP).doubleValue()); - resumen.put("total", formattedString); - resumen.put("lineas", lineas); - resumen.put("servicios", serviciosExtras); + resumen.put("resumen", detalles); return resumen; } diff --git a/src/main/java/com/imprimelibros/erp/common/Utils.java b/src/main/java/com/imprimelibros/erp/common/Utils.java index 1cd7a14..1ce76c8 100644 --- a/src/main/java/com/imprimelibros/erp/common/Utils.java +++ b/src/main/java/com/imprimelibros/erp/common/Utils.java @@ -93,18 +93,28 @@ public class Utils { if (hayDepositoLegal) { linea = new HashMap<>(); linea.put("descripcion", - messageSource.getMessage("pdf.ejemplares-deposito-legal", new Object[]{4}, locale)); + messageSource.getMessage("pdf.ejemplares-deposito-legal", new Object[] { 4 }, + locale)); lineas.add(linea); } String serviciosExtras = ""; if (servicios != null) { for (Map servicio : servicios) { - serviciosExtras += messageSource.getMessage( - "presupuesto.extras-" + servicio.get("id"), null, locale) + ", "; + if ("deposito-legal".equals(servicio.get("id")) || + "service-isbn".equals(servicio.get("id"))) { + serviciosExtras += messageSource.getMessage( + "presupuesto.extras-" + servicio.get("id"), null, locale) + + ", "; + } else { + serviciosExtras += messageSource.getMessage( + "presupuesto.extras-" + servicio.get("id"), null, locale) + .toLowerCase() + ", "; + } } if (!serviciosExtras.isEmpty()) { - serviciosExtras = serviciosExtras.substring(0, serviciosExtras.length() - 2);; + serviciosExtras = serviciosExtras.substring(0, serviciosExtras.length() - 2); + ; } if (servicios.stream().anyMatch(service -> "marcapaginas".equals(service.get("id")))) { ObjectMapper mapperServicio = new ObjectMapper(); diff --git a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java index 20e5161..392b416 100644 --- a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java +++ b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java @@ -113,8 +113,12 @@ public class SecurityConfig { RequestMatcher notStatic = new AndRequestMatcher( new NegatedRequestMatcher(PathRequest.toStaticResources().atCommonLocations()), new NegatedRequestMatcher(pathStartsWith("/assets/"))); + + RequestMatcher cartCount = new AndRequestMatcher( + new NegatedRequestMatcher(PathRequest.toStaticResources().atCommonLocations()), + new NegatedRequestMatcher(pathStartsWith("/cart/count"))); - cache.setRequestMatcher(new AndRequestMatcher(htmlPage, nonAjax, notStatic, notWellKnown)); + cache.setRequestMatcher(new AndRequestMatcher(htmlPage, nonAjax, notStatic, notWellKnown, cartCount)); rc.requestCache(cache); }) // ======================================================== @@ -135,7 +139,8 @@ public class SecurityConfig { "/presupuesto/public/**", "/error", "/favicon.ico", - "/.well-known/**" // opcional + "/.well-known/**", // opcional + "/api/pdf/presupuesto/**" ).permitAll() .requestMatchers("/users/**").hasAnyRole("SUPERADMIN", "ADMIN") .anyRequest().authenticated()) diff --git a/src/main/java/com/imprimelibros/erp/pdf/PdfService.java b/src/main/java/com/imprimelibros/erp/pdf/PdfService.java index dd3d101..b70b956 100644 --- a/src/main/java/com/imprimelibros/erp/pdf/PdfService.java +++ b/src/main/java/com/imprimelibros/erp/pdf/PdfService.java @@ -118,7 +118,7 @@ public class PdfService { model.put("empresa", empresa); model.put("cliente", Map.of( - "nombre", presupuesto.getUser().getFullName())); + "nombre", presupuesto.getUser() != null ? presupuesto.getUser().getFullName() : "")); model.put("titulo", presupuesto.getTitulo()); diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java b/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java index a796175..2669aed 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java @@ -38,8 +38,8 @@ public class PresupuestoFormatter { Object[] args = { p.getSelectedTirada(), - encuadernacion, - tipoImpresion, + encuadernacion.toLowerCase(), + tipoImpresion.toLowerCase(), (p.getPaginasColorTotal() != null ? p.getPaginasColorTotal() : p.getPaginasColor()) + p.getPaginasNegro(), p.getAncho(), p.getAlto(), diff --git a/src/main/resources/i18n/cart_es.properties b/src/main/resources/i18n/cart_es.properties index d40d8f9..4fff994 100644 --- a/src/main/resources/i18n/cart_es.properties +++ b/src/main/resources/i18n/cart_es.properties @@ -3,3 +3,12 @@ cart.empty=Tu cesta de la compra está vacía. cart.item.presupuesto-numero=Presupuesto # cart.precio=Precio + +cart.resumen.title=Resumen de la cesta +cart.resumen.base=Base imponible: +cart.resumen.iva-4=IVA 4%: +cart.resumen.iva-21=IVA 21%: +cart.resumen.total=Total cesta: +cart.resumen.tramitar=Tramitar pedido + +cart.resumen.fidelizacion=Si tiene descuento por fidelización, se aplicará al tramitar el pedido. \ No newline at end of file diff --git a/src/main/resources/i18n/pdf_es.properties b/src/main/resources/i18n/pdf_es.properties index e5ae686..d1e2ffc 100644 --- a/src/main/resources/i18n/pdf_es.properties +++ b/src/main/resources/i18n/pdf_es.properties @@ -14,8 +14,6 @@ pdf.presupuesto.client=CLIENTE: pdf.presupuesto.date=FECHA: pdf.presupuesto.titulo=Título: -pdf.presupuesto.descripcion=Descripción: - pdf.table.tirada=TIRADA pdf.table.impresion=IMPRESIÓN pdf.table.servicios=SERVICIOS @@ -23,11 +21,13 @@ pdf.table.iva-4=IVA 4% pdf.table.iva-21=IVA 21% pdf.table.precio-total=PRECIO TOTAL -pdf.servicios-adicionales=Servicios adicionales: +pdf.servicios-adicionales=Servicios adicionales: pdf.ejemplares-deposito-legal=Impresión de {0} ejemplares de depósito legal
pdf.datos-maquetacion=Datos de maquetación: pdf.datos-marcapaginas=Datos de marcapáginas: +pdf.incluye-envio=El presupuesto incluye el envío a una dirección de la península. + pdf.politica-privacidad=Política de privacidad pdf.politica-privacidad.responsable=Responsable: Impresión Imprime Libros - CIF: B04998886 - Teléfono de contacto: 910052574 pdf.politica-privacidad.correo-direccion=Correo electrónico: info@imprimelibros.com - Dirección postal: Calle José Picón, Nº 28 Local A, 28028, Madrid diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties index 5ba868c..84b1069 100644 --- a/src/main/resources/i18n/presupuesto_es.properties +++ b/src/main/resources/i18n/presupuesto_es.properties @@ -213,17 +213,17 @@ presupuesto.resumen.tabla.base=Base presupuesto.resumen.tabla.iva4=I.V.A. (4%) presupuesto.resumen.tabla.iva21=I.V.A. (21%) presupuesto.resumen.tabla.total=Total presupuesto -presupuesto.resumen-texto=Impresion de {0} unidades encuadernadas en {1} en {2} con {3} páginas en formato {4} x {5} mm. \ +presupuesto.resumen-texto=Impresion de {0} ejemplares con {3} páginas, tamaño {4}x{5} mm e impresas en {2} con encuadernación {1}. \
    \
  • Papel interior {6} {7} gr.
  • \
  • Cubierta {8} en {9} {10} gr.
  • presupuesto.resumen-texto-impresion-caras-cubierta=
  • Impresa a {0}.
  • presupuesto.resumen-texto-solapas-cubierta=
  • Solapas de {0} mm.
  • presupuesto.resumen-texto-guardas-cabezada=
  • Guardas {0} en {1} {2}. Color de la cabezada: {3}.
  • -presupuesto.resumen-texto-acabado-cubierta=
  • Acabado {0}.
  • +presupuesto.resumen-texto-acabado-cubierta=
  • Acabado: {0}.
  • presupuesto.resumen-texto-end=
-presupuesto.resumen-texto-sobrecubierta=
  • Sobrecubierta impresa en {0} {1} gr.
    • Acabado {2}
    • Solapas: {3} mm.
  • -presupuesto.resumen-texto-faja=
  • Faja impresa en {0} {1} gr. con un alto de {2} mm.
    • Acabado {3}
    • Solapas: {4} mm.
  • +presupuesto.resumen-texto-sobrecubierta=
  • Sobrecubierta impresa en {0} {1} gr.
    • Acabado: {2}
    • Solapas: {3} mm.
  • +presupuesto.resumen-texto-faja=
  • Faja impresa en {0} {1} gr. con un alto de {2} mm.
    • Acabado: {3}
    • Solapas: {4} mm.
  • presupuesto.resumen-deposito-legal=Ejemplares para el Depósito Legal presupuesto.volver-extras=Extras del libro presupuesto.resumen.inicie-sesion=Inicie sesión para continuar diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/cart/cart.js b/src/main/resources/static/assets/js/pages/imprimelibros/cart/cart.js new file mode 100644 index 0000000..707a93a --- /dev/null +++ b/src/main/resources/static/assets/js/pages/imprimelibros/cart/cart.js @@ -0,0 +1,67 @@ +import { formateaMoneda } from '../../imprimelibros/utils.js'; + +$(() => { + + updateTotal(); + + function updateTotal() { + const items = $(".product"); + let iva4 = 0; + let iva21 = 0; + let base = 0; + for (let i = 0; i < items.length; i++) { + const item = $(items[i]); + const b = item.data("base"); + const i4 = item.data("iva-4"); + const i21 = item.data("iva-21"); + base += parseFloat(b) || 0; + iva4 += parseFloat(i4) || 0; + iva21 += parseFloat(i21) || 0; + } + $("#base-cesta").text(formateaMoneda(base)); + if (iva4 > 0) { + $("#iva-4-cesta").text(formateaMoneda(iva4)); + $("#tr-iva-4").show(); + } else { + $("#tr-iva-4").hide(); + } + if (iva21 > 0) { + $("#iva-21-cesta").text(formateaMoneda(iva21)); + $("#tr-iva-21").show(); + } else { + $("#tr-iva-21").hide(); + } + const total = base + iva4 + iva21; + $("#total-cesta").text(formateaMoneda(total)); + } + + $(document).on("click", ".delete-item", async function (event) { + + event.preventDefault(); + const cartItemId = $(this).data("cart-item-id"); + const card = $(this).closest('.card.product'); + + // CSRF (Spring Security) + const csrfToken = document.querySelector('meta[name="_csrf"]')?.content || ''; + const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.content || 'X-CSRF-TOKEN'; + + try { + const res = await fetch(`/cart/delete/item/${cartItemId}`, { + method: 'DELETE', + headers: { [csrfHeader]: csrfToken } + }); + + if (!res.ok) { + console.error('Error al eliminar. Status:', res.status); + return; + } + else{ + card?.remove(); + updateTotal(); + } + + } catch (err) { + console.error('Error en la solicitud:', err); + } + }); +}); \ No newline at end of file diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js index a85640b..04bddd3 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js @@ -1700,6 +1700,7 @@ export default class PresupuestoWizard { window.PRESUPUESTO_ID = data.presupuesto_id; } body.presupuesto.id = window.PRESUPUESTO_ID || body.presupuesto.id || null; + this.opts.presupuestoId = window.PRESUPUESTO_ID; this.#updateResumenTable(data); }).catch((error) => { diff --git a/src/main/resources/templates/imprimelibros/cart/_cartItem.html b/src/main/resources/templates/imprimelibros/cart/_cartItem.html index 46745b2..5ee3a2c 100644 --- a/src/main/resources/templates/imprimelibros/cart/_cartItem.html +++ b/src/main/resources/templates/imprimelibros/cart/_cartItem.html @@ -1,5 +1,7 @@ -
    +
    @@ -25,20 +27,44 @@ -
      -
      +
        +
      + +
        + Servicios adicionales + +
      + +
        +
      • + Datos de maquetación: + +
      • +
      + +
        +
      • + Datos de marcapáginas: + +
      • +
      +

      Precio

      - 0,00 + 0,00
    @@ -50,14 +76,10 @@
    diff --git a/src/main/resources/templates/imprimelibros/cart/cart.html b/src/main/resources/templates/imprimelibros/cart/cart.html index b56779f..d54ce93 100644 --- a/src/main/resources/templates/imprimelibros/cart/cart.html +++ b/src/main/resources/templates/imprimelibros/cart/cart.html @@ -32,18 +32,67 @@
    - - -
    +
    -
    - +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    :
    :
    : + + + +
    + +
    + +
    +
    + + +
    + + +
    + @@ -51,6 +100,8 @@ + + diff --git a/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html b/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html index b883f3e..c7a8b0f 100644 --- a/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html +++ b/src/main/resources/templates/imprimelibros/pdf/presupuesto-a4.html @@ -54,9 +54,12 @@ PRESUPUESTO Nº: 153153 - CLIENTE: + CLIENTE: JUAN JOSÉ - MÉNDEZ + MÉNDEZ + + FECHA: 10/10/2025 @@ -69,9 +72,6 @@
    -
    - Descripción: -
    @@ -85,11 +85,15 @@ Servicios adicionales
    -
    +
    Datos de maquetación:
    -
    +
    Datos de marcapáginas:
    @@ -98,7 +102,7 @@
    - +
    @@ -133,6 +137,8 @@
    TIRADA