Compare commits

..

18 Commits

Author SHA1 Message Date
355c5b6019 trabajando2 2026-02-24 20:57:51 +01:00
e4c1692ef0 añadido lomo 2026-02-24 14:50:27 +01:00
f3d96361a0 corregidos varios problemas 2026-02-24 14:32:47 +01:00
b7453b1138 trabajando 2026-02-24 13:24:39 +01:00
1c5d501e6d Merge branch 'feat/titulos_en_pedidoslist' into 'main'
Feat/titulos en pedidoslist

See merge request jjimenez/erp-imprimelibros!47
2026-02-15 11:54:13 +00:00
87368c434a hecho 2026-02-15 12:53:47 +01:00
a9cdc6d5d6 hecho 2026-02-15 12:53:19 +01:00
1a04d5ace1 Merge branch 'fix/listado_usuarios_infinito' into 'main'
arreglado, no se estaba mandando paginación

See merge request jjimenez/erp-imprimelibros!46
2026-02-15 10:25:48 +00:00
cc8d1b8d44 arreglado, no se estaba mandando paginación 2026-02-15 11:24:50 +01:00
6121562f9b Merge branch 'mod/order_user_select_by_name' into 'main'
hecho

See merge request jjimenez/erp-imprimelibros!45
2026-02-14 10:50:28 +00:00
f73f108c71 hecho 2026-02-14 11:50:06 +01:00
60080eb89e Merge branch 'fix/problema_dir_fact_multipedido' into 'main'
solucionado

See merge request jjimenez/erp-imprimelibros!44
2026-02-14 10:44:24 +00:00
e9f75b85c5 solucionado 2026-02-14 11:44:05 +01:00
ce6f4085a0 Merge branch 'mod/listados_cartItem' into 'main'
se modifican los item de los listados para que se abran en una nueva pestaña....

See merge request jjimenez/erp-imprimelibros!43
2026-02-14 10:19:32 +00:00
97ee53cefd se modifican los item de los listados para que se abran en una nueva pestaña. Se añade un badge al carrito item para editar el presupuesto 2026-02-14 11:19:07 +01:00
014079cf04 Merge branch 'fix/tipo_entrega' into 'main'
quitado tipo de entrega de todos sitios. Trabajando en el iva dependiendo de...

See merge request jjimenez/erp-imprimelibros!42
2026-02-14 10:06:36 +00:00
8acd0bb890 terminado. Ahora se mira la direcicón de facturación para el iva. modificado, el valor en pedido, factura, presupuesto 2026-02-14 11:06:12 +01:00
fffc2b91c1 quitado tipo de entrega de todos sitios. Trabajando en el iva dependiendo de la dirección de facturación 2026-02-13 21:19:25 +01:00
38 changed files with 541 additions and 145 deletions

View File

@ -80,7 +80,7 @@ public class CartController {
else if (direcciones != null && direcciones.containsKey("direcciones")) else if (direcciones != null && direcciones.containsKey("direcciones"))
model.addAttribute("direcciones", direcciones.get("direcciones")); model.addAttribute("direcciones", direcciones.get("direcciones"));
var summary = service.getCartSummary(cart, locale); var summary = service.getCartSummary(cart, locale, true);
model.addAttribute("cartSummary", summary); model.addAttribute("cartSummary", summary);
if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost")) if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost"))
model.addAttribute("errorEnvio", true); model.addAttribute("errorEnvio", true);
@ -165,7 +165,7 @@ public class CartController {
try { try {
service.updateCart(id, updateRequest); service.updateCart(id, updateRequest);
var cartSummary = service.getCartSummary(service.getCartById(id), locale); var cartSummary = service.getCartSummary(service.getCartById(id), locale, true);
model.addAttribute("cartSummary", cartSummary); model.addAttribute("cartSummary", cartSummary);
return "imprimelibros/cart/_cartSummary :: cartSummary(summary=${cartSummary})"; return "imprimelibros/cart/_cartSummary :: cartSummary(summary=${cartSummary})";

View File

@ -163,7 +163,7 @@ public class CartService {
return itemRepo.findByCartId(cart.getId()).size(); return itemRepo.findByCartId(cart.getId()).size();
} }
public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale) { public Map<String, Object> getCartSummaryRaw(Cart cart, Locale locale, Boolean hasTaxes) {
double base = 0.0; double base = 0.0;
double iva4 = 0.0; double iva4 = 0.0;
@ -269,6 +269,11 @@ public class CartService {
} }
} }
if(!hasTaxes) {
iva4 = 0.0;
iva21 = 0.0;
}
double totalBeforeDiscount = base + iva4 + iva21 + shipment; double totalBeforeDiscount = base + iva4 + iva21 + shipment;
int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId()); int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId());
double descuento = totalBeforeDiscount * fidelizacion / 100.0; double descuento = totalBeforeDiscount * fidelizacion / 100.0;
@ -318,8 +323,8 @@ public class CartService {
return 0; return 0;
} }
public Map<String, Object> getCartSummary(Cart cart, Locale locale) { public Map<String, Object> getCartSummary(Cart cart, Locale locale, Boolean hasTaxes) {
Map<String, Object> raw = getCartSummaryRaw(cart, locale); Map<String, Object> raw = getCartSummaryRaw(cart, locale, hasTaxes);
double base = (Double) raw.get("base"); double base = (Double) raw.get("base");
double iva4 = (Double) raw.get("iva4"); double iva4 = (Double) raw.get("iva4");

View File

@ -48,7 +48,7 @@ public class CheckoutController {
"app.cancelar", "app.cancelar",
"app.seleccionar", "app.seleccionar",
"app.yes", "app.yes",
"checkout.billing-address.title", "checkout.billing-address.title",
"checkout.billing-address.new-address", "checkout.billing-address.new-address",
"checkout.billing-address.select-placeholder", "checkout.billing-address.select-placeholder",
"checkout.billing-address.errors.noAddressSelected"); "checkout.billing-address.errors.noAddressSelected");
@ -58,10 +58,28 @@ public class CheckoutController {
Long userId = Utils.currentUserId(principal); Long userId = Utils.currentUserId(principal);
Cart cart = cartService.getOrCreateActiveCart(userId); Cart cart = cartService.getOrCreateActiveCart(userId);
model.addAttribute("summary", cartService.getCartSummary(cart, locale)); model.addAttribute("summary", cartService.getCartSummary(cart, locale, true));
return "imprimelibros/checkout/checkout"; // crea esta vista si quieres (tabla simple) return "imprimelibros/checkout/checkout"; // crea esta vista si quieres (tabla simple)
} }
@GetMapping({ "/get-summary", "/get-summary/{direccionId}/{method}" })
public String getCheckoutSummary(@PathVariable(required = false) Long direccionId,
@PathVariable(required = false) String method, Principal principal, Model model, Locale locale) {
Long userId = Utils.currentUserId(principal);
Cart cart = cartService.getOrCreateActiveCart(userId);
Boolean hasTaxes = true;
if (direccionId != null) {
hasTaxes = direccionService.hasTaxes(direccionId);
}
Map<String, Object> summary = cartService.getCartSummary(cart, locale, hasTaxes);
model.addAttribute("summary", summary);
if (method != null) {
model.addAttribute("method", method);
}
return "imprimelibros/checkout/_summary :: checkoutSummary(summary=${summary})";
}
@GetMapping("/get-address/{id}") @GetMapping("/get-address/{id}")
public String getDireccionCard(@PathVariable Long id, Model model, Locale locale) { public String getDireccionCard(@PathVariable Long id, Model model, Locale locale) {
Direccion dir = direccionService.findById(id) Direccion dir = direccionService.findById(id)

View File

@ -153,4 +153,26 @@ public class DireccionService {
return false; return false;
} }
public Boolean hasTaxes(Long direccionId) {
if(direccionId == null) {
return true; // Si no hay dirección, asumimos que sí tiene impuestos
}
Optional<Direccion> dir = repo.findById(direccionId);
if (dir == null || dir.isEmpty()) {
throw new RuntimeException("Dirección no encontrada");
}
if(dir.get().getPaisCode3().toLowerCase().equals("esp")) {
int provincia = dir.get().getCp() / 1000;
if (provincia == 35 || provincia == 38 ) {
return false; // Canarias (sin IVA)lñ.
}
return true; // España (todas las provincias)
}
else{
// Fuera de España, asumimos que no tiene impuestos (puedes ajustar esto según tus necesidades)
return false;
}
}
} }

