sistema de pedidos pendientes de pago hechos

This commit is contained in:
2025-12-23 17:41:05 +01:00
parent d4120bb486
commit b94a099e01
15 changed files with 3895 additions and 868 deletions

File diff suppressed because it is too large Load Diff

View File

@ -231,20 +231,9 @@ public class PaymentController {
})
.add("transfer_id", pago -> {
if (pago.getPayment() != null) {
String responsePayload = pago.getResponsePayload();
Long cartId = null;
if (responsePayload != null && !responsePayload.isBlank()) {
try {
JsonNode node = new ObjectMapper().readTree(responsePayload);
if (node.has("cartId")) {
cartId = node.get("cartId").asLong();
}
} catch (Exception e) {
cartId = null;
}
}
if (cartId != null) {
return "TRANSF-" + cartId;
Long pedido = pago.getPayment().getOrderId();
if (pedido != null) {
return "TRANSF-" + pedido;
}
}
return "";

View File

@ -51,7 +51,7 @@ public class PaymentService {
}
public Payment findFailedPaymentByOrderId(Long orderId) {
return payRepo.findByOrderIdAndStatus(orderId, PaymentStatus.failed)
return payRepo.findFirstByOrderIdAndStatusOrderByIdDesc(orderId, PaymentStatus.failed)
.orElse(null);
}
@ -69,11 +69,17 @@ public class PaymentService {
var node = om.readTree(resp_payload);
Long cartId = null;
Long dirFactId = null;
if (node.has("cartId")) {
cartId = node.get("cartId").asLong();
if (node.has("Ds_MerchantData")) {
// format: "Ds_MerchantData": "{"dirFactId":3,"cartId":90}"
String merchantData = node.get("Ds_MerchantData").asText();
merchantData = merchantData.replace(""", "\"");
var mdNode = om.readTree(merchantData);
if (mdNode.has("cartId")) {
cartId = mdNode.get("cartId").asLong();
}
if (mdNode.has("dirFactId")) {
dirFactId = mdNode.get("dirFactId").asLong();
}
if (node.has("dirFactId")) {
dirFactId = node.get("dirFactId").asLong();
}
return Map.of(
"cartId", cartId,

View File

@ -10,5 +10,5 @@ import java.util.Optional;
public interface PaymentRepository extends JpaRepository<Payment, Long> {
Optional<Payment> findByGatewayAndGatewayOrderId(String gateway, String gatewayOrderId);
Optional<Payment> findByOrderIdAndStatus(Long orderId, PaymentStatus status);
Optional<Payment> findFirstByOrderIdAndStatusOrderByIdDesc(Long orderId, PaymentStatus status);
}

View File

@ -6,6 +6,8 @@ import com.imprimelibros.erp.direcciones.Direccion.TipoIdentificacionFiscal;
import com.imprimelibros.erp.paises.Paises;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Entity
@Table(name = "pedidos_direcciones")
@ -33,6 +35,9 @@ public class PedidoDireccion {
@Column(name = "is_ejemplar_prueba", nullable = false)
private boolean ejemplarPrueba = false;
@Column(name = "email", length = 255)
private String email;
@Column(name = "att", nullable = false, length = 150)
private String att;
@ -74,6 +79,9 @@ public class PedidoDireccion {
@Column(name = "identificacion_fiscal", length = 50)
private String identificacionFiscal;
@Column(name = "is_palets", nullable = false)
private boolean palets = false;
@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@ -124,6 +132,14 @@ public class PedidoDireccion {
this.ejemplarPrueba = ejemplarPrueba;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAtt() {
return att;
}
@ -220,6 +236,14 @@ public class PedidoDireccion {
this.identificacionFiscal = identificacionFiscal;
}
public boolean isPalets() {
return palets;
}
public void setPalets(boolean palets) {
this.palets = palets;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
@ -231,4 +255,54 @@ public class PedidoDireccion {
public void setPaisNombre(String paisNombre) {
this.paisNombre = paisNombre;
}
public Map<String, Object> toSkMap(Double pesoKg) {
Map<String, Object> direccion = new HashMap<>();
direccion.put("cantidad", this.getUnidades());
direccion.put("peso", pesoKg);
direccion.put("att", this.getAtt());
direccion.put("email", this.getEmail());
direccion.put("direccion", this.getDireccion());
direccion.put("pais_code3", this.getPaisCode3());
direccion.put("cp", this.getCp());
direccion.put("municipio", this.getCiudad());
direccion.put("provincia", this.getProvincia());
direccion.put("telefono", this.getTelefono());
direccion.put("entregaPieCalle", this.isPalets() ? 1 : 0);
direccion.put("is_ferro_prototipo", this.isEjemplarPrueba() ? 1 : 0);
direccion.put("num_ferro_prototipo", this.isEjemplarPrueba() ? 1 : 0);
Map<String, Object> map = new HashMap<>();
map.put("direccion", direccion);
map.put("unidades", this.getUnidades());
map.put("entregaPalets", this.isPalets() ? 1 : 0);
return map;
}
public static Map<String, Object> toSkMapDepositoLegal() {
Map<String, Object> direccion = new HashMap<>();
direccion.put("cantidad", 4);
direccion.put("peso", 0);
direccion.put("att", "Unidades para Depósito Legal (sin envío)");
direccion.put("email", "");
direccion.put("direccion", "");
direccion.put("pais_code3", "esp");
direccion.put("cp", "");
direccion.put("municipio", "");
direccion.put("provincia", "");
direccion.put("telefono", "");
direccion.put("entregaPieCalle", 0);
direccion.put("is_ferro_prototipo", 0);
direccion.put("num_ferro_prototipo", 0);
Map<String, Object> map = new HashMap<>();
map.put("direccion", direccion);
map.put("unidades", 4);
map.put("entregaPalets", 0);
return map;
}
}

View File

@ -1,6 +1,7 @@
package com.imprimelibros.erp.pedidos;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
@ -13,5 +14,13 @@ public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
PedidoDireccion findByPedidoIdAndFacturacionTrue(Long pedidoId);
}
@Query("""
select distinct d
from PedidoDireccion d
join d.pedidoLinea pl
where d.pedidoLinea.id = :pedidoLineaId
""")
List<PedidoDireccion> findByPedidoLineaId(Long pedidoLineaId);
}

View File

@ -22,6 +22,7 @@ import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserService;
import com.imprimelibros.erp.direcciones.DireccionService;
import com.imprimelibros.erp.externalApi.skApiClient;
import com.imprimelibros.erp.pedidos.PedidoLinea.Estado;
@Service
@ -35,10 +36,13 @@ public class PedidoService {
private final UserService userService;
private final PresupuestoService presupuestoService;
private final CartService cartService;
private final skApiClient skApiClient;
private final PresupuestoRepository presupuestoRepo;
public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository,
PresupuestoRepository presupuestoRepository, PedidoDireccionRepository pedidoDireccionRepository,
DireccionService direccionService, UserService userService, PresupuestoService presupuestoService, CartService cartService) {
DireccionService direccionService, UserService userService, PresupuestoService presupuestoService,
CartService cartService, skApiClient skApiClient, PresupuestoRepository presupuestoRepo) {
this.pedidoRepository = pedidoRepository;
this.pedidoLineaRepository = pedidoLineaRepository;
this.presupuestoRepository = presupuestoRepository;
@ -47,9 +51,10 @@ public class PedidoService {
this.userService = userService;
this.presupuestoService = presupuestoService;
this.cartService = cartService;
this.skApiClient = skApiClient;
this.presupuestoRepo = presupuestoRepo;
}
@Transactional
public Pedido crearPedido(
Long cartId,
@ -71,7 +76,7 @@ public class PedidoService {
pedido.setTotal((Double) cartSummaryRaw.getOrDefault("total", 0.0d));
// Proveedor
if(proveedor != null && proveedorRef != null) {
if (proveedor != null && proveedorRef != null) {
pedido.setProveedor(proveedor);
pedido.setProveedorRef(proveedorRef);
}
@ -114,39 +119,12 @@ public class PedidoService {
}
/*
// 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());
if(estadoInicial != null){
linea.setEstado(estadoInicial);
} else {
linea.setEstado(getEstadoInicial(presupuesto));
}
linea.setEstadoManual(false);
pedidoLineaRepository.save(linea);
@SuppressWarnings("unchecked")
Map<String, Map<String, Object>> direcciones = (Map<String, Map<String, Object>>) presupuestoDirecciones
.get(presupuesto.getId().toString());
if (direcciones != null) {
saveDireccionesPedidoLinea(direcciones, saved, linea, direccionFacturacionId);
}
}
*/
return pedidoGuardado;
}
public Boolean markPedidoAsProcesingPayment(Long pedidoId){
public Boolean markPedidoAsProcesingPayment(Long pedidoId) {
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
if(pedido == null){
if (pedido == null) {
return false;
}
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
@ -158,10 +136,9 @@ public class PedidoService {
return true;
}
public Boolean markPedidoAsPaymentDenied(Long pedidoId){
public Boolean markPedidoAsPaymentDenied(Long pedidoId) {
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
if(pedido == null){
if (pedido == null) {
return false;
}
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
@ -173,142 +150,12 @@ public class PedidoService {
return true;
}
public Boolean markPedidoAsPaid(Long pedidoId){
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
if(pedido == null){
return false;
}
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
for (PedidoLinea linea : lineas) {
linea.setEstado(this.getEstadoInicial(linea.getPresupuesto()));
pedidoLineaRepository.save(linea);
// Save presupuesto in SK
}
// Save pedido in SK
return true;
}
public Pedido findById(Long pedidoId){
public Pedido findById(Long pedidoId) {
return pedidoRepository.findById(pedidoId).orElse(null);
}
/*
@Transactional
public Long crearPedido(Long cartId, Long dirFactId, Locale locale) {
return crearPedido(cartId, dirFactId, locale, null);
}
@Transactional
// Crear pedido interno (no en el proveedor) a partir del carrito
public Long crearPedido(Long cartId, Long dirFactId, Locale locale, PedidoLinea.Estado estadoInicial) {
Cart cart = cartService.getCartById(cartId);
List<CartItem> items = cart.getItems();
List<Map<String, Object>> presupuestoRequests = new ArrayList<>();
Map<String, Object> presupuestoDireccionesRequest = new HashMap<>();
List<Long> presupuestoIds = new ArrayList<>();
for (Integer i = 0; i < items.size(); i++) {
CartItem item = items.get(i);
Presupuesto pCart = item.getPresupuesto();
// Asegurarnos de trabajar con la entidad gestionada por JPA
Presupuesto p = presupuestoRepository.findById(pCart.getId())
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
/*Map<String, Object> data_to_send = presupuestoService.toSkApiRequest(p, true);
data_to_send.put("createPedido", 0);
// Recuperar el mapa anidado datosCabecera
@SuppressWarnings("unchecked")
Map<String, Object> datosCabecera = (Map<String, Object>) data_to_send.get("datosCabecera");
if (datosCabecera != null) {
Object tituloOriginal = datosCabecera.get("titulo");
datosCabecera.put(
"titulo",
"[" + (i + 1) + "/" + items.size() + "] " + (tituloOriginal != null ? tituloOriginal : ""));
}
Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p);
data_to_send.put("direcciones", direcciones_presupuesto.get("direcciones"));
data_to_send.put("direccionesFP1", direcciones_presupuesto.get("direccionesFP1"));
presupuestoDireccionesRequest.put(p.getId().toString(), direcciones_presupuesto);
Map<String, Object> result = skApiClient.savePresupuesto(data_to_send);
if (result.containsKey("error")) {
System.out.println("Error al guardar presupuesto en SK");
System.out.println("-------------------------");
System.out.println(result.get("error"));
// decide si seguir con otros items o abortar:
// continue; o bien throw ...
continue;
}
Object dataObj = result.get("data");
if (!(dataObj instanceof Map<?, ?> dataRaw)) {
System.out.println("Formato inesperado de 'data' en savePresupuesto: " + result);
continue;
}
@SuppressWarnings("unchecked")
Map<String, Object> dataMap = (Map<String, Object>) 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()) {
throw new IllegalStateException("No se pudieron guardar los presupuestos en SK.");
} else {
ArrayList<Long> presupuestoSkIds = new ArrayList<>();
for (Map<String, Object> presData : presupuestoRequests) {
Long presId = ((Number) presData.get("id")).longValue();
presupuestoSkIds.add(presId);
}
Map<String, Object> ids = new HashMap<>();
ids.put("presupuesto_ids", presupuestoSkIds);
Long pedidoId = skApiClient.crearPedido(ids);
if (pedidoId == null) {
throw new IllegalStateException("No se pudo crear el pedido en SK.");
}
Pedido pedidoInterno = pedidoService.crearPedido(
presupuestoIds,
presupuestoDireccionesRequest,
dirFactId,
this.getCartSummaryRaw(cart, locale),
"Safekat",
String.valueOf(pedidoId),
cart.getUserId(),
estadoInicial);
return pedidoInterno.getId();
}
}
*/
/** Lista de los items del pedido preparados para la vista*/
/** Lista de los items del pedido preparados para la vista */
@Transactional
public List<Map<String, Object>> getLineas(Long pedidoId, Locale locale) {
Pedido p = pedidoRepository.findById(pedidoId).orElse(null);
@ -323,8 +170,8 @@ public class PedidoService {
Presupuesto presupuesto = item.getPresupuesto();
Map<String, Object> elemento = presupuestoService.getPresupuestoInfoForCard(presupuesto, locale);
elemento.put("estado", item.getEstado());
elemento.put("fechaEntrega", item.getFechaEntrega() != null ?
Utils.formatDate(item.getFechaEntrega(), locale) : "");
elemento.put("fechaEntrega",
item.getFechaEntrega() != null ? Utils.formatDate(item.getFechaEntrega(), locale) : "");
elemento.put("lineaId", item.getId());
resultados.add(elemento);
}
@ -345,19 +192,127 @@ public class PedidoService {
return false;
}
List<PedidoLinea> lineas = pedidoLineaRepository.findByPedidoId(pedidoId);
List<Map<String, Object>> referenciasProveedor = new ArrayList<>();
Integer total = lineas.size();
Integer counter = 1;
for (PedidoLinea linea : lineas) {
if (linea.getEstado() == Estado.pendiente_pago) {
if (linea.getEstado() == Estado.pendiente_pago
|| linea.getEstado() == Estado.denegado_pago) {
Presupuesto presupuesto = linea.getPresupuesto();
linea.setEstado(getEstadoInicial(presupuesto));
pedidoLineaRepository.save(linea);
// Save presupuesto in SK
Map<String, Object> result = savePresupuestoSK(linea.getId(), presupuesto, counter, total);
if (result == null) {
return false;
}
referenciasProveedor.add(result);
counter++;
}
}
if (referenciasProveedor.isEmpty()) {
return false;
}
// Save pedido in SK
ArrayList<Long> presupuestoSkIds = new ArrayList<>();
for (Map<String, Object> presData : referenciasProveedor) {
Long presId = ((Number) presData.get("id")).longValue();
presupuestoSkIds.add(presId);
}
Map<String, Object> ids = new HashMap<>();
ids.put("presupuesto_ids", presupuestoSkIds);
Long skPedidoId = skApiClient.crearPedido(ids);
if (skPedidoId == null) {
System.out.println("No se pudo crear el pedido en SK.");
return false;
}
pedido.setProveedor("Safekat");
pedido.setProveedorRef(skPedidoId.toString());
pedidoRepository.save(pedido);
return true;
}
/***************************
* MÉTODOS PRIVADOS
***************************/
@Transactional
private Map<String, Object> savePresupuestoSK(Long pedidoLineaId, Presupuesto presupuesto, Integer counter, Integer total) {
Map<String, Object> data_to_send = presupuestoService.toSkApiRequest(presupuesto, true);
data_to_send.put("createPedido", 0);
// Recuperar el mapa anidado datosCabecera
@SuppressWarnings("unchecked")
Map<String, Object> datosCabecera = (Map<String, Object>) data_to_send.get("datosCabecera");
if (datosCabecera != null) {
Object tituloOriginal = datosCabecera.get("titulo");
datosCabecera.put(
"titulo",
"[" + (counter) + "/" + total + "] " + (tituloOriginal != null ? tituloOriginal : ""));
}
List<PedidoDireccion> direccionesPedidoLinea = pedidoDireccionRepository
.findByPedidoLineaId(pedidoLineaId);
List<Map<String, Object>> direccionesPresupuesto = new ArrayList<>();
List<Map<String, Object>> direccionEjemplarPrueba = new ArrayList<>();
for (PedidoDireccion pd : direccionesPedidoLinea) {
if (pd.isEjemplarPrueba()) {
direccionEjemplarPrueba.add(
pd.toSkMap(presupuesto.getPeso()));
} else {
direccionesPresupuesto.add(
pd.toSkMap(presupuesto.getPeso() * pd.getUnidades()));
}
}
if (presupuesto.getServiciosJson() != null && presupuesto.getServiciosJson().contains("deposito-legal")) {
direccionesPresupuesto.add(
PedidoDireccion.toSkMapDepositoLegal());
}
data_to_send.put("direcciones", direccionesPresupuesto);
if (direccionEjemplarPrueba.size() > 0)
data_to_send.put("direccionesFP1", direccionEjemplarPrueba.get(0));
else {
data_to_send.put("direccionesFP1", new ArrayList<>());
}
Map<String, Object> result = skApiClient.savePresupuesto(data_to_send);
if (result.containsKey("error")) {
System.out.println("Error al guardar presupuesto en SK");
System.out.println("-------------------------");
System.out.println(result.get("error"));
// decide si seguir con otros items o abortar:
// continue; o bien throw ...
return null;
}
Object dataObj = result.get("data");
if (!(dataObj instanceof Map<?, ?> dataRaw)) {
System.out.println("Formato inesperado de 'data' en savePresupuesto: " + result);
return null;
}
@SuppressWarnings("unchecked")
Map<String, Object> dataMap = (Map<String, Object>) dataRaw;
Long presId = ((Number) dataMap.get("id")).longValue();
String skin = ((String) dataMap.get("iskn")).toString();
presupuesto.setProveedor("Safekat");
presupuesto.setProveedorRef1(skin);
presupuesto.setProveedorRef2(presId);
presupuesto.setEstado(Presupuesto.Estado.aceptado);
presupuestoRepo.save(presupuesto);
return dataMap;
}
// Obtener las direcciones de envío asociadas a un presupuesto en el carrito
private Map<String, Object> getDireccionesPresupuesto(Cart cart, Presupuesto presupuesto) {
@ -375,8 +330,7 @@ public class PedidoService {
false));
direccionesPresupuesto.add(direcciones.get(0).toSkMapDepositoLegal());
}
else {
} else {
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
presupuesto.getSelectedTirada(),
presupuesto.getPeso(),
@ -442,22 +396,28 @@ public class PedidoService {
return direccionesRet;
}
@Transactional
private void saveDireccionesPedidoLinea(
Map<String, Object> direcciones,
Pedido pedido,
PedidoLinea linea, Long direccionFacturacionId) {
String email = pedido.getCreatedBy().getUserName();
// direccion prueba
if (direcciones.containsKey("direccionesFP1")) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> fp1 = (Map<String, Object>) direcciones.get("direccionesFP1");
@SuppressWarnings("unchecked")
PedidoDireccion direccion = saveDireccion(
email,
false,
(HashMap<String, Object>) fp1.get("direccion"),
pedido,
linea, true,
linea,
true,
false);
pedidoDireccionRepository.save(direccion);
} catch (Exception e) {
@ -476,6 +436,10 @@ public class PedidoService {
}
@SuppressWarnings("unchecked")
PedidoDireccion direccion = saveDireccion(
email,
((Number) ((HashMap<String, Object>) dir)
.getOrDefault("entregaPalets", 0))
.intValue() == 1,
(HashMap<String, Object>) ((HashMap<String, Object>) dir).get("direccion"),
pedido,
linea, false,
@ -500,20 +464,30 @@ public class PedidoService {
dirFactMap.put("identificacion_fiscal", dirFact.getIdentificacionFiscal());
PedidoDireccion direccion = saveDireccion(
email,
false,
dirFactMap,
pedido,
linea, false,
linea,
false,
true);
pedidoDireccionRepository.save(direccion);
}
}
}
private PedidoDireccion saveDireccion(HashMap<String, Object> dir, Pedido pedido, PedidoLinea linea,
private PedidoDireccion saveDireccion(
String email,
Boolean palets,
HashMap<String, Object> dir,
Pedido pedido,
PedidoLinea linea,
Boolean isEjemplarPrueba,
Boolean isFacturacion) {
PedidoDireccion direccion = new PedidoDireccion();
direccion.setEmail(email);
direccion.setPalets(isEjemplarPrueba || isFacturacion ? false : palets);
direccion.setPedidoLinea(isFacturacion ? null : linea);
if (isFacturacion) {
direccion.setUnidades(null);
@ -549,9 +523,9 @@ public class PedidoService {
}
private Estado getEstadoInicial(Presupuesto p){
private Estado getEstadoInicial(Presupuesto p) {
if(presupuestoService.hasMaquetacion(p)){
if (presupuestoService.hasMaquetacion(p)) {
return Estado.maquetacion;
} else {
return Estado.haciendo_ferro;

View File

@ -20,6 +20,7 @@ import com.imprimelibros.erp.datatables.DataTable;
import com.imprimelibros.erp.datatables.DataTablesParser;
import com.imprimelibros.erp.datatables.DataTablesRequest;
import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.paises.PaisesService;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserDao;
@ -29,7 +30,6 @@ import jakarta.persistence.criteria.JoinType;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@ -44,21 +44,36 @@ public class PedidosController {
private final MessageSource messageSource;
private final PedidoLineaRepository repoPedidoLinea;
private final PaisesService paisesService;
private final TranslationService translationService;
public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser,
MessageSource messageSource,
MessageSource messageSource, TranslationService translationService,
PedidoLineaRepository repoPedidoLinea, PaisesService paisesService, PresupuestoService presupuestoService) {
this.repoPedido = repoPedido;
this.pedidoService = pedidoService;
this.repoUser = repoUser;
this.messageSource = messageSource;
this.translationService = translationService;
this.repoPedidoLinea = repoPedidoLinea;
this.paisesService = paisesService;
this.presupuestoService = presupuestoService;
}
@GetMapping
public String listarPedidos() {
public String listarPedidos(Model model, Locale locale) {
List<String> keys = List.of(
"app.cancelar",
"app.seleccionar",
"app.yes",
"checkout.payment.card",
"checkout.payment.bizum",
"checkout.payment.bank-transfer",
"checkout.error.select-method");
Map<String, String> translations = translationService.getTranslations(locale, keys);
model.addAttribute("languageBundle", translations);
if (Utils.isCurrentUserAdmin()) {
return "imprimelibros/pedidos/pedidos-list";
}
@ -173,7 +188,7 @@ public class PedidosController {
boolean hasDenegadoPago = lineas.stream()
.anyMatch(linea -> PedidoLinea.Estado.denegado_pago.equals(linea.getEstado()));
if (hasDenegadoPago) {
data += " <span class='badge bg-danger btn-pay' data-amount='" + pedido.getTotal() + "' data-id=\\'" + pedido.getId() + "\\' style='cursor: pointer;'>" + messageSource.getMessage("app.pay", null, locale) + "</span>";
data += " <span class='badge bg-danger btn-pay' data-amount='" + (int)(pedido.getTotal()*100) + "' data-id='" + pedido.getId() + "' style='cursor: pointer;'>" + messageSource.getMessage("app.pay", null, locale) + "</span>";
}
return data;
})

View File

@ -70,7 +70,8 @@ public class RedsysController {
if ("bank-transfer".equalsIgnoreCase(method)) {
// 1) Creamos el Payment interno SIN orderId (null)
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale, order.getId());
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale,
order.getId());
pedidoService.markPedidoAsProcesingPayment(order.getId());
@ -104,7 +105,8 @@ public class RedsysController {
}
// Tarjeta o Bizum (Redsys)
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method, order.getId());
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method,
order.getId());
String html = """
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>
@ -132,7 +134,6 @@ public class RedsysController {
.body(body);
}
@PostMapping(value = "/reintentar", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
public ResponseEntity<byte[]> reintentarPago(@RequestParam("amountCents") Long amountCents,
@ -153,19 +154,20 @@ public class RedsysController {
Long cartId = null;
Long dirFactId = null;
// Find payment transaction details from failedPayment if needed
try{
try {
Map<String, Long> transactionDetails = paymentService.getPaymentTransactionData(failedPayment.getId());
cartId = transactionDetails.get("cartId");
dirFactId = transactionDetails.get("dirFactId");
} catch (Exception e) {
throw new Exception("No se pudieron obtener los detalles de la transacción para el pago " + failedPayment.getId());
throw new Exception(
"No se pudieron obtener los detalles de la transacción para el pago " + failedPayment.getId());
}
if ("bank-transfer".equalsIgnoreCase(method)) {
// 1) Creamos el Payment interno SIN orderId (null)
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale, order.getId());
Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR", locale,
order.getId());
pedidoService.markPedidoAsProcesingPayment(order.getId());
@ -199,7 +201,8 @@ public class RedsysController {
}
// Tarjeta o Bizum (Redsys)
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method, order.getId());
FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method,
order.getId());
String html = """
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>

View File

@ -0,0 +1,23 @@
databaseChangeLog:
- changeSet:
id: 0021-add-email-and-is-palets-to-pedidos-direcciones
author: jjo
changes:
- sql:
dbms: mysql
splitStatements: false
stripComments: true
sql: >
ALTER TABLE pedidos_direcciones
ADD COLUMN is_palets TINYINT(1) NOT NULL DEFAULT 0 AFTER identificacion_fiscal,
ADD COLUMN email VARCHAR(255) NULL AFTER is_ejemplar_prueba;
rollback:
- sql:
dbms: mysql
splitStatements: false
stripComments: true
sql: >
ALTER TABLE pedidos_direcciones
DROP COLUMN is_palets,
DROP COLUMN email;

View File

@ -39,3 +39,5 @@ databaseChangeLog:
file: db/changelog/changesets/0019-add-estados-pago-to-pedidos-lineas.yml
- include:
file: db/changelog/changesets/0020-add-estados-pago-to-pedidos-lineas-2.yml
- include:
file: db/changelog/changesets/0021-add-email-and-is-palets-to-pedidos-direcciones.yml

View File

@ -13,6 +13,7 @@ checkout.payment.bizum=Bizum
checkout.payment.bank-transfer=Transferencia bancaria
checkout.error.payment=Error al procesar el pago: el pago ha sido cancelado o rechazado Por favor, inténtelo de nuevo.
checkout.success.payment=Pago realizado con éxito. Gracias por su compra.
checkout.error.select-method=Por favor, seleccione un método de pago.
checkout.make-payment=Realizar el pago
checkout.authorization-required=Certifico que tengo los derechos para imprimir los archivos incluidos en mi pedido y me hago responsable en caso de reclamación de los mismos

View File

@ -21,6 +21,7 @@ $(() => {
$(this).find('.direccion-id').attr('name', 'direcciones[' + i + '].id');
$(this).find('.direccion-cp').attr('name', 'direcciones[' + i + '].cp');
$(this).find('.direccion-pais-code3').attr('name', 'direcciones[' + i + '].paisCode3');
$(this).find('.is-palets').attr('name', 'direcciones[' + i + '].isPalets');
if ($(this).find('.presupuesto-id').length > 0 && $(this).find('.presupuesto-id').val() !== null
&& $(this).find('.presupuesto-id').val() !== "")
$(this).find('.presupuesto-id').attr('name', 'direcciones[' + i + '].presupuestoId');

View File

@ -5,42 +5,93 @@ $(() => {
window.location.href = url;
});
$(document).on('click', '.btn-pay', function () {
$(document).on('click', '.btn-pay', async function () {
const pedidoId = parseInt($(this).data('id'));
const amount = parseInt($(this).data('amount'));
const result = await swalMetodoPago();
if (!result.isConfirmed) return;
const method = result.value;
// crear y enviar un form normal (NO ajax)
const form = document.createElement('form');
form.method = 'POST';
form.action = '/pagos/redsys/reintentar';
// CSRF (Spring Security)
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
if (window.$ && csrfToken && csrfHeader) {
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader(csrfHeader, csrfToken);
}
const csrfParam = document.querySelector('meta[name="_csrf_parameter"]')?.getAttribute('content') || '_csrf';
const add = (name, value) => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = String(value);
form.appendChild(input);
};
add('amountCents', amount);
add('orderId', pedidoId);
add('method', method);
if (csrfToken) add(csrfParam, csrfToken);
document.body.appendChild(form);
form.submit();
});
}
let pedidoId = $(this).data('id');
let amount = $(this).data('amount');
$.ajax({
url: `/pagos/redsys/reintentar`,
method: 'POST',
data: {
amountCents: amount,
orderId: pedidoId
function swalMetodoPago() {
return Swal.fire({
title: window.languageBundle['checkout.payment'] || 'Método de pago',
width: '32rem',
html: `
<div style="width: 100%;" class="g-3 text-start">
<div class="form-check card-radio">
<input id="swalPaymentCard" name="paymentMethod" type="radio" class="form-check-input" value="card" checked>
<label class="form-check-label" for="swalPaymentCard">
${window.languageBundle['checkout.payment.card'] || 'Tarjeta'}
</label>
</div>
<div class="form-check card-radio">
<input id="swalPaymentBizum" name="paymentMethod" type="radio" class="form-check-input" value="bizum">
<label class="form-check-label" for="swalPaymentBizum">
${window.languageBundle['checkout.payment.bizum'] || 'Bizum'}
</label>
</div>
<div class="form-check card-radio">
<input id="swalPaymentTransfer" name="paymentMethod" type="radio" class="form-check-input" value="bank-transfer">
<label class="form-check-label" for="swalPaymentTransfer">
${window.languageBundle['checkout.payment.bank-transfer'] || 'Transferencia bancaria'}
</label>
</div>
</div>
`,
focusConfirm: false,
showCancelButton: true,
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-secondary me-2',
cancelButton: 'btn btn-light'
},
success: function (response) {
if (response && response.formHtml) {
$('body').append(response.formHtml);
$('#redsys-payment-form').submit();
} else {
alert('Error al procesar el pago. Por favor, inténtelo de nuevo.');
confirmButtonText: window.languageBundle['app.aceptar'] || 'Aceptar',
cancelButtonText: window.languageBundle['app.cancelar'] || 'Cancelar',
preConfirm: () => {
const selected = document.querySelector('input[name="paymentMethod"]:checked');
if (!selected) {
Swal.showValidationMessage(
window.languageBundle['checkout.error.select-method'] || 'Selecciona un método de pago'
);
return false;
}
},
error: function () {
alert('Error al procesar el pago. Por favor, inténtelo de nuevo.');
return selected.value;
}
});
});
}
})

View File

@ -7,7 +7,7 @@
<input type="hidden" class="direccion-cp" th:value="${direccion.cp}" />
<input type="hidden" class="direccion-pais-code3" th:value="${direccion.pais.code3}" />
<input type="hidden" class="item-tirada" th:value="${unidades != null ? unidades : ''}" />
<input type="hidden" class="is-palets" th:value="${isPalets != null ? isPalets : ''}" />
<input type="hidden" class="is-palets" th:value="${isPalets != null and (isPalets==1 or isPalets==true)? 1 : 0}" />
<div class="row g-3 align-items-start flex-nowrap">
<div class="col">