falta mover la cesta a cliente

This commit is contained in:
2025-10-31 08:25:13 +01:00
parent 167c136dca
commit 90c191d8f8
6 changed files with 166 additions and 89 deletions

View File

@ -20,6 +20,7 @@ import com.imprimelibros.erp.cart.dto.UpdateCartRequest;
import com.imprimelibros.erp.common.Utils; import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.direcciones.DireccionService; import com.imprimelibros.erp.direcciones.DireccionService;
import com.imprimelibros.erp.externalApi.skApiClient; import com.imprimelibros.erp.externalApi.skApiClient;
import com.imprimelibros.erp.pedido.PedidoService;
import com.imprimelibros.erp.presupuesto.PresupuestoRepository; import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
@Service @Service
@ -32,11 +33,12 @@ public class CartService {
private final Utils utils; private final Utils utils;
private final DireccionService direccionService; private final DireccionService direccionService;
private final skApiClient skApiClient; private final skApiClient skApiClient;
private final PedidoService pedidoService;
public CartService(CartRepository cartRepo, CartItemRepository itemRepo, public CartService(CartRepository cartRepo, CartItemRepository itemRepo,
MessageSource messageSource, PresupuestoFormatter presupuestoFormatter, MessageSource messageSource, PresupuestoFormatter presupuestoFormatter,
PresupuestoRepository presupuestoRepo, Utils utils, DireccionService direccionService, PresupuestoRepository presupuestoRepo, Utils utils, DireccionService direccionService,
skApiClient skApiClient) { skApiClient skApiClient, PedidoService pedidoService) {
this.cartRepo = cartRepo; this.cartRepo = cartRepo;
this.itemRepo = itemRepo; this.itemRepo = itemRepo;
this.messageSource = messageSource; this.messageSource = messageSource;
@ -44,6 +46,7 @@ public class CartService {
this.utils = utils; this.utils = utils;
this.direccionService = direccionService; this.direccionService = direccionService;
this.skApiClient = skApiClient; this.skApiClient = skApiClient;
this.pedidoService = pedidoService;
} }
/** Devuelve el carrito activo o lo crea si no existe. */ /** Devuelve el carrito activo o lo crea si no existe. */
@ -180,6 +183,7 @@ public class CartService {
for (CartItem item : items) { for (CartItem item : items) {
Presupuesto p = item.getPresupuesto(); Presupuesto p = item.getPresupuesto();
Double peso = p.getPeso() != null ? p.getPeso().doubleValue() : 0.0;
base += p.getBaseImponible().doubleValue(); base += p.getBaseImponible().doubleValue();
iva4 += p.getIvaImporte4().doubleValue(); iva4 += p.getIvaImporte4().doubleValue();
iva21 += p.getIvaImporte21().doubleValue(); iva21 += p.getIvaImporte21().doubleValue();
@ -190,43 +194,79 @@ public class CartService {
Boolean freeShipment = direccionService.checkFreeShipment(cd.getDireccion().getCp(), Boolean freeShipment = direccionService.checkFreeShipment(cd.getDireccion().getCp(),
cd.getDireccion().getPaisCode3()) && !cd.getIsPalets(); cd.getDireccion().getPaisCode3()) && !cd.getIsPalets();
if (!freeShipment) { if (!freeShipment) {
try { Integer unidades = p.getSelectedTirada();
Map<String, Object> data = Map.of( Map<String, Object> res = getShippingCost(cd, peso, unidades, locale);
"cp", cd.getDireccion().getCp(), if (res.get("success").equals(Boolean.FALSE)) {
"pais_code3", cd.getDireccion().getPaisCode3(),
"peso", p.getPeso() != null ? p.getPeso() : 0,
"unidades", p.getSelectedTirada(),
"palets", cd.getIsPalets() ? 1 : 0);
var shipmentCost = skApiClient.getCosteEnvio(data, locale);
if (shipmentCost != null && shipmentCost.get("data") != null) {
shipment += (Double) shipmentCost.get("data");
iva21 += ((Double) shipmentCost.get("data")) * 0.21;
} else {
errorShipementCost = true;
}
} catch (Exception e) {
errorShipementCost = true; errorShipementCost = true;
} }
else{
shipment += (Double) res.get("shipment");
iva21 += (Double) res.get("iva21");
}
} }
// si tiene prueba de envio, hay que añadir el coste // si tiene prueba de envio, hay que añadir el coste
if (p.getServiciosJson() != null && p.getServiciosJson().contains("ejemplar-prueba")) { if (p.getServiciosJson() != null && p.getServiciosJson().contains("ejemplar-prueba")) {
try {
Map<String, Object> data = Map.of( Map<String, Object> res = getShippingCost(cd, peso, 1, locale);
"cp", cd.getDireccion().getCp(), if (res.get("success").equals(Boolean.FALSE)) {
"pais_code3", cd.getDireccion().getPaisCode3(),
"peso", p.getPeso() != null ? p.getPeso() : 0,
"unidades", 1,
"palets", cd.getIsPalets() ? 1 : 0);
var shipmentCost = skApiClient.getCosteEnvio(data, locale);
if (shipmentCost != null && shipmentCost.get("data") != null) {
shipment += (Double) shipmentCost.get("data");
iva21 += ((Double) shipmentCost.get("data")) * 0.21;
} else {
errorShipementCost = true;
}
} catch (Exception e) {
errorShipementCost = true; errorShipementCost = true;
} }
else{
shipment += (Double) res.get("shipment");
iva21 += (Double) res.get("iva21");
}
}
}
} else {
// envio por cada presupuesto
// buscar la direccion asignada a este presupuesto
if (direcciones == null)
continue;
List<CartDireccion> cd_presupuesto = direcciones.stream()
.filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(p.getId())
&& d.getUnidades() != null && d.getUnidades() != null && d.getUnidades() > 0)
.toList();
Boolean firstDirection = true;
for (CartDireccion cd : cd_presupuesto) {
Integer unidades = cd.getUnidades();
if (firstDirection) {
Boolean freeShipment = direccionService.checkFreeShipment(cd.getDireccion().getCp(),
cd.getDireccion().getPaisCode3()) && !cd.getIsPalets();
if (!freeShipment && unidades != null && unidades > 0) {
Map<String, Object> res = getShippingCost(cd, peso, unidades, locale);
if (res.get("success").equals(Boolean.FALSE)) {
errorShipementCost = true;
} else {
shipment += (Double) res.get("shipment");
iva21 += (Double) res.get("iva21");
}
}
firstDirection = false;
} else {
Map<String, Object> res = getShippingCost(cd, peso, unidades, locale);
if (res.get("success").equals(Boolean.FALSE)) {
errorShipementCost = true;
} else {
shipment += (Double) res.get("shipment");
iva21 += (Double) res.get("iva21");
}
}
}
// ejemplar de prueba
CartDireccion cd_prueba = direcciones.stream()
.filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(p.getId())
&& d.getUnidades() == null)
.findFirst().orElse(null);
if (cd_prueba != null) {
Map<String, Object> res = getShippingCost(cd_prueba, peso, 1, locale);
if (res.get("success").equals(Boolean.FALSE)) {
errorShipementCost = true;
}
else{
shipment += (Double) res.get("shipment");
iva21 += (Double) res.get("iva21");
} }
} }
} }
@ -234,11 +274,17 @@ public class CartService {
double total = base + iva4 + iva21 + shipment; double total = base + iva4 + iva21 + shipment;
int fidelizacion = pedidoService.getDescuentoFidelizacion();
double descuento = (total) * fidelizacion / 100.0;
total -= descuento;
Map<String, Object> summary = new HashMap<>(); Map<String, Object> summary = new HashMap<>();
summary.put("base", Utils.formatCurrency(base, locale)); summary.put("base", Utils.formatCurrency(base, locale));
summary.put("iva4", Utils.formatCurrency(iva4, locale)); summary.put("iva4", Utils.formatCurrency(iva4, locale));
summary.put("iva21", Utils.formatCurrency(iva21, locale)); summary.put("iva21", Utils.formatCurrency(iva21, locale));
summary.put("shipment", Utils.formatCurrency(shipment, locale)); summary.put("shipment", Utils.formatCurrency(shipment, locale));
summary.put("fidelizacion", fidelizacion + "%");
summary.put("descuento", Utils.formatCurrency(-descuento, locale));
summary.put("total", Utils.formatCurrency(total, locale)); summary.put("total", Utils.formatCurrency(total, locale));
summary.put("errorShipmentCost", errorShipementCost); summary.put("errorShipmentCost", errorShipementCost);
@ -308,4 +354,44 @@ public class CartService {
return false; return false;
} }
} }
private Map<String, Object> getShippingCost(
CartDireccion cd,
Double peso,
Integer unidades,
Locale locale) {
Map<String, Object> result = new HashMap<>();
try {
Map<String, Object> data = Map.of(
"cp", cd.getDireccion().getCp(),
"pais_code3", cd.getDireccion().getPaisCode3(),
"peso", peso != null ? peso : 0.0,
"unidades", unidades,
"palets", Boolean.TRUE.equals(cd.getIsPalets()) ? 1 : 0);
var shipmentCost = skApiClient.getCosteEnvio(data, locale);
if (shipmentCost != null && shipmentCost.get("data") != null) {
Number n = (Number) shipmentCost.get("data");
double cost = n.doubleValue();
result.put("success", true);
result.put("shipment", cost);
result.put("iva21", cost * 0.21);
} else {
result.put("success", false);
result.put("shipment", 0.0);
result.put("iva21", 0.0);
}
} catch (Exception e) {
result.put("success", false);
result.put("shipment", 0.0);
result.put("iva21", 0.0);
}
return result;
}
} }

