mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-03-01 14:19:14 +00:00
Compare commits
18 Commits
fix/import
...
355c5b6019
| Author | SHA1 | Date | |
|---|---|---|---|
| 355c5b6019 | |||
| e4c1692ef0 | |||
| f3d96361a0 | |||
| b7453b1138 | |||
| 1c5d501e6d | |||
| 87368c434a | |||
| a9cdc6d5d6 | |||
| 1a04d5ace1 | |||
| cc8d1b8d44 | |||
| 6121562f9b | |||
| f73f108c71 | |||
| 60080eb89e | |||
| e9f75b85c5 | |||
| ce6f4085a0 | |||
| 97ee53cefd | |||
| 014079cf04 | |||
| 8acd0bb890 | |||
| fffc2b91c1 |
@ -80,7 +80,7 @@ public class CartController {
|
||||
else if (direcciones != null && direcciones.containsKey("direcciones"))
|
||||
model.addAttribute("direcciones", direcciones.get("direcciones"));
|
||||
|
||||
var summary = service.getCartSummary(cart, locale);
|
||||
var summary = service.getCartSummary(cart, locale, true);
|
||||
model.addAttribute("cartSummary", summary);
|
||||
if (summary.get("errorShipmentCost") != null && (Boolean) summary.get("errorShipmentCost"))
|
||||
model.addAttribute("errorEnvio", true);
|
||||
@ -165,7 +165,7 @@ public class CartController {
|
||||
|
||||
try {
|
||||
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);
|
||||
|
||||
return "imprimelibros/cart/_cartSummary :: cartSummary(summary=${cartSummary})";
|
||||
|
||||
@ -163,7 +163,7 @@ public class CartService {
|
||||
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 iva4 = 0.0;
|
||||
@ -269,6 +269,11 @@ public class CartService {
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasTaxes) {
|
||||
iva4 = 0.0;
|
||||
iva21 = 0.0;
|
||||
}
|
||||
|
||||
double totalBeforeDiscount = base + iva4 + iva21 + shipment;
|
||||
int fidelizacion = this.getDescuentoFidelizacion(cart.getUserId());
|
||||
double descuento = totalBeforeDiscount * fidelizacion / 100.0;
|
||||
@ -318,8 +323,8 @@ public class CartService {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCartSummary(Cart cart, Locale locale) {
|
||||
Map<String, Object> raw = getCartSummaryRaw(cart, locale);
|
||||
public Map<String, Object> getCartSummary(Cart cart, Locale locale, Boolean hasTaxes) {
|
||||
Map<String, Object> raw = getCartSummaryRaw(cart, locale, hasTaxes);
|
||||
|
||||
double base = (Double) raw.get("base");
|
||||
double iva4 = (Double) raw.get("iva4");
|
||||
|
||||
@ -48,7 +48,7 @@ public class CheckoutController {
|
||||
"app.cancelar",
|
||||
"app.seleccionar",
|
||||
"app.yes",
|
||||
"checkout.billing-address.title",
|
||||
"checkout.billing-address.title",
|
||||
"checkout.billing-address.new-address",
|
||||
"checkout.billing-address.select-placeholder",
|
||||
"checkout.billing-address.errors.noAddressSelected");
|
||||
@ -58,10 +58,28 @@ public class CheckoutController {
|
||||
|
||||
Long userId = Utils.currentUserId(principal);
|
||||
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)
|
||||
}
|
||||
|
||||
@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}")
|
||||
public String getDireccionCard(@PathVariable Long id, Model model, Locale locale) {
|
||||
Direccion dir = direccionService.findById(id)
|
||||
|
||||
@ -153,4 +153,26 @@ public class DireccionService {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ import com.imprimelibros.erp.facturacion.dto.FacturaDireccionMapper;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
|
||||
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.FacturaPagoRepository;
|
||||
import com.imprimelibros.erp.facturacion.repo.FacturaRepository;
|
||||
|
||||
@ -184,11 +184,14 @@ public class PaymentController {
|
||||
|
||||
// Campos ordenables
|
||||
List<String> orderable = List.of(
|
||||
"transferId",
|
||||
"client",
|
||||
"transfer_id",
|
||||
"status",
|
||||
"amountCents",
|
||||
"payment.amountRefundedCents",
|
||||
"createdAt", "updatedAt");
|
||||
"amountCentsRefund",
|
||||
"payment.orderId",
|
||||
"createdAt",
|
||||
"processedAt");
|
||||
|
||||
Specification<PaymentTransaction> base = (root, query, cb) -> cb.or(
|
||||
cb.equal(root.get("status"), PaymentTransactionStatus.pending),
|
||||
@ -215,6 +218,26 @@ public class PaymentController {
|
||||
return DataTable
|
||||
.of(repoPaymentTransaction, PaymentTransaction.class, dt, searchable)
|
||||
.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("processed_at", pago -> Utils.formatDateTime(pago.getProcessedAt(), locale))
|
||||
.add("client", pago -> {
|
||||
@ -237,14 +260,14 @@ public class PaymentController {
|
||||
return "";
|
||||
})
|
||||
.add("order_id", pago -> {
|
||||
if (pago.getStatus() != PaymentTransactionStatus.pending) {
|
||||
//if (pago.getStatus() != PaymentTransactionStatus.pending) {
|
||||
if (pago.getPayment() != null && pago.getPayment().getOrderId() != null) {
|
||||
return pago.getPayment().getOrderId().toString();
|
||||
} else {
|
||||
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_refund", pago ->
|
||||
|
||||
@ -13,7 +13,7 @@ public interface PedidoDireccionRepository extends JpaRepository<PedidoDireccion
|
||||
// Si en tu código sueles trabajar con el objeto:
|
||||
List<PedidoDireccion> findByPedidoLinea(PedidoLinea pedidoLinea);
|
||||
|
||||
PedidoDireccion findByPedidoIdAndFacturacionTrue(Long pedidoId);
|
||||
PedidoDireccion findFirstByPedidoIdAndFacturacionTrue(Long pedidoId);
|
||||
|
||||
@Query("""
|
||||
select distinct d
|
||||
|
||||
@ -14,14 +14,16 @@ public class PedidoLinea {
|
||||
procesando_pago("pedido.estado.procesando_pago", 2),
|
||||
denegado_pago("pedido.estado.denegado_pago", 3),
|
||||
aprobado("pedido.estado.aprobado", 4),
|
||||
maquetacion("pedido.estado.maquetacion", 5),
|
||||
haciendo_ferro("pedido.estado.haciendo_ferro", 6),
|
||||
esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 7),
|
||||
ferro_cliente("pedido.estado.ferro_cliente", 8),
|
||||
produccion("pedido.estado.produccion", 9),
|
||||
terminado("pedido.estado.terminado", 10),
|
||||
enviado("pedido.estado.enviado", 11),
|
||||
cancelado("pedido.estado.cancelado", 12);
|
||||
procesando_pedido("pedido.estado.procesando_pedido", 5),
|
||||
maquetacion("pedido.estado.maquetacion", 6),
|
||||
haciendo_ferro_digital("pedido.estado.haciendo_ferro_digital", 7),
|
||||
esperando_aceptacion_ferro_digital("pedido.estado.esperando_aceptacion_ferro_digital", 8),
|
||||
haciendo_ferro("pedido.estado.haciendo_ferro", 9),
|
||||
esperando_aceptacion_ferro("pedido.estado.esperando_aceptacion_ferro", 10),
|
||||
produccion("pedido.estado.produccion", 11),
|
||||
terminado("pedido.estado.terminado", 12),
|
||||
enviado("pedido.estado.enviado", 13),
|
||||
cancelado("pedido.estado.cancelado", 14);
|
||||
|
||||
private final String messageKey;
|
||||
private final int priority;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.imprimelibros.erp.pedidos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@ -68,7 +69,7 @@ public class PedidoService {
|
||||
}
|
||||
|
||||
public PedidoDireccion getPedidoDireccionFacturacionByPedidoId(Long pedidoId) {
|
||||
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -81,7 +82,8 @@ public class PedidoService {
|
||||
Pedido pedido = new Pedido();
|
||||
|
||||
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)
|
||||
pedido.setBase((Double) cartSummaryRaw.getOrDefault("base", 0.0d));
|
||||
@ -137,6 +139,11 @@ public class PedidoService {
|
||||
Presupuesto p = presupuestoRepository.findById(pCart.getId())
|
||||
.orElseThrow(() -> new IllegalStateException("Presupuesto no encontrado: " + pCart.getId()));
|
||||
p.setEstado(Presupuesto.Estado.aceptado);
|
||||
if(!hasTaxes){
|
||||
p.setIvaImporte21(BigDecimal.ZERO);
|
||||
p.setIvaImporte4(BigDecimal.ZERO);
|
||||
p.setTotalConIva(p.getBaseImponible());
|
||||
}
|
||||
presupuestoRepository.save(p);
|
||||
|
||||
PedidoLinea linea = new PedidoLinea();
|
||||
@ -150,9 +157,12 @@ public class PedidoService {
|
||||
|
||||
// Guardar las direcciones asociadas a la línea del pedido
|
||||
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;
|
||||
}
|
||||
@ -196,7 +206,7 @@ public class PedidoService {
|
||||
Pedido pedido = pedidoRepository.findById(pedidoId).orElse(null);
|
||||
if (pedido != null) {
|
||||
|
||||
PedidoDireccion direccionPedido = pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
PedidoDireccion direccionPedido = pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
|
||||
if (direccionPedido == null) {
|
||||
// crear
|
||||
@ -243,7 +253,7 @@ public class PedidoService {
|
||||
}
|
||||
|
||||
public PedidoDireccion getDireccionFacturacionPedido(Long pedidoId) {
|
||||
return pedidoDireccionRepository.findByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
return pedidoDireccionRepository.findFirstByPedidoIdAndFacturacionTrue(pedidoId);
|
||||
}
|
||||
|
||||
public List<PedidoDireccion> getDireccionesEntregaPedidoLinea(Long pedidoLineaId) {
|
||||
@ -261,7 +271,7 @@ public class PedidoService {
|
||||
Integer counter = 1;
|
||||
for (PedidoLinea linea : lineas) {
|
||||
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();
|
||||
linea.setEstado(getEstadoInicial(presupuesto));
|
||||
@ -301,6 +311,7 @@ public class PedidoService {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Map<String, Object> actualizarEstado(Long pedidoLineaId, Locale locale) {
|
||||
|
||||
PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null);
|
||||
@ -317,8 +328,8 @@ public class PedidoService {
|
||||
"message", messageSource.getMessage("pedido.errors.cannot-update", null, locale));
|
||||
}
|
||||
|
||||
// Rango: >= haciendo_ferro y < enviado
|
||||
if (estadoOld.getPriority() < PedidoLinea.Estado.haciendo_ferro.getPriority()
|
||||
// Rango: >= procesando_pedido y < enviado
|
||||
if (estadoOld.getPriority() < PedidoLinea.Estado.procesando_pedido.getPriority()
|
||||
|| estadoOld.getPriority() >= PedidoLinea.Estado.enviado.getPriority()) {
|
||||
return Map.of(
|
||||
"success", false,
|
||||
@ -645,7 +656,7 @@ public class PedidoService {
|
||||
private void saveDireccionesPedidoLinea(
|
||||
Map<String, Object> direcciones,
|
||||
Pedido pedido,
|
||||
PedidoLinea linea, Long direccionFacturacionId) {
|
||||
PedidoLinea linea) {
|
||||
|
||||
String email = pedido.getCreatedBy().getUserName();
|
||||
|
||||
@ -692,7 +703,12 @@ public class PedidoService {
|
||||
pedidoDireccionRepository.save(direccion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDireccionFacturacionPedido(Pedido pedido, Long direccionFacturacionId) {
|
||||
if (direccionFacturacionId != null) {
|
||||
String email = pedido.getCreatedBy().getUserName();
|
||||
|
||||
Direccion dirFact = direccionService.findById(direccionFacturacionId).orElse(null);
|
||||
if (dirFact != null) {
|
||||
HashMap<String, Object> dirFactMap = new HashMap<>();
|
||||
@ -713,7 +729,7 @@ public class PedidoService {
|
||||
false,
|
||||
dirFactMap,
|
||||
pedido,
|
||||
linea,
|
||||
null,
|
||||
false,
|
||||
true);
|
||||
pedidoDireccionRepository.save(direccion);
|
||||
@ -773,7 +789,7 @@ public class PedidoService {
|
||||
if (presupuestoService.hasMaquetacion(p)) {
|
||||
return Estado.maquetacion;
|
||||
} else {
|
||||
return Estado.haciendo_ferro;
|
||||
return Estado.procesando_pedido;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
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.i18n.TranslationService;
|
||||
import com.imprimelibros.erp.paises.PaisesService;
|
||||
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
|
||||
import com.imprimelibros.erp.users.UserDao;
|
||||
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
import jakarta.persistence.criteria.Subquery;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/pedidos")
|
||||
public class PedidosController {
|
||||
|
||||
private final PresupuestoService presupuestoService;
|
||||
|
||||
private final PedidoRepository repoPedido;
|
||||
private final PedidoService pedidoService;
|
||||
private final UserDao repoUser;
|
||||
@ -58,7 +58,7 @@ public class PedidosController {
|
||||
public PedidosController(PedidoRepository repoPedido, PedidoService pedidoService, UserDao repoUser,
|
||||
MessageSource messageSource, TranslationService translationService,
|
||||
PedidoLineaRepository repoPedidoLinea, PaisesService paisesService,
|
||||
FacturacionService facturacionService, PresupuestoService presupuestoService) {
|
||||
FacturacionService facturacionService) {
|
||||
this.repoPedido = repoPedido;
|
||||
this.pedidoService = pedidoService;
|
||||
this.repoUser = repoUser;
|
||||
@ -67,7 +67,6 @@ public class PedidosController {
|
||||
this.repoPedidoLinea = repoPedidoLinea;
|
||||
this.paisesService = paisesService;
|
||||
this.facturacionService = facturacionService;
|
||||
this.presupuestoService = presupuestoService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@ -111,6 +110,7 @@ public class PedidosController {
|
||||
"id",
|
||||
"createdBy.fullName",
|
||||
"createdAt",
|
||||
"titulos",
|
||||
"total",
|
||||
"estado");
|
||||
|
||||
@ -118,8 +118,10 @@ public class PedidosController {
|
||||
if (!isAdmin) {
|
||||
base = base.and((root, query, cb) -> cb.equal(root.get("createdBy").get("id"), currentUserId));
|
||||
}
|
||||
|
||||
String clientSearch = dt.getColumnSearch("cliente");
|
||||
String estadoSearch = dt.getColumnSearch("estado");
|
||||
String titulosSearch = dt.getColumnSearch("titulos");
|
||||
|
||||
// 2) Si hay filtro, traducirlo a userIds y añadirlo al Specification
|
||||
if (clientSearch != null) {
|
||||
@ -151,11 +153,45 @@ public class PedidosController {
|
||||
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);
|
||||
|
||||
return DataTable
|
||||
.of(repoPedido, Pedido.class, dt, searchable)
|
||||
.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("created_at", pedido -> Utils.formatInstant(pedido.getCreatedAt(), locale))
|
||||
.add("cliente", pedido -> {
|
||||
@ -171,6 +207,38 @@ public class PedidosController {
|
||||
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 -> {
|
||||
List<PedidoLinea> lineas = repoPedidoLinea.findByPedidoId(pedido.getId());
|
||||
if (lineas.isEmpty()) {
|
||||
@ -451,4 +519,4 @@ public class PedidosController {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,12 +48,12 @@ import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
|
||||
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.validation.PresupuestoValidationGroups;
|
||||
import com.imprimelibros.erp.users.UserDao;
|
||||
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.web.IpUtils;
|
||||
|
||||
|
||||
@ -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
|
||||
public Presupuesto clone() {
|
||||
@ -188,10 +173,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
||||
@Column(name = "iva_reducido")
|
||||
private Boolean ivaReducido;
|
||||
|
||||
@Column(name = "entrega_tipo")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Entrega entregaTipo;
|
||||
|
||||
@Column(name = "iva_importe_4", precision = 12, scale = 2)
|
||||
private BigDecimal ivaImporte4;
|
||||
|
||||
@ -300,6 +281,12 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
||||
@Column(name = "papel_interior_id")
|
||||
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)
|
||||
@Column(name = "gramaje_interior")
|
||||
private Integer gramajeInterior;
|
||||
@ -531,14 +518,6 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
||||
this.ivaReducido = ivaReducido;
|
||||
}
|
||||
|
||||
public Entrega getEntregaTipo() {
|
||||
return entregaTipo;
|
||||
}
|
||||
|
||||
public void setEntregaTipo(Entrega entregaTipo) {
|
||||
this.entregaTipo = entregaTipo;
|
||||
}
|
||||
|
||||
public BigDecimal getIvaImporte4() {
|
||||
return ivaImporte4;
|
||||
}
|
||||
@ -747,6 +726,22 @@ public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
||||
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() {
|
||||
return gramajeInterior;
|
||||
}
|
||||
|
||||
@ -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.databind.ObjectMapper;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -20,6 +18,8 @@ public class PresupuestoFormDataMapper {
|
||||
public Cubierta cubierta = new Cubierta();
|
||||
public Servicios servicios = new Servicios();
|
||||
public Integer selectedTirada;
|
||||
public Double lomo;
|
||||
public Double lomoCubierta;
|
||||
|
||||
// ===== Datos Generales =====
|
||||
public static class DatosGenerales {
|
||||
@ -37,7 +37,6 @@ public class PresupuestoFormDataMapper {
|
||||
public String paginasColor = "";
|
||||
public String posicionPaginasColor = "";
|
||||
public String tipoEncuadernacion = "fresado"; // enum name
|
||||
public String entregaTipo = "peninsula"; // enum name
|
||||
public boolean ivaReducido = true;
|
||||
}
|
||||
|
||||
@ -157,7 +156,6 @@ public class PresupuestoFormDataMapper {
|
||||
|
||||
vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado");
|
||||
|
||||
vm.datosGenerales.entregaTipo = enumName(p.getEntregaTipo(), "peninsula");
|
||||
vm.datosGenerales.ivaReducido = Boolean.TRUE.equals(p.getIvaReducido());
|
||||
|
||||
// ===== Interior
|
||||
@ -196,6 +194,10 @@ public class PresupuestoFormDataMapper {
|
||||
// ===== Selected tirada
|
||||
vm.selectedTirada = p.getSelectedTirada();
|
||||
|
||||
// ===== Lomos
|
||||
vm.lomo = p.getLomo();
|
||||
vm.lomoCubierta = p.getLomoCubierta();
|
||||
|
||||
// ===== Servicios desde JSONs
|
||||
vm.servicios.servicios = parse(p.getServiciosJson(),
|
||||
new TypeReference<List<PresupuestoFormDataDto.Servicios.DatosServicios>>() {
|
||||
@ -329,7 +329,7 @@ public class PresupuestoService {
|
||||
.filter(Objects::nonNull)
|
||||
.map(tirada -> tirada + 4)
|
||||
.collect(Collectors.toList()));
|
||||
if(presupuesto.getSelectedTirada() != null) {
|
||||
if (presupuesto.getSelectedTirada() != null) {
|
||||
presupuesto.setSelectedTirada(presupuesto.getSelectedTirada());
|
||||
}
|
||||
} else {
|
||||
@ -337,7 +337,7 @@ public class PresupuestoService {
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
|
||||
body.put("selectedTirada",
|
||||
presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : presupuesto.getTirada1());
|
||||
body.put("tamanio", tamanio);
|
||||
@ -352,7 +352,7 @@ public class PresupuestoService {
|
||||
body.put("cubierta", cubierta);
|
||||
body.put("guardas", null);
|
||||
// Para las reimpresiones
|
||||
if(presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) {
|
||||
if (presupuesto.getIsReimpresion() != null && presupuesto.getIsReimpresion()) {
|
||||
body.put("reimpresion", 1);
|
||||
body.put("iskn", presupuesto.getProveedorRef1());
|
||||
}
|
||||
@ -1124,9 +1124,9 @@ public class PresupuestoService {
|
||||
try {
|
||||
// retractilado: recalcular precio
|
||||
if (s.get("id").equals("retractilado")) {
|
||||
|
||||
|
||||
String p = obtenerPrecioRetractilado(cantidad);
|
||||
if(p != null){
|
||||
if (p != null) {
|
||||
double precio_retractilado = Double.parseDouble(p);
|
||||
s.put("price", precio_retractilado);
|
||||
} else {
|
||||
@ -1149,7 +1149,7 @@ public class PresupuestoService {
|
||||
}
|
||||
}
|
||||
try {
|
||||
if(presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada))
|
||||
if (presupuesto.getSelectedTirada() != null && presupuesto.getSelectedTirada().equals(tirada))
|
||||
presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios));
|
||||
} catch (Exception ignore) {
|
||||
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
|
||||
// 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
|
||||
// 4%
|
||||
if (presupuesto.getIvaReducido()) {
|
||||
ivaImporte4 = baseImponible.add(serviciosIva4).multiply(BigDecimal.valueOf(4)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
ivaImporte21 = serviciosTotal.subtract(serviciosIva4).multiply(BigDecimal.valueOf(21)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
} else {
|
||||
ivaImporte21 = baseImponible.add(serviciosTotal).multiply(BigDecimal.valueOf(21)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
}
|
||||
// Si el iva es reducido, el precio de la tirada y el del prototipo llevan IVA
|
||||
// 4%
|
||||
if (presupuesto.getIvaReducido()) {
|
||||
ivaImporte4 = baseImponible.add(serviciosIva4).multiply(BigDecimal.valueOf(4)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
ivaImporte21 = serviciosTotal.subtract(serviciosIva4).multiply(BigDecimal.valueOf(21)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
} else {
|
||||
ivaImporte21 = baseImponible.add(serviciosTotal).multiply(BigDecimal.valueOf(21)).divide(
|
||||
BigDecimal.valueOf(100), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
}
|
||||
baseImponible = baseImponible.add(serviciosTotal);
|
||||
BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4);
|
||||
@ -1332,10 +1330,10 @@ public class PresupuestoService {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public Boolean hasMaquetacion(Presupuesto presupuesto) {
|
||||
if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isEmpty()) {
|
||||
if(presupuesto.getServiciosJson().contains("maquetacion")) {
|
||||
if (presupuesto.getServiciosJson().contains("maquetacion")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1474,7 +1472,6 @@ public class PresupuestoService {
|
||||
target.setServiciosTotal(src.getServiciosTotal());
|
||||
target.setBaseImponible(src.getBaseImponible());
|
||||
target.setIvaReducido(src.getIvaReducido());
|
||||
target.setEntregaTipo(src.getEntregaTipo());
|
||||
target.setIvaImporte4(src.getIvaImporte4());
|
||||
target.setIvaImporte21(src.getIvaImporte21());
|
||||
target.setTotalConIva(src.getTotalConIva());
|
||||
|
||||
@ -29,6 +29,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||
@ -377,7 +378,7 @@ public class UserController {
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@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);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
spring.application.name=erp
|
||||
# Active profile
|
||||
#spring.profiles.active=local
|
||||
spring.profiles.active=dev
|
||||
spring.profiles.active=local
|
||||
#spring.profiles.active=dev
|
||||
#spring.profiles.active=test
|
||||
#spring.profiles.active=prod
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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'
|
||||
)
|
||||
@ -1,4 +1,4 @@
|
||||
databaseChangeLog:
|
||||
databaseChangeLog:
|
||||
- include:
|
||||
file: db/changelog/changesets/0001-baseline.yml
|
||||
- include:
|
||||
@ -48,4 +48,13 @@ databaseChangeLog:
|
||||
- include:
|
||||
file: db/changelog/changesets/0024-series-facturacion-seeder.yml
|
||||
- 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
|
||||
|
||||
|
||||
@ -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.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.fillBillingAddressItems=Debe seleccionar una dirección de facturación para poder realizar el pago.
|
||||
|
||||
|
||||
cart.resumen.title=Resumen de la cesta
|
||||
cart.resumen.base=Base imponible
|
||||
|
||||
@ -22,10 +22,12 @@ pedido.estado.pendiente_pago=Pendiente de pago
|
||||
pedido.estado.procesando_pago=Procesando pago
|
||||
pedido.estado.denegado_pago=Pago denegado
|
||||
pedido.estado.aprobado=Aprobado
|
||||
pedido.estado.procesando_pedido=Procesando pedido
|
||||
pedido.estado.maquetacion=Maquetación
|
||||
pedido.estado.haciendo_ferro=Haciendo ferro
|
||||
pedido.estado.esperando_aceptacion_ferro=Esperando aceptación de ferro
|
||||
pedido.estado.ferro_cliente=Esperando aprobación de ferro
|
||||
pedido.estado.haciendo_ferro_digital=Haciendo ferro digital
|
||||
pedido.estado.esperando_aceptacion_ferro_digital=Esperando aceptación de ferro digital
|
||||
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.terminado=Terminado
|
||||
pedido.estado.enviado=Enviado
|
||||
@ -47,6 +49,8 @@ pedido.prueba=Prueba
|
||||
pedido.table.id=Num. Pedido
|
||||
pedido.table.cliente=Cliente
|
||||
pedido.table.fecha=Fecha
|
||||
pedido.table.titulos=Títulos
|
||||
pedido.table.titulos.and-more=y {0} más
|
||||
pedido.table.importe=Importe
|
||||
pedido.table.estado=Estado
|
||||
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.success.estado-actualizado=Estado del pedido actualizado correctamente.
|
||||
pedido.success.same-estado=Sin cambios en el estado.
|
||||
pedido.success.pedido-cancelado=Pedido cancelado correctamente.
|
||||
pedido.success.pedido-cancelado=Pedido cancelado correctamente.
|
||||
|
||||
@ -18,8 +18,8 @@ $(() => {
|
||||
|
||||
$('#addBillingAddressBtn').on('click', seleccionarDireccionEnvio);
|
||||
|
||||
$('#authorization-required').on('change', function () {
|
||||
if($(this).is(':checked')) {
|
||||
$(document).on('change', '#authorization-required', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
if ($('#direccion-div .direccion-card').length > 0) {
|
||||
$('#btn-checkout').prop('disabled', false);
|
||||
}
|
||||
@ -148,6 +148,24 @@ $(() => {
|
||||
$('#btn-checkout').prop('disabled', false);
|
||||
}
|
||||
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;
|
||||
}
|
||||
hideLoader();
|
||||
@ -164,6 +182,19 @@ $(() => {
|
||||
$card.remove();
|
||||
$('#addBillingAddressBtn').removeClass('d-none');
|
||||
$('#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
|
||||
data: $form.serialize(),
|
||||
dataType: 'html',
|
||||
success: function (html) {
|
||||
success: async function (html) {
|
||||
// Si por cualquier motivo llega 200 con fragmento, lo insertamos igual
|
||||
if (typeof html === 'string' && html.indexOf('id="direccionForm"') !== -1 && html.indexOf('<html') === -1) {
|
||||
$('#direccionFormModalBody').html(html);
|
||||
@ -205,7 +236,8 @@ $(() => {
|
||||
}
|
||||
// Éxito real: cerrar y recargar tabla
|
||||
$('#direccionFormModal').modal('hide');
|
||||
seleccionarDireccionEnvio();
|
||||
await seleccionarDireccionEnvio();
|
||||
|
||||
},
|
||||
error: function (xhr) {
|
||||
// Con 422 devolvemos el fragmento con errores aquí
|
||||
|
||||
@ -71,7 +71,7 @@ $(() => {
|
||||
// -----------------------------
|
||||
$table.on('click', '.btn-view-factura', function () {
|
||||
const row = dt.row($(this).closest('tr')).data();
|
||||
window.location.href = `/facturas/${row.id}`;
|
||||
window.open('/facturas/' + row.id, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
|
||||
// -----------------------------
|
||||
|
||||
@ -48,6 +48,7 @@ $(() => {
|
||||
{ data: 'id', name: 'id', orderable: true },
|
||||
{ data: 'cliente', name: 'createdBy.fullName', orderable: true },
|
||||
{ data: 'created_at', name: 'createdAt', orderable: true },
|
||||
{ data: 'titulos', name: 'titulos', orderable: true },
|
||||
{ data: 'total', name: 'total', orderable: true },
|
||||
{ data: 'estado', name: 'estado', orderable: true },
|
||||
{ data: 'actions', name: 'actions', orderable: false, searchable: false }
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
$(() => {
|
||||
$(document).on('click', '.btn-view', function () {
|
||||
let pedidoId = $(this).data('id');
|
||||
let url = `/pedidos/view/${pedidoId}`;
|
||||
window.location.href = url;
|
||||
if(!pedidoId) return;
|
||||
window.open('/pedidos/view/' + pedidoId, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-pay', async function () {
|
||||
|
||||
@ -47,6 +47,7 @@ $(() => {
|
||||
columns: [
|
||||
{ data: 'id', name: 'id', orderable: true },
|
||||
{ data: 'created_at', name: 'createdAt', orderable: true },
|
||||
{ data: 'titulos', name: 'titulos', orderable: true },
|
||||
{ data: 'total', name: 'total', orderable: true },
|
||||
{ data: 'estado', name: 'estado', orderable: true },
|
||||
{ data: 'actions', name: 'actions', orderable: false, searchable: false }
|
||||
|
||||
@ -34,7 +34,6 @@ export default class PresupuestoWizard {
|
||||
paginasColor: 0,
|
||||
posicionPaginasColor: '',
|
||||
tipoEncuadernacion: 'fresado',
|
||||
entregaTipo: 'peninsula',
|
||||
ivaReducido: true,
|
||||
},
|
||||
interior: {
|
||||
@ -108,6 +107,8 @@ export default class PresupuestoWizard {
|
||||
}
|
||||
},
|
||||
selectedTirada: 10,
|
||||
lomo: 0,
|
||||
lomoCubierta: 0
|
||||
}
|
||||
|
||||
// pestaña datos generales
|
||||
@ -130,7 +131,6 @@ export default class PresupuestoWizard {
|
||||
this.divPosicionPaginasColor = $('#div-posicion-paginas-color');
|
||||
this.posicionPaginasColor = $('#posicionPaginasColor');
|
||||
this.paginas = $('#paginas');
|
||||
this.entregaTipo = $('#entregaTipo');
|
||||
this.ivaReducido = $('#iva-reducido');
|
||||
this.btnIvaReducidoDetail = $('#btn-iva-reducido-detail');
|
||||
this.datos_generales_alert = $('#datos-generales-alert');
|
||||
@ -445,7 +445,9 @@ export default class PresupuestoWizard {
|
||||
...this.#getDatosGeneralesData(),
|
||||
...this.#getInteriorData(),
|
||||
...this.#getCubiertaData(),
|
||||
selectedTirada: this.formData.selectedTirada
|
||||
selectedTirada: this.formData.selectedTirada,
|
||||
lomo: this.formData.lomo,
|
||||
lomoCubierta: this.formData.lomoCubierta
|
||||
};
|
||||
|
||||
const sobrecubierta = data.sobrecubierta;
|
||||
@ -666,7 +668,6 @@ export default class PresupuestoWizard {
|
||||
paginasColor: this.paginasColor.val(),
|
||||
posicionPaginasColor: this.posicionPaginasColor.val(),
|
||||
tipoEncuadernacion: $('.tipo-libro input:checked').val() || 'fresado',
|
||||
entregaTipo: this.entregaTipo.val(),
|
||||
ivaReducido: this.ivaReducido.is(':checked'),
|
||||
};
|
||||
}
|
||||
@ -688,7 +689,6 @@ export default class PresupuestoWizard {
|
||||
paginasColor: data.paginasColor,
|
||||
posicionPaginasColor: data.posicionPaginasColor,
|
||||
tipoEncuadernacion: data.tipoEncuadernacion,
|
||||
entregaTipo: data.entregaTipo,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -970,6 +969,7 @@ export default class PresupuestoWizard {
|
||||
} else {
|
||||
const maxSolapas = data.solapas ?? 120;
|
||||
const lomo = data.lomo ?? 0;
|
||||
|
||||
$('.solapas-presupuesto').attr('max', maxSolapas);
|
||||
$('.max-solapa-text').text(function (_, textoActual) {
|
||||
return textoActual.replace(/\d+/, maxSolapas);
|
||||
@ -1023,6 +1023,7 @@ export default class PresupuestoWizard {
|
||||
|
||||
const dataCubierta = this.#getCubiertaData();
|
||||
this.#updateCubiertaData(dataCubierta);
|
||||
this.formData.lomo = lomo;
|
||||
this.#cacheFormData();
|
||||
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ import { preguntarTipoPresupuesto, duplicar, reimprimir } from './presupuesto-ut
|
||||
e.preventDefault();
|
||||
const id = $(this).data('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();
|
||||
const id = $(this).data('id');
|
||||
if (id) {
|
||||
window.location.href = '/presupuesto/edit/' + id;
|
||||
window.open('/presupuesto/edit/' + id, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -55,7 +55,11 @@ export async function preguntarTipoPresupuesto() {
|
||||
url: 'users/api/get-users', // ajusta a tu endpoint
|
||||
dataType: 'json',
|
||||
delay: 250,
|
||||
data: (params) => ({ q: params.term }),
|
||||
data: (params) => ({
|
||||
q: params.term || '',
|
||||
page: params.page || 1,
|
||||
size: 10,
|
||||
}),
|
||||
processResults: data => ({
|
||||
results: data.results,
|
||||
pagination: data.pagination
|
||||
|
||||
@ -56,6 +56,8 @@
|
||||
<h5 class="fs-14 text-truncate mb-1">
|
||||
<span th:text="#{cart.item.presupuesto-numero}">Presupuesto #</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>
|
||||
|
||||
<ul class="list-unstyled text-muted mb-1 ps-0">
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
<form th:action="@{/pagos/redsys/crear}" method="post">
|
||||
<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" id="dirFactId" name="dirFactId" value="" />
|
||||
<button id="btn-checkout" type="submit" class="btn btn-secondary w-100 mt-2"
|
||||
|
||||
@ -50,6 +50,9 @@
|
||||
<div>
|
||||
<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"
|
||||
th:text="#{cart.shipping.add}">Añadir dirección
|
||||
</button>
|
||||
|
||||
61
src/main/resources/templates/imprimelibros/error/error.html
Normal file
61
src/main/resources/templates/imprimelibros/error/error.html
Normal 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>
|
||||
@ -37,6 +37,7 @@
|
||||
<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.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.estado}">Estado</th>
|
||||
<th class="text-start" scope="col" th:text="#{pedido.table.acciones}">Acciones</th>
|
||||
@ -44,6 +45,7 @@
|
||||
<tr>
|
||||
<th><input type="text" class="form-control form-control-sm input-filter" data-col="id" /></th>
|
||||
<th></th>
|
||||
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
|
||||
<th></th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm input-filter" data-col="estado">
|
||||
|
||||
@ -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.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.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.estado}">Estado</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="createdBy.fullName" /></th>
|
||||
<th></th>
|
||||
<th><input type="text" class="form-control form-control-sm input-filter" data-col="titulos" /></th>
|
||||
<th></th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm input-filter" data-col="estado">
|
||||
|
||||
@ -302,19 +302,6 @@
|
||||
</label>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user