arreglados varios temas además del DL (redsys, etc)

This commit is contained in:
2025-11-15 10:59:05 +01:00
parent 6bd36dbe8c
commit 69f27df98b
15 changed files with 720 additions and 3238 deletions

File diff suppressed because it is too large Load Diff

View File

@ -445,7 +445,6 @@ public class CartService {
cartDireccionRepo.deleteByDireccionIdAndCartStatus(direccionId, Cart.Status.ACTIVE); cartDireccionRepo.deleteByDireccionIdAndCartStatus(direccionId, Cart.Status.ACTIVE);
} }
@Transactional @Transactional
public Long crearPedido(Long cartId, Locale locale) { public Long crearPedido(Long cartId, Locale locale) {
@ -540,11 +539,23 @@ public class CartService {
if (cart.getOnlyOneShipment()) { if (cart.getOnlyOneShipment()) {
List<CartDireccion> direcciones = cart.getDirecciones().stream().limit(1).toList(); List<CartDireccion> direcciones = cart.getDirecciones().stream().limit(1).toList();
if (!direcciones.isEmpty()) { if (!direcciones.isEmpty()) {
direccionesPresupuesto.add(direcciones.get(0).toSkMap( if (presupuesto.getServiciosJson() != null
presupuesto.getSelectedTirada(), && presupuesto.getServiciosJson().contains("deposito-legal")) {
presupuesto.getPeso(), direccionesPresupuesto.add(direcciones.get(0).toSkMap(
direcciones.get(0).getIsPalets(), presupuesto.getSelectedTirada()-4,
false)); presupuesto.getPeso(),
direcciones.get(0).getIsPalets(),
false));
direccionesPresupuesto.add(direcciones.get(0).toSkMapDepositoLegal());
}
else {
direccionesPresupuesto.add(direcciones.get(0).toSkMap(
presupuesto.getSelectedTirada(),
presupuesto.getPeso(),
direcciones.get(0).getIsPalets(),
false));
}
if (presupuesto.getServiciosJson() != null if (presupuesto.getServiciosJson() != null
&& presupuesto.getServiciosJson().contains("ejemplar-prueba")) { && presupuesto.getServiciosJson().contains("ejemplar-prueba")) {
direccionesPrueba.add(direcciones.get(0).toSkMap( direccionesPrueba.add(direcciones.get(0).toSkMap(
@ -553,10 +564,7 @@ public class CartService {
false, false,
true)); true));
} }
if (presupuesto.getServiciosJson() != null
&& presupuesto.getServiciosJson().contains("deposito-legal")) {
direccionesPresupuesto.add(direcciones.get(0).toSkMapDepositoLegal());
}
Map<String, Object> direccionesRet = new HashMap<>(); Map<String, Object> direccionesRet = new HashMap<>();
direccionesRet.put("direcciones", direccionesPresupuesto); direccionesRet.put("direcciones", direccionesPresupuesto);
if (!direccionesPrueba.isEmpty()) if (!direccionesPrueba.isEmpty())

View File

@ -2,8 +2,6 @@ package com.imprimelibros.erp.direcciones;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction; import org.hibernate.annotations.SQLRestriction;

View File

@ -32,7 +32,6 @@ import com.imprimelibros.erp.users.UserDao;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@Controller @Controller
@RequestMapping("/pagos") @RequestMapping("/pagos")

View File

@ -273,6 +273,14 @@ public class PaymentService {
throw new IllegalStateException("Error al solicitar la devolución a Redsys", e); throw new IllegalStateException("Error al solicitar la devolución a Redsys", e);
} }
// 🔧 NORMALIZAR ANTES DE GUARDAR
if (gatewayRefundId != null) {
gatewayRefundId = gatewayRefundId.trim();
if (gatewayRefundId.isEmpty() || "000000".equals(gatewayRefundId)) {
gatewayRefundId = null; // → múltiples NULL NO rompen el UNIQUE
}
}
PaymentTransaction tx = new PaymentTransaction(); PaymentTransaction tx = new PaymentTransaction();
tx.setPayment(p); tx.setPayment(p);
tx.setType(PaymentTransactionType.REFUND); tx.setType(PaymentTransactionType.REFUND);

View File

@ -6,9 +6,6 @@ import java.time.LocalDateTime;
@Entity @Entity
@Table( @Table(
name = "payment_transactions", name = "payment_transactions",
uniqueConstraints = {
@UniqueConstraint(name = "uq_tx_gateway_txid", columnNames = {"gateway_transaction_id"})
},
indexes = { indexes = {
@Index(name = "idx_tx_pay", columnList = "payment_id"), @Index(name = "idx_tx_pay", columnList = "payment_id"),
@Index(name = "idx_tx_type_status", columnList = "type,status"), @Index(name = "idx_tx_type_status", columnList = "type,status"),

View File

@ -8,10 +8,11 @@ import com.imprimelibros.erp.payments.model.PaymentTransactionType;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public interface PaymentTransactionRepository extends JpaRepository<PaymentTransaction, Long>, JpaSpecificationExecutor<PaymentTransaction> { public interface PaymentTransactionRepository extends JpaRepository<PaymentTransaction, Long>, JpaSpecificationExecutor<PaymentTransaction> {
Optional<PaymentTransaction> findByGatewayTransactionId(String gatewayTransactionId); List<PaymentTransaction> findByGatewayTransactionId(String gatewayTransactionId);
Optional<PaymentTransaction> findByIdempotencyKey(String idempotencyKey); Optional<PaymentTransaction> findByIdempotencyKey(String idempotencyKey);
Optional<PaymentTransaction> findFirstByPaymentIdAndTypeAndStatusOrderByIdDesc( Optional<PaymentTransaction> findFirstByPaymentIdAndTypeAndStatusOrderByIdDesc(
Long paymentId, Long paymentId,

View File

@ -9,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional;
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.presupuesto.service.PresupuestoService;
@Service @Service
public class PedidoService { public class PedidoService {

View File

@ -189,9 +189,9 @@ public class RedsysController {
try { try {
String idem = "refund-" + paymentId + "-" + amountCents + "-" + UUID.randomUUID(); String idem = "refund-" + paymentId + "-" + amountCents + "-" + UUID.randomUUID();
paymentService.refundViaRedsys(paymentId, amountCents, idem); paymentService.refundViaRedsys(paymentId, amountCents, idem);
return ResponseEntity.ok("{success:true}"); return ResponseEntity.ok("{\"success\":true}");
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.badRequest().body("{success:false, error: '" + e.getMessage() + "'}"); return ResponseEntity.badRequest().body("{\"success\":false, \"error\": \"" + e.getMessage() + "\"}");
} }
} }
} }

View File

@ -321,11 +321,33 @@ public class RedsysService {
Map<String, Object> decoded = decodeMerchantParametersToMap(dsMerchantParametersResp); Map<String, Object> decoded = decodeMerchantParametersToMap(dsMerchantParametersResp);
String dsResponse = String.valueOf(decoded.get("Ds_Response")); String dsResponse = String.valueOf(decoded.get("Ds_Response"));
if (!"0900".equals(dsResponse)) { if (dsResponse == null) {
throw new IllegalStateException("Respuesta Redsys refund sin Ds_Response");
}
int code;
try {
code = Integer.parseInt(dsResponse);
} catch (NumberFormatException e) {
throw new IllegalStateException("Código Ds_Response no numérico en refund: " + dsResponse, e);
}
// ✅ Consideramos OK: 099 (éxito típico) o 900 (0900)
boolean ok = (code >= 0 && code <= 99) || code == 900;
if (!ok) {
throw new IllegalStateException("Devolución rechazada, Ds_Response=" + dsResponse); throw new IllegalStateException("Devolución rechazada, Ds_Response=" + dsResponse);
} }
return String.valueOf(decoded.getOrDefault("Ds_AuthorisationCode", order)); // Devolvemos algún identificador razonable para la transacción de refund
Object authCodeObj = decoded.get("Ds_AuthorisationCode");
String authCode = authCodeObj != null ? String.valueOf(authCodeObj).trim() : null;
if (authCode == null || authCode.isEmpty()) {
// Fallback: usa el Ds_Order original como ID de refund
return order;
}
return authCode;
} }
} }

View File

@ -0,0 +1,31 @@
databaseChangeLog:
- changeSet:
id: 0012--drop-unique-gateway-txid-2
author: jjo
changes:
# 1) Eliminar el índice UNIQUE actual
- dropIndex:
indexName: uq_tx_gateway_txid
tableName: payment_transactions
# 2) Crear un índice normal (no único) sobre gateway_transaction_id
- createIndex:
indexName: idx_tx_gateway_txid
tableName: payment_transactions
columns:
- column:
name: gateway_transaction_id
rollback:
# Rollback: volver al índice UNIQUE como estaba antes
- dropIndex:
indexName: idx_tx_gateway_txid
tableName: payment_transactions
- createIndex:
indexName: uq_tx_gateway_txid
tableName: payment_transactions
unique: true
columns:
- column:
name: gateway_transaction_id

View File

@ -1,28 +0,0 @@
databaseChangeLog:
- changeSet:
id: 0012-drop-unique-tx-gateway
author: JJO
# ✅ Solo ejecuta el changeSet si existe la UNIQUE constraint
preConditions:
- onFail: MARK_RAN
- uniqueConstraintExists:
tableName: payment_transactions
constraintName: idx_payment_tx_gateway_txid
changes:
# 1⃣ Eliminar la UNIQUE constraint si existe
- dropIndex:
tableName: payment_transactions
indexName: idx_payment_tx_gateway_txid
rollback:
# 🔙 1) Eliminar el índice normal creado en este changeSet
- createIndex:
tableName: payment_transactions
indexName: idx_payment_tx_gateway_txid
columns:
- column:
name: gateway_transaction_id

View File

@ -0,0 +1,33 @@
databaseChangeLog:
- changeSet:
id: 0013-drop-unique-refund-gateway-id
author: jjo
changes:
# 1) Eliminar el índice UNIQUE actual sobre gateway_refund_id
- dropIndex:
indexName: uq_refund_gateway_id
tableName: refunds
# 2) Crear un índice normal (no único) sobre gateway_refund_id
- createIndex:
indexName: idx_refund_gateway_id
tableName: refunds
columns:
- column:
name: gateway_refund_id
rollback:
# Rollback: quitar el índice normal
- dropIndex:
indexName: idx_refund_gateway_id
tableName: refunds
# y restaurar el UNIQUE como estaba antes
- createIndex:
indexName: uq_refund_gateway_id
tableName: refunds
unique: true
columns:
- column:
name: gateway_refund_id

View File

@ -20,4 +20,8 @@ databaseChangeLog:
- include: - include:
file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml
- include: - include:
file: db/changelog/changesets/0011-update-pedidos-presupuesto.yml file: db/changelog/changesets/0011-update-pedidos-presupuesto.yml
- include:
file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml
- include:
file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml

View File

@ -24,6 +24,7 @@ $(() => {
orderCellsTop: true, orderCellsTop: true,
pageLength: 50, pageLength: 50,
lengthMenu: [10, 25, 50, 100, 500], lengthMenu: [10, 25, 50, 100, 500],
order: [[5, 'desc']], // Ordena por fecha por defecto
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' }, language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
responsive: true, responsive: true,
dom: 'lBrtip', dom: 'lBrtip',
@ -139,6 +140,7 @@ $(() => {
orderCellsTop: true, orderCellsTop: true,
pageLength: 50, pageLength: 50,
lengthMenu: [10, 25, 50, 100, 500], lengthMenu: [10, 25, 50, 100, 500],
order: [[5, 'desc']],
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' }, language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
responsive: true, responsive: true,
dom: 'lBrtip', dom: 'lBrtip',
@ -161,7 +163,7 @@ $(() => {
url: '/pagos/datatable/transferencias', url: '/pagos/datatable/transferencias',
method: 'GET', method: 'GET',
}, },
order: [[7, 'desc']], // Ordena por fecha por defecto order: [[6, 'desc']], // Ordena por fecha por defecto
columns: [ columns: [
{ data: 'client', name: 'client', orderable: true }, { data: 'client', name: 'client', orderable: true },
{ data: 'transfer_id', name: 'transfer_id', orderable: true }, { data: 'transfer_id', name: 'transfer_id', orderable: true },