View File

@ -61,7 +61,8 @@ public class DireccionService {
String alias = opt.get("alias").toLowerCase(); String alias = opt.get("alias").toLowerCase();
String text = opt.get("text").toLowerCase(); String text = opt.get("text").toLowerCase();
String direccion = opt.get("direccion").toLowerCase(); String direccion = opt.get("direccion").toLowerCase();
return text.contains(q) || cp.contains(q) || ciudad.contains(q) || att.contains(q) || alias.contains(q) || direccion.contains(q); return text.contains(q) || cp.contains(q) || ciudad.contains(q) || att.contains(q)
|| alias.contains(q) || direccion.contains(q);
}) })
.sorted(Comparator.comparing(m -> m.get("text"), Collator.getInstance())) .sorted(Comparator.comparing(m -> m.get("text"), Collator.getInstance()))
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -81,15 +82,16 @@ public class DireccionService {
} }
public Boolean checkFreeShipment(Integer cp, String paisCode3) { public Boolean checkFreeShipment(Integer cp, String paisCode3) {
if (paisCode3 != null && paisCode3.equals("ESP") && cp != null) { if (paisCode3 != null && paisCode3.toLowerCase().equals("esp") && cp != null) {
// Excluir Canarias (35xxx y 38xxx), Baleares (07xxx), Ceuta (51xxx), Melilla (52xxx) // Excluir Canarias (35xxx y 38xxx), Baleares (07xxx), Ceuta (51xxx), Melilla
int provincia = cp / 1000; // (52xxx)
int provincia = cp / 1000;
if (provincia != 7 && provincia != 35 && provincia != 38 && provincia != 51 && provincia != 52) { if (provincia != 7 && provincia != 35 && provincia != 38 && provincia != 51 && provincia != 52) {
return true; // España peninsular return true; // España peninsular
}
} }
return false;
} }
return false;
}
} }

