terminado carrito

This commit is contained in:
2025-10-31 11:36:03 +01:00
parent 90c191d8f8
commit 40dc719e89
13 changed files with 234 additions and 59 deletions

View File

@ -59,6 +59,8 @@ public class CartController {
"cart.shipping.send-in-palets", "cart.shipping.send-in-palets",
"cart.shipping.send-in-palets.info", "cart.shipping.send-in-palets.info",
"cart.shipping.tipo-envio", "cart.shipping.tipo-envio",
"cart.pass-to.customer.error",
"cart.pass-to.customer.error-move",
"app.yes", "app.yes",
"app.aceptar", "app.aceptar",
"app.cancelar"); "app.cancelar");
@ -73,14 +75,14 @@ public class CartController {
model.addAttribute("items", items); model.addAttribute("items", items);
Map<String, Object> direcciones = service.getCartDirecciones(cart.getId(), locale); Map<String, Object> direcciones = service.getCartDirecciones(cart.getId(), locale);
if(direcciones != null && direcciones.containsKey("mainDir")) if (direcciones != null && direcciones.containsKey("mainDir"))
model.addAttribute("mainDir", direcciones.get("mainDir")); model.addAttribute("mainDir", direcciones.get("mainDir"));
else if(direcciones != null && direcciones.containsKey("direcciones")) else if (direcciones != null && direcciones.containsKey("direcciones"))
model.addAttribute("direcciones", direcciones.get("direcciones")); model.addAttribute("direcciones", direcciones.get("direcciones"));
var summary = service.getCartSummary(cart, locale); var summary = service.getCartSummary(cart, locale);
model.addAttribute("cartSummary", summary); model.addAttribute("cartSummary", summary);
if(summary.get("errorShipmentCost") != null && (Boolean)summary.get("errorShipmentCost")) if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost"))
model.addAttribute("errorEnvio", true); model.addAttribute("errorEnvio", true);
else else
model.addAttribute("errorEnvio", false); model.addAttribute("errorEnvio", false);
@ -158,7 +160,8 @@ public class CartController {
} }
@PostMapping(value = "/update/{id}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @PostMapping(value = "/update/{id}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateCart(@PathVariable Long id, UpdateCartRequest updateRequest, Model model, Locale locale, Principal principal) { public String updateCart(@PathVariable Long id, UpdateCartRequest updateRequest, Model model, Locale locale,
Principal principal) {
try { try {
service.updateCart(id, updateRequest); service.updateCart(id, updateRequest);
@ -174,7 +177,26 @@ public class CartController {
model.addAttribute("errorMessage", errorMessage); model.addAttribute("errorMessage", errorMessage);
return "redirect:/cart"; return "redirect:/cart";
} }
} }
@PostMapping(value = "/pass-to-customer/{customerId}")
public ResponseEntity<?> moveToCustomer(
@PathVariable Long customerId,
Principal principal) {
if(!Utils.isCurrentUserAdmin()) {
return ResponseEntity.status(403).body(Map.of("error", "Forbidden"));
}
Long userId = Utils.currentUserId(principal);
Cart cart = service.getOrCreateActiveCart(userId);
boolean ok = service.moveCartToCustomer(cart.getId(), customerId);
if (ok)
return ResponseEntity.ok().build();
return ResponseEntity.status(400).body(Map.of("error", "cart.errors.move-cart"));
}
} }

View File

