diff --git a/src/main/java/com/imprimelibros/erp/cart/CartService.java b/src/main/java/com/imprimelibros/erp/cart/CartService.java index 0662fdd..c636ee9 100644 --- a/src/main/java/com/imprimelibros/erp/cart/CartService.java +++ b/src/main/java/com/imprimelibros/erp/cart/CartService.java @@ -11,6 +11,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; + import java.math.BigDecimal; +import java.math.RoundingMode; import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter; import com.imprimelibros.erp.presupuesto.dto.Presupuesto; @@ -20,10 +22,10 @@ import com.imprimelibros.erp.cart.dto.DireccionCardDTO; import com.imprimelibros.erp.cart.dto.DireccionShipment; import com.imprimelibros.erp.cart.dto.UpdateCartRequest; import com.imprimelibros.erp.common.Utils; -import com.imprimelibros.erp.direcciones.Direccion; import com.imprimelibros.erp.direcciones.DireccionService; import com.imprimelibros.erp.externalApi.skApiClient; -import com.imprimelibros.erp.pedido.PedidoService; +import com.imprimelibros.erp.pedidos.Pedido; +import com.imprimelibros.erp.pedidos.PedidoService; import com.imprimelibros.erp.presupuesto.PresupuestoRepository; @Service @@ -191,14 +193,13 @@ public class CartService { return resumen; } - public Map getCartSummary(Cart cart, Locale locale) { + public Map getCartSummaryRaw(Cart cart, Locale locale) { double base = 0.0; double iva4 = 0.0; double iva21 = 0.0; double shipment = 0.0; - - Boolean errorShipementCost = false; + boolean errorShipementCost = false; List items = cart.getItems(); List direcciones = cart.getDirecciones(); @@ -209,28 +210,29 @@ public class CartService { base += p.getBaseImponible().doubleValue(); iva4 += p.getIvaImporte4().doubleValue(); iva21 += p.getIvaImporte21().doubleValue(); + if (cart.getOnlyOneShipment() != null && cart.getOnlyOneShipment()) { - // Si es envío único, que es a españa y no ha canarias - if (direcciones != null && direcciones.size() > 0) { + if (direcciones != null && !direcciones.isEmpty()) { CartDireccion cd = direcciones.get(0); - Boolean freeShipment = direccionService.checkFreeShipment(cd.getDireccion().getCp(), + boolean freeShipment = direccionService.checkFreeShipment( + cd.getDireccion().getCp(), cd.getDireccion().getPaisCode3()) && !cd.getIsPalets(); + if (!freeShipment) { Integer unidades = p.getSelectedTirada(); Map res = getShippingCost(cd, peso, unidades, locale); - if (res.get("success").equals(Boolean.FALSE)) { + if (Boolean.FALSE.equals(res.get("success"))) { errorShipementCost = true; } else { shipment += (Double) res.get("shipment"); iva21 += (Double) res.get("iva21"); } - } - // si tiene prueba de envio, hay que añadir el coste - if (p.getServiciosJson() != null && p.getServiciosJson().contains("ejemplar-prueba")) { + // ejemplar de prueba + if (p.getServiciosJson() != null && p.getServiciosJson().contains("ejemplar-prueba")) { Map res = getShippingCost(cd, peso, 1, locale); - if (res.get("success").equals(Boolean.FALSE)) { + if (Boolean.FALSE.equals(res.get("success"))) { errorShipementCost = true; } else { shipment += (Double) res.get("shipment"); @@ -239,23 +241,27 @@ public class CartService { } } } else { - // envio por cada presupuesto - // buscar la direccion asignada a este presupuesto if (direcciones == null) continue; + List cd_presupuesto = direcciones.stream() - .filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(p.getId()) - && d.getUnidades() != null && d.getUnidades() != null && d.getUnidades() > 0) + .filter(d -> d.getPresupuesto() != null + && d.getPresupuesto().getId().equals(p.getId()) + && d.getUnidades() != null + && d.getUnidades() > 0) .toList(); - Boolean firstDirection = true; + + boolean firstDirection = true; for (CartDireccion cd : cd_presupuesto) { Integer unidades = cd.getUnidades(); if (firstDirection) { - Boolean freeShipment = direccionService.checkFreeShipment(cd.getDireccion().getCp(), + boolean freeShipment = direccionService.checkFreeShipment( + cd.getDireccion().getCp(), cd.getDireccion().getPaisCode3()) && !cd.getIsPalets(); + if (!freeShipment && unidades != null && unidades > 0) { Map res = getShippingCost(cd, peso, unidades, locale); - if (res.get("success").equals(Boolean.FALSE)) { + if (Boolean.FALSE.equals(res.get("success"))) { errorShipementCost = true; } else { shipment += (Double) res.get("shipment"); @@ -265,7 +271,7 @@ public class CartService { firstDirection = false; } else { Map res = getShippingCost(cd, peso, unidades, locale); - if (res.get("success").equals(Boolean.FALSE)) { + if (Boolean.FALSE.equals(res.get("success"))) { errorShipementCost = true; } else { shipment += (Double) res.get("shipment"); @@ -273,15 +279,17 @@ public class CartService { } } } + // ejemplar de prueba CartDireccion cd_prueba = direcciones.stream() - .filter(d -> d.getPresupuesto() != null && d.getPresupuesto().getId().equals(p.getId()) + .filter(d -> d.getPresupuesto() != null + && d.getPresupuesto().getId().equals(p.getId()) && d.getUnidades() == null) .findFirst().orElse(null); - if (cd_prueba != null) { + if (cd_prueba != null) { Map res = getShippingCost(cd_prueba, peso, 1, locale); - if (res.get("success").equals(Boolean.FALSE)) { + if (Boolean.FALSE.equals(res.get("success"))) { errorShipementCost = true; } else { shipment += (Double) res.get("shipment"); @@ -291,11 +299,45 @@ public class CartService { } } - double total = base + iva4 + iva21 + shipment; - + double totalBeforeDiscount = base + iva4 + iva21 + shipment; int fidelizacion = pedidoService.getDescuentoFidelizacion(); - double descuento = (total) * fidelizacion / 100.0; - total -= descuento; + double descuento = totalBeforeDiscount * fidelizacion / 100.0; + double total = totalBeforeDiscount - descuento; + + // Redondeo a 2 decimales + base = Utils.round2(base); + iva4 = Utils.round2(iva4); + iva21 = Utils.round2(iva21); + shipment = Utils.round2(shipment); + descuento = Utils.round2(descuento); + total = Utils.round2(total); + + Map summary = new HashMap<>(); + summary.put("base", base); + summary.put("iva4", iva4); + summary.put("iva21", iva21); + summary.put("shipment", shipment); + summary.put("fidelizacion", fidelizacion); + summary.put("descuento", descuento); + summary.put("total", total); + summary.put("amountCents", Math.round(total * 100)); + summary.put("errorShipmentCost", errorShipementCost); + summary.put("cartId", cart.getId()); + + return summary; + } + + + public Map getCartSummary(Cart cart, Locale locale) { + Map raw = getCartSummaryRaw(cart, locale); + + double base = (Double) raw.get("base"); + double iva4 = (Double) raw.get("iva4"); + double iva21 = (Double) raw.get("iva21"); + double shipment = (Double) raw.get("shipment"); + int fidelizacion = (Integer) raw.get("fidelizacion"); + double descuento = (Double) raw.get("descuento"); + double total = (Double) raw.get("total"); Map summary = new HashMap<>(); summary.put("base", Utils.formatCurrency(base, locale)); @@ -303,11 +345,11 @@ public class CartService { summary.put("iva21", Utils.formatCurrency(iva21, locale)); summary.put("shipment", Utils.formatCurrency(shipment, locale)); summary.put("fidelizacion", fidelizacion + "%"); - summary.put("descuento", Utils.formatCurrency(-descuento, locale)); + summary.put("descuento", Utils.formatCurrency(-descuento, locale)); // negativo para mostrar summary.put("total", Utils.formatCurrency(total, locale)); - summary.put("amountCents", Math.round(total * 100)); - summary.put("errorShipmentCost", errorShipementCost); - summary.put("cartId", cart.getId()); + summary.put("amountCents", raw.get("amountCents")); + summary.put("errorShipmentCost", raw.get("errorShipmentCost")); + summary.put("cartId", raw.get("cartId")); return summary; } @@ -402,17 +444,18 @@ public class CartService { // delete cart directions by direccion id in ACTIVE carts @Transactional public void deleteCartDireccionesByDireccionId(Long direccionId) { - + cartDireccionRepo.deleteByDireccionIdAndCartStatus(direccionId, Cart.Status.ACTIVE); } @Transactional - public Long crearPedido(Long cartId) { + public Long crearPedido(Long cartId, Locale locale) { Cart cart = this.getCartById(cartId); List items = cart.getItems(); List> presupuestoRequests = new ArrayList<>(); + List presupuestoIds = new ArrayList<>(); for (Integer i = 0; i < items.size(); i++) { CartItem item = items.get(i); @@ -427,9 +470,8 @@ public class CartService { if (datosCabecera != null) { Object tituloOriginal = datosCabecera.get("titulo"); datosCabecera.put( - "titulo", - "[" + (i + 1) + "/" + items.size() + "] " + (tituloOriginal != null ? tituloOriginal : "") - ); + "titulo", + "[" + (i + 1) + "/" + items.size() + "] " + (tituloOriginal != null ? tituloOriginal : "")); } } Map direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p); @@ -453,24 +495,36 @@ public class CartService { @SuppressWarnings("unchecked") Map dataMap = (Map) dataRaw; + Long presId = ((Number) dataMap.get("id")).longValue(); + String skin = ((String) dataMap.get("iskn")).toString(); + p.setProveedor("Safekat"); + p.setProveedorRef1(skin); + p.setProveedorRef2(presId); + p.setEstado(Presupuesto.Estado.aceptado); + presupuestoRepo.save(p); + presupuestoIds.add(p.getId()); presupuestoRequests.add(dataMap); } // Crear el pedido en base a los presupuestos guardados - if(presupuestoRequests.isEmpty()) { + if (presupuestoRequests.isEmpty()) { throw new IllegalStateException("No se pudieron guardar los presupuestos en SK."); - } - else{ - List presupuestoIds = new ArrayList<>(); + } else { + ArrayList presupuestoSkIds = new ArrayList<>(); for (Map presData : presupuestoRequests) { Long presId = ((Number) presData.get("id")).longValue(); - presupuestoIds.add(presId); + presupuestoSkIds.add(presId); } Map ids = new HashMap<>(); - ids.put("presupuesto_ids", presupuestoIds); + ids.put("presupuesto_ids", presupuestoSkIds); Long pedidoId = skApiClient.crearPedido(ids); - return pedidoId; + if (pedidoId == null) { + throw new IllegalStateException("No se pudo crear el pedido en SK."); + } + Pedido pedidoInterno = pedidoService.crearPedido(presupuestoIds, this.getCartSummaryRaw(cart, locale), + "Safekat", String.valueOf(pedidoId), cart.getUserId()); + return pedidoInterno.getId(); } } @@ -510,7 +564,7 @@ public class CartService { for (CartDireccion cd : cart.getDirecciones()) { // direccion de ejemplar de prueba - if(cd.getPresupuesto() == null || !cd.getPresupuesto().getId().equals(presupuesto.getId())) { + if (cd.getPresupuesto() == null || !cd.getPresupuesto().getId().equals(presupuesto.getId())) { continue; } if (cd.getUnidades() == null || cd.getUnidades() <= 0) { @@ -535,9 +589,9 @@ public class CartService { } Map direccionesRet = new HashMap<>(); direccionesRet.put("direcciones", direccionesPresupuesto); - if(!direccionesPrueba.isEmpty()) + if (!direccionesPrueba.isEmpty()) direccionesRet.put("direccionesFP1", direccionesPrueba.get(0)); - else{ + else { direccionesRet.put("direccionesFP1", new ArrayList<>()); } return direccionesRet; diff --git a/src/main/java/com/imprimelibros/erp/common/Utils.java b/src/main/java/com/imprimelibros/erp/common/Utils.java index d5aae69..2c19503 100644 --- a/src/main/java/com/imprimelibros/erp/common/Utils.java +++ b/src/main/java/com/imprimelibros/erp/common/Utils.java @@ -47,10 +47,17 @@ public class Utils { this.messageSource = messageSource; } + public static double round2(double value) { + return BigDecimal.valueOf(value) + .setScale(2, RoundingMode.HALF_UP) + .doubleValue(); + } + 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")); + .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN") + || a.getAuthority().equals("ROLE_SUPERADMIN")); } public static Long currentUserId(Principal principal) { diff --git a/src/main/java/com/imprimelibros/erp/payments/PaymentController.java b/src/main/java/com/imprimelibros/erp/payments/PaymentController.java index f47421f..ef1df9b 100644 --- a/src/main/java/com/imprimelibros/erp/payments/PaymentController.java +++ b/src/main/java/com/imprimelibros/erp/payments/PaymentController.java @@ -295,11 +295,11 @@ public class PaymentController { } @PostMapping(value = "/transfer/completed/{id}", produces = "application/json") - public ResponseEntity> markTransferAsCaptured(@PathVariable Long id) { + public ResponseEntity> markTransferAsCaptured(@PathVariable Long id, Locale locale) { Map response; try { - paymentService.markBankTransferAsCaptured(id); + paymentService.markBankTransferAsCaptured(id, locale); response = Map.of("success", true); return ResponseEntity.ok(response); diff --git a/src/main/java/com/imprimelibros/erp/payments/PaymentService.java b/src/main/java/com/imprimelibros/erp/payments/PaymentService.java index 6392f56..099af90 100644 --- a/src/main/java/com/imprimelibros/erp/payments/PaymentService.java +++ b/src/main/java/com/imprimelibros/erp/payments/PaymentService.java @@ -15,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional; import com.imprimelibros.erp.payments.repo.WebhookEventRepository; import java.time.LocalDateTime; +import java.util.Locale; import java.util.Objects; @Service @@ -82,7 +83,7 @@ public class PaymentService { } @Transactional - public void handleRedsysNotification(String dsSignature, String dsMerchantParameters) throws Exception { + public void handleRedsysNotification(String dsSignature, String dsMerchantParameters, Locale locale) throws Exception { // 0) Intentamos parsear la notificación. Si falla, registramos el webhook crudo // y salimos. @@ -197,7 +198,7 @@ public class PaymentService { } if (authorized) { - processOrder(notif.cartId); + processOrder(notif.cartId, locale); } payRepo.save(p); @@ -317,7 +318,7 @@ public class PaymentService { } @Transactional - public void markBankTransferAsCaptured(Long paymentId) { + public void markBankTransferAsCaptured(Long paymentId, Locale locale) { Payment p = payRepo.findById(paymentId) .orElseThrow(() -> new IllegalArgumentException("Payment no encontrado: " + paymentId)); @@ -354,7 +355,7 @@ public class PaymentService { // 4) Procesar el pedido asociado al carrito (si existe) if (p.getOrderId() != null) { - processOrder(p.getOrderId()); + processOrder(p.getOrderId(), locale); } } @@ -450,19 +451,26 @@ public class PaymentService { return code >= 0 && code <= 99; } - private Boolean processOrder(Long cartId) { - // GENERAR PEDIDO A PARTIR DEL CARRITO + /** + * Procesa el pedido asociado al carrito: + * - bloquea el carrito + * - crea el pedido a partir del carrito + * + */ + private Boolean processOrder(Long cartId, Locale locale) { + Cart cart = this.cartService.findById(cartId); if (cart != null) { // Bloqueamos el carrito this.cartService.lockCartById(cart.getId()); // Creamos el pedido - this.cartService.crearPedido(cart.getId()); - // order ID es generado dentro de createOrderFromCart donde se marcan los - // presupuestos como no editables - // Long orderId = - // this.cartService.pedidoService.createOrderFromCart(cart.getId(), p.getId()); - // p.setOrderId(orderId); + Long orderId = this.cartService.crearPedido(cart.getId(), locale); + if(orderId == null){ + return false; + } + else{ + // envio de correo de confirmacion de pedido podria ir aqui + } } return true; diff --git a/src/main/java/com/imprimelibros/erp/pedido/PedidoService.java b/src/main/java/com/imprimelibros/erp/pedido/PedidoService.java deleted file mode 100644 index 679a765..0000000 --- a/src/main/java/com/imprimelibros/erp/pedido/PedidoService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.imprimelibros.erp.pedido; - -import org.springframework.stereotype.Service; - -import com.imprimelibros.erp.externalApi.skApiClient; -import com.imprimelibros.erp.presupuesto.service.PresupuestoService; - - -@Service -public class PedidoService { - - protected final skApiClient skApiClient; - protected final PresupuestoService presupuestoService; - - public PedidoService(skApiClient skApiClient, PresupuestoService presupuestoService) { - this.skApiClient = skApiClient; - this.presupuestoService = presupuestoService; - } - - public int getDescuentoFidelizacion() { - // 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 - if (totalGastado < 1200) { - return 0; - } else if (totalGastado >= 1200 && totalGastado < 1999) { - return 1; - } else if (totalGastado >= 2000 && totalGastado < 2999) { - return 2; - } else if (totalGastado >= 3000 && totalGastado < 3999) { - return 3; - } else if (totalGastado >= 4000 && totalGastado < 4999) { - return 4; - } else if (totalGastado >= 5000) { - return 5; - } - return 0; - } - - - -} diff --git a/src/main/java/com/imprimelibros/erp/pedidos/Pedido.java b/src/main/java/com/imprimelibros/erp/pedidos/Pedido.java new file mode 100644 index 0000000..b89ca3f --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/pedidos/Pedido.java @@ -0,0 +1,191 @@ +package com.imprimelibros.erp.pedidos; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "pedidos") +public class Pedido { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // Campos económicos + @Column(name = "base", nullable = false) + private Double base; + + @Column(name = "envio", nullable = false) + private Double envio = 0.0; + + @Column(name = "iva4", nullable = false) + private Double iva4 = 0.0; + + @Column(name = "iva21", nullable = false) + private Double iva21 = 0.0; + + @Column(name = "descuento", nullable = false) + private Double descuento = 0.0; + + @Column(name = "total", nullable = false) + private Double total = 0.0; + + // Datos de proveedor + @Column(name = "proveedor", length = 100) + private String proveedor; + + @Column(name = "proveedor_ref", length = 100) + private String proveedorRef; + + // Auditoría básica (coincidiendo con las columnas que se ven en la captura) + @Column(name = "created_by") + private Long createdBy; + + @Column(name = "updated_by") + private Long updatedBy; + + @Column(name = "deleted_by") + private Long deletedBy; + + @Column(name = "deleted", nullable = false) + private boolean deleted = false; + + @Column(name = "created_at", updatable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @Column(name = "deleted_at") + private LocalDateTime deletedAt; + + // --- Getters y setters --- + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Double getBase() { + return base; + } + + public void setBase(Double base) { + this.base = base; + } + + public Double getEnvio() { + return envio; + } + + public void setEnvio(Double envio) { + this.envio = envio; + } + + public Double getIva4() { + return iva4; + } + + public void setIva4(Double iva4) { + this.iva4 = iva4; + } + + public Double getIva21() { + return iva21; + } + + public void setIva21(Double iva21) { + this.iva21 = iva21; + } + + public Double getDescuento() { + return descuento; + } + + public void setDescuento(Double descuento) { + this.descuento = descuento; + } + + public Double getTotal() { + return total; + } + + public void setTotal(Double total) { + this.total = total; + } + + public String getProveedor() { + return proveedor; + } + + public void setProveedor(String proveedor) { + this.proveedor = proveedor; + } + + public String getProveedorRef() { + return proveedorRef; + } + + public void setProveedorRef(String proveedorRef) { + this.proveedorRef = proveedorRef; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public Long getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(Long updatedBy) { + this.updatedBy = updatedBy; + } + + public Long getDeletedBy() { + return deletedBy; + } + + public void setDeletedBy(Long deletedBy) { + this.deletedBy = deletedBy; + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } + + public LocalDateTime getDeletedAt() { + return deletedAt; + } + + public void setDeletedAt(LocalDateTime deletedAt) { + this.deletedAt = deletedAt; + } +} diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java new file mode 100644 index 0000000..f19f72a --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java @@ -0,0 +1,71 @@ +package com.imprimelibros.erp.pedidos; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +import com.imprimelibros.erp.presupuesto.dto.Presupuesto; + +@Entity +@Table(name = "pedidos_lineas") +public class PedidoLinea { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "pedido_id", nullable = false) + private Pedido pedido; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "presupuesto_id", nullable = false) + private Presupuesto presupuesto; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "created_by", nullable = false) + private Long createdBy; + + // --- Getters y setters --- + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Pedido getPedido() { + return pedido; + } + + public void setPedido(Pedido pedido) { + this.pedido = pedido; + } + + public Presupuesto getPresupuesto() { + return presupuesto; + } + + public void setPresupuesto(Presupuesto presupuesto) { + this.presupuesto = presupuesto; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } +} diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoLineaRepository.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLineaRepository.java new file mode 100644 index 0000000..d4e8333 --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLineaRepository.java @@ -0,0 +1,14 @@ +package com.imprimelibros.erp.pedidos; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface PedidoLineaRepository extends JpaRepository { + + List findByPedidoId(Long pedidoId); + + List findByPresupuestoId(Long presupuestoId); +} diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoRepository.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoRepository.java new file mode 100644 index 0000000..6f19f9d --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoRepository.java @@ -0,0 +1,10 @@ +package com.imprimelibros.erp.pedidos; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PedidoRepository extends JpaRepository { + // aquí podrás añadir métodos tipo: + // List findByDeletedFalse(); +} diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java new file mode 100644 index 0000000..9fa7a10 --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java @@ -0,0 +1,102 @@ +package com.imprimelibros.erp.pedidos; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.imprimelibros.erp.presupuesto.PresupuestoRepository; +import com.imprimelibros.erp.presupuesto.dto.Presupuesto; +import com.imprimelibros.erp.presupuesto.service.PresupuestoService; + +@Service +public class PedidoService { + + private final PedidoRepository pedidoRepository; + private final PedidoLineaRepository pedidoLineaRepository; + private final PresupuestoRepository presupuestoRepository; + + public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository, + PresupuestoRepository presupuestoRepository) { + this.pedidoRepository = pedidoRepository; + this.pedidoLineaRepository = pedidoLineaRepository; + this.presupuestoRepository = presupuestoRepository; + } + + public int getDescuentoFidelizacion() { + // 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 + if (totalGastado < 1200) { + return 0; + } else if (totalGastado >= 1200 && totalGastado < 1999) { + return 1; + } else if (totalGastado >= 2000 && totalGastado < 2999) { + return 2; + } else if (totalGastado >= 3000 && totalGastado < 3999) { + return 3; + } else if (totalGastado >= 4000 && totalGastado < 4999) { + return 4; + } else if (totalGastado >= 5000) { + return 5; + } + return 0; + } + + /** + * Crea un pedido a partir de: + * - lista de IDs de presupuesto + * - resumen numérico del carrito (getCartSummaryRaw) + * - datos de proveedor + * - usuario que crea el pedido + */ + @Transactional + public Pedido crearPedido(List presupuestoIds, + Map cartSummaryRaw, + String proveedor, + String proveedorRef, + Long userId) { + + Pedido pedido = new Pedido(); + + // Datos económicos (ojo con las claves, son las del summaryRaw) + pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d)); + pedido.setEnvio((Double) cartSummaryRaw.getOrDefault("shipment", 0.0d)); + pedido.setIva4((Double) cartSummaryRaw.getOrDefault("iva4", 0.0d)); + pedido.setIva21((Double) cartSummaryRaw.getOrDefault("iva21", 0.0d)); + pedido.setDescuento((Double) cartSummaryRaw.getOrDefault("descuento", 0.0d)); + pedido.setTotal((Double) cartSummaryRaw.getOrDefault("total", 0.0d)); + + // Proveedor + pedido.setProveedor(proveedor); + pedido.setProveedorRef(proveedorRef); + + // Auditoría mínima + pedido.setCreatedBy(userId); + pedido.setCreatedAt(LocalDateTime.now()); + pedido.setDeleted(false); + pedido.setUpdatedAt(LocalDateTime.now()); + pedido.setUpdatedBy(userId); + + // Guardamos el pedido + Pedido saved = pedidoRepository.save(pedido); + + // Crear líneas del pedido + for (Long presupuestoId : presupuestoIds) { + Presupuesto presupuesto = presupuestoRepository.getReferenceById(presupuestoId); + + PedidoLinea linea = new PedidoLinea(); + linea.setPedido(saved); + linea.setPresupuesto(presupuesto); + linea.setCreatedBy(userId); + linea.setCreatedAt(LocalDateTime.now()); + + pedidoLineaRepository.save(linea); + } + + return saved; + } + +} diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java index 73b89a8..2e6c558 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java @@ -573,7 +573,7 @@ public class PresupuestoController { String path = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) .getRequest().getRequestURI(); String mode = path.contains("/view/") ? "view" : "edit"; - if (mode.equals("view")) { + if (mode.equals("view") || presupuestoOpt.get().getEstado() != Presupuesto.Estado.borrador) { model.addAttribute("appMode", "view"); } else { model.addAttribute("cliente_id", presupuestoOpt.get().getUser().getId()); diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java index 1f1e4c3..4d0bd5d 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoDatatableService.java @@ -120,7 +120,7 @@ public class PresupuestoDatatableService { String id = String.valueOf(p.getId()); String editBtn = ""; + (p.getOrigen().equals(Presupuesto.Origen.publico) || p.getEstado() == Presupuesto.Estado.aceptado ? "eye" : "pencil") + "-line\">"; String deleteBtn = borrador ? " okPost(@RequestParam("Ds_Signature") String signature, - @RequestParam("Ds_MerchantParameters") String merchantParameters) { + @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { // opcional: idempotente, si /notify ya ha hecho el trabajo no pasa nada - paymentService.handleRedsysNotification(signature, merchantParameters); + paymentService.handleRedsysNotification(signature, merchantParameters, locale); return ResponseEntity.ok("

Pago realizado correctamente

Volver"); } catch (Exception e) { return ResponseEntity.badRequest() @@ -127,11 +127,11 @@ public class RedsysController { @ResponseBody public ResponseEntity koPost( @RequestParam("Ds_Signature") String signature, - @RequestParam("Ds_MerchantParameters") String merchantParameters) { + @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { // Procesamos la notificación IGUAL que en /ok y /notify - paymentService.handleRedsysNotification(signature, merchantParameters); + paymentService.handleRedsysNotification(signature, merchantParameters, locale); // Mensaje para el usuario (pago cancelado/rechazado) String html = "

Pago cancelado o rechazado

Volver"; @@ -146,9 +146,9 @@ public class RedsysController { @PostMapping(value = "/notify", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public String notifyRedsys(@RequestParam("Ds_Signature") String signature, - @RequestParam("Ds_MerchantParameters") String merchantParameters) { + @RequestParam("Ds_MerchantParameters") String merchantParameters, Locale locale) { try { - paymentService.handleRedsysNotification(signature, merchantParameters); + paymentService.handleRedsysNotification(signature, merchantParameters, locale); return "OK"; } catch (Exception e) { e.printStackTrace(); // 👈 para ver el motivo del 500 en logs diff --git a/src/main/resources/db/changelog/changesets/0011-update-pedidos-presupuesto.yml b/src/main/resources/db/changelog/changesets/0011-update-pedidos-presupuesto.yml new file mode 100644 index 0000000..ba0400b --- /dev/null +++ b/src/main/resources/db/changelog/changesets/0011-update-pedidos-presupuesto.yml @@ -0,0 +1,210 @@ +databaseChangeLog: + - changeSet: + id: 0011-update-pedidos-presupuesto + author: jjo + + changes: + # 1) Nuevas columnas en PRESUPUESTO + - addColumn: + tableName: presupuesto + columns: + - column: + name: comentario + type: TEXT + afterColumn: pricing_snapshot + constraints: + nullable: true + - column: + name: proveedor + type: VARCHAR(100) + constraints: + nullable: true + - column: + name: proveedor_ref1 + type: VARCHAR(100) + constraints: + nullable: true + - column: + name: proveedor_ref2 + type: BIGINT + constraints: + nullable: true + + # 2) Cambios en PEDIDOS + # 2.1 Eliminar FK fk_pedidos_presupuesto + - dropForeignKeyConstraint: + baseTableName: pedidos + constraintName: fk_pedidos_presupuesto + + # 2.2 Eliminar columna presupuesto_id + - dropColumn: + tableName: pedidos + columnName: presupuesto_id + + # 2.3 Añadir nuevas columnas después de id + - addColumn: + tableName: pedidos + columns: + - column: + name: base + type: DOUBLE + afterColumn: id + constraints: + nullable: false + - column: + name: envio + type: DOUBLE + defaultValueNumeric: 0 + constraints: + nullable: false + - column: + name: iva4 + type: DOUBLE + defaultValueNumeric: 0 + constraints: + nullable: false + - column: + name: iva21 + type: DOUBLE + defaultValueNumeric: 0 + constraints: + nullable: false + - column: + name: descuento + type: DOUBLE + defaultValueNumeric: 0 + constraints: + nullable: false + - column: + name: total + type: DOUBLE + defaultValueNumeric: 0 + constraints: + nullable: false + - column: + name: proveedor + type: VARCHAR(100) + afterColumn: total + constraints: + nullable: true + - column: + name: proveedor_ref + type: VARCHAR(100) + afterColumn: proveedor + constraints: + nullable: true + + # 3) Crear tabla PEDIDOS_LINEAS + - createTable: + tableName: pedidos_lineas + columns: + - column: + name: id + type: BIGINT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: pedido_id + type: BIGINT + constraints: + nullable: false + - column: + name: presupuesto_id + type: BIGINT + constraints: + nullable: false + - column: + name: created_at + type: DATETIME(3) + constraints: + nullable: true + - column: + name: created_by + type: BIGINT + constraints: + nullable: false + + # FKs de pedidos_lineas + - addForeignKeyConstraint: + baseTableName: pedidos_lineas + baseColumnNames: pedido_id + constraintName: fk_pedidos_lineas_pedido + referencedTableName: pedidos + referencedColumnNames: id + onDelete: RESTRICT + + - addForeignKeyConstraint: + baseTableName: pedidos_lineas + baseColumnNames: presupuesto_id + constraintName: fk_pedidos_lineas_presupuesto + referencedTableName: presupuesto + referencedColumnNames: id + onDelete: RESTRICT + + - addForeignKeyConstraint: + baseTableName: pedidos_lineas + baseColumnNames: created_by + constraintName: fk_pedidos_lineas_created_by_user + referencedTableName: users + referencedColumnNames: id + onDelete: RESTRICT + + rollback: + # 3) Eliminar tabla pedidos_lineas y sus FKs + - dropTable: + tableName: pedidos_lineas + + # 2) Revertir cambios en PEDIDOS + - dropColumn: + tableName: pedidos + columns: + - column: + name: base + - column: + name: envio + - column: + name: iva4 + - column: + name: iva21 + - column: + name: descuento + - column: + name: total + - column: + name: proveedor + - column: + name: proveedor_ref + + # 2.2 Volver a crear presupuesto_id + - addColumn: + tableName: pedidos + columns: + - column: + name: presupuesto_id + type: BIGINT + constraints: + nullable: true + + # 2.1 Volver a crear la FK fk_pedidos_presupuesto + - addForeignKeyConstraint: + baseTableName: pedidos + baseColumnNames: presupuesto_id + constraintName: fk_pedidos_presupuesto + referencedTableName: presupuesto + referencedColumnNames: id + onDelete: RESTRICT + + # 1) Eliminar columnas añadidas en PRESUPUESTO + - dropColumn: + tableName: presupuesto + columns: + - column: + name: comentario + - column: + name: proveedor + - column: + name: proveedor_ref1 + - column: + name: proveedor_ref2 diff --git a/src/main/resources/db/changelog/master.yml b/src/main/resources/db/changelog/master.yml index 6512f54..2dd01a3 100644 --- a/src/main/resources/db/changelog/master.yml +++ b/src/main/resources/db/changelog/master.yml @@ -18,4 +18,6 @@ databaseChangeLog: - include: file: db/changelog/changesets/0009-add-composite-unique-txid-type.yml - include: - file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml \ No newline at end of file + file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml + - include: + file: db/changelog/changesets/0011-update-pedidos-presupuesto.yml \ No newline at end of file diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html new file mode 100644 index 0000000..de9a93c --- /dev/null +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html @@ -0,0 +1,72 @@ +
+ +
+
+ + +
+
+
Resumen +
+
+ +
+
+
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DescripciónCantidadPrecio unitarioPrecio total
Total0,00 €
IVA (4%)0,00 €
IVA (21%)0,00 €
Total con IVA + 0,00 €
+
+ +
+ + + +
+
+
+ +
\ No newline at end of file diff --git a/src/test/java/com/imprimelibros/erp/presupuesto/savePresupuestosTest.java b/src/test/java/com/imprimelibros/erp/presupuesto/savePresupuestosTest.java index d466e73..f8d10f4 100644 --- a/src/test/java/com/imprimelibros/erp/presupuesto/savePresupuestosTest.java +++ b/src/test/java/com/imprimelibros/erp/presupuesto/savePresupuestosTest.java @@ -1,5 +1,7 @@ package com.imprimelibros.erp.presupuesto; +import java.util.Locale; + import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -14,7 +16,8 @@ public class savePresupuestosTest { @Test void testGuardarPresupuesto() { - Long resultado = cartService.crearPedido(9L); + Locale locale = new Locale("es", "ES"); + Long resultado = cartService.crearPedido(9L, locale); System.out.println("📦 Presupuesto guardado:"); System.out.println(resultado);