View File

@ -0,0 +1,47 @@
package com.imprimelibros.erp.error;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.imprimelibros.erp.common.Utils;
import java.time.ZonedDateTime;
@Controller
public class ErrorPageController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request, Model model) {
Object statusObj = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
Integer statusCode = statusObj != null ? Integer.valueOf(statusObj.toString()) : 500;
HttpStatus status = HttpStatus.resolve(statusCode);
if (status == null) status = HttpStatus.INTERNAL_SERVER_ERROR;
Object message = request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
Object exception = request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
Object path = request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
model.addAttribute("status", status.value());
model.addAttribute("error", status.getReasonPhrase());
model.addAttribute("message", message != null ? message : "");
model.addAttribute("path", path != null ? path : "");
model.addAttribute("timestamp", ZonedDateTime.now());
// Puedes usar esto para cambiar iconos/texto según status
model.addAttribute("is404", status == HttpStatus.NOT_FOUND);
model.addAttribute("is403", status == HttpStatus.FORBIDDEN);
model.addAttribute("is500", status.is5xxServerError());
if(Utils.isCurrentUserAdmin())
// una sola vista para todos los errores
return "imprimelibros/error/error";
else
return "redirect:/"; // redirige a home para usuarios no admin
}
}

View File

@ -8,7 +8,6 @@ import com.imprimelibros.erp.facturacion.dto.FacturaDireccionMapper;
import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto; import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto;
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto; import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto; import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto;
import com.imprimelibros.erp.facturacion.repo.FacturaDireccionRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaLineaRepository; import com.imprimelibros.erp.facturacion.repo.FacturaLineaRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaPagoRepository; import com.imprimelibros.erp.facturacion.repo.FacturaPagoRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaRepository; import com.imprimelibros.erp.facturacion.repo.FacturaRepository;

View File