View File

@ -5,7 +5,7 @@ import org.springframework.stereotype.Service;
@Service @Service
public class PedidoService { public class PedidoService {
public int hasDescuentoFidelidad() { public int getDescuentoFidelizacion() {
// descuento entre el 1% y el 6% para clientes fidelidad (mas de 1500€ en el ultimo año) // descuento entre el 1% y el 6% para clientes fidelidad (mas de 1500€ en el ultimo año)
double totalGastado = 1600.0; // Ejemplo, deberías obtenerlo del historial del cliente double totalGastado = 1600.0; // Ejemplo, deberías obtenerlo del historial del cliente
if(totalGastado < 1200) { if(totalGastado < 1200) {
@ -18,10 +18,8 @@ public class PedidoService {
return 3; return 3;
} else if(totalGastado >= 4000 && totalGastado < 4999) { } else if(totalGastado >= 4000 && totalGastado < 4999) {
return 4; return 4;
} else if(totalGastado >= 5000 && totalGastado < 9999) { } else if(totalGastado >= 5000) {
return 5; return 5;
} else if(totalGastado >= 10000) {
return 6;
} }
return 0; return 0;
} }

View File

@ -30,14 +30,20 @@ cart.shipping.errors.noAddressSelected=Debe seleccionar una dirección de envío
cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta. cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta.
cart.resumen.title=Resumen de la cesta cart.resumen.title=Resumen de la cesta
cart.resumen.base=Base imponible: cart.resumen.base=Base imponible
cart.resumen.envio=Coste de envío: cart.resumen.envio=Coste de envío
cart.resumen.iva-4=IVA 4%: cart.resumen.iva-4=IVA 4%
cart.resumen.iva-21=IVA 21%: cart.resumen.iva-21=IVA 21%
cart.resumen.total=Total cesta: cart.resumen.descuento=Descuento fidelización
cart.resumen.total=Total cesta
cart.resumen.tramitar=Tramitar pedido cart.resumen.tramitar=Tramitar pedido
cart.resumen.fidelizacion=Si tiene descuento por fidelización, se aplicará al tramitar el pedido. cart.pass-to.customer=Mover cesta a cliente
cart.pass-to.customer.info=Puede mover la cesta actual al cliente seleccionado. Esto eliminará la cesta del usuario actual y la asociará al cliente seleccionado.
cart.pass-to.customer.warning=Advertencia: Esta acción no se puede deshacer y sobrescribirá la cesta del cliente seleccionado. Asegúrese de que el cliente seleccionado es correcto.
cart.pass-to.select-customer=Seleccione un cliente
cart.pass-to.button=Mover cesta
cart.pass-to.success=Cesta movida correctamente al cliente {0}.
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

@ -31,11 +31,11 @@ $(() => {
}).always(() => { }).always(() => {
hideLoader(); hideLoader();
}); });
updateTotal();
checkAddressesForItems(); checkAddressesForItems();
}); });
updateTotal(); checkAddressesForItems();
function checkAddressesForItems(){ function checkAddressesForItems(){
if($('#onlyOneShipment').is(':checked')){ if($('#onlyOneShipment').is(':checked')){
@ -89,37 +89,6 @@ $(() => {
} }
} }
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) { $(document).on("click", ".delete-item", async function (event) {
event.preventDefault(); event.preventDefault();

View File

@ -24,6 +24,10 @@
<td><span th:text="#{cart.resumen.iva-21}"></span> : </td> <td><span th:text="#{cart.resumen.iva-21}"></span> : </td>
<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">
<td><span th:text="#{cart.resumen.descuento} + ' (' + ${summary.fidelizacion} + ')'"></span> : </td>
<td class="text-end" id="descuento-cesta" th:text="${summary.descuento}"></td>
</tr>
<tr class="table-active"> <tr class="table-active">
<th><span th:text="#{cart.resumen.total}"></span>:</th> <th><span th:text="#{cart.resumen.total}"></span>:</th>
<td class="text-end"> <td class="text-end">
@ -35,7 +39,7 @@
<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 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>
</div> </div>
@ -43,13 +47,25 @@
</div> </div>
</div> </div>
<div class="alert border-dashed alert-danger" role="alert">
<div class="d-flex align-items-center"> <div class="card">
<div class="ms-2"> <div class="card-header border-bottom-dashed">
<h5 class="fs-14 text-danger fw-semibold" th:text="#{cart.resumen.fidelizacion}"></h5> <h5 th:text="#{cart.pass-to.customer}" class="card-title mb-0"></h5>
</div> </div>
<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-warning" role="alert" th:text="#{cart.pass-to.customer.warning}"></div>
<form th:action="@{/cart/pass-to-customer}" method="post">
<div class="mb-3">
<label for="select-customer" class="form-label" th:text="#{cart.pass-to.select-customer}"></label>
<select id="select-customer" name="customerId" class="form-select" required>
</select>
</div>
<button type="submit" class="btn btn-secondary w-100"
th:text="#{cart.pass-to.button}">Mover cesta</button>
</div> </div>
</div> </div>
</div> </div>
<!-- end stickey --> <!-- end stickey -->