@ -112,8 +112,9 @@ public class CartService {
@Transactional @Transactional
public void removeByPresupuesto(Long userId, Long presupuestoId) { public void removeByPresupuesto(Long userId, Long presupuestoId) {
Cart cart = getOrCreateActiveCart(userId); Cart cart = getOrCreateActiveCart(userId);
itemRepo.findByCartIdAndPresupuestoId(cart.getId(), presupuestoId) CartItem item = itemRepo.findByCartIdAndPresupuestoId(cart.getId(), presupuestoId)
.ifPresent(itemRepo::delete); .orElseThrow(() -> new IllegalArgumentException("Item no encontrado"));
itemRepo.deleteById(item.getId());
} }
/** Vacía todo el carrito activo. */ /** Vacía todo el carrito activo. */
@ -355,6 +356,33 @@ public class CartService {
} }
} }
public Boolean moveCartToCustomer(Long cartId, Long customerId) {
try {
// Remove the cart from the customer if they have one
Cart existingCart = cartRepo.findByUserIdAndStatus(customerId, Cart.Status.ACTIVE)
.orElse(null);
if (existingCart != null) {
cartRepo.delete(existingCart);
}
Cart cart = cartRepo.findById(cartId)
.orElseThrow(() -> new IllegalArgumentException("Carrito no encontrado"));
cart.setUserId(customerId);
cartRepo.save(cart);
return true;
} catch (Exception e) {
// Manejo de excepciones
return false;
}
}
/***************************************
* MÉTODOS PRIVADOS
***************************************/
private Map<String, Object> getShippingCost( private Map<String, Object> getShippingCost(
CartDireccion cd, CartDireccion cd,
Double peso, Double peso,

View File

@ -14,6 +14,7 @@ import java.util.function.BiFunction;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
@ -44,6 +45,12 @@ public class Utils {
this.messageSource = messageSource; this.messageSource = messageSource;
} }
public static boolean isCurrentUserAdmin() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN") || a.getAuthority().equals("ROLE_SUPERADMIN"));
}
public static Long currentUserId(Principal principal) { public static Long currentUserId(Principal principal) {
if (principal == null) { if (principal == null) {

View File

@ -4,6 +4,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import java.security.Principal;
import java.time.Instant; import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@ -621,14 +622,14 @@ public class PresupuestoController {
@ResponseBody @ResponseBody
public DataTablesResponse<Map<String, Object>> datatable( public DataTablesResponse<Map<String, Object>> datatable(
HttpServletRequest request, Authentication auth, Locale locale, HttpServletRequest request, Authentication auth, Locale locale,
@PathVariable("tipo") String tipo) { @PathVariable("tipo") String tipo, Principal principal) {
DataTablesRequest dt = DataTablesParser.from(request); DataTablesRequest dt = DataTablesParser.from(request);
if ("anonimos".equals(tipo)) { if ("anonimos".equals(tipo)) {
return dtService.datatablePublicos(dt, locale); return dtService.datatablePublicos(dt, locale, principal);
} else if ("clientes".equals(tipo)) { } else if ("clientes".equals(tipo)) {
return dtService.datatablePrivados(dt, locale); return dtService.datatablePrivados(dt, locale, principal);
} else { } else {
throw new IllegalArgumentException("Tipo de datatable no válido"); throw new IllegalArgumentException("Tipo de datatable no válido");
} }

View File

@ -1,15 +1,18 @@
package com.imprimelibros.erp.presupuesto; package com.imprimelibros.erp.presupuesto;
import com.imprimelibros.erp.common.Utils; import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
import com.imprimelibros.erp.datatables.*; import com.imprimelibros.erp.datatables.*;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.security.Principal;
import java.time.*; import java.time.*;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
@ -26,18 +29,29 @@ public class PresupuestoDatatableService {
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
public DataTablesResponse<Map<String, Object>> datatablePublicos(DataTablesRequest dt, Locale locale) { public DataTablesResponse<Map<String, Object>> datatablePublicos(DataTablesRequest dt, Locale locale,
return commonDataTable(dt, locale, "publico", true); Principal principal) {
return commonDataTable(dt, locale, "publico", true, principal);
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
public DataTablesResponse<Map<String, Object>> datatablePrivados(DataTablesRequest dt, Locale locale) { public DataTablesResponse<Map<String, Object>> datatablePrivados(DataTablesRequest dt, Locale locale,
return commonDataTable(dt, locale, "privado", false); Principal principal) {
return commonDataTable(dt, locale, "privado", false, principal);
} }
private DataTablesResponse<Map<String, Object>> commonDataTable(DataTablesRequest dt, Locale locale, String origen, private DataTablesResponse<Map<String, Object>> commonDataTable(DataTablesRequest dt, Locale locale, String origen,
boolean publico) { boolean publico, Principal principal) {
Long count = repo.findAllByOrigen(Presupuesto.Origen.valueOf(origen)).stream().count();
Specification<Presupuesto> base = Specification.allOf(
(root, query, cb) -> cb.equal(root.get("origen"), Presupuesto.Origen.valueOf(origen)));
Boolean isAdmin = Utils.isCurrentUserAdmin();
if (!isAdmin) {
base = base.and((root, query, cb) -> cb.equal(root.get("user").get("id"), Utils.currentUserId(principal)));
}
Long count = repo.count(base);
List<String> orderable = List.of( List<String> orderable = List.of(
"id", "titulo", "user.fullName", "tipoEncuadernacion", "tipoCubierta", "tipoImpresion", "id", "titulo", "user.fullName", "tipoEncuadernacion", "tipoCubierta", "tipoImpresion",
@ -74,6 +88,7 @@ public class PresupuestoDatatableService {
.add("updatedAt", p -> formatDate(p.getUpdatedAt(), locale)) .add("updatedAt", p -> formatDate(p.getUpdatedAt(), locale))
.addIf(!publico, "user", p -> p.getUser() != null ? p.getUser().getFullName() : "") .addIf(!publico, "user", p -> p.getUser() != null ? p.getUser().getFullName() : "")
.add("actions", this::generarBotones) .add("actions", this::generarBotones)
.where(base)
.toJson(count); .toJson(count);
} }

View File

@ -1,6 +1,9 @@
package com.imprimelibros.erp.users; package com.imprimelibros.erp.users;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import java.util.Map;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;

View File

@ -2,10 +2,21 @@ package com.imprimelibros.erp.users;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.text.Collator;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.imprimelibros.erp.direcciones.Direccion;
@Service @Service
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
@ -29,5 +40,4 @@ public class UserServiceImpl implements UserService {
if (query == null || query.isBlank()) query = null; if (query == null || query.isBlank()) query = null;
return userDao.searchUsers(role, query, pageable); return userDao.searchUsers(role, query, pageable);
} }
} }

View File

@ -44,6 +44,7 @@ cart.pass-to.customer.warning=Advertencia: Esta acción no se puede deshacer y s
cart.pass-to.select-customer=Seleccione un cliente cart.pass-to.select-customer=Seleccione un cliente
cart.pass-to.button=Mover cesta cart.pass-to.button=Mover cesta
cart.pass-to.success=Cesta movida correctamente al cliente {0}. cart.pass-to.success=Cesta movida correctamente al cliente {0}.
cart.pass-to.customer.error=Debe seleccionar un cliente para mover la cesta.
cart.pass-to.customer.error-move=Error al mover la cesta de la compra
cart.errors.update-cart=Error al actualizar la cesta de la compra: {0} cart.errors.update-cart=Error al actualizar la cesta de la compra: {0}
cart.errors.shipping=No se puede calcular el coste del envío para alguna de las direcciones seleccionadas. Por favor, póngase en contacto con el servicio de atención al cliente. cart.errors.shipping=No se puede calcular el coste del envío para alguna de las direcciones seleccionadas. Por favor, póngase en contacto con el servicio de atención al cliente.

View File

@ -16,10 +16,10 @@ $(() => {
$(this).find('.direccion-id').attr('name', 'direcciones[' + i + '].id'); $(this).find('.direccion-id').attr('name', 'direcciones[' + i + '].id');
$(this).find('.direccion-cp').attr('name', 'direcciones[' + i + '].cp'); $(this).find('.direccion-cp').attr('name', 'direcciones[' + i + '].cp');
$(this).find('.direccion-pais-code3').attr('name', 'direcciones[' + i + '].paisCode3'); $(this).find('.direccion-pais-code3').attr('name', 'direcciones[' + i + '].paisCode3');
if($(this).find('.presupuesto-id').length > 0 && $(this).find('.presupuesto-id').val() !== null if ($(this).find('.presupuesto-id').length > 0 && $(this).find('.presupuesto-id').val() !== null
&& $(this).find('.presupuesto-id').val() !== "") && $(this).find('.presupuesto-id').val() !== "")
$(this).find('.presupuesto-id').attr('name', 'direcciones[' + i + '].presupuestoId'); $(this).find('.presupuesto-id').attr('name', 'direcciones[' + i + '].presupuestoId');
if($(this).find('.item-tirada').length > 0 && $(this).find('.item-tirada').val() !== null if ($(this).find('.item-tirada').length > 0 && $(this).find('.item-tirada').val() !== null
&& $(this).find('.item-tirada').val() !== "") && $(this).find('.item-tirada').val() !== "")
$(this).find('.item-tirada').attr('name', 'direcciones[' + i + '].unidades'); $(this).find('.item-tirada').attr('name', 'direcciones[' + i + '].unidades');
}); });
@ -31,15 +31,28 @@ $(() => {
}).always(() => { }).always(() => {
hideLoader(); hideLoader();
}); });
checkAddressesForItems(); checkAddressesForItems();
}); });
checkAddressesForItems(); checkAddressesForItems();
function checkAddressesForItems(){ function checkAddressesForItems() {
if($('#onlyOneShipment').is(':checked')){ if ($('.product').length === 0) {
if($("#shippingAddressesContainer .direccion-card").length === 0){ $("#alert-empty").removeClass("d-none");
$('.cart-content').addClass('d-none');
return;
}
else {
$('.cart-content').removeClass('d-none');
$("#alert-empty").addClass("d-none");
// check if select2 is initialized
if ($('#select-customer').length && !$('#select-customer').hasClass('select2-hidden-accessible')) {
initMoveCartToCustomer();
}
}
if ($('#onlyOneShipment').is(':checked')) {
if ($("#shippingAddressesContainer .direccion-card").length === 0) {
$(".alert-shipment").removeClass("d-none"); $(".alert-shipment").removeClass("d-none");
$('#btn-checkout').prop('disabled', true); $('#btn-checkout').prop('disabled', true);
return; return;
@ -47,42 +60,42 @@ $(() => {
$(".alert-shipment").addClass("d-none"); $(".alert-shipment").addClass("d-none");
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
} }
else{ else {
const items = $(".product"); const items = $(".product");
let errorFound = false; let errorFound = false;
for(let i=0; i<items.length; i++){ for (let i = 0; i < items.length; i++) {
let errorFoundItem = false; let errorFoundItem = false;
const item = $(items[i]); const item = $(items[i]);
const tirada = parseInt(item.find(".item-tirada").val()) || 0; const tirada = parseInt(item.find(".item-tirada").val()) || 0;
const direcciones = item.find(".direccion-card"); const direcciones = item.find(".direccion-card");
let totalUnidades = 0; let totalUnidades = 0;
direcciones.each(function(){ direcciones.each(function () {
const unidades = parseInt($(this).find(".item-tirada").val()) || 0; const unidades = parseInt($(this).find(".item-tirada").val()) || 0;
totalUnidades += unidades; totalUnidades += unidades;
}); });
if(totalUnidades < tirada){ if (totalUnidades < tirada) {
errorFoundItem = true; errorFoundItem = true;
} }
if(item.find(".shipping-addresses-sample")){ if (item.find(".shipping-addresses-sample")) {
const container = item.find(".shipping-addresses-sample"); const container = item.find(".shipping-addresses-sample");
if(container.find('.direccion-card').toArray().length === 0){ if (container.find('.direccion-card').toArray().length === 0) {
errorFoundItem = true; errorFoundItem = true;
} }
} }
if(errorFoundItem){ if (errorFoundItem) {
errorFound = true; errorFound = true;
item.find(".alert-icon-shipment").removeClass("d-none"); item.find(".alert-icon-shipment").removeClass("d-none");
} }
else{ else {
item.find(".alert-icon-shipment").addClass("d-none"); item.find(".alert-icon-shipment").addClass("d-none");
} }
} }
if(errorFound){ if (errorFound) {
$(".alert-shipment").removeClass("d-none"); $(".alert-shipment").removeClass("d-none");
$('#btn-checkout').prop('disabled', true); $('#btn-checkout').prop('disabled', true);
} }
else{ else {
$(".alert-shipment").addClass("d-none"); $(".alert-shipment").addClass("d-none");
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
} }
@ -92,7 +105,7 @@ $(() => {
$(document).on("click", ".delete-item", async function (event) { $(document).on("click", ".delete-item", async function (event) {
event.preventDefault(); event.preventDefault();
const cartItemId = $(this).data("cart-item-id"); const presupuestoId = $(this).data("cart-item-id");
const card = $(this).closest('.card.product'); const card = $(this).closest('.card.product');
// CSRF (Spring Security) // CSRF (Spring Security)
@ -100,7 +113,7 @@ $(() => {
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.content || 'X-CSRF-TOKEN'; const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.content || 'X-CSRF-TOKEN';
try { try {
const res = await fetch(`/cart/delete/item/${cartItemId}`, { const res = await fetch(`/cart/delete/item/${presupuestoId}`, {
method: 'DELETE', method: 'DELETE',
headers: { [csrfHeader]: csrfToken } headers: { [csrfHeader]: csrfToken }
}); });
@ -111,11 +124,79 @@ $(() => {
} }
else { else {
card?.remove(); card?.remove();
updateTotal(); $(document).trigger('updateCart');
} }
} catch (err) { } catch (err) {
console.error('Error en la solicitud:', err); console.error('Error en la solicitud:', err);
} }
}); });
function initMoveCartToCustomer() {
if ($('#select-customer').length) {
$('#moveCart').on('click', async function (e) {
e.preventDefault();
const customerId = $('#select-customer').val();
if (!customerId) {
// set text and show alert
$('#alert-select-customer').text(window.languageBundle['cart.pass-to.customer.error'] || 'Debe seleccionar un cliente para mover la cesta.');
$('#alert-select-customer').removeClass('d-none').hide().fadeIn();
setTimeout(() => {
$('#alert-select-customer').fadeOut(function () {
$(this).addClass('d-none');
});
}, 5000);
return;
}
// 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/pass-to-customer/${customerId}`, {
method: 'POST',
headers: { [csrfHeader]: csrfToken }
});
if (!res.ok) {
$('#alert-select-customer').text(window.languageBundle['cart.pass-to.customer.move.error'] || 'Error al mover la cesta de la compra');
$('#alert-select-customer').removeClass('d-none').hide().fadeIn();
setTimeout(() => {
$('#alert-select-customer').fadeOut(function () {
$(this).addClass('d-none');
});
}, 5000);
return;
}
else {
window.location.href = '/cart';
}
} catch (err) {
console.error('Error en la solicitud:', err);
$('#alert-select-customer').text(window.languageBundle['cart.errors.move-cart'] || 'Error al mover la cesta de la compra');
$('#alert-select-customer').removeClass('d-none').hide().fadeIn();
setTimeout(() => {
$('#alert-select-customer').fadeOut(function () {
$(this).addClass('d-none');
});
}, 5000);
}
});
$('#select-customer').select2({
width: '100%',
ajax: {
url: 'users/api/get-users',
dataType: 'json',
delay: 250,
},
allowClear: true
});
}
}
initMoveCartToCustomer();
}); });

View File

@ -1,4 +1,4 @@
<div th:fragment="cartContent(items, cartId)" class="cart-content container-fluid row gy-4"> <div th:fragment="cartContent(items, cartId)" th:class="${'cart-content container-fluid row gy-4' + (items.isEmpty() ? ' d-none' : '')}">
<div id="sectionLoader" class="position-absolute top-0 start-0 w-100 h-100 d-none justify-content-center align-items-center <div id="sectionLoader" class="position-absolute top-0 start-0 w-100 h-100 d-none justify-content-center align-items-center
bg-body bg-opacity-75" style="z-index:10;"> bg-body bg-opacity-75" style="z-index:10;">
@ -7,11 +7,10 @@
</div> </div>
</div> </div>
<div th:if="${items.isEmpty()}"> <div id="errorEnvio" th:class="${'alert alert-danger' + (errorEnvio ? '' : ' d-none')}" role="alert"
<div class="alert alert-info" role="alert" th:text="#{cart.empty}"></div> th:text="#{cart.errors.shipping}"></div>
</div> <div th:if="${!#strings.isEmpty(errorMessage) and items != null and !items.isEmpty()}" class="alert alert-danger "
<div id="errorEnvio" th:class="${'alert alert-danger' + (errorEnvio ? '' : ' d-none')}" role="alert" th:text="#{cart.errors.shipping}"></div> role="alert" th:text="${errorMessage}"></div>
<div th:if="${errorMessage}" class="alert alert-danger " role="alert" th:text="${errorMessage}"></div>
<div class="alert alert-danger alert-shipment d-none" role="alert" <div class="alert alert-danger alert-shipment d-none" role="alert"
th:text="#{cart.shipping.errors.fillAddressesItems}"></div> th:text="#{cart.shipping.errors.fillAddressesItems}"></div>

View File

@ -198,7 +198,7 @@
<!-- Botón eliminar --> <!-- Botón eliminar -->
<div> <div>
<a href="javascript:void(0);" class="d-block text-body p-1 px-2 delete-item" <a href="javascript:void(0);" class="d-block text-body p-1 px-2 delete-item"
th:attr="data-cart-item-id=${item.cartItemId}"> th:attr="data-cart-item-id=${item.presupuestoId}">
<i class="ri-delete-bin-fill text-muted align-bottom me-1"></i> Eliminar <i class="ri-delete-bin-fill text-muted align-bottom me-1"></i> Eliminar
</a> </a>
</div> </div>

View File

@ -25,7 +25,9 @@
<td class="text-end" id="iva-21-cesta" th:text="${summary.iva21}"></td> <td class="text-end" id="iva-21-cesta" th:text="${summary.iva21}"></td>
</tr> </tr>
<tr id="tr-iva-21"> <tr id="tr-iva-21">
<td><span th:text="#{cart.resumen.descuento} + ' (' + ${summary.fidelizacion} + ')'"></span> : </td> <td><span
th:text="#{cart.resumen.descuento} + ' (' + ${summary.fidelizacion} + ')'"></span>
: </td>
<td class="text-end" id="descuento-cesta" th:text="${summary.descuento}"></td> <td class="text-end" id="descuento-cesta" th:text="${summary.descuento}"></td>
</tr> </tr>
<tr class="table-active"> <tr class="table-active">
@ -37,8 +39,8 @@
</tbody> </tbody>
</table> </table>
<form th:action="@{/pagos/redsys/crear}" method="post"> <form th:action="@{/pagos/redsys/crear}" method="post">
<input type="hidden" name="order" value="123456789012"/> <input type="hidden" name="order" value="123456789012" />
<input type="hidden" name="amountCents" value="12525"/> <input type="hidden" name="amountCents" value="12525" />
<button id="btn-checkout" type="submit" class="btn btn-secondary w-100 mt-2" <button id="btn-checkout" type="submit" class="btn btn-secondary w-100 mt-2"
th:text="#{cart.resumen.tramitar}">Checkout</button> th:text="#{cart.resumen.tramitar}">Checkout</button>
</form> </form>
@ -47,25 +49,27 @@
</div> </div>
</div> </div>
<div sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')" class="card">
<div class="card">
<div class="card-header border-bottom-dashed"> <div class="card-header border-bottom-dashed">
<h5 th:text="#{cart.pass-to.customer}" class="card-title mb-0"></h5> <h5 th:text="#{cart.pass-to.customer}" class="card-title mb-0"></h5>
</div> </div>
<div class="card-body pt-2"> <div class="card-body pt-2">
<div class="alert alert-info" role="alert" th:text="#{cart.pass-to.customer.info}"></div> <div class="alert alert-info" role="alert" th:text="#{cart.pass-to.customer.info}"></div>
<div class="alert alert-warning" role="alert" th:text="#{cart.pass-to.customer.warning}"></div> <div class="alert alert-warning" role="alert" th:text="#{cart.pass-to.customer.warning}"></div>
<form th:action="@{/cart/pass-to-customer}" method="post"> <div id="alert-select-customer" class="alert alert-danger d-none" role="alert"
<div class="mb-3"> th:text="#{cart.pass-to.customer.error}"></div>
<label for="select-customer" class="form-label" th:text="#{cart.pass-to.select-customer}"></label> <div class="mb-3">
<select id="select-customer" name="customerId" class="form-select" required> <label for="select-customer" class="form-label" th:text="#{cart.pass-to.select-customer}"></label>
</select> <select id="select-customer" name="customerId" class="form-select" required>
</div> </select>
<button type="submit" class="btn btn-secondary w-100" </div>
th:text="#{cart.pass-to.button}">Mover cesta</button> <div class="mb-3">
<button id="moveCart" class="btn btn-secondary w-100" th:text="#{cart.pass-to.button}">Mover
cesta</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- end stickey --> <!-- end stickey -->

View File

@ -36,6 +36,10 @@
</nav> </nav>
</div> </div>
<div th:if="${items.isEmpty()}">
<div id="alert-empty"class="alert alert-info" role="alert" th:text="#{cart.empty}"></div>
</div>
<div th:insert="~{imprimelibros/cart/_cartContent :: cartContent(${items}, ${cartId})}"></div> <div th:insert="~{imprimelibros/cart/_cartContent :: cartContent(${items}, ${cartId})}"></div>
</th:block> </th:block>