@ -184,11 +184,14 @@ public class PaymentController {
// Campos ordenables // Campos ordenables
List<String> orderable = List.of( List<String> orderable = List.of(
"transferId", "client",
"transfer_id",
"status", "status",
"amountCents", "amountCents",
"payment.amountRefundedCents", "amountCentsRefund",
"createdAt", "updatedAt"); "payment.orderId",
"createdAt",
"processedAt");
Specification<PaymentTransaction> base = (root, query, cb) -> cb.or( Specification<PaymentTransaction> base = (root, query, cb) -> cb.or(
cb.equal(root.get("status"), PaymentTransactionStatus.pending), cb.equal(root.get("status"), PaymentTransactionStatus.pending),
@ -215,6 +218,26 @@ public class PaymentController {
return DataTable return DataTable
.of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable) .of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable)
.orderable(orderable) .orderable(orderable)
.orderable("client", (root, query, cb) -> {
var sq = query.subquery(String.class);
var u = sq.from(User.class);
sq.select(u.get("fullName"));
sq.where(cb.equal(u.get("id"), root.join("payment").get("userId")));
return sq;
})
.orderable("transfer_id", (root, query, cb) -> {
var orderId = root.join("payment").get("orderId").as(Long.class);
return cb.<Long>selectCase()
.when(cb.isNull(orderId), Long.MAX_VALUE)
.otherwise(orderId);
})
.orderable("payment.orderId", (root, query, cb) -> {
var orderId = root.join("payment").get("orderId").as(Long.class);
return cb.<Long>selectCase()
.when(cb.isNull(orderId), Long.MAX_VALUE)
.otherwise(orderId);
})
.orderable("amountCentsRefund", (root, query, cb) -> root.join("payment").get("amountRefundedCents"))
.add("created_at", pago -> Utils.formatDateTime(pago.getCreatedAt(), locale)) .add("created_at", pago -> Utils.formatDateTime(pago.getCreatedAt(), locale))
.add("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale)) .add("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale))
.add("client", pago -> { .add("client", pago -> {
@ -237,14 +260,14 @@ public class PaymentController {
return ""; return "";
}) })
.add("order_id", pago -> { .add("order_id", pago -> {
if (pago.getStatus() != PaymentTransactionStatus.pending) { //if (pago.getStatus() != PaymentTransactionStatus.pending) {
if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) { if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) {
return pago.getPayment().getOrderId().toString(); return pago.getPayment().getOrderId().toString();
} else { } else {
return ""; return "";
} }
} //}
return messageSource.getMessage("pagos.transferencia.no-pedido", null, "Pendiente", locale); //return messageSource.getMessage("pagos.transferencia.no-pedido", null, "Pendiente", locale);
}).add("amount_cents", pago -> Utils.formatCurrency(pago.getAmountCents() / 100.0, locale)) }).add("amount_cents", pago -> Utils.formatCurrency(pago.getAmountCents() / 100.0, locale))
.add("amount_cents_refund", pago -> .add("amount_cents_refund", pago ->

View File

@ -13,7 +13,7 @@ public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion
// Si en tu código sueles trabajar con el objeto: // Si en tu código sueles trabajar con el objeto:
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea); List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
PedidoDireccion findByPedidoIdAndFacturacionTrue(Long pedidoId); PedidoDireccion findFirstByPedidoIdAndFacturacionTrue(Long pedidoId);
@Query(""" @Query("""
select distinct d select distinct d

View File

@ -14,14 +14,16 @@ public class PedidoLinea {
procesando_pago("pedido.estado.procesando_pago", 2), procesando_pago("pedido.estado.procesando_pago", 2),
denegado_pago("pedido.estado.denegado_pago", 3), denegado_pago("pedido.estado.denegado_pago", 3),
aprobado("pedido.estado.aprobado", 4), aprobado("pedido.estado.aprobado", 4),
maquetacion("pedido.estado.maquetacion", 5), procesando_pedido("pedido.estado.procesando_pedido", 5),
haciendo_ferro("pedido.estado.haciendo_ferro", 6), maquetacion("pedido.estado.maquetacion", 6),
esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 7), haciendo_ferro_digital("pedido.estado.haciendo_ferro_digital", 7),
ferro_cliente("pedido.estado.ferro_cliente", 8), esperando_aceptacion_ferro_digital("pedido.estado.esperando_aceptacion_ferro_digital", 8),
produccion("pedido.estado.produccion", 9), haciendo_ferro("pedido.estado.haciendo_ferro", 9),
terminado("pedido.estado.terminado", 10), esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 10),
enviado("pedido.estado.enviado", 11), produccion("pedido.estado.produccion", 11),
cancelado("pedido.estado.cancelado", 12); terminado("pedido.estado.terminado", 12),
enviado("pedido.estado.enviado", 13),
cancelado("pedido.estado.cancelado", 14);
private final String messageKey; private final String messageKey;
private final int priority; private final int priority;

View File

@ -1,5 +1,6 @@
package com.imprimelibros.erp.pedidos; package com.imprimelibros.erp.pedidos;
import java.math.BigDecimal;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@ -68,7 +69,7 @@ public class PedidoService {
} }
public PedidoDireccion getPedidoDireccionFacturacionByPedidoId(Long pedidoId) { public PedidoDireccion getPedidoDireccionFacturacionByPedidoId(Long pedidoId) {
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
} }
@Transactional @Transactional
@ -81,7 +82,8 @@ public class PedidoService {
Pedido pedido = new Pedido(); Pedido pedido = new Pedido();
Cart cart = cartService.getCartById(cartId); Cart cart = cartService.getCartById(cartId);
Map<String, Object> cartSummaryRaw = cartService.getCartSummaryRaw(cart, Locale.getDefault()); Boolean hasTaxes = direccionService.hasTaxes(direccionFacturacionId);
Map<String, Object> cartSummaryRaw = cartService.getCartSummaryRaw(cart, Locale.getDefault(), hasTaxes);
// Datos económicos (ojo con las claves, son las del summaryRaw) // Datos económicos (ojo con las claves, son las del summaryRaw)
pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d)); pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d));
@ -137,6 +139,11 @@ public class PedidoService {
Presupuesto p = presupuestoRepository.findById(pCart.getId()) Presupuesto p = presupuestoRepository.findById(pCart.getId())
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId())); .orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
p.setEstado(Presupuesto.Estado.aceptado); p.setEstado(Presupuesto.Estado.aceptado);
if(!hasTaxes){
p.setIvaImporte21(BigDecimal.ZERO);
p.setIvaImporte4(BigDecimal.ZERO);
p.setTotalConIva(p.getBaseImponible());
}
presupuestoRepository.save(p); presupuestoRepository.save(p);
PedidoLinea linea = new PedidoLinea(); PedidoLinea linea = new PedidoLinea();
@ -150,9 +157,12 @@ public class PedidoService {
// Guardar las direcciones asociadas a la línea del pedido // Guardar las direcciones asociadas a la línea del pedido
Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p); Map<String, Object> direcciones_presupuesto = this.getDireccionesPresupuesto(cart, p);
saveDireccionesPedidoLinea(direcciones_presupuesto, pedidoGuardado, linea, direccionFacturacionId); saveDireccionesPedidoLinea(direcciones_presupuesto, pedidoGuardado, linea);
} }
if(direccionFacturacionId != null && pedidoGuardado != null && pedidoGuardado.getId() != null){
saveDireccionFacturacionPedido(pedidoGuardado, direccionFacturacionId);
}
return pedidoGuardado; return pedidoGuardado;
} }
@ -196,7 +206,7 @@ public class PedidoService {
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null); Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
if (pedido != null) { if (pedido != null) {
PedidoDireccion direccionPedido = pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); PedidoDireccion direccionPedido = pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
if (direccionPedido == null) { if (direccionPedido == null) {
// crear // crear
@ -243,7 +253,7 @@ public class PedidoService {
} }
public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) { public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) {
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId); return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
} }
public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) { public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) {
@ -261,7 +271,7 @@ public class PedidoService {
Integer counter = 1; Integer counter = 1;
for (PedidoLinea linea : lineas) { for (PedidoLinea linea : lineas) {
if (linea.getEstado() == Estado.pendiente_pago if (linea.getEstado() == Estado.pendiente_pago
|| linea.getEstado() == Estado.denegado_pago) { || linea.getEstado() == Estado.denegado_pago || linea.getEstado() == Estado.procesando_pago) {
Presupuesto presupuesto = linea.getPresupuesto(); Presupuesto presupuesto = linea.getPresupuesto();
linea.setEstado(getEstadoInicial(presupuesto)); linea.setEstado(getEstadoInicial(presupuesto));
@ -301,6 +311,7 @@ public class PedidoService {
return true; return true;
} }
@Transactional
public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) { public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) {
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null); PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
@ -317,8 +328,8 @@ public class PedidoService {
"message", messageSource.getMessage("pedido.errors.cannot-update", null, locale)); "message", messageSource.getMessage("pedido.errors.cannot-update", null, locale));
} }
// Rango: >= haciendo_ferro y < enviado // Rango: >= procesando_pedido y < enviado
if (estadoOld.getPriority() < PedidoLinea.Estado.haciendo_ferro.getPriority() if (estadoOld.getPriority() < PedidoLinea.Estado.procesando_pedido.getPriority()
|| estadoOld.getPriority() >= PedidoLinea.Estado.enviado.getPriority()) { || estadoOld.getPriority() >= PedidoLinea.Estado.enviado.getPriority()) {
return Map.of( return Map.of(
"success", false, "success", false,
@ -645,7 +656,7 @@ public class PedidoService {
private void saveDireccionesPedidoLinea( private void saveDireccionesPedidoLinea(
Map<String, Object> direcciones, Map<String, Object> direcciones,
Pedido pedido, Pedido pedido,
PedidoLinea linea, Long direccionFacturacionId) { PedidoLinea linea) {
String email = pedido.getCreatedBy().getUserName(); String email = pedido.getCreatedBy().getUserName();
@ -692,7 +703,12 @@ public class PedidoService {
pedidoDireccionRepository.save(direccion); pedidoDireccionRepository.save(direccion);
} }
} }
}
private void saveDireccionFacturacionPedido(Pedido pedido, Long direccionFacturacionId) {
if (direccionFacturacionId != null) { if (direccionFacturacionId != null) {
String email = pedido.getCreatedBy().getUserName();
Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null); Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null);
if (dirFact != null) { if (dirFact != null) {
HashMap<String, Object> dirFactMap = new HashMap<>(); HashMap<String, Object> dirFactMap = new HashMap<>();
@ -713,7 +729,7 @@ public class PedidoService {
false, false,
dirFactMap, dirFactMap,
pedido, pedido,
linea, null,
false, false,
true); true);
pedidoDireccionRepository.save(direccion); pedidoDireccionRepository.save(direccion);
@ -773,7 +789,7 @@ public class PedidoService {
if (presupuestoService.hasMaquetacion(p)) { if (presupuestoService.hasMaquetacion(p)) {
return Estado.maquetacion; return Estado.maquetacion;
} else { } else {
return Estado.haciendo_ferro; return Estado.procesando_pedido;
} }
} }

View File

@ -12,6 +12,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
@ -29,23 +30,22 @@ import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.facturacion.service.FacturacionService; import com.imprimelibros.erp.facturacion.service.FacturacionService;
import com.imprimelibros.erp.i18n.TranslationService; import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.paises.PaisesService; import com.imprimelibros.erp.paises.PaisesService;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDao;
import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Subquery;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
@Controller @Controller
@RequestMapping("/pedidos") @RequestMapping("/pedidos")
public class PedidosController { public class PedidosController {
private final PresupuestoService presupuestoService;
private final PedidoRepository repoPedido; private final PedidoRepository repoPedido;
private final PedidoService pedidoService; private final PedidoService pedidoService;
private final UserDao repoUser; private final UserDao repoUser;
@ -58,7 +58,7 @@ public class PedidosController {
public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser, public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser,
MessageSource messageSource, TranslationService translationService, MessageSource messageSource, TranslationService translationService,
PedidoLineaRepository repoPedidoLinea, PaisesService paisesService, PedidoLineaRepository repoPedidoLinea, PaisesService paisesService,
FacturacionService facturacionService, PresupuestoService presupuestoService) { FacturacionService facturacionService) {
this.repoPedido = repoPedido; this.repoPedido = repoPedido;
this.pedidoService = pedidoService; this.pedidoService = pedidoService;
this.repoUser = repoUser; this.repoUser = repoUser;
@ -67,7 +67,6 @@ public class PedidosController {
this.repoPedidoLinea = repoPedidoLinea; this.repoPedidoLinea = repoPedidoLinea;
this.paisesService = paisesService; this.paisesService = paisesService;
this.facturacionService = facturacionService; this.facturacionService = facturacionService;
this.presupuestoService = presupuestoService;
} }
@GetMapping @GetMapping
@ -111,6 +110,7 @@ public class PedidosController {
"id", "id",
"createdBy.fullName", "createdBy.fullName",
"createdAt", "createdAt",
"titulos",
"total", "total",
"estado"); "estado");
@ -118,8 +118,10 @@ public class PedidosController {
if (!isAdmin) { if (!isAdmin) {
base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId)); base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId));
} }
String clientSearch = dt.getColumnSearch("cliente"); String clientSearch = dt.getColumnSearch("cliente");
String estadoSearch = dt.getColumnSearch("estado"); String estadoSearch = dt.getColumnSearch("estado");
String titulosSearch = dt.getColumnSearch("titulos");
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification // 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
if (clientSearch != null) { if (clientSearch != null) {
@ -151,11 +153,45 @@ public class PedidosController {
base = base.and((root, query, cb) -> cb.disjunction()); base = base.and((root, query, cb) -> cb.disjunction());
} }
} }
if (titulosSearch != null && !titulosSearch.isBlank()) {
String like = "%" + titulosSearch.trim().toLowerCase() + "%";
base = base.and((root, query, cb) -> {
Subquery<Long> minLineaIdSq = query.subquery(Long.class);
var plMin = minLineaIdSq.from(PedidoLinea.class);
minLineaIdSq.select(cb.min(plMin.get("id")));
minLineaIdSq.where(cb.equal(plMin.get("pedido"), root));
Subquery<String> firstTitleSq = query.subquery(String.class);
var plFirst = firstTitleSq.from(PedidoLinea.class);
var prFirst = plFirst.join("presupuesto", JoinType.LEFT);
firstTitleSq.select(cb.lower(cb.coalesce(prFirst.get("titulo"), "")));
firstTitleSq.where(
cb.equal(plFirst.get("pedido"), root),
cb.equal(plFirst.get("id"), minLineaIdSq));
return cb.like(firstTitleSq, like);
});
}
Long total = repoPedido.count(base); Long total = repoPedido.count(base);
return DataTable return DataTable
.of(repoPedido, Pedido.class, dt, searchable) .of(repoPedido, Pedido.class, dt, searchable)
.orderable(orderable) .orderable(orderable)
.orderable("titulos", (root, query, cb) -> {
Subquery<Long> minLineaIdSq = query.subquery(Long.class);
var plMin = minLineaIdSq.from(PedidoLinea.class);
minLineaIdSq.select(cb.min(plMin.get("id")));
minLineaIdSq.where(cb.equal(plMin.get("pedido"), root));
Subquery<String> firstTitleSq = query.subquery(String.class);
var plFirst = firstTitleSq.from(PedidoLinea.class);
var prFirst = plFirst.join("presupuesto", JoinType.LEFT);
firstTitleSq.select(cb.lower(cb.coalesce(prFirst.get("titulo"), "")));
firstTitleSq.where(
cb.equal(plFirst.get("pedido"), root),
cb.equal(plFirst.get("id"), minLineaIdSq));
return firstTitleSq;
})
.add("id", Pedido::getId) .add("id", Pedido::getId)
.add("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale)) .add("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale))
.add("cliente", pedido -> { .add("cliente", pedido -> {
@ -171,6 +207,38 @@ public class PedidosController {
return ""; return "";
} }
}) })
.add("titulos", pedido -> {
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoIdOrderByIdAsc(pedido.getId());
if (lineas.isEmpty()) {
return "";
}
List<Presupuesto> presupuestos = lineas.stream()
.map(PedidoLinea::getPresupuesto)
.filter(presupuesto -> presupuesto != null)
.collect(Collectors.toList());
if (presupuestos.isEmpty()) {
return "";
}
String primerTitulo = presupuestos.get(0).getTitulo();
if (primerTitulo == null) {
primerTitulo = "";
} else {
primerTitulo = primerTitulo.trim();
}
int extras = presupuestos.size() - 1;
if (extras <= 0) {
return primerTitulo;
}
String suffix = messageSource.getMessage(
"pedido.table.titulos.and-more",
new Object[] { extras },
locale);
return primerTitulo + " " + suffix;
})
.add("estado", pedido -> { .add("estado", pedido -> {
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId()); List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId());
if (lineas.isEmpty()) { if (lineas.isEmpty()) {
@ -451,4 +519,4 @@ public class PedidosController {
} }
} }
} }

