mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 00:48:49 +00:00
terminado. trabajando en el carrito. falta mensaje de ya añadido
This commit is contained in:
@ -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";
|
||||
|
||||
@ -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<Map<String, Object>> 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<String, Object> 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<HashMap<String, Object>> lineas = new ArrayList<>();
|
||||
HashMap<String, Object> 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<Map<String, Object>> serviciosExtras = new ArrayList<>();
|
||||
if (servicios != null) {
|
||||
for (Map<String, Object> servicio : servicios) {
|
||||
HashMap<String, Object> 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;
|
||||
}
|
||||
|
||||
@ -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<String, Object> 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();
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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.
|
||||
@ -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=<u>Servicios adicionales:</u>
|
||||
pdf.servicios-adicionales=Servicios adicionales:
|
||||
pdf.ejemplares-deposito-legal=Impresión de {0} ejemplares de depósito legal<br />
|
||||
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
|
||||
|
||||
@ -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}. \
|
||||
<ul> \
|
||||
<li>Papel interior {6} {7} gr.</li> \
|
||||
<li>Cubierta {8} en {9} {10} gr.</li>
|
||||
presupuesto.resumen-texto-impresion-caras-cubierta=<li>Impresa a {0}.</li>
|
||||
presupuesto.resumen-texto-solapas-cubierta=<li>Solapas de {0} mm.</li>
|
||||
presupuesto.resumen-texto-guardas-cabezada= <li>Guardas {0} en {1} {2}. Color de la cabezada: {3}.</li>
|
||||
presupuesto.resumen-texto-acabado-cubierta= <li>Acabado {0}. </li>
|
||||
presupuesto.resumen-texto-acabado-cubierta= <li>Acabado: {0}. </li>
|
||||
presupuesto.resumen-texto-end=</ul>
|
||||
presupuesto.resumen-texto-sobrecubierta=<li>Sobrecubierta impresa en {0} {1} gr. <ul><li>Acabado {2}</li><li>Solapas: {3} mm.</li></ul></li>
|
||||
presupuesto.resumen-texto-faja=<li>Faja impresa en {0} {1} gr. con un alto de {2} mm. <ul><li>Acabado {3}</li><li>Solapas: {4} mm.</li></ul></li>
|
||||
presupuesto.resumen-texto-sobrecubierta=<li>Sobrecubierta impresa en {0} {1} gr. <ul><li>Acabado: {2}</li><li>Solapas: {3} mm.</li></ul></li>
|
||||
presupuesto.resumen-texto-faja=<li>Faja impresa en {0} {1} gr. con un alto de {2} mm. <ul><li>Acabado: {3}</li><li>Solapas: {4} mm.</li></ul></li>
|
||||
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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -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) => {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
<!-- _cartItem.html -->
|
||||
<div th:fragment="cartItem(item)" class="card product mb-3 shadow-sm">
|
||||
<div th:fragment="cartItem(item)" class="card product mb-3 shadow-sm" th:attr="data-iva-4=${item.iva4},
|
||||
data-iva-21=${item.iva21},
|
||||
data-base=${item.base}">
|
||||
<div class="card-body">
|
||||
<div class="row gy-3">
|
||||
|
||||
@ -25,20 +27,44 @@
|
||||
</h5>
|
||||
|
||||
<!-- Detalles opcionales (ej: cliente, fecha, etc.) -->
|
||||
<ul class="list-inline text-muted mb-2">
|
||||
<div th:each="linea : ${item.lineas}">
|
||||
<ul class="list-inline text-muted mb-1">
|
||||
<div th:each="linea : ${item.resumen.lineas}">
|
||||
<li class="list-inline-item me-3">
|
||||
<div th:utext="${linea['descripcion']}"></div>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<ul class="list-inline text-muted mb-1" th:if="${item.resumen.servicios != null}">
|
||||
<span th:utext="#{pdf.servicios-adicionales}">Servicios adicionales</span>
|
||||
<span class="spec-label" th:text="${item.resumen.servicios}"></span>
|
||||
</ul>
|
||||
|
||||
<ul class="list-inline text-muted mb-1" th:if="${item.resumen != null
|
||||
and #maps.containsKey(item.resumen, 'datosMaquetacion')
|
||||
and item.resumen['datosMaquetacion'] != null}">
|
||||
<li class="list-inline-item spec-row mb-1">
|
||||
<span th:text="#{pdf.datos-maquetacion}">Datos de maquetación:</span>
|
||||
<span th:utext="${item.resumen.datosMaquetacion}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="list-inline text-muted mb-1" th:if="${item.resumen != null
|
||||
and #maps.containsKey(item.resumen, 'datosMarcapaginas')
|
||||
and item.resumen['datosMarcapaginas'] != null}">
|
||||
<li class="list-inline-item spec-row mb-1">
|
||||
<span th:text="#{pdf.datos-marcapaginas}">Datos de marcapáginas:</span>
|
||||
<span th:utext="${item.resumen.datosMarcapaginas}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Precio o totales (si los tienes) -->
|
||||
<div class="col-sm-auto text-end">
|
||||
<p class="text-muted mb-1" th:text="#{cart.precio}">Precio</p>
|
||||
<h5 class="fs-14 mb-0">
|
||||
<span th:text="${item.total != null ? item.total : '-'}">0,00</span>
|
||||
<span th:text="${item.baseTotal != null ? item.baseTotal : '-'}">0,00</span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,14 +76,10 @@
|
||||
<div class="d-flex flex-wrap my-n1">
|
||||
<!-- Botón eliminar -->
|
||||
<div>
|
||||
<form th:action="@{|/cart/${item.cartItemId}/remove|}" method="post" class="d-inline">
|
||||
<input type="hidden" name="_method" value="delete" />
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
|
||||
<a href="#" class="d-block text-body p-1 px-2"
|
||||
onclick="this.closest('form').submit(); return false;">
|
||||
<i class="ri-delete-bin-fill text-muted align-bottom me-1"></i> Eliminar
|
||||
</a>
|
||||
</form>
|
||||
<a href="javascript:void(0);" class="d-block text-body p-1 px-2 delete-item"
|
||||
th:attr="data-cart-item-id=${item.cartItemId}">
|
||||
<i class="ri-delete-bin-fill text-muted align-bottom me-1"></i> Eliminar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -32,18 +32,67 @@
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="container-fluid row gy-4">
|
||||
<div th:if="${items.isEmpty()}">
|
||||
<div class="alert alert-info" role="alert" th:text="#{cart.empty}"></div>
|
||||
</div>
|
||||
<div th:unless="${#lists.isEmpty(items)}">
|
||||
|
||||
<div class="col-xl-8 col-12">
|
||||
<div th:each="item : ${items}" th:insert="~{imprimelibros/cart/_cartItem :: cartItem(${item})}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4">
|
||||
<div class="sticky-side-div">
|
||||
<div class="card">
|
||||
<div class="card-header border-bottom-dashed">
|
||||
<h5 th:text="#{cart.resumen.title}" class="card-title mb-0"></h5>
|
||||
</div>
|
||||
<div class="card-body pt-2">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-borderless mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span th:text="#{cart.resumen.base}"></span></td>
|
||||
<td class="text-end" id="base-cesta"></td>
|
||||
</tr>
|
||||
<tr id="tr-iva-4">
|
||||
<td><span th:text="#{cart.resumen.iva-4}"></span> : </td>
|
||||
<td class="text-end" id="iva-4-cesta"></td>
|
||||
</tr>
|
||||
<tr id="tr-iva-21">
|
||||
<td><span th:text="#{cart.resumen.iva-21}"></span> : </td>
|
||||
<td class="text-end" id="iva-21-cesta"></td>
|
||||
</tr>
|
||||
<tr class="table-active">
|
||||
<th><span th:text="#{cart.resumen.total}"></span>:</th>
|
||||
<td class="text-end">
|
||||
<span id="total-cesta" class="fw-semibold">
|
||||
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="button" class="btn btn-secondary w-100 mt-2"
|
||||
th:text="#{cart.resumen.tramitar}">Checkout</button>
|
||||
</div>
|
||||
<!-- end table-responsive -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert border-dashed alert-danger" role="alert">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="ms-2">
|
||||
<h5 class="fs-14 text-danger fw-semibold" th:text="#{cart.resumen.fidelizacion}"></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end stickey -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</th:block>
|
||||
|
||||
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||
@ -51,6 +100,8 @@
|
||||
<script th:inline="javascript">
|
||||
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||
</script>
|
||||
|
||||
<script type="module" th:src="@{/assets/js/pages/imprimelibros/cart/cart.js}"></script>
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
|
||||
@ -54,9 +54,12 @@
|
||||
<tr>
|
||||
<td class="text-start"><span th:text="#{'pdf.presupuesto.number'}" class="lbl">PRESUPUESTO Nº:</span> <span
|
||||
class="val" th:text="${numero}">153153</span></td>
|
||||
<td class="text-center"><span th:text="#{pdf.presupuesto.client}" class="lbl">CLIENTE:</span> <span class="val"
|
||||
<td class="text-center">
|
||||
<span th:if="${cliente?.nombre} != ''" th:text="#{pdf.presupuesto.client}" class="lbl">CLIENTE:</span> <span class="val"
|
||||
th:text="${cliente?.nombre} ?: '-'">JUAN JOSÉ
|
||||
MÉNDEZ</span></td>
|
||||
MÉNDEZ
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end"><span class="lbl" th:text="#{pdf.presupuesto.date}">FECHA:</span> <span class="val"
|
||||
th:text="${#temporals.format(fecha, 'dd/MM/yyyy')}">10/10/2025</span></td>
|
||||
</tr>
|
||||
@ -69,9 +72,6 @@
|
||||
</div>
|
||||
|
||||
<!-- DATOS TÉCNICOS -->
|
||||
<div class="line-title">
|
||||
<span class="lbl" th:text="#{pdf.presupuesto.descripcion}">Descripción:</span>
|
||||
</div>
|
||||
<div class="specs-wrapper align-with-text ">
|
||||
<div class="specs">
|
||||
<div th:if="${specs} == null">
|
||||
@ -85,11 +85,15 @@
|
||||
<span th:utext="#{pdf.servicios-adicionales}">Servicios adicionales</span>
|
||||
<span class="spec-label" th:text="${specs.servicios}"></span>
|
||||
</div>
|
||||
<div th:if="${specs.datosMaquetacion != null}" class="spec-row mb-1">
|
||||
<div th:if="${specs != null
|
||||
and #maps.containsKey(specs, 'datosMaquetacion')
|
||||
and specs['datosMaquetacion'] != null}" class="spec-row mb-1">
|
||||
<span th:text="#{pdf.datos-maquetacion}">Datos de maquetación:</span>
|
||||
<span th:utext="${specs.datosMaquetacion}"></span>
|
||||
</div>
|
||||
<div th:if="${specs.datosMarcapaginas != null}" class="spec-row mb-1">
|
||||
<div th:if="${specs != null
|
||||
and #maps.containsKey(specs, 'datosMarcapaginas')
|
||||
and specs['datosMarcapaginas'] != null}" class="spec-row mb-1">
|
||||
<span th:text="#{pdf.datos-marcapaginas}">Datos de marcapáginas:</span>
|
||||
<span th:utext="${specs.datosMarcapaginas}"></span>
|
||||
</div>
|
||||
@ -98,7 +102,7 @@
|
||||
</div>
|
||||
|
||||
<!-- TABLA TIRADAS -->
|
||||
<table class="prices" class="align-items-center">
|
||||
<table class="prices align-items-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center col-tirada" th:text="#{pdf.table.tirada}">TIRADA</th>
|
||||
@ -133,6 +137,8 @@
|
||||
|
||||
<!-- PIE -->
|
||||
<div class="footer">
|
||||
<div class="fw-bold fs-6 mb-1" th:text="#{pdf.incluye-envio}">El presupuesto incluye el envío a una dirección de la
|
||||
península.</div>
|
||||
<div class="privacy">
|
||||
<div class="pv-title" th:text="#{pdf.politica-privacidad}">Política de privacidad</div>
|
||||
<div class="pv-text" th:text="#{pdf.politica-privacidad.responsable}">Responsable: Impresión Imprime Libros -
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div th:fragment="buttons(appMode, btnClass, showPrev, showNext, showActions, showCart)" class="buttons-bar mt-2">
|
||||
<div th:fragment="buttons(appMode, btnClass, showPrev, showNext, showActions, showCart, showPrint)" class="buttons-bar mt-2">
|
||||
|
||||
<!-- Fila 1: ACCIONES, centradas -->
|
||||
<div class="buttons-row center" th:if="${showActions}">
|
||||
@ -8,7 +8,7 @@
|
||||
<span th:text="#{presupuesto.guardar}">Guardar</span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-secondary d-flex align-items-center mx-2 btn-imprimir">
|
||||
<button th:if="${showPrint}" type="button" class="btn btn-secondary d-flex align-items-center mx-2 btn-imprimir">
|
||||
<i class="ri-printer-line me-2"></i>
|
||||
<span th:text="#{app.imprimir}">Imprimir</span>
|
||||
</button>
|
||||
|
||||
@ -408,6 +408,11 @@
|
||||
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-4 w-100">
|
||||
<div th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-cubierta', true, true, true, false)}"></div>
|
||||
<div sec:authorize="isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-cubierta', true, true, true, false, true)}">
|
||||
</div>
|
||||
<div sec:authorize="!isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-cubierta', true, true, false, false, false)}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -299,7 +299,7 @@
|
||||
|
||||
|
||||
<div
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-datos-generales', false, true, false, false)}">
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-datos-generales', false, true, false, false, false)}">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
<!-- End Ribbon Shape -->
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-4 w-100">
|
||||
<div th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-extras', true, true, true, false)}"></div>
|
||||
<div sec:authorize="isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-extras', true, true, true, false, true)}"></div>
|
||||
<div sec:authorize="!isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-extras', true, true, false, false, false)}"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,8 +57,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-interior', true, true, true, false)}">
|
||||
<div sec:authorize="!isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-interior', true, true, false, false, false)}">
|
||||
</div>
|
||||
<div sec:authorize="isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-interior', true, true, true, false, true)}">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-4 w-100">
|
||||
<div
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-resumen', true, false, true, true)}">
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-resumen', true, false, true, true, true)}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -11,12 +11,10 @@
|
||||
</div>
|
||||
|
||||
<div class="ribbon-content mt-4">
|
||||
<div id="div-tiradas-error"
|
||||
class="d-flex justify-content-center mt-4 w-100 d-none">
|
||||
<div id="div-tiradas-error" class="d-flex justify-content-center mt-4 w-100 d-none">
|
||||
|
||||
</div>
|
||||
<div id="div-tiradas"
|
||||
class="row row-cols-auto justify-content-center g-4 mx-auto mb-4"
|
||||
<div id="div-tiradas" class="row row-cols-auto justify-content-center g-4 mx-auto mb-4"
|
||||
style="max-width:1120px" th:data-per-unit="#{presupuesto.precio-unidad}"
|
||||
th:data-total="#{presupuesto.total}" th:data-select="#{presupuesto.seleccionar-tirada}"
|
||||
th:data-selected="#{presupuesto.tirada-seleccionada}" th:data-units="#{presupuesto.unidades}">
|
||||
@ -27,6 +25,11 @@
|
||||
<!-- End Ribbon Shape -->
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-4 w-100">
|
||||
<div th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-seleccion-tirada', true, true, true, false)}"></div>
|
||||
<div sec:authorize="isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-seleccion-tirada', true, true, true, false, true)}">
|
||||
</div>
|
||||
<div sec:authorize="!isAuthenticated()"
|
||||
th:replace="~{imprimelibros/presupuestos/presupuestador-items/_buttons :: buttons(${appMode}, 'btn-change-tab-seleccion-tirada', true, true, false, false, false)}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user