añadidas las direcciones de pedido

This commit is contained in:
2025-11-16 21:56:03 +01:00
parent 84a822db22
commit d19cd1923c
19 changed files with 1747 additions and 709 deletions

File diff suppressed because it is too large Load Diff

View File

@ -446,12 +446,13 @@ public class CartService {
} }
@Transactional @Transactional
public Long crearPedido(Long cartId, Locale locale) { public Long crearPedido(Long cartId, Long dirFactId, Locale locale) {
Cart cart = this.getCartById(cartId); Cart cart = this.getCartById(cartId);
List<CartItem> items = cart.getItems(); List<CartItem> items = cart.getItems();
List<Map<String, Object>> presupuestoRequests = new ArrayList<>(); List<Map<String, Object>> presupuestoRequests = new ArrayList<>();
Map<String, Object> presupuestoDireccionesRequest = new HashMap<>();
List<Long> presupuestoIds = new ArrayList<>(); List<Long> presupuestoIds = new ArrayList<>();
for (Integer i = 0; i < items.size(); i++) { for (Integer i = 0; i < items.size(); i++) {
@ -479,6 +480,8 @@ public class CartService {
data_to_send.put("direcciones", direcciones_presupuesto.get("direcciones")); data_to_send.put("direcciones", direcciones_presupuesto.get("direcciones"));
data_to_send.put("direccionesFP1", direcciones_presupuesto.get("direccionesFP1")); 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); Map<String, Object> result = skApiClient.savePresupuesto(data_to_send);
if (result.containsKey("error")) { if (result.containsKey("error")) {
@ -505,7 +508,7 @@ public class CartService {
p.setProveedorRef2(presId); p.setProveedorRef2(presId);
p.setEstado(Presupuesto.Estado.aceptado); p.setEstado(Presupuesto.Estado.aceptado);
presupuestoRepo.save(p); presupuestoRepo.save(p);
presupuestoIds.add(p.getId()); presupuestoIds.add(p.getId());
presupuestoRequests.add(dataMap); presupuestoRequests.add(dataMap);
@ -526,8 +529,14 @@ public class CartService {
if (pedidoId == null) { if (pedidoId == null) {
throw new IllegalStateException("No se pudo crear el pedido en SK."); throw new IllegalStateException("No se pudo crear el pedido en SK.");
} }
Pedido pedidoInterno = pedidoService.crearPedido(presupuestoIds, this.getCartSummaryRaw(cart, locale), Pedido pedidoInterno = pedidoService.crearPedido(
"Safekat", String.valueOf(pedidoId), cart.getUserId()); presupuestoIds,
presupuestoDireccionesRequest,
dirFactId,
this.getCartSummaryRaw(cart, locale),
"Safekat",
String.valueOf(pedidoId),
cart.getUserId());
return pedidoInterno.getId(); return pedidoInterno.getId();
} }
} }

View File

@ -16,6 +16,8 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.common.Utils; import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.datatables.DataTable; import com.imprimelibros.erp.datatables.DataTable;
import com.imprimelibros.erp.datatables.DataTablesParser; import com.imprimelibros.erp.datatables.DataTablesParser;
@ -98,7 +100,7 @@ public class PaymentController {
Specification<PaymentTransaction> base = Specification.allOf( Specification<PaymentTransaction> base = Specification.allOf(
(root, query, cb) -> cb.equal(root.get("status"), PaymentTransactionStatus.succeeded)); (root, query, cb) -> cb.equal(root.get("status"), PaymentTransactionStatus.succeeded));
base = base.and((root, query, cb) -> cb.equal(root.get("type"), PaymentTransactionType.CAPTURE)); base = base.and((root, query, cb) -> cb.equal(root.get("type"), PaymentTransactionType.CAPTURE));
base = base.and((root, query, cb) -> cb.notEqual(root.join("payment").get("gateway"), "bank_transfer"));
String clientSearch = dt.getColumnSearch("client"); String clientSearch = dt.getColumnSearch("client");
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification // 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
@ -229,10 +231,23 @@ public class PaymentController {
}) })
.add("transfer_id", pago -> { .add("transfer_id", pago -> {
if (pago.getPayment() != null) { if (pago.getPayment() != null) {
return "TRANSF-" + pago.getPayment().getOrderId(); String responsePayload = pago.getResponsePayload();
} else { Long cartId = null;
return ""; 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;
}
} }
return "";
}) })
.add("order_id", pago -> { .add("order_id", pago -> {
if (pago.getStatus() != PaymentTransactionStatus.pending) { if (pago.getStatus() != PaymentTransactionStatus.pending) {

View File

@ -47,7 +47,7 @@ public class PaymentService {
* oficial (ApiMacSha256). * oficial (ApiMacSha256).
*/ */
@Transactional @Transactional
public FormPayload createRedsysPayment(Long cartId, long amountCents, String currency, String method) public FormPayload createRedsysPayment(Long cartId, Long dirFactId, Long amountCents, String currency, String method)
throws Exception { throws Exception {
Payment p = new Payment(); Payment p = new Payment();
p.setOrderId(null); p.setOrderId(null);
@ -73,7 +73,7 @@ public class PaymentService {
payRepo.save(p); payRepo.save(p);
RedsysService.PaymentRequest req = new RedsysService.PaymentRequest(dsOrder, amountCents, RedsysService.PaymentRequest req = new RedsysService.PaymentRequest(dsOrder, amountCents,
"Compra en Imprimelibros", cartId); "Compra en Imprimelibros", cartId, dirFactId);
if ("bizum".equalsIgnoreCase(method)) { if ("bizum".equalsIgnoreCase(method)) {
return redsysService.buildRedirectFormBizum(req); return redsysService.buildRedirectFormBizum(req);
@ -213,7 +213,10 @@ public class PaymentService {
} }
if (authorized) { if (authorized) {
processOrder(notif.cartId, locale); Long orderId = processOrder(notif.cartId, notif.dirFactId, locale);
if (orderId != null) {
p.setOrderId(orderId);
}
} }
payRepo.save(p); payRepo.save(p);
@ -308,15 +311,13 @@ public class PaymentService {
} }
@Transactional @Transactional
public Payment createBankTransferPayment(Long cartId, long amountCents, String currency) { public Payment createBankTransferPayment(Long cartId, Long dirFactId, long amountCents, String currency) {
Payment p = new Payment(); Payment p = new Payment();
p.setOrderId(null); p.setOrderId(null);
Cart cart = this.cartService.findById(cartId); Cart cart = this.cartService.findById(cartId);
if (cart != null && cart.getUserId() != null) { if (cart != null && cart.getUserId() != null) {
p.setUserId(cart.getUserId()); p.setUserId(cart.getUserId());
// En el orderId de la transferencia pendiente guardamos el ID del carrito
p.setOrderId(cartId);
// Se bloquea el carrito para evitar modificaciones mientras se procesa el pago // Se bloquea el carrito para evitar modificaciones mientras se procesa el pago
this.cartService.lockCartById(cartId); this.cartService.lockCartById(cartId);
} }
@ -334,6 +335,18 @@ public class PaymentService {
tx.setStatus(PaymentTransactionStatus.pending); tx.setStatus(PaymentTransactionStatus.pending);
tx.setAmountCents(amountCents); tx.setAmountCents(amountCents);
tx.setCurrency(currency); tx.setCurrency(currency);
String payload = "";
if (cartId != null) {
payload = "{\"cartId\":" + cartId + "}";
}
if (dirFactId != null) {
if (!payload.isEmpty()) {
payload = payload.substring(0, payload.length() - 1) + ",\"dirFactId\":" + dirFactId + "}";
} else {
payload = "{\"dirFactId\":" + dirFactId + "}";
}
}
tx.setResponsePayload(payload);
// tx.setProcessedAt(null); // la dejas nula hasta que se confirme // tx.setProcessedAt(null); // la dejas nula hasta que se confirme
txRepo.save(tx); txRepo.save(tx);
@ -374,12 +387,33 @@ public class PaymentService {
p.setAmountCapturedCents(p.getAmountTotalCents()); p.setAmountCapturedCents(p.getAmountTotalCents());
p.setCapturedAt(LocalDateTime.now()); p.setCapturedAt(LocalDateTime.now());
p.setStatus(PaymentStatus.captured); p.setStatus(PaymentStatus.captured);
payRepo.save(p);
// 4) Procesar el pedido asociado al carrito (si existe) Long cartId = null;
if (p.getOrderId() != null) { Long dirFactId = null;
processOrder(p.getOrderId(), locale); try {
// Intentar extraer cartId del payload de la transacción
if (tx.getResponsePayload() != null && !tx.getResponsePayload().isBlank()) {
ObjectMapper om = new ObjectMapper();
var node = om.readTree(tx.getResponsePayload());
if (node.has("cartId")) {
cartId = node.get("cartId").asLong();
}
if (node.has("dirFactId")) {
dirFactId = node.get("dirFactId").asLong();
}
}
} catch (Exception e) {
// ignorar
} }
// 4) Procesar el pedido asociado al carrito (si existe)
if (cartId != null) {
Long orderId = processOrder(cartId, dirFactId, locale);
if (orderId != null) {
p.setOrderId(orderId);
}
}
payRepo.save(p);
} }
/** /**
@ -481,22 +515,23 @@ public class PaymentService {
* *
*/ */
@Transactional @Transactional
private Boolean processOrder(Long cartId, Locale locale) { private Long processOrder(Long cartId, Long dirFactId, Locale locale) {
Cart cart = this.cartService.findById(cartId); Cart cart = this.cartService.findById(cartId);
if (cart != null) { if (cart != null) {
// Bloqueamos el carrito // Bloqueamos el carrito
this.cartService.lockCartById(cart.getId()); this.cartService.lockCartById(cart.getId());
// Creamos el pedido // Creamos el pedido
Long orderId = this.cartService.crearPedido(cart.getId(), locale); Long orderId = this.cartService.crearPedido(cart.getId(), dirFactId, locale);
if (orderId == null) { if (orderId == null) {
return false; return null;
} else { } else {
// envio de correo de confirmacion de pedido podria ir aqui // envio de correo de confirmacion de pedido podria ir aqui
return orderId;
} }
} }
return true; return null;
} }
} }

View File

@ -0,0 +1,211 @@
package com.imprimelibros.erp.pedidos;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import com.imprimelibros.erp.direcciones.Direccion.TipoIdentificacionFiscal;
import java.time.LocalDateTime;
@Entity
@Table(name = "pedidos_direcciones")
public class PedidoDireccion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// FK a pedidos_lineas.id (nullable, on delete set null)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pedido_linea_id")
private PedidoLinea pedidoLinea;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pedido_id")
private Pedido pedido;
@Column(name = "unidades")
private Integer unidades;
@Column(name = "is_facturacion", nullable = false)
private boolean facturacion = false;
@Column(name = "is_ejemplar_prueba", nullable = false)
private boolean ejemplarPrueba = false;
@Column(name = "att", nullable = false, length = 150)
private String att;
@Column(name = "direccion", nullable = false, length = 255)
private String direccion;
@Column(name = "cp", nullable = false)
private Integer cp;
@Column(name = "ciudad", nullable = false, length = 100)
private String ciudad;
@Column(name = "provincia", nullable = false, length = 100)
private String provincia;
@Column(name = "pais_code3", nullable = false, length = 3)
private String paisCode3 = "esp";
@Column(name = "telefono", nullable = false, length = 30)
private String telefono;
@Column(name = "instrucciones", length = 255)
private String instrucciones;
@Column(name = "razon_social", length = 150)
private String razonSocial;
@Enumerated(EnumType.STRING)
@Column(name = "tipo_identificacion_fiscal", nullable = false, length = 20)
private TipoIdentificacionFiscal tipoIdentificacionFiscal = TipoIdentificacionFiscal.DNI;
@Column(name = "identificacion_fiscal", length = 50)
private String identificacionFiscal;
@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
// ===== GETTERS & SETTERS =====
public Long getId() {
return id;
}
public PedidoLinea getPedidoLinea() {
return pedidoLinea;
}
public void setPedidoLinea(PedidoLinea pedidoLinea) {
this.pedidoLinea = pedidoLinea;
}
public Pedido getPedido() {
return pedido;
}
public void setPedido(Pedido pedido) {
this.pedido = pedido;
}
public Integer getUnidades() {
return unidades;
}
public void setUnidades(Integer unidades) {
this.unidades = unidades;
}
public boolean isFacturacion() {
return facturacion;
}
public void setFacturacion(boolean facturacion) {
this.facturacion = facturacion;
}
public boolean isEjemplarPrueba() {
return ejemplarPrueba;
}
public void setEjemplarPrueba(boolean ejemplarPrueba) {
this.ejemplarPrueba = ejemplarPrueba;
}
public String getAtt() {
return att;
}
public void setAtt(String att) {
this.att = att;
}
public String getDireccion() {
return direccion;
}
public void setDireccion(String direccion) {
this.direccion = direccion;
}
public Integer getCp() {
return cp;
}
public void setCp(Integer cp) {
this.cp = cp;
}
public String getCiudad() {
return ciudad;
}
public void setCiudad(String ciudad) {
this.ciudad = ciudad;
}
public String getProvincia() {
return provincia;
}
public void setProvincia(String provincia) {
this.provincia = provincia;
}
public String getPaisCode3() {
return paisCode3;
}
public void setPaisCode3(String paisCode3) {
this.paisCode3 = paisCode3;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
public String getInstrucciones() {
return instrucciones;
}
public void setInstrucciones(String instrucciones) {
this.instrucciones = instrucciones;
}
public String getRazonSocial() {
return razonSocial;
}
public void setRazonSocial(String razonSocial) {
this.razonSocial = razonSocial;
}
public TipoIdentificacionFiscal getTipoIdentificacionFiscal() {
return tipoIdentificacionFiscal;
}
public void setTipoIdentificacionFiscal(TipoIdentificacionFiscal tipoIdentificacionFiscal) {
this.tipoIdentificacionFiscal = tipoIdentificacionFiscal;
}
public String getIdentificacionFiscal() {
return identificacionFiscal;
}
public void setIdentificacionFiscal(String identificacionFiscal) {
this.identificacionFiscal = identificacionFiscal;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
}

View File

@ -0,0 +1,15 @@
package com.imprimelibros.erp.pedidos;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion, Long> {
// Todas las direcciones de una línea de pedido
List<PedidoDireccion> findByPedidoLinea_Id(Long pedidoLineaId);
// Si en tu código sueles trabajar con el objeto:
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
}

View File

@ -9,6 +9,24 @@ import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
@Table(name = "pedidos_lineas") @Table(name = "pedidos_lineas")
public class PedidoLinea { public class PedidoLinea {
public enum Estado {
aprobado("pedido.estado.aprobado"),
maquetacion("pedido.estado.maquetacion"),
haciendo_ferro("pedido.estado.haciendo_ferro"),
produccion("pedido.estado.produccion"),
cancelado("pedido.estado.cancelado");
private final String messageKey;
Estado(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@ -21,6 +39,13 @@ public class PedidoLinea {
@JoinColumn(name = "presupuesto_id", nullable = false) @JoinColumn(name = "presupuesto_id", nullable = false)
private Presupuesto presupuesto; private Presupuesto presupuesto;
@Enumerated(EnumType.STRING)
@Column(name = "estado", nullable = false)
private Estado estado = Estado.aprobado;
@Column(name = "estado_manual", nullable = false)
private Boolean estadoManual;
@Column(name = "created_at") @Column(name = "created_at")
private LocalDateTime createdAt; private LocalDateTime createdAt;
@ -53,6 +78,22 @@ public class PedidoLinea {
this.presupuesto = presupuesto; this.presupuesto = presupuesto;
} }
public Estado getEstado() {
return estado;
}
public void setEstado(Estado estado) {
this.estado = estado;
}
public Boolean getEstadoManual() {
return estadoManual;
}
public void setEstadoManual(Boolean estadoManual) {
this.estadoManual = estadoManual;
}
public LocalDateTime getCreatedAt() { public LocalDateTime getCreatedAt() {
return createdAt; return createdAt;
} }

View File

@ -1,14 +1,17 @@
package com.imprimelibros.erp.pedidos; package com.imprimelibros.erp.pedidos;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import com.imprimelibros.erp.direcciones.Direccion;
import com.imprimelibros.erp.presupuesto.PresupuestoRepository; import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.direcciones.DireccionService;
@Service @Service
public class PedidoService { public class PedidoService {
@ -16,12 +19,17 @@ public class PedidoService {
private final PedidoRepository pedidoRepository; private final PedidoRepository pedidoRepository;
private final PedidoLineaRepository pedidoLineaRepository; private final PedidoLineaRepository pedidoLineaRepository;
private final PresupuestoRepository presupuestoRepository; private final PresupuestoRepository presupuestoRepository;
private final PedidoDireccionRepository pedidoDireccionRepository;
private final DireccionService direccionService;
public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository, public PedidoService(PedidoRepository pedidoRepository, PedidoLineaRepository pedidoLineaRepository,
PresupuestoRepository presupuestoRepository) { PresupuestoRepository presupuestoRepository, PedidoDireccionRepository pedidoDireccionRepository,
DireccionService direccionService) {
this.pedidoRepository = pedidoRepository; this.pedidoRepository = pedidoRepository;
this.pedidoLineaRepository = pedidoLineaRepository; this.pedidoLineaRepository = pedidoLineaRepository;
this.presupuestoRepository = presupuestoRepository; this.presupuestoRepository = presupuestoRepository;
this.pedidoDireccionRepository = pedidoDireccionRepository;
this.direccionService = direccionService;
} }
public int getDescuentoFidelizacion() { public int getDescuentoFidelizacion() {
@ -52,7 +60,10 @@ public class PedidoService {
* - usuario que crea el pedido * - usuario que crea el pedido
*/ */
@Transactional @Transactional
public Pedido crearPedido(List<Long> presupuestoIds, public Pedido crearPedido(
List<Long> presupuestoIds,
Map<String, Object> presupuestoDirecciones,
Long direccionFacturacionId,
Map<String, Object> cartSummaryRaw, Map<String, Object> cartSummaryRaw,
String proveedor, String proveedor,
String proveedorRef, String proveedorRef,
@ -91,11 +102,124 @@ public class PedidoService {
linea.setPresupuesto(presupuesto); linea.setPresupuesto(presupuesto);
linea.setCreatedBy(userId); linea.setCreatedBy(userId);
linea.setCreatedAt(LocalDateTime.now()); linea.setCreatedAt(LocalDateTime.now());
linea.setEstado(PedidoLinea.Estado.aprobado);
linea.setEstadoManual(false);
pedidoLineaRepository.save(linea); 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 saved; return saved;
} }
@Transactional
private void saveDireccionesPedidoLinea(
Map<String, Map<String, Object>> direcciones,
Pedido pedido,
PedidoLinea linea, Long direccionFacturacionId) {
// direccion prueba
if (direcciones.containsKey("direccionesFP1")) {
try {
Map<String, Object> fp1 = (Map<String, Object>) direcciones.get("direccionesFP1");
@SuppressWarnings("unchecked")
PedidoDireccion direccion = saveDireccion(
(HashMap<String, Object>) fp1.get("direccion"),
pedido,
linea, true,
false);
pedidoDireccionRepository.save(direccion);
} catch (Exception e) {
// Viene vacio
}
}
if (direcciones.containsKey("direcciones")) {
List<?> dirs = (List<?>) direcciones.get("direcciones");
for (Object dir : dirs) {
@SuppressWarnings("unchecked")
HashMap<String, Object> direccionEnvio = (HashMap<String, Object>) ((HashMap<String, Object>) dir)
.get("direccion");
if (direccionEnvio.get("cantidad") != null && (Integer) direccionEnvio.get("cantidad") == 4
&& direccionEnvio.get("att").toString().contains("Depósito Legal")) {
continue; // Saltar la dirección de depósito legal
}
@SuppressWarnings("unchecked")
PedidoDireccion direccion = saveDireccion(
(HashMap<String, Object>) ((HashMap<String, Object>) dir).get("direccion"),
pedido,
linea, false,
false);
pedidoDireccionRepository.save(direccion);
}
}
if (direccionFacturacionId != null) {
Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null);
if (dirFact != null) {
HashMap<String, Object> dirFactMap = new HashMap<>();
dirFactMap.put("att", dirFact.getAtt());
dirFactMap.put("direccion", dirFact.getDireccion());
dirFactMap.put("cp", dirFact.getCp());
dirFactMap.put("municipio", dirFact.getCiudad());
dirFactMap.put("provincia", dirFact.getProvincia());
dirFactMap.put("pais_code3", dirFact.getPaisCode3());
dirFactMap.put("telefono", dirFact.getTelefono());
dirFactMap.put("instrucciones", dirFact.getInstrucciones());
dirFactMap.put("razon_social", dirFact.getRazonSocial());
dirFactMap.put("tipo_identificacion_fiscal", dirFact.getTipoIdentificacionFiscal().name());
dirFactMap.put("identificacion_fiscal", dirFact.getIdentificacionFiscal());
PedidoDireccion direccion = saveDireccion(
dirFactMap,
pedido,
linea, false,
true);
pedidoDireccionRepository.save(direccion);
}
}
}
private PedidoDireccion saveDireccion(HashMap<String, Object> dir, Pedido pedido, PedidoLinea linea,
Boolean isEjemplarPrueba,
Boolean isFacturacion) {
PedidoDireccion direccion = new PedidoDireccion();
direccion.setPedidoLinea(isFacturacion ? null : linea);
if (isFacturacion) {
direccion.setUnidades(null);
direccion.setFacturacion(true);
direccion.setPedido(pedido);
} else {
if (isEjemplarPrueba) {
direccion.setUnidades(1);
direccion.setEjemplarPrueba(true);
} else {
direccion.setUnidades((Integer) dir.getOrDefault("cantidad", 1));
direccion.setEjemplarPrueba(false);
}
}
direccion.setFacturacion(false);
direccion.setAtt((String) dir.getOrDefault("att", ""));
direccion.setDireccion((String) dir.getOrDefault("direccion", ""));
direccion.setCp((Integer) dir.getOrDefault("cp", 0));
direccion.setCiudad((String) dir.getOrDefault("municipio", ""));
direccion.setProvincia((String) dir.getOrDefault("provincia", ""));
direccion.setPaisCode3((String) dir.getOrDefault("pais_code3", "esp"));
direccion.setTelefono((String) dir.getOrDefault("telefono", ""));
direccion.setInstrucciones((String) dir.getOrDefault("instrucciones", ""));
direccion.setRazonSocial((String) dir.getOrDefault("razon_social", ""));
direccion.setTipoIdentificacionFiscal(Direccion.TipoIdentificacionFiscal
.valueOf((String) dir.getOrDefault("tipo_identificacion_fiscal",
Direccion.TipoIdentificacionFiscal.DNI.name())));
direccion.setIdentificacionFiscal((String) dir.getOrDefault("identificacion_fiscal", ""));
return direccion;
}
} }

View File

@ -387,6 +387,9 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
@Column(name = "proveedor_ref2") @Column(name = "proveedor_ref2")
private Long proveedorRef2; private Long proveedorRef2;
@Column(name = "is_reimpresion", nullable = false)
private Boolean isReimpresion;
// ====== MÉTODOS AUX ====== // ====== MÉTODOS AUX ======
public String resumenPresupuesto() { public String resumenPresupuesto() {
@ -965,6 +968,14 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.proveedorRef2 = proveedorRef2; this.proveedorRef2 = proveedorRef2;
} }
public Boolean getIsReimpresion() {
return isReimpresion;
}
public void setIsReimpresion(Boolean isReimpresion) {
this.isReimpresion = isReimpresion;
}
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }

View File

@ -50,13 +50,14 @@ public class RedsysController {
@ResponseBody @ResponseBody
public ResponseEntity<byte[]> crearPago(@RequestParam("amountCents") Long amountCents, public ResponseEntity<byte[]> crearPago(@RequestParam("amountCents") Long amountCents,
@RequestParam("method") String method, @RequestParam("cartId") Long cartId, @RequestParam("method") String method, @RequestParam("cartId") Long cartId,
@RequestParam(value = "dirFactId", required = false) Long dirFactId,
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, Locale locale) HttpServletResponse response, Locale locale)
throws Exception { throws Exception {
if ("bank-transfer".equalsIgnoreCase(method)) { if ("bank-transfer".equalsIgnoreCase(method)) {
// 1) Creamos el Payment interno SIN orderId (null) // 1) Creamos el Payment interno SIN orderId (null)
Payment p = paymentService.createBankTransferPayment(cartId, amountCents, "EUR"); Payment p = paymentService.createBankTransferPayment(cartId, dirFactId, amountCents, "EUR");
// 1⃣ Crear la "aplicación" web de Thymeleaf (Jakarta) // 1⃣ Crear la "aplicación" web de Thymeleaf (Jakarta)
JakartaServletWebApplication app = JakartaServletWebApplication.buildApplication(servletContext); JakartaServletWebApplication app = JakartaServletWebApplication.buildApplication(servletContext);
@ -88,7 +89,7 @@ public class RedsysController {
} }
// Tarjeta o Bizum (Redsys) // Tarjeta o Bizum (Redsys)
FormPayload form = paymentService.createRedsysPayment(cartId, amountCents, "EUR", method); FormPayload form = paymentService.createRedsysPayment(cartId, dirFactId, amountCents, "EUR", method);
String html = """ String html = """
<html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head> <html><head><meta charset="utf-8"><title>Redirigiendo a Redsys…</title></head>

View File

@ -49,7 +49,7 @@ public class RedsysService {
// ---------- RECORDS ---------- // ---------- RECORDS ----------
// Pedido a Redsys // Pedido a Redsys
public record PaymentRequest(String order, long amountCents, String description, Long cartId) { public record PaymentRequest(String order, long amountCents, String description, Long cartId, Long dirFactId) {
} }
// Payload para el formulario // Payload para el formulario
@ -84,7 +84,10 @@ public class RedsysService {
// Si tu PaymentRequest no lo lleva todavía, puedes pasarlo en description o // Si tu PaymentRequest no lo lleva todavía, puedes pasarlo en description o
// crear otro campo. // crear otro campo.
JSONObject ctx = new JSONObject(); JSONObject ctx = new JSONObject();
ctx.put("cartId", req.cartId()); // o req.cartId() si decides añadirlo al record ctx.put("cartId", req.cartId());
if (req.dirFactId() != null) {
ctx.put("dirFactId", req.dirFactId());
}
api.setParameter("DS_MERCHANT_MERCHANTDATA", ctx.toString()); api.setParameter("DS_MERCHANT_MERCHANTDATA", ctx.toString());
if (req.description() != null && !req.description().isBlank()) { if (req.description() != null && !req.description().isBlank()) {
@ -195,6 +198,7 @@ public class RedsysService {
public final long amountCents; public final long amountCents;
public final String currency; public final String currency;
public final Long cartId; public final Long cartId;
public final Long dirFactId;
public final String processedPayMethod; // Ds_ProcessedPayMethod public final String processedPayMethod; // Ds_ProcessedPayMethod
public final String bizumIdOper; // Ds_Bizum_IdOper public final String bizumIdOper; // Ds_Bizum_IdOper
public final String authorisationCode; // Ds_AuthorisationCode public final String authorisationCode; // Ds_AuthorisationCode
@ -206,6 +210,7 @@ public class RedsysService {
this.currency = str(raw.get("Ds_Currency")); this.currency = str(raw.get("Ds_Currency"));
this.amountCents = parseLongSafe(raw.get("Ds_Amount")); this.amountCents = parseLongSafe(raw.get("Ds_Amount"));
this.cartId = extractCartId(raw.get("Ds_MerchantData")); this.cartId = extractCartId(raw.get("Ds_MerchantData"));
this.dirFactId = extractDirFactId(raw.get("Ds_MerchantData"));
this.processedPayMethod = str(raw.get("Ds_ProcessedPayMethod")); this.processedPayMethod = str(raw.get("Ds_ProcessedPayMethod"));
this.bizumIdOper = str(raw.get("Ds_Bizum_IdOper")); this.bizumIdOper = str(raw.get("Ds_Bizum_IdOper"));
this.authorisationCode = str(raw.get("Ds_AuthorisationCode")); this.authorisationCode = str(raw.get("Ds_AuthorisationCode"));
@ -228,6 +233,24 @@ public class RedsysService {
} }
} }
private static Long extractDirFactId(Object merchantDataObj) {
if (merchantDataObj == null)
return null;
try {
String json = String.valueOf(merchantDataObj);
// 👇 DES-ESCAPAR las comillas HTML que vienen de Redsys
json = json.replace("&#34;", "\"");
org.json.JSONObject ctx = new org.json.JSONObject(json);
long v = ctx.optLong("dirFactId", 0L);
return v != 0L ? v : null;
} catch (Exception e) {
e.printStackTrace(); // te ayudará si vuelve a fallar
return null;
}
}
public boolean authorized() { public boolean authorized() {
try { try {
int r = Integer.parseInt(response); int r = Integer.parseInt(response);

View File

@ -0,0 +1,151 @@
databaseChangeLog:
- changeSet:
id: 0014-create-pedidos-direcciones
author: jjo
changes:
- createTable:
tableName: pedidos_direcciones
columns:
- column:
name: id
type: BIGINT AUTO_INCREMENT
constraints:
primaryKey: true
nullable: false
- column:
name: pedido_linea_id
type: BIGINT
constraints:
nullable: true
- column:
name: pedido_id
type: BIGINT
constraints:
nullable: true
- column:
name: unidades
type: MEDIUMINT UNSIGNED
constraints:
nullable: true
- column:
name: is_facturacion
type: TINYINT(1)
defaultValueNumeric: 0
constraints:
nullable: false
- column:
name: is_ejemplar_prueba
type: TINYINT(1)
defaultValueNumeric: 0
constraints:
nullable: false
- column:
name: att
type: VARCHAR(150)
constraints:
nullable: false
- column:
name: direccion
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: cp
type: MEDIUMINT UNSIGNED
constraints:
nullable: false
- column:
name: ciudad
type: VARCHAR(100)
constraints:
nullable: false
- column:
name: provincia
type: VARCHAR(100)
constraints:
nullable: false
- column:
name: pais_code3
type: CHAR(3)
defaultValue: esp
constraints:
nullable: false
- column:
name: telefono
type: VARCHAR(30)
constraints:
nullable: false
- column:
name: instrucciones
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: razon_social
type: VARCHAR(150)
constraints:
nullable: true
- column:
name: tipo_identificacion_fiscal
type: ENUM('DNI','NIE','CIF','Pasaporte','VAT_ID')
defaultValue: DNI
constraints:
nullable: false
- column:
name: identificacion_fiscal
type: VARCHAR(50)
constraints:
nullable: true
- column:
name: created_at
type: TIMESTAMP
defaultValueComputed: CURRENT_TIMESTAMP
constraints:
nullable: false
- addForeignKeyConstraint:
baseTableName: pedidos_direcciones
baseColumnNames: pedido_linea_id
referencedTableName: pedidos_lineas
referencedColumnNames: id
constraintName: fk_pedidos_direcciones_pedido_linea
onDelete: SET NULL
onUpdate: CASCADE
- addForeignKeyConstraint:
baseTableName: pedidos_direcciones
baseColumnNames: pedido_id
referencedTableName: pedidos
referencedColumnNames: id
constraintName: fk_pedidos_direcciones_pedidos
onDelete: SET NULL
onUpdate: CASCADE
- createIndex:
tableName: pedidos_direcciones
indexName: idx_pedidos_direcciones_pedido_linea_id
columns:
- column:
name: pedido_linea_id
rollback:
- dropTable:
tableName: pedidos_direcciones
cascadeConstraints: true

View File

@ -0,0 +1,48 @@
databaseChangeLog:
- changeSet:
id: 0015-alter-pedidos-lineas-and-presupuesto-estados
author: jjo
changes:
# Añadir columnas a pedidos_lineas
- addColumn:
tableName: pedidos_lineas
columns:
- column:
name: estado
type: "ENUM('aprobado','maquetación','haciendo_ferro','producción','terminado','cancelado')"
defaultValue: aprobado
constraints:
nullable: false
afterColumn: presupuesto_id
- column:
name: estado_manual
type: TINYINT(1)
defaultValueNumeric: 0
constraints:
nullable: false
afterColumn: estado
# Añadir columna a presupuesto
- addColumn:
tableName: presupuesto
columns:
- column:
name: is_reimpresion
type: TINYINT(1)
defaultValueNumeric: 0
constraints:
nullable: false
rollback:
- dropColumn:
tableName: pedidos_lineas
columnName: estado
- dropColumn:
tableName: pedidos_lineas
columnName: estado_manual
- dropColumn:
tableName: presupuesto
columnName: is_reimpresion

View File

@ -24,4 +24,8 @@ databaseChangeLog:
- include: - include:
file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml
- include: - include:
file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml
- include:
file: db/changelog/changesets/0014-create-pedidos-direcciones.yml
- include:
file: db/changelog/changesets/0015-alter-pedidos-lineas-and-presupuesto-estados.yml

View File

@ -15,4 +15,11 @@ checkout.error.payment=Error al procesar el pago: el pago ha sido cancelado o re
checkout.success.payment=Pago realizado con éxito. Gracias por su compra. checkout.success.payment=Pago realizado con éxito. Gracias por su compra.
checkout.make-payment=Realizar el 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 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
pedido.estado.aprobado=Aprobado
pedido.estado.maquetacion=Maquetación
pedido.estado.haciendo_ferro=Haciendo ferro
pedido.estado.produccion=Producción
pedido.estado.terminado=Terminado
pedido.estado.cancelado=Cancelado

View File

@ -140,6 +140,7 @@ $(() => {
let uri = `/checkout/get-address/${direccionId}`; let uri = `/checkout/get-address/${direccionId}`;
const response = await fetch(uri); const response = await fetch(uri);
if (response.ok) { if (response.ok) {
$('#dirFactId').val(direccionId);
const html = await response.text(); const html = await response.text();
$('#direccion-div').append(html); $('#direccion-div').append(html);
$('#addBillingAddressBtn').addClass('d-none'); $('#addBillingAddressBtn').addClass('d-none');

View File

@ -50,6 +50,7 @@
<input type="hidden" name="amountCents" th:value="${summary.amountCents}" /> <input type="hidden" name="amountCents" th:value="${summary.amountCents}" />
<input type="hidden" name="method" value="card" /> <input type="hidden" name="method" value="card" />
<input type="hidden" name="cartId" th:value="${summary.cartId}" /> <input type="hidden" name="cartId" th:value="${summary.cartId}" />
<input type="hidden" id="dirFactId" name="dirFactId" value="" />
<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="#{checkout.make-payment}" disabled>Checkout</button> th:text="#{checkout.make-payment}" disabled>Checkout</button>
</form> </form>

View File

@ -18,7 +18,7 @@ public class envioCarroTest {
void addPedido(){ void addPedido(){
Locale locale = Locale.forLanguageTag("es-ES"); Locale locale = Locale.forLanguageTag("es-ES");
cartService.crearPedido(carritoId, locale); cartService.crearPedido(carritoId, null, locale);
} }

View File

@ -17,7 +17,7 @@ public class savePresupuestosTest {
@Test @Test
void testGuardarPresupuesto() { void testGuardarPresupuesto() {
Locale locale = new Locale("es", "ES"); Locale locale = new Locale("es", "ES");
Long resultado = cartService.crearPedido(9L, locale); Long resultado = cartService.crearPedido(9L, null, locale);
System.out.println("📦 Presupuesto guardado:"); System.out.println("📦 Presupuesto guardado:");
System.out.println(resultado); System.out.println(resultado);