View File

@ -48,12 +48,12 @@ import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto; import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoFormDataMapper;
import com.imprimelibros.erp.presupuesto.dto.PresupuestoFormDataMapper.PresupuestoFormDataDto;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService; import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDao;
import com.imprimelibros.erp.users.UserDetailsImpl; import com.imprimelibros.erp.users.UserDetailsImpl;
import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper;
import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper.PresupuestoFormDataDto;
import com.imprimelibros.erp.common.Utils; import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.common.web.IpUtils; import com.imprimelibros.erp.common.web.IpUtils;

View File

@ -109,21 +109,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
} }
} }
public enum Entrega {
peninsula("presupuesto.entrega.peninsula"),
canarias("presupuesto.entrega.canarias"),
paises_ue("presupuesto.entrega.paises-ue");
private final String messageKey;
Entrega(String messageKey) {
this.messageKey = messageKey;
}
public String getMessageKey() {
return messageKey;
}
}
@Override @Override
public Presupuesto clone() { public Presupuesto clone() {
@ -188,10 +173,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
@Column(name = "iva_reducido") @Column(name = "iva_reducido")
private Boolean ivaReducido; private Boolean ivaReducido;
@Column(name = "entrega_tipo")
@Enumerated(EnumType.STRING)
private Entrega entregaTipo;
@Column(name = "iva_importe_4", precision = 12, scale = 2) @Column(name = "iva_importe_4", precision = 12, scale = 2)
private BigDecimal ivaImporte4; private BigDecimal ivaImporte4;
@ -300,6 +281,12 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
@Column(name = "papel_interior_id") @Column(name = "papel_interior_id")
private Integer papelInteriorId; private Integer papelInteriorId;
@Column(name = "lomo")
private Double lomo;
@Column(name = "lomo_cubierta")
private Double lomoCubierta;
@NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class) @NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class)
@Column(name = "gramaje_interior") @Column(name = "gramaje_interior")
private Integer gramajeInterior; private Integer gramajeInterior;
@ -531,14 +518,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.ivaReducido = ivaReducido; this.ivaReducido = ivaReducido;
} }
public Entrega getEntregaTipo() {
return entregaTipo;
}
public void setEntregaTipo(Entrega entregaTipo) {
this.entregaTipo = entregaTipo;
}
public BigDecimal getIvaImporte4() { public BigDecimal getIvaImporte4() {
return ivaImporte4; return ivaImporte4;
} }
@ -747,6 +726,22 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
this.papelInteriorId = papelInteriorId; this.papelInteriorId = papelInteriorId;
} }
public Double getLomo() {
return lomo;
}
public void setLomo(Double lomo) {
this.lomo = lomo;
}
public Double getLomoCubierta() {
return lomoCubierta;
}
public void setLomoCubierta(Double lomoCubierta) {
this.lomoCubierta = lomoCubierta;
}
public Integer getGramajeInterior() { public Integer getGramajeInterior() {
return gramajeInterior; return gramajeInterior;
} }

View File

