diff --git a/pom.xml b/pom.xml index cf69953..d1d4ba6 100644 --- a/pom.xml +++ b/pom.xml @@ -191,6 +191,16 @@ ${project.basedir}/src/main/resources/lib/org.json.jar + + org.apache.xmlgraphics + batik-transcoder + 1.17 + + + org.apache.xmlgraphics + batik-codec + 1.17 + diff --git a/src/main/java/com/imprimelibros/erp/checkout/CheckoutController.java b/src/main/java/com/imprimelibros/erp/checkout/CheckoutController.java index c33ee1d..eabd393 100644 --- a/src/main/java/com/imprimelibros/erp/checkout/CheckoutController.java +++ b/src/main/java/com/imprimelibros/erp/checkout/CheckoutController.java @@ -22,7 +22,6 @@ import com.imprimelibros.erp.direcciones.DireccionService; import com.imprimelibros.erp.cart.Cart; import com.imprimelibros.erp.cart.CartService; - @Controller @RequestMapping("/checkout") public class CheckoutController { @@ -49,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"); @@ -63,20 +62,23 @@ public class CheckoutController { return "imprimelibros/checkout/checkout"; // crea esta vista si quieres (tabla simple) } - @GetMapping({"/get-summary", "/get-summary/{direccionId}"}) - public String getCheckoutSummary(@PathVariable(required = false) Long direccionId, Principal principal, Model model, Locale locale) { + @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) { + if (direccionId != null) { hasTaxes = direccionService.hasTaxes(direccionId); } Map 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) { diff --git a/src/main/java/com/imprimelibros/erp/error/ErrorPageController.java b/src/main/java/com/imprimelibros/erp/error/ErrorPageController.java new file mode 100644 index 0000000..725eb5e --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/error/ErrorPageController.java @@ -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 + } +} \ No newline at end of file diff --git a/src/main/java/com/imprimelibros/erp/externalApi/skApiClient.java b/src/main/java/com/imprimelibros/erp/externalApi/skApiClient.java index bbeb5f7..3efbd41 100644 --- a/src/main/java/com/imprimelibros/erp/externalApi/skApiClient.java +++ b/src/main/java/com/imprimelibros/erp/externalApi/skApiClient.java @@ -128,6 +128,62 @@ public class skApiClient { }); } + public Map getLomos(Map requestBody) { + try { + String jsonResponse = performWithRetry(() -> { + String url = this.skApiUrl + "api/get-lomos"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(authService.getToken()); // token actualizado + + Map data = new HashMap<>(); + data.put("clienteId", requestBody.get("clienteId")); + data.put("tamanio", requestBody.get("tamanio")); + data.put("tirada", requestBody.get("tirada")); + data.put("paginas", requestBody.get("paginas")); + data.put("paginasColor", requestBody.get("paginasColor")); + data.put("papelInteriorDiferente", 0); + data.put("paginasCuadernillo", requestBody.get("paginasCuadernillo")); + data.put("tipo", requestBody.get("tipo")); + data.put("isColor", requestBody.get("isColor")); + data.put("isHq", requestBody.get("isHq")); + data.put("interior", requestBody.get("interior")); + data.put("cubierta", requestBody.get("cubierta")); + + HttpEntity> entity = new HttpEntity<>(data, headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + entity, + String.class); + + return response.getBody(); + }); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(jsonResponse); + + if (root.get("lomoInterior") == null || !root.get("lomoInterior").isDouble()) { + throw new RuntimeException(); + } + + Double lomoInterior = root.get("lomoInterior").asDouble(); + Double lomoCubierta = root.get("lomoCubierta").asDouble(); + return Map.of( + "lomoInterior", lomoInterior, + "lomoCubierta", lomoCubierta); + + } catch (JsonProcessingException e) { + // Fallback al 80% del ancho + return Map.of( + "lomoInterior", 0.0, + "lomoCubierta", 0.0); + + } + } + public Map savePresupuesto(Map requestBody) { return performWithRetryMap(() -> { String url = this.skApiUrl + "api/guardar"; diff --git a/src/main/java/com/imprimelibros/erp/payments/PaymentController.java b/src/main/java/com/imprimelibros/erp/payments/PaymentController.java index ebb12c8..ffbbbcc 100644 --- a/src/main/java/com/imprimelibros/erp/payments/PaymentController.java +++ b/src/main/java/com/imprimelibros/erp/payments/PaymentController.java @@ -184,11 +184,14 @@ public class PaymentController { // Campos ordenables List orderable = List.of( - "transferId", + "client", + "transfer_id", "status", "amountCents", - "payment.amountRefundedCents", - "createdAt", "updatedAt"); + "amountCentsRefund", + "payment.orderId", + "createdAt", + "processedAt"); Specification 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.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.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 -> diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java index 6b8a7d0..7679978 100644 --- a/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoLinea.java @@ -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; diff --git a/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java b/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java index 1a59055..342998f 100644 --- a/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java +++ b/src/main/java/com/imprimelibros/erp/pedidos/PedidoService.java @@ -271,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)); @@ -311,6 +311,7 @@ public class PedidoService { return true; } + @Transactional public Map actualizarEstado(Long pedidoLineaId, Locale locale) { PedidoLinea pedidoLinea = pedidoLineaRepository.findById(pedidoLineaId).orElse(null); @@ -327,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, @@ -788,7 +789,7 @@ public class PedidoService { if (presupuestoService.hasMaquetacion(p)) { return Estado.maquetacion; } else { - return Estado.haciendo_ferro; + return Estado.procesando_pedido; } } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java index 2dfaefc..7876f73 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.http.CacheControl; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import jakarta.validation.Validator; @@ -48,12 +49,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; @@ -151,7 +152,8 @@ public class PresupuestoController { return ResponseEntity.badRequest().body(errores); } Map resultado = new HashMap<>(); - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); resultado.putAll(presupuestoService.obtenerOpcionesAcabadosCubierta(presupuesto, locale)); @@ -273,7 +275,8 @@ public class PresupuestoController { } } - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); @@ -309,10 +312,11 @@ public class PresupuestoController { presupuesto.setGramajeInterior(Integer.parseInt(opciones.get(0))); // Asignar primera opción } } - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); - + return ResponseEntity.ok(resultado); } @@ -335,10 +339,11 @@ public class PresupuestoController { } Map resultado = new HashMap<>(); - Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), locale); + Map datosInterior = apiClient.getMaxSolapas(presupuestoService.toSkApiRequest(presupuesto), + locale); resultado.put("solapas", datosInterior.get("maxSolapas")); resultado.put("lomo", datosInterior.get("lomo")); - + return ResponseEntity.ok(resultado); } @@ -650,8 +655,7 @@ public class PresupuestoController { "presupuesto.reimprimir.success.title", "presupuesto.reimprimir.success.text", "presupuesto.reimprimir.error.title", - "presupuesto.reimprimir.error.internal" - ); + "presupuesto.reimprimir.error.internal"); Map translations = translationService.getTranslations(locale, keys); model.addAttribute("languageBundle", translations); @@ -676,7 +680,7 @@ public class PresupuestoController { "presupuesto.add.error.save.title", "presupuesto.iva-reducido", "presupuesto.iva-reducido-descripcion", - "presupuesto.duplicar.title", + "presupuesto.duplicar.title", "presupuesto.duplicar.text", "presupuesto.duplicar.confirm", "presupuesto.duplicar.cancelar", @@ -908,6 +912,14 @@ public class PresupuestoController { return ResponseEntity.badRequest().body(errores); } + try { + Map lomos = presupuestoService.obtenerLomos(presupuesto); + presupuesto.setLomo(Double.valueOf(lomos.get("lomoInterior").toString())); + presupuesto.setLomoCubierta(Double.valueOf(lomos.get("lomoCubierta").toString())); + } catch (Exception ex) { + log.error("Error al obtener lomos desde SK API", ex); + } + try { Map saveResult = presupuestoService.guardarPresupuesto( @@ -933,6 +945,30 @@ public class PresupuestoController { } } + @GetMapping("/api/plantilla-cubierta.png") + public ResponseEntity getPlantillaCubierta( + @RequestParam("tipo") String tipoLibro, + @RequestParam("tapa") String tapa, + @RequestParam("ancho") Integer ancho, + @RequestParam("alto") Integer alto, + @RequestParam("lomo") Integer lomo, + @RequestParam("solapas") Integer solapas, + Locale locale) { + + Presupuesto.TipoEncuadernacion tipoEncuadernacion = Presupuesto.TipoEncuadernacion.valueOf(tipoLibro); + Presupuesto.TipoCubierta tipoCubierta = Presupuesto.TipoCubierta.valueOf(tapa); + + byte[] png = presupuestoService.obtenerPlantillaCubierta(tipoEncuadernacion, tipoCubierta, ancho, alto, lomo, + solapas, locale); + if (png == null) + return ResponseEntity.notFound().build(); + + return ResponseEntity.ok() + .cacheControl(CacheControl.noCache()) + .contentType(MediaType.IMAGE_PNG) + .body(png); + } + @PostMapping("/{id}/comentario") @ResponseBody public String actualizarComentario(@PathVariable Long id, @@ -944,18 +980,17 @@ public class PresupuestoController { @PostMapping("/api/duplicar/{id}") @ResponseBody public Map duplicarPresupuesto( - @PathVariable Long id, - @RequestParam(name = "titulo", defaultValue = "") String titulo) { - + @PathVariable Long id, + @RequestParam(name = "titulo", defaultValue = "") String titulo) { + Long entity = presupuestoService.duplicarPresupuesto(id, titulo); return Map.of("id", entity); } - @PostMapping("/api/reimprimir/{id}") @ResponseBody public Map reimprimirPresupuesto(@PathVariable Long id) { - + Long entity = presupuestoService.reimprimirPresupuesto(id); return Map.of("id", entity); } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java b/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java index 2669aed..7c3f2cc 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/classes/PresupuestoFormatter.java @@ -126,6 +126,13 @@ public class PresupuestoFormatter { }, locale); } + textoResumen += ms.getMessage( + "presupuesto.resumen-lomos", + new Object[] { + p.getLomo() != null ? Math.round(p.getLomo()) : "N/D", + p.getLomoCubierta() != null ? Math.round(p.getLomoCubierta()) : "N/D" + }, + locale); textoResumen += ms.getMessage("presupuesto.resumen-texto-end", null, locale); return textoResumen; diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/dto/Presupuesto.java b/src/main/java/com/imprimelibros/erp/presupuesto/dto/Presupuesto.java index b8408cf..79bb623 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/dto/Presupuesto.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/dto/Presupuesto.java @@ -281,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; @@ -720,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; } diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java b/src/main/java/com/imprimelibros/erp/presupuesto/dto/PresupuestoFormDataMapper.java similarity index 98% rename from src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java rename to src/main/java/com/imprimelibros/erp/presupuesto/dto/PresupuestoFormDataMapper.java index 529980b..9a1ff32 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/dto/PresupuestoFormDataMapper.java @@ -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 { @@ -194,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>() { diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java index a92c8cd..fe2fbb3 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java @@ -6,6 +6,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.Locale; @@ -42,6 +44,15 @@ import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesReposito import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository; import com.imprimelibros.erp.users.UserDao; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.springframework.core.io.ClassPathResource; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; + import jakarta.servlet.http.HttpServletRequest; import com.imprimelibros.erp.externalApi.skApiClient; @@ -1297,6 +1308,76 @@ public class PresupuestoService { } + public Map obtenerLomos(Presupuesto presupuesto) { + try { + Map response = apiClient.getLomos(this.toSkApiRequest(presupuesto)); + + return response; + } catch (Exception e) { + System.out.println("Error obteniendo lomos: " + e.getMessage()); + return Map.of(); + } + } + + public byte[] obtenerPlantillaCubierta( + Presupuesto.TipoEncuadernacion tipoLibro, + Presupuesto.TipoCubierta tapa, + Integer ancho, + Integer alto, + Integer lomo, + Integer solapas, + Locale locale) { + + try { + String plantillaName = "plantilla"; + if (tipoLibro == Presupuesto.TipoEncuadernacion.grapado) { + plantillaName += "-grapado"; + } + if (solapas > 0) { + plantillaName += "-solapas"; + } + plantillaName += ".svg"; + + Integer sangrado = 5; // mm, + if (tapa != Presupuesto.TipoCubierta.tapaBlanda && tipoLibro != Presupuesto.TipoEncuadernacion.grapado) { + sangrado = 20; + } + + Integer ancho_t = lomo + sangrado * 2 + ancho * 2 + solapas * 2; + Integer alto_t = alto + sangrado * 2; + + // 3) Leer SVG template como texto + String basePath = "static/assets/images/imprimelibros/presupuestador/templates-cubierta/"; + String svg = readClasspathText(basePath + plantillaName); + + // 4) Sustituciones {{...}} + Map vars = new HashMap<>(); + NumberFormat nf = NumberFormat.getIntegerInstance(locale); + + vars.put("ancho", nf.format(ancho)); // mm o lo que representes + vars.put("alto", nf.format(alto)); + vars.put("ancho_t", nf.format(ancho_t)); + vars.put("alto_t", nf.format(alto_t)); + vars.put("lomo_t", nf.format(lomo != null ? lomo : 0)); + vars.put("solapa_t", nf.format(solapas != null ? solapas : 0)); + vars.put("sangrado", nf.format(sangrado)); + vars.put("portada", messageSource.getMessage("presupuesto.plantilla-cubierta.portada", null, locale)); + vars.put("contraportada", + messageSource.getMessage("presupuesto.plantilla-cubierta.contraportada", null, locale)); + vars.put("lomo", messageSource.getMessage("presupuesto.plantilla-cubierta.lomo", null, locale)); + vars.put("solapa", messageSource.getMessage("presupuesto.plantilla-cubierta.solapa", null, locale)); + svg = replaceMustache(svg, vars); + + // 5) Render SVG -> PNG (Batik) + svg = sanitizeForBatik(svg); + return svgToPng(svg, /* dpi */ 300f); + + } catch (Exception e) { + System.err.println("Error obteniendo plantilla de cubierta: " + e.getMessage()); + return null; + } + } + /** * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. */ @@ -1540,4 +1621,77 @@ public class PresupuestoService { return ip; } + private static String readClasspathText(String path) throws IOException { + ClassPathResource res = new ClassPathResource(path); + try (InputStream in = res.getInputStream()) { + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } + } + + private static String replaceMustache(String svg, Map vars) { + String out = svg; + for (var entry : vars.entrySet()) { + out = out.replace("{{" + entry.getKey() + "}}", entry.getValue()); + } + return out; + } + + private static byte[] svgToPng(String svgXml, float dpi) throws Exception { + PNGTranscoder t = new PNGTranscoder(); + + // Esto SÍ es correcto (convierte unidades a mm según DPI) + t.addTranscodingHint(PNGTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, 25.4f / dpi); + + // ❌ NO uses KEY_AOI con null (provoca tu error) + // t.addTranscodingHint(PNGTranscoder.KEY_AOI, null); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + TranscoderInput input = new TranscoderInput(new StringReader(svgXml)); + TranscoderOutput output = new TranscoderOutput(out); + t.transcode(input, output); + return out.toByteArray(); + } + } + + private static String sanitizeForBatik(String svg) { + String out = svg; + + // 1) Batik: context-stroke/context-fill + out = out.replace("context-stroke", "#000"); + out = out.replace("context-fill", "none"); + + // 2) Batik: auto-start-reverse + out = out.replace("auto-start-reverse", "auto"); + + // 3) Reemplazar markers Triangle*/marker6* -> Arrow2S* + out = replaceMarkerAttr(out, "marker-start", "Arrow2Sstart"); + out = replaceMarkerAttr(out, "marker-end", "Arrow2Send"); + + // 4) Lo MISMO pero cuando viene dentro de style="...marker-start:url(#X);..." + out = replaceMarkerInStyle(out, "marker-start", "Arrow2Sstart"); + out = replaceMarkerInStyle(out, "marker-end", "Arrow2Send"); + + return out; + } + + private static String replaceMarkerAttr(String svg, String attr, String newId) { + // Soporta: marker-start="url(#Triangle-5)" o marker-start='url( #Triangle-5 )' + Pattern p = Pattern.compile( + "(" + attr + + "\\s*=\\s*)([\"'])\\s*url\\(\\s*#(Triangle[^\\s\\)\"']*|marker6[^\\s\\)\"']*)\\s*\\)\\s*\\2", + Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(svg); + return m.replaceAll("$1$2url(#" + newId + ")$2"); + } + + private static String replaceMarkerInStyle(String svg, String prop, String newId) { + // Soporta dentro de style: marker-start:url(#Triangle-5) (con espacios + // opcionales) + Pattern p = Pattern.compile( + "(" + prop + "\\s*:\\s*)url\\(\\s*#(Triangle[^\\s\\)\"';]*|marker6[^\\s\\)\"';]*)\\s*\\)", + Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(svg); + return m.replaceAll("$1url(#" + newId + ")"); + } + } diff --git a/src/main/resources/db/changelog/changesets/0027-add-lomo-to-presupuesto.yml b/src/main/resources/db/changelog/changesets/0027-add-lomo-to-presupuesto.yml new file mode 100644 index 0000000..903dbca --- /dev/null +++ b/src/main/resources/db/changelog/changesets/0027-add-lomo-to-presupuesto.yml @@ -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 diff --git a/src/main/resources/db/changelog/changesets/0028-add-lomo-cubierta-to-presupuesto.yml b/src/main/resources/db/changelog/changesets/0028-add-lomo-cubierta-to-presupuesto.yml new file mode 100644 index 0000000..e78350c --- /dev/null +++ b/src/main/resources/db/changelog/changesets/0028-add-lomo-cubierta-to-presupuesto.yml @@ -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 diff --git a/src/main/resources/db/changelog/changesets/0029-update-estados-pedidos-lineas.yml b/src/main/resources/db/changelog/changesets/0029-update-estados-pedidos-lineas.yml new file mode 100644 index 0000000..f703d65 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/0029-update-estados-pedidos-lineas.yml @@ -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' + ) diff --git a/src/main/resources/db/changelog/master.yml b/src/main/resources/db/changelog/master.yml index 975bb53..9a28114 100644 --- a/src/main/resources/db/changelog/master.yml +++ b/src/main/resources/db/changelog/master.yml @@ -1,53 +1,59 @@ -databaseChangeLog: - - include: - file: db/changelog/changesets/0001-baseline.yml - - include: - file: db/changelog/changesets/0002-create-pedidos.yml - - include: - file: db/changelog/changesets/0003-create-paises.yml - - include: - file: db/changelog/changesets/0004-create-direcciones.yml - - include: - file: db/changelog/changesets/0005-add-carts-onlyoneshipment.yml - - include: - file: db/changelog/changesets/0006-add-cart-direcciones.yml - - include: - file: db/changelog/changesets/0007-payments-core.yml - - include: - file: db/changelog/changesets/0008-update-cart-status-constraint.yml - - include: - file: db/changelog/changesets/0009-add-composite-unique-txid-type.yml - - include: - file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml - - include: - file: db/changelog/changesets/0011-update-pedidos-presupuesto.yml - - include: - file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml - - include: - file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml - - include: - file: db/changelog/changesets/0014-create-pedidos-direcciones.yml - - include: - file: db/changelog/changesets/0015-alter-pedidos-lineas-and-presupuesto-estados.yml - - include: - file: db/changelog/changesets/0016-fix-enum-estado-pedidos-lineas.yml - - include: - file: db/changelog/changesets/0017-add-fecha-entrega-to-pedidos-lineas.yml - - include: - file: db/changelog/changesets/0018-change-presupuesto-ch-3.yml - - include: - file: db/changelog/changesets/0019-add-estados-pago-to-pedidos-lineas.yml - - include: - file: db/changelog/changesets/0020-add-estados-pago-to-pedidos-lineas-2.yml - - include: - file: db/changelog/changesets/0021-add-email-and-is-palets-to-pedidos-direcciones.yml - - include: - file: db/changelog/changesets/0022-add-estados-pago-to-pedidos-lineas-3.yml - - include: - file: db/changelog/changesets/0023-facturacion.yml - - include: - file: db/changelog/changesets/0024-series-facturacion-seeder.yml - - include: - file: db/changelog/changesets/0025-create-facturas-direcciones.yml - - include: - file: db/changelog/changesets/0026-drop-entrega-tipo-from-presupuesto.yml +databaseChangeLog: + - include: + file: db/changelog/changesets/0001-baseline.yml + - include: + file: db/changelog/changesets/0002-create-pedidos.yml + - include: + file: db/changelog/changesets/0003-create-paises.yml + - include: + file: db/changelog/changesets/0004-create-direcciones.yml + - include: + file: db/changelog/changesets/0005-add-carts-onlyoneshipment.yml + - include: + file: db/changelog/changesets/0006-add-cart-direcciones.yml + - include: + file: db/changelog/changesets/0007-payments-core.yml + - include: + file: db/changelog/changesets/0008-update-cart-status-constraint.yml + - include: + file: db/changelog/changesets/0009-add-composite-unique-txid-type.yml + - include: + file: db/changelog/changesets/0010-drop-unique-tx-gateway.yml + - include: + file: db/changelog/changesets/0011-update-pedidos-presupuesto.yml + - include: + file: db/changelog/changesets/0012--drop-unique-gateway-txid-2.yml + - include: + file: db/changelog/changesets/0013-drop-unique-refund-gateway-id.yml + - include: + file: db/changelog/changesets/0014-create-pedidos-direcciones.yml + - include: + file: db/changelog/changesets/0015-alter-pedidos-lineas-and-presupuesto-estados.yml + - include: + file: db/changelog/changesets/0016-fix-enum-estado-pedidos-lineas.yml + - include: + file: db/changelog/changesets/0017-add-fecha-entrega-to-pedidos-lineas.yml + - include: + file: db/changelog/changesets/0018-change-presupuesto-ch-3.yml + - include: + file: db/changelog/changesets/0019-add-estados-pago-to-pedidos-lineas.yml + - include: + file: db/changelog/changesets/0020-add-estados-pago-to-pedidos-lineas-2.yml + - include: + file: db/changelog/changesets/0021-add-email-and-is-palets-to-pedidos-direcciones.yml + - include: + file: db/changelog/changesets/0022-add-estados-pago-to-pedidos-lineas-3.yml + - include: + file: db/changelog/changesets/0023-facturacion.yml + - include: + file: db/changelog/changesets/0024-series-facturacion-seeder.yml + - include: + 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 diff --git a/src/main/resources/i18n/cart_es.properties b/src/main/resources/i18n/cart_es.properties index 30c633a..b1a5120 100644 --- a/src/main/resources/i18n/cart_es.properties +++ b/src/main/resources/i18n/cart_es.properties @@ -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 diff --git a/src/main/resources/i18n/pedidos_es.properties b/src/main/resources/i18n/pedidos_es.properties index c770f4c..376c7bb 100644 --- a/src/main/resources/i18n/pedidos_es.properties +++ b/src/main/resources/i18n/pedidos_es.properties @@ -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 diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties index 9253013..7c5a1de 100644 --- a/src/main/resources/i18n/presupuesto_es.properties +++ b/src/main/resources/i18n/presupuesto_es.properties @@ -117,6 +117,10 @@ presupuesto.continuar-cubierta=Diseño cubierta # Pestaña cubierta presupuesto.plantilla-cubierta=Plantilla de cubierta presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.


Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido. +presupuesto.plantilla-cubierta.portada=Portada +presupuesto.plantilla-cubierta.contraportada=Contraportada +presupuesto.plantilla-cubierta.lomo=Lomo +presupuesto.plantilla-cubierta.solapa=Solapa presupuesto.tipo-cubierta=Tipo de cubierta presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones presupuesto.tapa-blanda=Tapa blanda @@ -234,6 +238,7 @@ presupuesto.resumen-texto-acabado-cubierta=
  • Acabado: {0}.
  • presupuesto.resumen-texto-end= presupuesto.resumen-texto-sobrecubierta=
  • Sobrecubierta impresa en {0} {1} gr.
    • Acabado: {2}
    • Solapas: {3} mm.
  • presupuesto.resumen-texto-faja=
  • Faja impresa en {0} {1} gr. con un alto de {2} mm.
    • Acabado: {3}
    • Solapas: {4} mm.
  • +presupuesto.resumen-lomos=
  • Dimensiones de los lomos:
    • Lomo interior: {0} mm
    • Lomo total: {1} mm
  • presupuesto.resumen-deposito-legal=Ejemplares para el Depósito Legal presupuesto.volver-extras=Extras del libro presupuesto.resumen.inicie-sesion=Inicie sesión para continuar diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg new file mode 100644 index 0000000..1ca6929 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado-solapas.svg @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{solapa}} + + + + + {{solapa}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + + {{solapa_t}} mm + + {{solapa_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + + + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg new file mode 100644 index 0000000..872c0c9 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-grapado.svg @@ -0,0 +1,502 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + + + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg new file mode 100644 index 0000000..13136e3 --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla-solapas.svg @@ -0,0 +1,748 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{solapa}} + + + + + {{solapa}} + + + + {{lomo}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{lomo_t}} mm + + {{solapa_t}} mm + + {{solapa_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg new file mode 100644 index 0000000..b605f7b --- /dev/null +++ b/src/main/resources/static/assets/images/imprimelibros/presupuestador/templates-cubierta/plantilla.svg @@ -0,0 +1,576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{contraportada}} + + + + {{portada}} + + + + {{lomo}} + + + {{alto}} mm + {{ancho}} mm + {{ancho}} mm + + {{ancho_t}} mm + {{lomo_t}} mm + {{alto_t}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + {{sangrado}} mm + + diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/checkout/checkout.js b/src/main/resources/static/assets/js/pages/imprimelibros/checkout/checkout.js index 76d9fab..5711dfd 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/checkout/checkout.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/checkout/checkout.js @@ -151,13 +151,14 @@ $(() => { if (direccionId) { $.ajax({ - url: `/checkout/get-summary/${direccionId}`, + 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.'); @@ -187,6 +188,7 @@ $(() => { success: function (response) { const parent = $('.cart-summary-container').parent(); $('.cart-summary-container').remove(); + $('#dirFactWarning').removeClass('d-none'); parent.append(response); }, error: function () { diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js index 7aac975..eb5b5b2 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/wizard.js @@ -107,6 +107,8 @@ export default class PresupuestoWizard { } }, selectedTirada: 10, + lomo: 0, + lomoCubierta: 0 } // pestaña datos generales @@ -443,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; @@ -714,17 +718,14 @@ export default class PresupuestoWizard { .prop('checked', true); this.#updateTipoEncuadernacion(); - this.formatoPersonalizado.trigger('change'); - - $('.paginas').trigger('change'); if (this.formatoPersonalizado.is(':checked')) { this.ancho.val(this.formData.datosGenerales.ancho); this.alto.val(this.formData.datosGenerales.alto); } else { - const option = this.formato.find('option').filter(() => { - return $(this).data('ancho') == this.formData.datosGenerales.ancho && - $(this).data('alto') == this.formData.datosGenerales.alto; + const option = this.formato.find('option').filter((index, element) => { + return $(element).data('ancho') == this.formData.datosGenerales.ancho && + $(element).data('alto') == this.formData.datosGenerales.alto; }); if (option.length) { @@ -732,6 +733,8 @@ export default class PresupuestoWizard { } } + this.formatoPersonalizado.trigger('change'); + $('.paginas').trigger('change'); this.ivaReducido.prop('checked', this.formData.datosGenerales.ivaReducido); } @@ -965,6 +968,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); @@ -989,16 +993,16 @@ export default class PresupuestoWizard { this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado); this.fajaSobrecubierta.val(this.formData.cubierta.faja.acabado); - if(lomo < 10){ + if (lomo < 10) { this.formData.cubierta.cabezada = "NOCAB"; this.cabezada.val("NOCAB"); this.cabezada.prop("disabled", true); - if(this.formData.cubierta.tipoCubierta === 'tapaDuraLomoRedondo'){ - this.formData.cubierta.tipoCubierta = 'tapaDura'; + if (this.formData.cubierta.tipoCubierta === 'tapaDuraLomoRedondo') { + this.formData.cubierta.tipoCubierta = 'tapaDura'; } $("#tapaDuraLomoRedondo").addClass("d-none"); } - else{ + else { this.cabezada.prop("disabled", false); $("#tapaDuraLomoRedondo").removeClass("d-none"); } @@ -1018,6 +1022,7 @@ export default class PresupuestoWizard { const dataCubierta = this.#getCubiertaData(); this.#updateCubiertaData(dataCubierta); + this.formData.lomo = lomo; this.#cacheFormData(); } @@ -1139,13 +1144,21 @@ export default class PresupuestoWizard { #initCubierta() { this.btn_plantilla_cubierta.on('click', () => { + let url = `/presupuesto/api/plantilla-cubierta.png + ?tipo=${this.formData.datosGenerales.tipoEncuadernacion} + &tapa=${this.formData.cubierta.tipoCubierta} + &ancho=${this.formData.datosGenerales.ancho} + &alto=${this.formData.datosGenerales.alto} + &lomo=${Math.round(this.formData.lomo) || 0} + &solapas=${this.formData.cubierta.solapasCubierta == 1 ? this.formData.cubierta.tamanioSolapasCubierta : 0}`; + url = url.trim().replace(/\s+/g, ''); Swal.fire({ position: 'top-end', icon: 'info', title: window.languageBundle.get('presupuesto.plantilla-cubierta'), html: `
    - +
    ${window.languageBundle.get('presupuesto.plantilla-cubierta-text')} @@ -1437,7 +1450,10 @@ export default class PresupuestoWizard { #getCubiertaData() { const tipoCubierta = $('.tapa-cubierta input:checked').val() || 'tapaBlanda'; - const solapas = $('.solapas-cubierta input:checked').val() == 'sin-solapas' ? 0 : 1 || 0; + let solapas = 0; + if (tipoCubierta === 'tapaBlanda') { + solapas = $('.solapas-cubierta input:checked').val() == 'conSolapas' ? 1 : 0 || 0; + } const tamanioSolapasCubierta = $('#tamanio-solapas-cubierta').val() || '80'; const cubiertaCaras = parseInt(this.carasImpresionCubierta.val()) || 2; const papelGuardasId = parseInt($('#papel-guardas option:selected').data('papel-id')) || 3; @@ -1684,6 +1700,11 @@ export default class PresupuestoWizard { this.divTiradas.append(item.renderCol(this.divTiradas)); } + if (data.lomo_cubierta) { + this.formData.lomoCubierta = data.lomo_cubierta; + this.#cacheFormData(); + } + if (this.divTiradas.find('.tirada-card input[type="radio"]:checked').length === 0) { this.divTiradas.find('.tirada-card input[type="radio"]').first().prop('checked', true).trigger('change'); this.formData.selectedTirada = this.divTiradas.find('.tirada-card input[type="radio"]').first().data('unidades') || data.tiradas[0]; diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js new file mode 100644 index 0000000..3e43652 --- /dev/null +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestos/plantilla-cubierta.js @@ -0,0 +1,33 @@ +$(() => { + $('#btn-plantilla-cubierta').on('click', () => { + let url = `/presupuesto/api/plantilla-cubierta.png + ?tipo=${$('#tipoEncuadernacion').val()} + &tapa=${$('#tipoCubierta').val()} + &ancho=${$('#ancho').val()} + &alto=${$('#alto').val()} + &lomo=${Math.round($('#lomo').val()) || 0} + &solapas=${$('#solapasCubierta').val() == 1 ? $('#tamanioSolapasCubierta').val() : 0}`; + url = url.trim().replace(/\s+/g, ''); + Swal.fire({ + position: 'top-end', + icon: 'info', + title: window.languageBundle.get('presupuesto.plantilla-cubierta'), + html: ` +
    + +
    +
    + ${window.languageBundle.get('presupuesto.plantilla-cubierta-text')} +
    + `, + confirmButtonClass: 'btn btn-primary w-xs mt-2', + showConfirmButton: false, + showCloseButton: true, + buttonsStyling: false, + customClass: { + confirmButton: 'btn btn-secondary me-2', // clases para el botón confirmar + cancelButton: 'btn btn-light' // clases para cancelar + }, + }); + }); +}); diff --git a/src/main/resources/templates/imprimelibros/checkout/_summary.html b/src/main/resources/templates/imprimelibros/checkout/_summary.html index 85ce845..6b5bcb9 100644 --- a/src/main/resources/templates/imprimelibros/checkout/_summary.html +++ b/src/main/resources/templates/imprimelibros/checkout/_summary.html @@ -48,7 +48,7 @@
    - + diff --git a/src/main/resources/templates/imprimelibros/error/error.html b/src/main/resources/templates/imprimelibros/error/error.html new file mode 100644 index 0000000..ee62b9d --- /dev/null +++ b/src/main/resources/templates/imprimelibros/error/error.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
    +
    + +

    404

    +

    Not Found

    + +

    + Ha ocurrido un error inesperado. +

    + +
    +
    Ruta: /logout
    +
    Fecha: --
    +
    + + + + +
    +
    + No se ha encontrado el recurso solicitado. +
    +
    + +
    +
    +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html index d64acac..4bcd3c5 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-items/_resumen_final.html @@ -16,8 +16,19 @@
    -
    REIMPRESION
    +
    REIMPRESION
    + +
    +
    + +
    +
    diff --git a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html index 0c361fb..638db40 100644 --- a/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html +++ b/src/main/resources/templates/imprimelibros/presupuestos/presupuestador-view.html @@ -44,18 +44,40 @@ + + + + + + + +
    -

    Resumen del +

    + Resumen del presupuesto

    -
    REIMPRESION
    +
    REIMPRESION
    +
    + +
    +
    + +
    @@ -207,7 +229,7 @@
    - +
    @@ -234,6 +256,8 @@ + +