package com.imprimelibros.erp.presupuesto.service; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import java.util.Locale; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.math.BigDecimal; import java.math.RoundingMode; import com.imprimelibros.erp.configurationERP.VariableService; import com.imprimelibros.erp.presupuesto.GeoIpService; import com.imprimelibros.erp.presupuesto.PresupuestoRepository; import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto; import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems; import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter; import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPrecios; import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionPreciosRepository; import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas; 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.Presupuesto.TipoCubierta; import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatricesRepository; import com.imprimelibros.erp.presupuesto.marcapaginas.MarcapaginasRepository; import com.imprimelibros.erp.users.UserDao; import com.imprimelibros.erp.users.UserDetailsImpl; import jakarta.persistence.criteria.CriteriaBuilder.In; import jakarta.servlet.http.HttpServletRequest; import com.imprimelibros.erp.externalApi.skApiClient; @Service public class PresupuestoService { @Autowired protected VariableService variableService; @Autowired protected MessageSource messageSource; @Autowired protected MaquetacionPreciosRepository maquetacionPreciosRepository; @Autowired protected MaquetacionMatricesRepository maquetacionMatricesRepository; @Autowired protected MarcapaginasRepository marcapaginasRepository; @Autowired protected PresupuestoRepository presupuestoRepository; private final PresupuestadorItems presupuestadorItems; private final PresupuestoFormatter presupuestoFormatter; private final skApiClient apiClient; private final GeoIpService geoIpService; private final UserDao userRepo; public PresupuestoService(PresupuestadorItems presupuestadorItems, PresupuestoFormatter presupuestoFormatter, skApiClient apiClient, GeoIpService geoIpService, UserDao userRepo) { this.presupuestadorItems = presupuestadorItems; this.presupuestoFormatter = presupuestoFormatter; this.apiClient = apiClient; this.geoIpService = geoIpService; this.userRepo = userRepo; } public boolean validateDatosGenerales(int[] tiradas) { for (int tirada : tiradas) { if (tirada <= 0) { return false; // Invalid tirada found } } return true; } public Boolean isPOD(Presupuesto presupuesto) { int pod_value = variableService.getValorEntero("POD"); return (presupuesto.getTirada1() != null && presupuesto.getTirada1() <= pod_value) || (presupuesto.getTirada2() != null && presupuesto.getTirada2() <= pod_value) || (presupuesto.getTirada3() != null && presupuesto.getTirada3() <= pod_value) || (presupuesto.getTirada4() != null && presupuesto.getTirada4() <= pod_value); } public Map obtenerOpcionesColor(Presupuesto presupuesto, Locale locale) { List opciones = new ArrayList<>(); if (presupuesto.getPaginasColor() > 0) { if (!this.isPOD(presupuesto)) { // POD solo color foto ImagenPresupuesto opcionColor = this.presupuestadorItems.getImpresionColor(locale); opcionColor.setSelected(Presupuesto.TipoImpresion.color.equals(presupuesto.getTipoImpresion())); opciones.add(opcionColor); } ImagenPresupuesto opcionColorHq = this.presupuestadorItems.getImpresionColorPremium(locale); if (Presupuesto.TipoImpresion.colorhq.equals(presupuesto.getTipoImpresion())) opcionColorHq.setSelected(true); opciones.add(opcionColorHq); } else { ImagenPresupuesto opcionNegro = this.presupuestadorItems.getImpresionNegro(locale); if (Presupuesto.TipoImpresion.negro.equals(presupuesto.getTipoImpresion())) opcionNegro.setSelected(true); opciones.add(opcionNegro); ImagenPresupuesto opcionNegroHq = this.presupuestadorItems.getImpresionNegroPremium(locale); if (Presupuesto.TipoImpresion.negrohq.equals(presupuesto.getTipoImpresion())) opcionNegroHq.setSelected(true); opciones.add(opcionNegroHq); } boolean opcionSeleccionada = opciones.stream().anyMatch(ImagenPresupuesto::isSelected); if (!opcionSeleccionada) { opciones.get(0).setSelected(true); presupuesto.setTipoImpresion(Presupuesto.TipoImpresion.valueOf(opciones.get(0).getId())); } Map response = new HashMap<>(); response.put("opciones_color", opciones); return response; } public Map obtenerOpcionesPapelInterior(Presupuesto presupuesto, Locale locale) { List opciones = new ArrayList<>(); opciones.add(this.presupuestadorItems.getPapelOffsetBlanco(locale)); if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) { opciones.add(this.presupuestadorItems.getPapelOffsetBlancoVolumen(locale)); } opciones.add(this.presupuestadorItems.getPapelOffsetAhuesado(locale)); if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) { opciones.add(this.presupuestadorItems.getPapelOffsetAhuesadoVolumen(locale)); } opciones.add(this.presupuestadorItems.getPapelEstucadoMate(locale)); for (ImagenPresupuesto imagenPresupuesto : opciones) { imagenPresupuesto.setSelected( presupuesto.getPapelInteriorId() != null && imagenPresupuesto.getExtra_data().get("sk-id").equals( String.valueOf(presupuesto.getPapelInteriorId()))); } boolean yaSeleccionado = opciones.stream().anyMatch(ImagenPresupuesto::isSelected); if (!yaSeleccionado && !opciones.isEmpty()) { ImagenPresupuesto primeraOpcion = opciones.get(0); primeraOpcion.setSelected(true); presupuesto.setPapelInteriorId(Integer.parseInt(primeraOpcion.getExtra_data().get("sk-id"))); } Map response = new HashMap<>(); response.put("opciones_papel_interior", opciones); return response; } public Map obtenerOpcionesGramajeInterior(Presupuesto presupuesto) { List gramajes = new ArrayList<>(); final int BLANCO_OFFSET_ID = 3; final int BLANCO_OFFSET_VOLUMEN_ID = 7; final int AHUESADO_OFFSET_ID = 4; final int AHUESADO_OFFSET_VOLUMEN_ID = 6; final int ESTUCADO_MATE_ID = 2; if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == BLANCO_OFFSET_ID) { gramajes.add("80"); gramajes.add("90"); if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) { gramajes.add("100"); gramajes.add("120"); gramajes.add("150"); gramajes.add("170"); } } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == BLANCO_OFFSET_VOLUMEN_ID) { if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) { gramajes.add("80"); } } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_ID) { gramajes.add("80"); gramajes.add("90"); if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) { gramajes.add("100"); } } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == AHUESADO_OFFSET_VOLUMEN_ID) { if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) { gramajes.add("70"); gramajes.add("80"); } } else if (presupuesto.getPapelInteriorId() != null && presupuesto.getPapelInteriorId() == ESTUCADO_MATE_ID) { if (presupuesto.getTipoImpresion() != Presupuesto.TipoImpresion.color) { gramajes.add("90"); } if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) { gramajes.add("100"); gramajes.add("115"); } if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negro || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.color) { gramajes.add("120"); } if (presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.negrohq || presupuesto.getTipoImpresion() == Presupuesto.TipoImpresion.colorhq) { gramajes.add("125"); gramajes.add("135"); gramajes.add("150"); gramajes.add("170"); gramajes.add("200"); } } Map response = new HashMap<>(); response.put("opciones_gramaje_interior", gramajes); return response; } public Map obtenerOpcionesPapelCubierta(Presupuesto presupuesto, Locale locale) { List opciones = new ArrayList<>(); if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) { opciones.add(this.presupuestadorItems.getCartulinaGraficaCubierta(locale)); } opciones.add(this.presupuestadorItems.getEstucadoMateCubierta(locale)); for (ImagenPresupuesto imagenPresupuesto : opciones) { imagenPresupuesto.setSelected( presupuesto.getPapelCubiertaId() != null && imagenPresupuesto.getExtra_data().get("sk-id").equals( String.valueOf(presupuesto.getPapelCubiertaId()))); } Map response = new HashMap<>(); response.put("opciones_papel_cubierta", opciones); return response; } public Map obtenerOpcionesGramajeCubierta(Presupuesto presupuesto) { List gramajes = new ArrayList<>(); final int CARTULINA_GRAFICA_ID = 5; final int ESTUCADO_MATE_ID = 2; if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == CARTULINA_GRAFICA_ID) { gramajes.add("240"); gramajes.add("270"); gramajes.add("300"); gramajes.add("350"); } else if (presupuesto.getPapelCubiertaId() != null && presupuesto.getPapelCubiertaId() == ESTUCADO_MATE_ID) { if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaBlanda) { gramajes.add("250"); gramajes.add("300"); gramajes.add("350"); } else { gramajes.add("170"); } } Map response = new HashMap<>(); response.put("opciones_gramaje_cubierta", gramajes); return response; } public Map toSkApiRequest(Presupuesto presupuesto) { final int SK_CLIENTE_ID = 1284; final int SK_PAGINAS_CUADERNILLO = 32; Map tamanio = Map.of( "ancho", presupuesto.getAncho(), "alto", presupuesto.getAlto()); Map interior = Map.of( "papelInterior", presupuesto.getPapelInteriorId(), "gramajeInterior", presupuesto.getGramajeInterior()); Map cubierta = Map.of( "tipoCubierta", presupuesto.getTipoCubierta().name(), "papelCubierta", presupuesto.getPapelCubiertaId(), "gramajeCubierta", presupuesto.getGramajeCubierta(), "carasImpresion", presupuesto.getCubiertaCaras(), "solapas", presupuesto.getSolapasCubierta() ? presupuesto.getTamanioSolapasCubierta() : 0, "acabado", presupuesto.getAcabado(), "cabezada", presupuesto.getCabezada(), "lomoRedondo", presupuesto.getTipoCubierta() == TipoCubierta.tapaDuraLomoRedondo ? 1 : 0); Map body = new HashMap<>(); body.put("tipo_impresion_id", this.getTipoImpresionId(presupuesto)); body.put("tirada", Arrays.stream(presupuesto.getTiradas()) .filter(Objects::nonNull) .collect(Collectors.toList())); body.put("tamanio", tamanio); body.put("tipo", presupuesto.getTipoEncuadernacion()); body.put("clienteId", SK_CLIENTE_ID); body.put("isColor", presupuesto.getTipoImpresion().name().contains("color") ? 1 : 0); body.put("isHq", presupuesto.getTipoImpresion().name().contains("hq") ? 1 : 0); body.put("paginas", presupuesto.getPaginasNegro() + presupuesto.getPaginasColor()); body.put("paginasColor", presupuesto.getPaginasColor()); body.put("paginasCuadernillo", SK_PAGINAS_CUADERNILLO); body.put("interior", interior); body.put("cubierta", cubierta); body.put("guardas", null); if (presupuesto.getSobrecubierta()) { Map sobrecubierta = new HashMap<>(); sobrecubierta.put("papel", presupuesto.getPapelSobrecubiertaId()); sobrecubierta.put("gramaje", presupuesto.getGramajeSobrecubierta()); sobrecubierta.put("solapas", presupuesto.getTamanioSolapasSobrecubierta()); sobrecubierta.put("acabado", presupuesto.getAcabadoSobrecubierta()); body.put("sobrecubierta", sobrecubierta); } if (presupuesto.getFaja()) { Map faja = new HashMap<>(); faja.put("papel", presupuesto.getPapelFajaId()); faja.put("gramaje", presupuesto.getGramajeFaja()); faja.put("solapas", presupuesto.getTamanioSolapasFaja()); faja.put("acabado", presupuesto.getAcabadoFaja()); faja.put("alto", presupuesto.getAltoFaja()); body.put("faja", faja); } // body.put("servicios", servicios); return body; } public Integer getTipoImpresionId(Presupuesto presupuesto) { if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.fresado) { if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura || presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) { return 1; // Fresado tapa dura } else { return 2; // Fresado tapa blanda } } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.cosido) { if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura || presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) { return 3; // Cosido tapa dura } else { return 4; // Cosido tapa blanda } } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.espiral) { if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura || presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) { return 5; // Espiral tapa dura } else { return 6; // Espiral tapa blanda } } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.wireo) { if (presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDura || presupuesto.getTipoCubierta() == Presupuesto.TipoCubierta.tapaDuraLomoRedondo) { return 7; // Wireo tapa dura } else { return 8; // Wireo tapa blanda } } else if (presupuesto.getTipoEncuadernacion() == Presupuesto.TipoEncuadernacion.grapado) { return 21; // Grapado } else { return 0; // Default case, no valid type } } public Map obtenerOpcionesAcabadosCubierta(Presupuesto presupuesto, Locale locale) { Map resultado = new HashMap<>(); List opciones = new ArrayList<>(); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-ninguno", null, locale)); put("sk-id", 0); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-brillo-1c", null, locale)); put("sk-id", 1); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c", null, locale)); put("sk-id", 5); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-1c-antirrayado", null, locale)); put("sk-id", 8); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi", null, locale)); put("sk-id", 2); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi3d", null, locale)); put("sk-id", 3); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-mate-uvi-braile", null, locale)); put("sk-id", 4); } }); opciones.add(new HashMap<>() { { put("name", messageSource.getMessage("presupuesto.acabado-plastificado-sandy-1c", null, locale)); put("sk-id", 9); } }); resultado.put("opciones_acabados_cubierta", opciones); return resultado; } public String obtenerPrecioRetractilado(Presupuesto presupuesto, Locale locale) { Integer[] tiradas = presupuesto.getTiradas(); Integer tirada_min = Arrays.stream(tiradas) .filter(Objects::nonNull) .min(Integer::compareTo) .orElse(0); Map requestBody = new HashMap<>(); requestBody.put("tirada", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : tirada_min); Double precio_retractilado = apiClient.getRetractilado(requestBody); return precio_retractilado != null ? String.valueOf(Math.round(precio_retractilado * 100.0) / 100.0) : "0.00"; } private String obtenerPrecioRetractilado(Integer tirada) { Map requestBody = new HashMap<>(); requestBody.put("tirada", tirada != null ? tirada : 0); Double precio_retractilado = apiClient.getRetractilado(requestBody); return precio_retractilado != null ? String.valueOf(Math.round(precio_retractilado * 100.0) / 100.0) : "0.00"; } public Map obtenerServiciosExtras(Presupuesto presupuesto, Locale locale) { List opciones = new ArrayList<>(); Double price_prototipo = this.obtenerPrototipo(presupuesto); opciones.add(new HashMap() { { put("id", "retractilado"); put("title", messageSource.getMessage("presupuesto.extras-retractilado", null, locale)); put("description", ""); put("price", obtenerPrecioRetractilado(presupuesto, locale)); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } }); opciones.add(new HashMap() { { put("id", "service-isbn"); put("title", messageSource.getMessage("presupuesto.extras-isbn", null, locale)); put("description", ""); put("price", "30"); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } }); opciones.add(new HashMap() { { put("id", "deposito-legal"); put("title", messageSource.getMessage("presupuesto.extras-deposito-legal", null, locale)); put("description", messageSource.getMessage("presupuesto.extras-deposito-legal-descripcion", null, locale)); put("price", "30"); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } }); opciones.add(new HashMap() { { put("id", "revision-archivos"); put("title", messageSource.getMessage("presupuesto.extras-revision-archivos", null, locale)); put("description", ""); put("price", "50"); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } }); opciones.add(new HashMap() { { put("id", "maquetacion-cubierta"); put("title", messageSource.getMessage("presupuesto.extras-maquetacion-cubierta", null, locale)); put("description", ""); put("price", "50"); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } }); opciones.add(new HashMap() { { put("id", "ferro-digital"); put("title", messageSource.getMessage("presupuesto.extras-ferro-digital", null, locale)); put("description", ""); put("price", "0"); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); put("checked", "true"); put("allowChange", "false"); put("ribbonText", messageSource.getMessage("presupuesto.extras-ferro-digital-ribbon", null, locale)); } }); opciones.add(new HashMap() { { put("id", "ejemplar-prueba"); put("title", messageSource.getMessage("presupuesto.extras-ejemplar-prueba", null, locale)); put("description", ""); if (price_prototipo == 0.0) { put("price", messageSource.getMessage("presupuesto.consultar-soporte", null, locale)); put("priceUnit", ""); } else { put("price", String.valueOf(Math.round(price_prototipo * 100.0) / 100.0)); put("priceUnit", messageSource.getMessage("app.currency-symbol", null, locale)); } } }); opciones.add(new HashMap() { { put("id", "marcapaginas"); put("title", messageSource.getMessage("presupuesto.extras-marcapaginas", null, locale)); put("description", ""); put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale)); } }); opciones.add(new HashMap() { { put("id", "maquetacion"); put("title", messageSource.getMessage("presupuesto.extras-maquetacion", null, locale)); put("description", ""); put("price", messageSource.getMessage("presupuesto.extras-calcular", null, locale)); } }); Map response = new HashMap<>(); response.put("servicios_extra", opciones); return response; } public HashMap getPrecioMaquetacion(PresupuestoMaquetacion presupuestoMaquetacion, Locale locale) { try { List lista = maquetacionPreciosRepository.findAll(); java.util.function.Function price = key -> lista.stream() .filter(p -> key.equals(p.getKey())) .map(MaquetacionPrecios::getValue) .findFirst() .orElse(0.0); BigDecimal precio = BigDecimal.ZERO; BigDecimal millares = BigDecimal.valueOf(presupuestoMaquetacion.getNumCaracteres()).divide( BigDecimal.valueOf(1000), 6, RoundingMode.HALF_UP); precio = precio.add(millares.multiply(BigDecimal.valueOf(price.apply("millar_maquetacion")))); int numPaginas = 0; Integer matricesPorPagina = maquetacionMatricesRepository.findMatrices( presupuestoMaquetacion.getFormato(), presupuestoMaquetacion.getCuerpoTexto()); if (matricesPorPagina != null && matricesPorPagina > 0) { numPaginas = presupuestoMaquetacion.getNumCaracteres() / matricesPorPagina; } BigDecimal precioRedondeado = precio.setScale(2, RoundingMode.HALF_UP); double precioPaginaEstimado = 0.0; if (numPaginas > 0) { precioPaginaEstimado = precioRedondeado .divide(BigDecimal.valueOf(numPaginas), 2, RoundingMode.HALF_UP) .doubleValue(); } precio = precio .add(BigDecimal.valueOf(presupuestoMaquetacion.getNumTablas()) .multiply(BigDecimal.valueOf(price.apply("tabla")))); precio = precio.add( BigDecimal.valueOf(presupuestoMaquetacion.getNumColumnas()) .multiply(BigDecimal.valueOf(price.apply("columnas")))); precio = precio .add(BigDecimal.valueOf(presupuestoMaquetacion.getNumFotos()) .multiply(BigDecimal.valueOf(price.apply("foto")))); if (presupuestoMaquetacion.isCorreccionOrtotipografica()) { precio = precio .add(millares.multiply(BigDecimal.valueOf(price.apply("correccion_ortotipografica")))); } if (presupuestoMaquetacion.isTextoMecanografiado()) { precio = precio.add(millares.multiply(BigDecimal.valueOf(price.apply("mecanoescritura_por_millar")))); } if (presupuestoMaquetacion.isDisenioPortada()) { precio = precio.add(BigDecimal.valueOf(price.apply("disenio_portada"))); } if (presupuestoMaquetacion.isEpub()) { precio = precio.add(BigDecimal.valueOf(price.apply("epub"))); } precioRedondeado = precio.setScale(2, RoundingMode.HALF_UP); HashMap out = new HashMap<>(); out.put("precio", precioRedondeado.doubleValue()); out.put("numPaginasEstimadas", numPaginas); out.put("precioPaginaEstimado", precioPaginaEstimado); HashMap language = new HashMap<>(); language.put("num_paginas_estimadas", messageSource.getMessage("presupuesto.maquetacion.num-paginas-estimadas", null, locale)); language.put("precio_por_pagina_estimado", messageSource.getMessage("presupuesto.maquetacion.precio-por-pagina-estimado", null, locale)); language.put("add_to_presupuesto", messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale)); language.put("cancel", messageSource.getMessage("app.cancelar", null, locale)); language.put("presupuesto_maquetacion", messageSource.getMessage("presupuesto.maquetacion", null, locale)); out.put("language", language); return out; } catch (Exception e) { System.out.println("Error procesando presupuesto maquetacion: " + e.getMessage()); } HashMap out = new HashMap<>(); out.put("precio", 0.0); out.put("numPaginasEstimadas", 0); out.put("precioPaginaEstimado", 0.0); return out; } public HashMap getPrecioMarcapaginas(PresupuestoMarcapaginas presupuestoMarcapaginas, Locale locale) { try { List m = marcapaginasRepository.findPrecios(presupuestoMarcapaginas); if (m.isEmpty() || m.get(0) == null) { HashMap out = new HashMap<>(); out.put("precio_unidad", 0.0); out.put("precio_total", 0.0); return out; } Marcapaginas marcapaginas = m.get(0); Double precio = 0.0; Double margen = 0.0; Double pvp = 0.0; BigDecimal data = BigDecimal.ZERO; if (marcapaginas.getUnidades_max() >= presupuestoMarcapaginas.getUnidades()) { precio = marcapaginas.getPrecio_unidades_min() + (presupuestoMarcapaginas.getUnidades() - marcapaginas.getUnidades_min()) * (marcapaginas.getPrecio_unidades_max() - marcapaginas.getPrecio_unidades_min()) / (marcapaginas.getUnidades_max() - marcapaginas.getUnidades_min()); data = new BigDecimal(precio); precio = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); margen = 1.0 * marcapaginas.getMargen_unidades_min() + (1.0 * presupuestoMarcapaginas.getUnidades() - 1.0 * marcapaginas.getUnidades_min()) * (1.0 * marcapaginas.getMargen_unidades_max() - 1.0 * marcapaginas.getMargen_unidades_min()) / (1.0 * marcapaginas.getUnidades_max() - 1.0 * marcapaginas.getUnidades_min()); data = new BigDecimal(margen); margen = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); pvp = precio + (precio * margen / 100); data = new BigDecimal(pvp); pvp = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); } else { // precio unidad para el máximo de unidades precio = marcapaginas.getPrecio_unidades_max() / marcapaginas.getUnidades_max(); precio = precio * presupuestoMarcapaginas.getUnidades(); data = new BigDecimal(precio); precio = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); margen = 1.0 * marcapaginas.getMargen_unidades_max(); data = new BigDecimal(margen); margen = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); pvp = precio + (precio * margen / 100); data = new BigDecimal(pvp); pvp = data.setScale(2, RoundingMode.HALF_UP).doubleValue(); } Double precio_unidad = pvp / presupuestoMarcapaginas.getUnidades(); data = new BigDecimal(precio_unidad); precio_unidad = data.setScale(6, RoundingMode.HALF_UP).doubleValue(); HashMap resultado; resultado = new HashMap<>(); resultado.put("precio_unitario", precio_unidad); resultado.put("precio_total", pvp); HashMap language = new HashMap<>(); language.put("precio_unidad", messageSource.getMessage("presupuesto.marcapaginas.precio-unidad", null, locale)); language.put("precio_total", messageSource.getMessage("presupuesto.marcapaginas.precio-total", null, locale)); language.put("add_to_presupuesto", messageSource.getMessage("presupuesto.add-to-presupuesto", null, locale)); language.put("cancel", messageSource.getMessage("app.cancelar", null, locale)); language.put("presupuesto_marcapaginas", messageSource.getMessage("presupuesto.marcapaginas", null, locale)); resultado.put("language", language); return resultado; } catch (Exception e) { System.out.println("Error procesando presupuesto marcapaginas: " + e.getMessage()); } HashMap out = new HashMap<>(); out.put("precio_unidad", 0.0); out.put("precio_total", 0.0); return out; } /** * Calcula el resumen (SIN persistir cambios de estado). * Mantiene firma para no romper llamadas existentes. */ public Map getTextosResumen(Presupuesto presupuesto, List> servicios, Locale locale) { Map resumen = new HashMap<>(); resumen.put("titulo", presupuesto.getTitulo()); Presupuesto pressupuestoTemp = presupuesto.clone(); resumen.put("imagen", "/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion() + ".png"); resumen.put("imagen_alt", messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null, locale)); boolean hayDepositoLegal = servicios != null && servicios.stream() .map(m -> java.util.Objects.toString(m.get("id"), "")) .map(String::trim) .anyMatch("deposito-legal"::equals); if (hayDepositoLegal) { pressupuestoTemp.setSelectedTirada( presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() + 4 : 4); for (Integer i = 0; i < pressupuestoTemp.getTiradas().length; i++) { Integer tirada = pressupuestoTemp.getTiradas()[i]; if (tirada != null && tirada >= 4) { tirada = tirada + 4; } pressupuestoTemp.getTiradas()[i] = tirada; } } HashMap precios = this.calcularPresupuesto(pressupuestoTemp, locale); if (precios.containsKey("error")) { resumen.put("error", precios.get("error")); return resumen; } resumen.put("precios", precios); HashMap linea = new HashMap<>(); Double precio_unitario = 0.0; Double precio_total = 0.0; Integer counter = 0; linea.put("descripcion", presupuestoFormatter.resumen(presupuesto, servicios, locale)); linea.put("cantidad", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0); precio_unitario = ((List) ((Map) precios.get("data")).get("precios")).get(0); precio_total = precio_unitario * (presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0); linea.put("precio_unitario", precio_unitario); linea.put("precio_total", BigDecimal.valueOf(precio_total).setScale(2, RoundingMode.HALF_UP)); resumen.put("linea" + counter, linea); counter++; if (hayDepositoLegal) { linea = new HashMap<>(); linea.put("descripcion", messageSource.getMessage("presupuesto.resumen-deposito-legal", null, locale)); linea.put("cantidad", 4); linea.put("precio_unitario", precio_unitario); linea.put("precio_total", BigDecimal.valueOf(precio_unitario * 4).setScale(2, RoundingMode.HALF_UP)); resumen.put("linea" + counter, linea); counter++; } List> serviciosExtras = new ArrayList<>(); if (servicios != null) { for (Map servicio : servicios) { HashMap servicioData = new HashMap<>(); servicioData.put("id", servicio.get("id")); servicioData.put("descripcion", servicio.get("label")); servicioData.put("precio", servicio.get("id").equals("marcapaginas") ? Double.parseDouble(servicio.get("price").toString()) / Double.parseDouble(servicio.get("units").toString()) : servicio.get("price")); servicioData.put("unidades", servicio.get("units")); serviciosExtras.add(servicioData); } } resumen.put("servicios", serviciosExtras); return resumen; } /** * PÚBLICO: calcula el resumen y GUARDA el presupuesto completo como BORRADOR. * Se invoca al entrar en la pestaña "Resumen" del presupuestador público. */ // PresupuestoService.java public Map getResumen( Presupuesto presupuesto, List> servicios, Boolean save, String mode, Locale locale, String sessionId, String ip) { // 1) Calcula el resumen (como ya haces) Map resumen = getTextosResumen(presupuesto, servicios, locale); if (resumen.containsKey("error")) return resumen; presupuesto = generateTotalizadores(presupuesto, servicios, resumen, locale); // 3) Enriquecer el Presupuesto a persistir presupuesto.setEstado(Presupuesto.Estado.borrador); if (mode.equals("public")) { presupuesto = getDatosLocalizacion(presupuesto, sessionId, ip); } else presupuesto.setOrigen(Presupuesto.Origen.privado); // 4) UPSERT: si viene id -> actualiza; si no, reusa el último borrador de la // sesión Presupuesto entidad; if (presupuesto.getId() != null) { entidad = presupuestoRepository.findById(presupuesto.getId()).orElse(presupuesto); } else { entidad = presupuestoRepository .findTopBySessionIdAndEstadoOrderByCreatedAtDesc(sessionId, Presupuesto.Estado.borrador) .orElse(presupuesto); // Si se reutiliza un borrador existente, copia el ID a nuestro objeto para // hacer merge presupuesto.setId(entidad.getId()); } // 5) Guardar/actualizar entidad = mergePresupuesto(entidad, presupuesto); if (save != null && save) { // Si NO es para guardar (solo calcular resumen), devolver sin persistir presupuestoRepository.saveAndFlush(presupuesto); } // Opcional: devolver el id guardado al frontend para que lo envíe en llamadas // siguientes resumen.put("presupuesto_id", entidad.getId()); resumen.put("precio_unitario", presupuesto.getPrecioUnitario()); resumen.put("precio_total_tirada", presupuesto.getPrecioTotalTirada()); resumen.put("servicios_total", presupuesto.getServiciosTotal()); resumen.put("base_imponible", presupuesto.getBaseImponible()); resumen.put("iva_importe_4", presupuesto.getIvaImporte4()); resumen.put("iva_importe_21", presupuesto.getIvaImporte21()); resumen.put("total_con_iva", presupuesto.getTotalConIva()); return resumen; } public Presupuesto getDatosLocalizacion(Presupuesto presupuesto, String sessionId, String ip) { presupuesto.setOrigen(Presupuesto.Origen.publico); presupuesto.setSessionId(sessionId); // IP: guarda hash y trunc (si tienes campos). Si no, guarda tal cual en // ip_trunc/ip_hash según tu modelo. String ipTrunc = anonymizeIp(ip); presupuesto.setIpTrunc(ipTrunc); presupuesto.setIpHash(Integer.toHexString(ip.hashCode())); // ubicación (si tienes un servicio GeoIP disponible; si no, omite estas tres // líneas) try { GeoIpService.GeoData geo = geoIpService.lookup(ip).orElse(null); presupuesto.setPais(geo.getPais()); presupuesto.setRegion(geo.getRegion()); presupuesto.setCiudad(geo.getCiudad()); } catch (Exception ignore) { } return presupuesto; } public Presupuesto generateTotalizadores( Presupuesto presupuesto, List> servicios, Map resumen, Locale locale) { Map> pricing_snapshot = new HashMap<>(); @SuppressWarnings("unchecked") Map preciosNode = (Map) resumen.getOrDefault("precios", Map.of()); @SuppressWarnings("unchecked") Map data = (Map) preciosNode.getOrDefault("data", Map.of()); @SuppressWarnings("unchecked") List tiradas = (List) data.getOrDefault("tiradas", List.of()); @SuppressWarnings("unchecked") List precios = (List) data.getOrDefault("precios", List.of()); @SuppressWarnings("unchecked") List pesos = (List) data.getOrDefault("peso", List.of()); boolean hayDepositoLegal = servicios != null && servicios.stream() .map(m -> java.util.Objects.toString(m.get("id"), "")) .map(String::trim) .anyMatch("deposito-legal"::equals); if (precios.isEmpty()) { var preciosCalc = this.calcularPresupuesto(presupuesto, locale); precios = (List) ((Map) preciosCalc.get("data")).getOrDefault("precios", List.of()); } // iterate getTiradas with a foreach with not null for (Integer tirada : presupuesto.getTiradas()) { if (tirada == null) { continue; } // Genera los totalizadores (precio unitario, total tirada, etc.) sin guardar double precioUnit = 0.0; int cantidad = tirada != null ? tirada : 0; int index = tiradas.indexOf(tirada); try { if (index >= 0 && index < precios.size()) { precioUnit = precios.get(index); } else if (!precios.isEmpty()) { precioUnit = precios.get(0); // fallback al primero } // guarda el snapshot completo de precios para auditoría presupuesto.setPreciosPorTiradaJson(new ObjectMapper().writeValueAsString(precios)); } catch (Exception ignore) { precioUnit = 0.0; } BigDecimal precioTotalTirada = BigDecimal.valueOf(precioUnit) .multiply(BigDecimal.valueOf(cantidad)) .setScale(2, RoundingMode.HALF_UP); if( hayDepositoLegal ){ precioTotalTirada = precioTotalTirada.add(BigDecimal.valueOf(precioUnit).multiply(BigDecimal.valueOf(4))).setScale(6, RoundingMode.HALF_UP); } // servicios_total BigDecimal serviciosIva4 = BigDecimal.ZERO; BigDecimal serviciosTotal = BigDecimal.ZERO; if (servicios != null) { for (Map s : servicios) { try { // retractilado: recalcular precio if (s.get("id").equals("retractilado")) { double precio_retractilado = obtenerPrecioRetractilado(cantidad) != null ? Double.parseDouble(obtenerPrecioRetractilado(cantidad)) : 0.0; s.put("price", precio_retractilado); } // si tiene protitipo, guardamos el valor para el IVA al 4% else if (s.get("id").equals("ejemplar-prueba")) { serviciosIva4 = BigDecimal.valueOf( s.get("price") != null ? Double.parseDouble(String.valueOf(s.get("price"))) : 0.0); } else if (s.get("id").equals("marcapaginas")) { PresupuestoMarcapaginas pm = presupuesto.getDatosMarcapaginasJson() != null ? new ObjectMapper().readValue(presupuesto.getDatosMarcapaginasJson(), PresupuestoMarcapaginas.class) : null; Map precio_marcapaginas = this.getPrecioMarcapaginas(pm, locale); s.put("price", precio_marcapaginas.getOrDefault("precio_total", 0.0)); } else if (s.get("id").equals("maquetacion")) { PresupuestoMaquetacion pm = presupuesto.getDatosMaquetacionJson() != null ? new ObjectMapper().readValue(presupuesto.getDatosMaquetacionJson(), PresupuestoMaquetacion.class) : null; Map precio_maquetacion = this.getPrecioMaquetacion(pm, locale); s.put("price", precio_maquetacion.getOrDefault("precio", 0.0)); } double unidades = Double.parseDouble(String.valueOf(s.getOrDefault("units", 0))); double precio = Double.parseDouble(String.valueOf( s.get("id").equals("marcapaginas") ? (Double.parseDouble(String.valueOf(s.get("price"))) / unidades) // unidad : s.getOrDefault("price", 0))); serviciosTotal = serviciosTotal.add( BigDecimal.valueOf(precio).multiply(BigDecimal.valueOf(unidades))); } catch (Exception ignore) { } } try { presupuesto.setServiciosJson(new ObjectMapper().writeValueAsString(servicios)); } catch (Exception ignore) { } } BigDecimal baseImponible = precioTotalTirada; BigDecimal ivaImporte4 = BigDecimal.ZERO; BigDecimal ivaImporte21 = BigDecimal.ZERO; // 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); } } baseImponible = baseImponible.add(serviciosTotal); BigDecimal totalConIva = baseImponible.add(ivaImporte21).add(ivaImporte4); // precios y totales if (tirada == (presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0)) { presupuesto.setPrecioUnitario(BigDecimal.valueOf(precioUnit).setScale(6, RoundingMode.HALF_UP)); presupuesto.setPrecioTotalTirada(precioTotalTirada); presupuesto.setServiciosTotal(serviciosTotal); presupuesto.setBaseImponible(baseImponible); presupuesto.setIvaImporte4(ivaImporte4); presupuesto.setIvaImporte21(ivaImporte21); presupuesto.setTotalConIva(totalConIva); } Map snap = new HashMap<>(); snap.put("precio_unitario", BigDecimal.valueOf(precioUnit).setScale(6, RoundingMode.HALF_UP)); snap.put("precio_total_tirada", precioTotalTirada); snap.put("servicios_total", serviciosTotal); snap.put("base_imponible", baseImponible); snap.put("iva_importe_4", ivaImporte4); snap.put("iva_importe_21", ivaImporte21); snap.put("total_con_iva", totalConIva); snap.put("peso", (index >= 0 && index < pesos.size()) ? pesos.get(index) : 0.0); pricing_snapshot.put(tirada, snap); } try { String json = new ObjectMapper() .writer() .withDefaultPrettyPrinter() // opcional .writeValueAsString(pricing_snapshot); presupuesto.setPricingSnapshotJson(pricing_snapshot.isEmpty() ? null : json); } catch (Exception ignore) { } return presupuesto; } @Transactional public HashMap guardarPresupuesto( Presupuesto presupuesto, List> serviciosList, Map datosMaquetacion, Map datosMarcapaginas, String mode, Long cliente_id, Long id, HttpServletRequest request, Locale locale) { HashMap result = new HashMap<>(); try { presupuesto.setDatosMaquetacionJson( datosMaquetacion != null ? new ObjectMapper().writeValueAsString(datosMaquetacion) : null); presupuesto.setDatosMarcapaginasJson( datosMarcapaginas != null ? new ObjectMapper().writeValueAsString(datosMarcapaginas) : null); var resumen = this.getTextosResumen(presupuesto, serviciosList, locale); Object serviciosObj = resumen.get("servicios"); if (serviciosObj instanceof List servicios && !servicios.isEmpty()) { // serializa a JSON válido ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(servicios); presupuesto.setServiciosJson(json); } else { // decide tu política: null o "[]" presupuesto.setServiciosJson(null); // o presupuesto.setServiciosJson("[]"); } if (cliente_id != null && !mode.equals("public")) { presupuesto.setUser(userRepo.findById(cliente_id).orElse(null)); presupuesto.setOrigen(Presupuesto.Origen.privado); if (id != null) { presupuesto.setId(id); } } if (mode.equals("public")) { presupuesto.setOrigen(Presupuesto.Origen.publico); String sessionId = request.getSession(true).getId(); String ip = request.getRemoteAddr(); presupuesto = this.getDatosLocalizacion(presupuesto, sessionId, ip); if (id != null) { presupuesto.setId(id); // para que actualice, no cree uno nuevo } } presupuesto = this.generateTotalizadores(presupuesto, serviciosList, resumen, locale); presupuestoRepository.saveAndFlush(presupuesto); result.put("success", true); result.put("presupuesto_id", presupuesto.getId()); return result; } catch (Exception e) { System.out.println("Error guardando presupuesto: " + e.getMessage()); result.put("success", false); return result; } } /** * PRIVADO (futuro botón "Guardar"): persiste el presupuesto como borrador. */ public HashMap calcularPresupuesto(Presupuesto presupuesto, Locale locale) { HashMap price = new HashMap<>(); String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuesto), presupuesto.getTipoEncuadernacion(), presupuesto.getTipoCubierta()); try { price = new ObjectMapper().readValue(priceStr, new TypeReference<>() { }); } catch (JsonProcessingException e) { price = new HashMap<>(); price.put("error", messageSource.getMessage("presupuesto.error-obtener-precio", null, locale)); } return price; } public Boolean canAccessPresupuesto(Presupuesto presupuesto, Authentication authentication) { boolean isUser = authentication.getAuthorities().stream() .anyMatch(a -> a.getAuthority().equals("ROLE_USER")); if (isUser) { // Si es usuario, solo puede ver sus propios presupuestos String username = authentication.getName(); if (presupuesto.getUser() == null || !presupuesto.getUser().getUserName().equals(username)) { return false; } } return true; } // ======================================================================= // Métodos privados // ======================================================================= /** * Copia de campos "actualizables" para no machacar otros (created_at, etc.) */ private Presupuesto mergePresupuesto(Presupuesto target, Presupuesto src) { // Campos funcionales target.setTitulo(src.getTitulo()); target.setTipoEncuadernacion(src.getTipoEncuadernacion()); target.setTipoCubierta(src.getTipoCubierta()); target.setTipoImpresion(src.getTipoImpresion()); target.setPaginasNegro(src.getPaginasNegro()); target.setPaginasColor(src.getPaginasColor()); target.setPaginasColorTotal(src.getPaginasColorTotal()); target.setPosicionPaginasColor(src.getPosicionPaginasColor()); target.setAncho(src.getAncho()); target.setAlto(src.getAlto()); target.setPapelInteriorId(src.getPapelInteriorId()); target.setGramajeInterior(src.getGramajeInterior()); target.setPapelCubiertaId(src.getPapelCubiertaId()); target.setGramajeCubierta(src.getGramajeCubierta()); target.setCubiertaCaras(src.getCubiertaCaras()); target.setSolapasCubierta(src.getSolapasCubierta()); target.setTamanioSolapasCubierta(src.getTamanioSolapasCubierta()); target.setSobrecubierta(src.getSobrecubierta()); target.setPapelSobrecubiertaId(src.getPapelSobrecubiertaId()); target.setGramajeSobrecubierta(src.getGramajeSobrecubierta()); target.setTamanioSolapasSobrecubierta(src.getTamanioSolapasSobrecubierta()); target.setAcabado(src.getAcabado()); target.setCabezada(src.getCabezada()); target.setTipoCubierta(src.getTipoCubierta()); target.setSelectedTirada(src.getSelectedTirada()); target.setTirada1(src.getTirada1()); target.setTirada2(src.getTirada2()); target.setTirada3(src.getTirada3()); target.setTirada4(src.getTirada4()); // Metadatos y totales target.setEstado(Presupuesto.Estado.borrador); target.setOrigen(src.getOrigen()); target.setSessionId(src.getSessionId()); target.setIpHash(src.getIpHash()); target.setIpTrunc(src.getIpTrunc()); target.setPais(src.getPais()); target.setRegion(src.getRegion()); target.setCiudad(src.getCiudad()); target.setServiciosJson(src.getServiciosJson()); target.setPreciosPorTiradaJson(src.getPreciosPorTiradaJson()); target.setPrecioUnitario(src.getPrecioUnitario()); target.setPrecioTotalTirada(src.getPrecioTotalTirada()); 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()); target.setCreatedBy(target.getCreatedBy() == null ? src.getCreatedBy() : target.getCreatedBy()); // no pisar si // ya existe return target; } private Double obtenerPrototipo(Presupuesto presupuesto) { // Obtenemos el precio de 1 unidad para el ejemplar de prueba HashMap price = new HashMap<>(); Presupuesto presupuestoTemp = presupuesto.clone(); presupuestoTemp.setTirada1(1); presupuestoTemp.setTirada2(null); presupuestoTemp.setTirada3(null); presupuestoTemp.setTirada4(null); if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.color) { presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.colorhq); } else if (presupuestoTemp.getTipoImpresion() == Presupuesto.TipoImpresion.negro) { presupuestoTemp.setTipoImpresion(Presupuesto.TipoImpresion.negrohq); } String priceStr = apiClient.getPrice(this.toSkApiRequest(presupuestoTemp), presupuestoTemp.getTipoEncuadernacion(), presupuestoTemp.getTipoCubierta()); Double price_prototipo = 0.0; try { price = new ObjectMapper().readValue(priceStr, new TypeReference<>() { }); price_prototipo = ((List) ((Map) price.get("data")).get("precios")).get(0); if (price_prototipo < 25) { price_prototipo = 25.0; } } catch (JsonProcessingException e) { } catch (Exception exception) { } return price_prototipo; } // Utilidad local (puedes moverla a una clase Utils si quieres) private static String anonymizeIp(String ip) { if (ip == null) return null; // IPv4 if (ip.contains(".") && !ip.contains(":")) { String[] p = ip.split("\\."); if (p.length == 4) { return p[0] + "." + p[1] + "." + p[2] + ".0"; } } // IPv6: quedarnos con /64 -> primera mitad y rellenar if (ip.contains(":")) { String[] parts = ip.split(":", -1); // expand no estricta, nos quedamos con primeros 4 bloques y completamos int blocks = Math.min(parts.length, 4); StringBuilder sb = new StringBuilder(); for (int i = 0; i < blocks; i++) { if (i > 0) sb.append(":"); sb.append(parts[i].isEmpty() ? "0" : parts[i]); } // completar a /64 con ceros for (int i = blocks; i < 8; i++) sb.append(":0"); return sb.toString(); } return ip; } }