@ -1,9 +1,7 @@
package com.imprimelibros.erp.presupuesto.service; package com.imprimelibros.erp.presupuesto.dto;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -20,6 +18,8 @@ public class PresupuestoFormDataMapper {
public Cubierta cubierta = new Cubierta(); public Cubierta cubierta = new Cubierta();
public Servicios servicios = new Servicios(); public Servicios servicios = new Servicios();
public Integer selectedTirada; public Integer selectedTirada;
public Double lomo;
public Double lomoCubierta;
// ===== Datos Generales ===== // ===== Datos Generales =====
public static class DatosGenerales { public static class DatosGenerales {
@ -37,7 +37,6 @@ public class PresupuestoFormDataMapper {
public String paginasColor = ""; public String paginasColor = "";
public String posicionPaginasColor = ""; public String posicionPaginasColor = "";
public String tipoEncuadernacion = "fresado"; // enum name public String tipoEncuadernacion = "fresado"; // enum name
public String entregaTipo = "peninsula"; // enum name
public boolean ivaReducido = true; public boolean ivaReducido = true;
} }
@ -157,7 +156,6 @@ public class PresupuestoFormDataMapper {
vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado"); vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado");
vm.datosGenerales.entregaTipo = enumName(p.getEntregaTipo(), "peninsula");
vm.datosGenerales.ivaReducido = Boolean.TRUE.equals(p.getIvaReducido()); vm.datosGenerales.ivaReducido = Boolean.TRUE.equals(p.getIvaReducido());
// ===== Interior // ===== Interior
@ -196,6 +194,10 @@ public class PresupuestoFormDataMapper {
// ===== Selected tirada // ===== Selected tirada
vm.selectedTirada = p.getSelectedTirada(); vm.selectedTirada = p.getSelectedTirada();
// ===== Lomos
vm.lomo = p.getLomo();
vm.lomoCubierta = p.getLomoCubierta();
// ===== Servicios desde JSONs // ===== Servicios desde JSONs
vm.servicios.servicios = parse(p.getServiciosJson(), vm.servicios.servicios = parse(p.getServiciosJson(),
new TypeReference<List<PresupuestoFormDataDto.Servicios.DatosServicios>>() { new TypeReference<List<PresupuestoFormDataDto.Servicios.DatosServicios>>() {

View File

@ -329,7 +329,7 @@ public class PresupuestoService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(tirada -> tirada + 4) .map(tirada -> tirada + 4)
.collect(Collectors.toList())); .collect(Collectors.toList()));
if(presupuesto.getSelectedTirada() != null) { if (presupuesto.getSelectedTirada() != null) {
presupuesto.setSelectedTirada(presupuesto.getSelectedTirada()); presupuesto.setSelectedTirada(presupuesto.getSelectedTirada());
} }
} else { } else {
@ -337,7 +337,7 @@ public class PresupuestoService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
body.put("selectedTirada", body.put("selectedTirada",
presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : presupuesto.getTirada1()); presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : presupuesto.getTirada1());
body.put("tamanio", tamanio); body.put("tamanio", tamanio);
@ -352,7 +352,7 @@ public class PresupuestoService {
body.put("cubierta", cubierta); body.put("cubierta", cubierta);
body.put("guardas", null); body.put("guardas", null);
// Para las reimpresiones // Para las reimpresiones
if(presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) { if (presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) {
body.put("reimpresion", 1); body.put("reimpresion", 1);
body.put("iskn", presupuesto.getProveedorRef1()); body.put("iskn", presupuesto.getProveedorRef1());
} }
@ -1124,9 +1124,9 @@ public class PresupuestoService {
try { try {
// retractilado: recalcular precio // retractilado: recalcular precio
if (s.get("id").equals("retractilado")) { if (s.get("id").equals("retractilado")) {
String p = obtenerPrecioRetractilado(cantidad); String p = obtenerPrecioRetractilado(cantidad);
if(p != null){ if (p != null) {
double precio_retractilado = Double.parseDouble(p); double precio_retractilado = Double.parseDouble(p);
s.put("price", precio_retractilado); s.put("price", precio_retractilado);
} else { } else {
@ -1149,7 +1149,7 @@ public class PresupuestoService {
} }
} }
try { try {
if(presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada)) if (presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada))
presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios)); presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios));
} catch (Exception ignore) { } catch (Exception ignore) {
System.out.println("Error guardando servicios JSON: " + ignore.getMessage()); System.out.println("Error guardando servicios JSON: " + ignore.getMessage());
@ -1162,21 +1162,19 @@ public class PresupuestoService {
// Si la entrega es en peninsula, se mira el valor del iva // Si la entrega es en peninsula, se mira el valor del iva
// Canarias y paises UE no llevan IVA // Canarias y paises UE no llevan IVA
if (presupuesto.getEntregaTipo() == Presupuesto.Entrega.peninsula) { // Si el iva es reducido, el precio de la tirada y el del prototipo llevan IVA
// Si el iva es reducido, el precio de la tirada y el del prototipo llevan IVA // 4%
// 4% if (presupuesto.getIvaReducido()) {
if (presupuesto.getIvaReducido()) { ivaImporte4 = baseImponible.add(serviciosIva4).multiply(BigDecimal.valueOf(4)).divide(
ivaImporte4 = baseImponible.add(serviciosIva4).multiply(BigDecimal.valueOf(4)).divide( BigDecimal.valueOf(100), 2,
BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
RoundingMode.HALF_UP); ivaImporte21 = serviciosTotal.subtract(serviciosIva4).multiply(BigDecimal.valueOf(21)).divide(
ivaImporte21 = serviciosTotal.subtract(serviciosIva4).multiply(BigDecimal.valueOf(21)).divide( BigDecimal.valueOf(100), 2,
BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
RoundingMode.HALF_UP); } else {
} else { ivaImporte21 = baseImponible.add(serviciosTotal).multiply(BigDecimal.valueOf(21)).divide(
ivaImporte21 = baseImponible.add(serviciosTotal).multiply(BigDecimal.valueOf(21)).divide( BigDecimal.valueOf(100), 2,
BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
RoundingMode.HALF_UP);
}
} }
baseImponible = baseImponible.add(serviciosTotal); baseImponible = baseImponible.add(serviciosTotal);
BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4); BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4);
@ -1332,10 +1330,10 @@ public class PresupuestoService {
} }
return true; return true;
} }
public Boolean hasMaquetacion(Presupuesto presupuesto) { public Boolean hasMaquetacion(Presupuesto presupuesto) {
if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isEmpty()) { if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isEmpty()) {
if(presupuesto.getServiciosJson().contains("maquetacion")) { if (presupuesto.getServiciosJson().contains("maquetacion")) {
return true; return true;
} }
} }
@ -1474,7 +1472,6 @@ public class PresupuestoService {
target.setServiciosTotal(src.getServiciosTotal()); target.setServiciosTotal(src.getServiciosTotal());
target.setBaseImponible(src.getBaseImponible()); target.setBaseImponible(src.getBaseImponible());
target.setIvaReducido(src.getIvaReducido()); target.setIvaReducido(src.getIvaReducido());
target.setEntregaTipo(src.getEntregaTipo());
target.setIvaImporte4(src.getIvaImporte4()); target.setIvaImporte4(src.getIvaImporte4());
target.setIvaImporte21(src.getIvaImporte21()); target.setIvaImporte21(src.getIvaImporte21());
target.setTotalConIva(src.getTotalConIva()); target.setTotalConIva(src.getTotalConIva());

View File

@ -29,6 +29,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.imprimelibros.erp.datatables.DataTablesRequest; import com.imprimelibros.erp.datatables.DataTablesRequest;
import com.imprimelibros.erp.datatables.DataTablesParser; import com.imprimelibros.erp.datatables.DataTablesParser;
@ -377,7 +378,7 @@ public class UserController {
@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) { @RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(Math.max(0, page - 1), size); Pageable pageable = PageRequest.of(Math.max(0, page - 1), size, Sort.by(Sort.Direction.ASC, "fullName"));
Page<User> users = userService.findByRoleAndSearch(role, q, pageable); Page<User> users = userService.findByRoleAndSearch(role, q, pageable);

View File

@ -0,0 +1,20 @@
databaseChangeLog:
- changeSet:
id: 0026-drop-entrega-tipo-from-presupuesto
author: jjo
changes:
- dropColumn:
tableName: presupuesto
columnName: entrega_tipo
rollback:
- addColumn:
tableName: presupuesto
columns:
- column:
name: entrega_tipo
type: ENUM('peninsula', 'canarias', 'paises_ue')
defaultValue: peninsula
afterColumn: base_imponible
constraints:
nullable: false

View File

@ -0,0 +1,18 @@
databaseChangeLog:
- changeSet:
id: 0027-add-lomo-to-presupuesto
author: jjo
changes:
- addColumn:
tableName: presupuesto
columns:
- column:
name: lomo
type: DECIMAL(12, 2)
defaultValueNumeric: 0
afterColumn: papel_interior_id
rollback:
- dropColumn:
tableName: presupuesto
columnName: lomo

View File

@ -0,0 +1,18 @@
databaseChangeLog:
- changeSet:
id: 0028-add-lomo-cubierta-to-presupuesto
author: jjo
changes:
- addColumn:
tableName: presupuesto
columns:
- column:
name: lomo_cubierta
type: DECIMAL(12, 2)
defaultValueNumeric: 0
afterColumn: lomo
rollback:
- dropColumn:
tableName: presupuesto
columnName: lomo_cubierta

View File

@ -0,0 +1,43 @@
databaseChangeLog:
- changeSet:
id: 0029-update-estados-pedidos-lineas
author: jjo
changes:
- modifyDataType:
tableName: pedidos_lineas
columnName: estado
newDataType: >
enum(
'pendiente_pago',
'procesando_pago',
'denegado_pago',
'aprobado',
'procesando_pedido',
'maquetacion',
'haciendo_ferro_digital',
'esperando_aceptacion_ferro_digital',
'haciendo_ferro',
'esperando_aceptacion_ferro',
'produccion',
'terminado',
'enviado',
'cancelado'
)
rollback:
- modifyDataType:
tableName: pedidos_lineas
columnName: estado
newDataType: >
enum(
'pendiente_pago',
'procesando_pago',
'denegado_pago',
'aprobado',
'maquetacion',
'haciendo_ferro',
'esperando_aceptacion_ferro',
'produccion',
'terminado',
'enviado',
'cancelado'
)

View File

@ -1,4 +1,4 @@
databaseChangeLog: databaseChangeLog:
- include: - include:
file: db/changelog/changesets/0001-baseline.yml file: db/changelog/changesets/0001-baseline.yml
- include: - include:
@ -48,4 +48,13 @@ databaseChangeLog:
- include: - include:
file: db/changelog/changesets/0024-series-facturacion-seeder.yml file: db/changelog/changesets/0024-series-facturacion-seeder.yml
- include: - include:
file: db/changelog/changesets/0025-create-facturas-direcciones.yml file: db/changelog/changesets/0025-create-facturas-direcciones.yml
- include:
file: db/changelog/changesets/0026-drop-entrega-tipo-from-presupuesto.yml
- include:
file: db/changelog/changesets/0027-add-lomo-to-presupuesto.yml
- include:
file: db/changelog/changesets/0028-add-lomo-cubierta-to-presupuesto.yml
- include:
file: db/changelog/changesets/0029-update-estados-pedidos-lineas.yml

View File

@ -29,6 +29,8 @@ cart.shipping.tipo-envio=Tipo de envío:
cart.shipping.errors.units-error=Por favor, introduzca un número válido entre 1 y {max}. cart.shipping.errors.units-error=Por favor, introduzca un número válido entre 1 y {max}.
cart.shipping.errors.noAddressSelected=Debe seleccionar una dirección de envío para el pedido. cart.shipping.errors.noAddressSelected=Debe seleccionar una dirección de envío para el pedido.
cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta. cart.shipping.errors.fillAddressesItems=Debe seleccionar una dirección de envío para cada artículo de la cesta.
cart.shipping.errors.fillBillingAddressItems=Debe seleccionar una dirección de facturación para poder realizar el pago.
cart.resumen.title=Resumen de la cesta cart.resumen.title=Resumen de la cesta
cart.resumen.base=Base imponible cart.resumen.base=Base imponible

View File

@ -22,10 +22,12 @@ pedido.estado.pendiente_pago=Pendiente de pago
pedido.estado.procesando_pago=Procesando pago pedido.estado.procesando_pago=Procesando pago
pedido.estado.denegado_pago=Pago denegado pedido.estado.denegado_pago=Pago denegado
pedido.estado.aprobado=Aprobado pedido.estado.aprobado=Aprobado
pedido.estado.procesando_pedido=Procesando pedido
pedido.estado.maquetacion=Maquetación pedido.estado.maquetacion=Maquetación
pedido.estado.haciendo_ferro=Haciendo ferro pedido.estado.haciendo_ferro_digital=Haciendo ferro digital
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ferro pedido.estado.esperando_aceptacion_ferro_digital=Esperando aceptación de ferro digital
pedido.estado.ferro_cliente=Esperando aprobación de ferro pedido.estado.haciendo_ferro=Haciendo ejemplar de prueba
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ejemplar de prueba
pedido.estado.produccion=Producción pedido.estado.produccion=Producción
pedido.estado.terminado=Terminado pedido.estado.terminado=Terminado
pedido.estado.enviado=Enviado pedido.estado.enviado=Enviado
@ -47,6 +49,8 @@ pedido.prueba=Prueba
pedido.table.id=Num. Pedido pedido.table.id=Num. Pedido
pedido.table.cliente=Cliente pedido.table.cliente=Cliente
pedido.table.fecha=Fecha pedido.table.fecha=Fecha
pedido.table.titulos=Títulos
pedido.table.titulos.and-more=y {0} más
pedido.table.importe=Importe pedido.table.importe=Importe
pedido.table.estado=Estado pedido.table.estado=Estado
pedido.table.acciones=Acciones pedido.table.acciones=Acciones
@ -73,4 +77,4 @@ pedido.errors.connecting-server-error=Error al conectar con el servidor externo.
pedido.errors.cannot-update=No se puede actualizar el estado de una línea con ese estado inicial. pedido.errors.cannot-update=No se puede actualizar el estado de una línea con ese estado inicial.
pedido.success.estado-actualizado=Estado del pedido actualizado correctamente. pedido.success.estado-actualizado=Estado del pedido actualizado correctamente.
pedido.success.same-estado=Sin cambios en el estado. pedido.success.same-estado=Sin cambios en el estado.
pedido.success.pedido-cancelado=Pedido cancelado correctamente. pedido.success.pedido-cancelado=Pedido cancelado correctamente.

View File

@ -18,8 +18,8 @@ $(() => {
$('#addBillingAddressBtn').on('click', seleccionarDireccionEnvio); $('#addBillingAddressBtn').on('click', seleccionarDireccionEnvio);
$('#authorization-required').on('change', function () { $(document).on('change', '#authorization-required', function () {
if($(this).is(':checked')) { if ($(this).is(':checked')) {
if ($('#direccion-div .direccion-card').length > 0) { if ($('#direccion-div .direccion-card').length > 0) {
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
} }
@ -148,6 +148,24 @@ $(() => {
$('#btn-checkout').prop('disabled', false); $('#btn-checkout').prop('disabled', false);
} }
hideLoader(); hideLoader();
if (direccionId) {
$.ajax({
url: `/checkout/get-summary/${direccionId}/${$('input[name="method"]').val()}`,
type: 'GET',
success: function (response) {
const parent = $('.cart-summary-container').parent();
$('.cart-summary-container').remove();
parent.append(response);
$('#dirFactId').val(direccionId);
$('#dirFactWarning').addClass('d-none');
},
error: function () {
console.error('Error al actualizar el resumen del carrito.');
}
});
}
return true; return true;
} }
hideLoader(); hideLoader();
@ -164,6 +182,19 @@ $(() => {
$card.remove(); $card.remove();
$('#addBillingAddressBtn').removeClass('d-none'); $('#addBillingAddressBtn').removeClass('d-none');
$('#btn-checkout').prop('disabled', true); $('#btn-checkout').prop('disabled', true);
$.ajax({
url: `/checkout/get-summary`,
type: 'GET',
success: function (response) {
const parent = $('.cart-summary-container').parent();
$('.cart-summary-container').remove();
$('#dirFactWarning').removeClass('d-none');
parent.append(response);
},
error: function () {
console.error('Error al actualizar el resumen del carrito.');
}
});
}); });
@ -194,7 +225,7 @@ $(() => {
type: 'POST', // PUT simulado via _method type: 'POST', // PUT simulado via _method
data: $form.serialize(), data: $form.serialize(),
dataType: 'html', dataType: 'html',
success: function (html) { success: async function (html) {
// Si por cualquier motivo llega 200 con fragmento, lo insertamos igual // Si por cualquier motivo llega 200 con fragmento, lo insertamos igual
if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) { if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) {
$('#direccionFormModalBody').html(html); $('#direccionFormModalBody').html(html);
@ -205,7 +236,8 @@ $(() => {
} }
// Éxito real: cerrar y recargar tabla // Éxito real: cerrar y recargar tabla
$('#direccionFormModal').modal('hide'); $('#direccionFormModal').modal('hide');
seleccionarDireccionEnvio(); await seleccionarDireccionEnvio();
}, },
error: function (xhr) { error: function (xhr) {
// Con 422 devolvemos el fragmento con errores aquí // Con 422 devolvemos el fragmento con errores aquí

View File

@ -71,7 +71,7 @@ $(() => {
// ----------------------------- // -----------------------------
$table.on('click', '.btn-view-factura', function () { $table.on('click', '.btn-view-factura', function () {
const row = dt.row($(this).closest('tr')).data(); const row = dt.row($(this).closest('tr')).data();
window.location.href = `/facturas/${row.id}`; window.open('/facturas/' + row.id, '_blank', 'noopener,noreferrer');
}); });
// ----------------------------- // -----------------------------

View File

@ -48,6 +48,7 @@ $(() => {
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'cliente', name: 'createdBy.fullName', orderable: true }, { data: 'cliente', name: 'createdBy.fullName', orderable: true },
{ data: 'created_at', name: 'createdAt', orderable: true }, { data: 'created_at', name: 'createdAt', orderable: true },
{ data: 'titulos', name: 'titulos', orderable: true },
{ data: 'total', name: 'total', orderable: true }, { data: 'total', name: 'total', orderable: true },
{ data: 'estado', name: 'estado', orderable: true }, { data: 'estado', name: 'estado', orderable: true },
{ data: 'actions', name: 'actions', orderable: false, searchable: false } { data: 'actions', name: 'actions', orderable: false, searchable: false }

View File

@ -1,8 +1,8 @@
$(() => { $(() => {
$(document).on('click', '.btn-view', function () { $(document).on('click', '.btn-view', function () {
let pedidoId = $(this).data('id'); let pedidoId = $(this).data('id');
let url = `/pedidos/view/${pedidoId}`; if(!pedidoId) return;
window.location.href = url; window.open('/pedidos/view/' + pedidoId, '_blank', 'noopener,noreferrer');
}); });
$(document).on('click', '.btn-pay', async function () { $(document).on('click', '.btn-pay', async function () {

View File

@ -47,6 +47,7 @@ $(() => {
columns: [ columns: [
{ data: 'id', name: 'id', orderable: true }, { data: 'id', name: 'id', orderable: true },
{ data: 'created_at', name: 'createdAt', orderable: true }, { data: 'created_at', name: 'createdAt', orderable: true },
{ data: 'titulos', name: 'titulos', orderable: true },
{ data: 'total', name: 'total', orderable: true }, { data: 'total', name: 'total', orderable: true },
{ data: 'estado', name: 'estado', orderable: true }, { data: 'estado', name: 'estado', orderable: true },
{ data: 'actions', name: 'actions', orderable: false, searchable: false } { data: 'actions', name: 'actions', orderable: false, searchable: false }

View File

@ -34,7 +34,6 @@ export default class PresupuestoWizard {
paginasColor: 0, paginasColor: 0,
posicionPaginasColor: '', posicionPaginasColor: '',
tipoEncuadernacion: 'fresado', tipoEncuadernacion: 'fresado',
entregaTipo: 'peninsula',
ivaReducido: true, ivaReducido: true,
}, },
interior: { interior: {
@ -108,6 +107,8 @@ export default class PresupuestoWizard {
} }
}, },
selectedTirada: 10, selectedTirada: 10,
lomo: 0,
lomoCubierta: 0
} }
// pestaña datos generales // pestaña datos generales
@ -130,7 +131,6 @@ export default class PresupuestoWizard {
this.divPosicionPaginasColor = $('#div-posicion-paginas-color'); this.divPosicionPaginasColor = $('#div-posicion-paginas-color');
this.posicionPaginasColor = $('#posicionPaginasColor'); this.posicionPaginasColor = $('#posicionPaginasColor');
this.paginas = $('#paginas'); this.paginas = $('#paginas');
this.entregaTipo = $('#entregaTipo');
this.ivaReducido = $('#iva-reducido'); this.ivaReducido = $('#iva-reducido');
this.btnIvaReducidoDetail = $('#btn-iva-reducido-detail'); this.btnIvaReducidoDetail = $('#btn-iva-reducido-detail');
this.datos_generales_alert = $('#datos-generales-alert'); this.datos_generales_alert = $('#datos-generales-alert');
@ -445,7 +445,9 @@ export default class PresupuestoWizard {
...this.#getDatosGeneralesData(), ...this.#getDatosGeneralesData(),
...this.#getInteriorData(), ...this.#getInteriorData(),
...this.#getCubiertaData(), ...this.#getCubiertaData(),
selectedTirada: this.formData.selectedTirada selectedTirada: this.formData.selectedTirada,
lomo: this.formData.lomo,
lomoCubierta: this.formData.lomoCubierta
}; };
const sobrecubierta = data.sobrecubierta; const sobrecubierta = data.sobrecubierta;
@ -666,7 +668,6 @@ export default class PresupuestoWizard {
paginasColor: this.paginasColor.val(), paginasColor: this.paginasColor.val(),
posicionPaginasColor: this.posicionPaginasColor.val(), posicionPaginasColor: this.posicionPaginasColor.val(),
tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado', tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado',
entregaTipo: this.entregaTipo.val(),
ivaReducido: this.ivaReducido.is(':checked'), ivaReducido: this.ivaReducido.is(':checked'),
}; };
} }
@ -688,7 +689,6 @@ export default class PresupuestoWizard {
paginasColor: data.paginasColor, paginasColor: data.paginasColor,
posicionPaginasColor: data.posicionPaginasColor, posicionPaginasColor: data.posicionPaginasColor,
tipoEncuadernacion: data.tipoEncuadernacion, tipoEncuadernacion: data.tipoEncuadernacion,
entregaTipo: data.entregaTipo,
ivaReducido: data.ivaReducido, ivaReducido: data.ivaReducido,
}; };
} }
@ -736,7 +736,6 @@ export default class PresupuestoWizard {
} }
} }
this.entregaTipo.val(this.formData.datosGenerales.entregaTipo);
this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido); this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido);
} }
@ -970,6 +969,7 @@ export default class PresupuestoWizard {
} else { } else {
const maxSolapas = data.solapas ?? 120; const maxSolapas = data.solapas ?? 120;
const lomo = data.lomo ?? 0; const lomo = data.lomo ?? 0;
$('.solapas-presupuesto').attr('max', maxSolapas); $('.solapas-presupuesto').attr('max', maxSolapas);
$('.max-solapa-text').text(function (_, textoActual) { $('.max-solapa-text').text(function (_, textoActual) {
return textoActual.replace(/\d+/, maxSolapas); return textoActual.replace(/\d+/, maxSolapas);
@ -1023,6 +1023,7 @@ export default class PresupuestoWizard {
const dataCubierta = this.#getCubiertaData(); const dataCubierta = this.#getCubiertaData();
this.#updateCubiertaData(dataCubierta); this.#updateCubiertaData(dataCubierta);
this.formData.lomo = lomo;
this.#cacheFormData(); this.#cacheFormData();
} }
@ -1897,15 +1898,6 @@ export default class PresupuestoWizard {
...result, ...result,
}; };
if (!this.formData.servicios.servicios.some(s => s.id === "marcapaginas") && result.precio > 0) {
this.formData.servicios.servicios.push({
id: "marcapaginas",
label: $(`label[for="marcapaginas"] .service-title`).text().trim(),
units: 1,
price: result.precio,
});
}
this.#cacheFormData(); this.#cacheFormData();
}); });

View File

@ -72,7 +72,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
e.preventDefault(); e.preventDefault();
const id = $(this).data('id'); const id = $(this).data('id');
if (id) { if (id) {
window.location.href = '/presupuesto/view/' + id; window.open('/presupuesto/view/' + id, '_blank', 'noopener,noreferrer');
} }
}); });
@ -196,7 +196,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
e.preventDefault(); e.preventDefault();
const id = $(this).data('id'); const id = $(this).data('id');
if (id) { if (id) {
window.location.href = '/presupuesto/edit/' + id; window.open('/presupuesto/edit/' + id, '_blank', 'noopener,noreferrer');
} }
}); });

View File

@ -55,7 +55,11 @@ export async function preguntarTipoPresupuesto() {
url: 'users/api/get-users', // ajusta a tu endpoint url: 'users/api/get-users', // ajusta a tu endpoint
dataType: 'json', dataType: 'json',
delay: 250, delay: 250,
data: (params) => ({ q: params.term }), data: (params) => ({
q: params.term || '',
page: params.page || 1,
size: 10,
}),
processResults: data => ({ processResults: data => ({
results: data.results, results: data.results,
pagination: data.pagination pagination: data.pagination

View File

@ -56,6 +56,8 @@
<h5 class="fs-14 text-truncate mb-1"> <h5 class="fs-14 text-truncate mb-1">
<span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</span> <span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</span>
<span th:text="${item.presupuestoId != null ? item.presupuestoId : ''}">#</span> <span th:text="${item.presupuestoId != null ? item.presupuestoId : ''}">#</span>
<a th:href="@{|/presupuesto/edit/${item.presupuestoId}|}"
th:text="#{pedido.view.view-presupuesto}" class="badge bg-secondary">Ver presupuesto</a>
</h5> </h5>
<ul class="list-unstyled text-muted mb-1 ps-0"> <ul class="list-unstyled text-muted mb-1 ps-0">

View File

@ -48,7 +48,7 @@
</div> </div>
<form th:action="@{/pagos/redsys/crear}" method="post"> <form th:action="@{/pagos/redsys/crear}" method="post">
<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" th:value="${method} ?: '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="" /> <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"

View File

@ -50,6 +50,9 @@
<div> <div>
<h5 th:text="#{checkout.billing-address}" class="mb-3">Dirección de envío</h5> <h5 th:text="#{checkout.billing-address}" class="mb-3">Dirección de envío</h5>
<div id="dirFactWarning" class="alert alert-danger alert-shipment" role="alert"
th:text="#{cart.shipping.errors.fillBillingAddressItems}"></div>
<button type="button" class="btn btn-secondary mb-3" id="addBillingAddressBtn" <button type="button" class="btn btn-secondary mb-3" id="addBillingAddressBtn"
th:text="#{cart.shipping.add}">Añadir dirección th:text="#{cart.shipping.add}">Añadir dirección
</button> </button>

View File

@ -0,0 +1,61 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{imprimelibros/layout}">
<head>
<th:block layout:fragment="pagetitle" />
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
<th:block layout:fragment="pagecss">
<link th:href="@{/assets/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
</th:block>
</head>
<body>
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
sec:authorize="isAuthenticated() and hasAnyRole('SUPERADMIN','ADMIN')">
<th:block layout:fragment="content">
<!-- CONTENIDO -->
<div class="row justify-content-center">
<div class="col-lg-7">
<div class="card shadow-sm">
<div class="card-body p-4 p-md-5 text-center">
<h1 class="display-4 mb-2" th:text="${status}">404</h1>
<h4 class="mb-3" th:text="${error}">Not Found</h4>
<p class="text-muted mb-4"
th:text="${message != null and !#strings.isEmpty(message)} ? ${message} : 'Ha ocurrido un error inesperado.'">
Ha ocurrido un error inesperado.
</p>
<div class="small text-muted mb-4">
<div><strong>Ruta:</strong> <span th:text="${path}">/logout</span></div>
<div><strong>Fecha:</strong> <span
th:text="${#temporals.format(timestamp, 'dd/MM/yyyy HH:mm:ss')}">--</span></div>
</div>
<div class="d-flex gap-2 justify-content-center">
<a class="btn btn-primary" th:href="@{/}">Volver al inicio</a>
<a class="btn btn-outline-secondary" href="javascript:history.back()">Atrás</a>
</div>
<!-- Opcional: bloque extra sólo para 404 -->
<div class="mt-4" th:if="${is404}">
<div class="alert alert-warning mb-0">
No se ha encontrado el recurso solicitado.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /CONTENIDO -->
</div>
</body>
</html>

View File

@ -37,6 +37,7 @@
<tr> <tr>
<th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th> <th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th>
<th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th> <th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th>
<th class="text-start" scope="col" th:text="#{pedido.table.titulos}">Títulos</th>
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th> <th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th>
<th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th> <th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th>
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th> <th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
@ -44,6 +45,7 @@
<tr> <tr>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th> <th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th>
<th></th> <th></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
<th></th> <th></th>
<th> <th>
<select class="form-select form-select-sm input-filter" data-col="estado"> <select class="form-select form-select-sm input-filter" data-col="estado">

View File

@ -38,6 +38,7 @@
<th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th> <th class="text-start" scope="col" th:text="#{pedido.table.id}">Num. Pedido</th>
<th class="text-start" scope="col" th:text="#{pedido.table.cliente}">Cliente</th> <th class="text-start" scope="col" th:text="#{pedido.table.cliente}">Cliente</th>
<th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th> <th class="text-start" scope="col" th:text="#{pedido.table.fecha}">Fecha</th>
<th class="text-start" scope="col" th:text="#{pedido.table.titulos}">Títulos</th>
<th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th> <th class="text-start" scope="col" th:text="#{pedido.table.importe}">Importe</th>
<th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th> <th class="text-start" scope="col" th:text="#{pedido.table.estado}">Estado</th>
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th> <th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
@ -46,6 +47,7 @@
<th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th> <th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="createdBy.fullName" /></th> <th><input type="text" class="form-control form-control-sm input-filter" data-col="createdBy.fullName" /></th>
<th></th> <th></th>
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
<th></th> <th></th>
<th> <th>
<select class="form-select form-select-sm input-filter" data-col="estado"> <select class="form-select form-select-sm input-filter" data-col="estado">

View File

@ -302,19 +302,6 @@
</label> </label>
</div> </div>
</div> </div>
<div class="row justify-content-center mb-2">
<div class="col-sm-3 justify-content-center">
<label for="entregaTipo" class="form-label mt-2"
th:text="#{presupuesto.entrega}">Entrega</label>
<select class="form-select select2 datos-generales-data" id="entregaTipo" name="entregaTipo">
<option selected value="peninsula" th:text="#{presupuesto.entrega.peninsula}">Península
y
Baleares</option>
<option value="canarias" th:text="#{presupuesto.entrega.canarias}">Canarias</option>
<option value="paises_ue" th:text="#{presupuesto.entrega.paises-ue}">Países UE</option>
</select>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>