primera version final del presupuesto

This commit is contained in:
2025-10-17 09:21:31 +02:00
parent ea8a005cde
commit 46715d1017
11 changed files with 323 additions and 73 deletions

View File

@ -8,11 +8,8 @@ import com.imprimelibros.erp.users.UserDetailsImpl;
import jakarta.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.imprimelibros.erp.users.User;
import org.springframework.boot.autoconfigure.graphql.GraphQlProperties.Http;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;

View File

@ -1,6 +1,5 @@
package com.imprimelibros.erp.cart;
import jakarta.mail.Message;
import jakarta.transaction.Transactional;
import org.springframework.context.MessageSource;
@ -21,7 +20,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
import com.imprimelibros.erp.presupuesto.service.PresupuestoService;
@Service

View File

@ -0,0 +1,227 @@
package com.imprimelibros.erp.common;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.presupuesto.maquetacion.MaquetacionMatrices;
import com.imprimelibros.erp.presupuesto.marcapaginas.Marcapaginas;
@Component
public class Utils {
private final PresupuestoFormatter presupuestoFormatter;
private final MessageSource messageSource;
public Utils(PresupuestoFormatter presupuestoFormatter,
MessageSource messageSource) {
this.presupuestoFormatter = presupuestoFormatter;
this.messageSource = messageSource;
}
public static String formatCurrency(BigDecimal amount, Locale locale) {
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);
return currencyFormatter.format(amount);
}
public static String formatCurrency(Double amount, Locale locale) {
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);
return currencyFormatter.format(amount);
}
public Map<String, Object> getTextoPresupuesto(Presupuesto presupuesto, Locale locale) {
Map<String, Object> resumen = new HashMap<>();
resumen.put("titulo", presupuesto.getTitulo());
resumen.put("imagen",
"/assets/images/imprimelibros/presupuestador/" + presupuesto.getTipoEncuadernacion()
+ ".png");
resumen.put("imagen_alt",
messageSource.getMessage("presupuesto." + presupuesto.getTipoEncuadernacion(), null,
locale));
resumen.put("presupuestoId", presupuesto.getId());
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> servicios = new ArrayList<>();
if (presupuesto.getServiciosJson() != null && !presupuesto.getServiciosJson().isBlank())
try {
servicios = mapper.readValue(presupuesto.getServiciosJson(), new TypeReference<>() {
});
} catch (JsonProcessingException e) {
// Manejar la excepción
}
boolean hayDepositoLegal = servicios != null && servicios.stream()
.map(m -> java.util.Objects.toString(m.get("id"), ""))
.map(String::trim)
.anyMatch("deposito-legal"::equals);
List<HashMap<String, Object>> lineas = new ArrayList<>();
HashMap<String, Object> linea = new HashMap<>();
Double precio_unitario = 0.0;
Double precio_total = 0.0;
BigDecimal total = BigDecimal.ZERO;
linea.put("descripcion", presupuestoFormatter.resumen(presupuesto, servicios, locale));
linea.put("cantidad", presupuesto.getSelectedTirada() != null ? presupuesto.getSelectedTirada() : 0);
precio_unitario = (presupuesto.getPrecioUnitario() != null
? presupuesto.getPrecioUnitario().doubleValue()
: 0.0);
precio_total = (presupuesto.getPrecioTotalTirada() != null
? presupuesto.getPrecioTotalTirada().doubleValue()
: 0.0);
linea.put("precio_unitario", precio_unitario);
linea.put("precio_total", BigDecimal.valueOf(precio_total).setScale(2, RoundingMode.HALF_UP));
total = total.add(BigDecimal.valueOf(precio_total));
lineas.add(linea);
if (hayDepositoLegal) {
linea = new HashMap<>();
linea.put("descripcion",
messageSource.getMessage("pdf.ejemplares-deposito-legal", new Object[]{4}, locale));
lineas.add(linea);
}
String serviciosExtras = "";
if (servicios != null) {
for (Map<String, Object> servicio : servicios) {
serviciosExtras += messageSource.getMessage(
"presupuesto.extras-" + servicio.get("id"), null, locale) + ", ";
}
if (!serviciosExtras.isEmpty()) {
serviciosExtras = serviciosExtras.substring(0, serviciosExtras.length() - 2);;
}
if (servicios.stream().anyMatch(service -> "marcapaginas".equals(service.get("id")))) {
ObjectMapper mapperServicio = new ObjectMapper();
Object raw = presupuesto.getDatosMarcapaginasJson();
Map<String, Object> datosMarcapaginas;
String descripcion = "";
try {
if (raw instanceof String s) {
datosMarcapaginas = mapperServicio.readValue(s,
new TypeReference<Map<String, Object>>() {
});
} else if (raw instanceof Map<?, ?> m) {
datosMarcapaginas = mapperServicio.convertValue(m,
new TypeReference<Map<String, Object>>() {
});
} else {
throw new IllegalArgumentException(
"Tipo no soportado para datosMarcapaginas: "
+ raw);
}
} catch (JsonProcessingException e) {
throw new RuntimeException("Error parsing datosMarcapaginasJson", e);
}
descripcion += "<br/><ul><li>";
descripcion += Marcapaginas.Tamanios
.valueOf(datosMarcapaginas.get("tamanio").toString()).getLabel()
+ ", ";
descripcion += Marcapaginas.Caras_Impresion
.valueOf(datosMarcapaginas.get("carasImpresion").toString())
.getMessageKey() + ", ";
descripcion += messageSource
.getMessage(Marcapaginas.Papeles
.valueOf(datosMarcapaginas.get("papel")
.toString())
.getMessageKey(), null, locale)
+ " - " +
datosMarcapaginas.get("gramaje").toString() + " gr, ";
descripcion += messageSource.getMessage(
Marcapaginas.Acabado.valueOf(
datosMarcapaginas.get("acabado").toString())
.getMessageKey(),
null, locale);
descripcion += "</li></ul>";
resumen.put("datosMarcapaginas", descripcion);
}
if (servicios.stream().anyMatch(service -> "maquetacion".equals(service.get("id")))) {
ObjectMapper mapperServicio = new ObjectMapper();
Object raw = presupuesto.getDatosMaquetacionJson();
Map<String, Object> datosMaquetacion;
String descripcion = "";
try {
if (raw instanceof String s) {
datosMaquetacion = mapperServicio.readValue(s,
new TypeReference<Map<String, Object>>() {
});
} else if (raw instanceof Map<?, ?> m) {
datosMaquetacion = mapperServicio.convertValue(m,
new TypeReference<Map<String, Object>>() {
});
} else {
throw new IllegalArgumentException(
"Tipo no soportado para datosMaquetacion: "
+ raw);
}
} catch (JsonProcessingException e) {
throw new RuntimeException("Error parsing datosMaquetacionJson", e);
}
descripcion += "<br/><ul><li>";
descripcion += (datosMaquetacion.get("num_caracteres") + " "
+ messageSource.getMessage("presupuesto.maquetacion.caracteres",
null, locale))
+ ", ";
descripcion += MaquetacionMatrices.Formato
.valueOf(datosMaquetacion.get("formato_maquetacion").toString())
.getLabel() + ", ";
descripcion += messageSource.getMessage(MaquetacionMatrices.FontSize
.valueOf(datosMaquetacion.get("cuerpo_texto").toString())
.getMessageKey(), null, locale)
+ ", ";
descripcion += messageSource.getMessage("presupuesto.maquetacion.num-columnas",
null, locale) + ": "
+ datosMaquetacion.get("num_columnas").toString() + ", ";
descripcion += messageSource.getMessage("presupuesto.maquetacion.num-tablas",
null, locale) + ": "
+ datosMaquetacion.get("num_tablas").toString() + ", ";
descripcion += messageSource.getMessage("presupuesto.maquetacion.num-fotos",
null, locale) + ": "
+ datosMaquetacion.get("num_fotos").toString();
if ((boolean) datosMaquetacion.get("correccion_ortotipografica")) {
descripcion += ", " + messageSource
.getMessage("presupuesto.maquetacion.correccion-ortotipografica",
null, locale);
}
if ((boolean) datosMaquetacion.get("texto_mecanografiado")) {
descripcion += ", " + messageSource.getMessage(
"presupuesto.maquetacion.texto-mecanografiado",
null, locale);
}
if ((boolean) datosMaquetacion.get("disenio_portada")) {
descripcion += ", "
+ messageSource.getMessage(
"presupuesto.maquetacion.diseno-portada",
null, locale);
}
if ((boolean) datosMaquetacion.get("epub")) {
descripcion += ", " + messageSource.getMessage(
"presupuesto.maquetacion.epub", null, locale);
}
descripcion += "</li></ul>";
resumen.put("datosMaquetacion", descripcion);
}
}
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
String formattedString = currencyFormat.format(total.setScale(2, RoundingMode.HALF_UP).doubleValue());
resumen.put("total", formattedString);
resumen.put("lineas", lineas);
resumen.put("servicios", serviciosExtras);
return resumen;
}
}

View File

@ -3,7 +3,6 @@ package com.imprimelibros.erp.pdf;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@ -16,12 +15,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.common.Utils;
@Service
public class PdfService {
private final TemplateRegistry registry;
private final PdfTemplateEngine engine;
private final PdfRenderer renderer;
private final PresupuestoRepository presupuestoRepository;
private final Utils utils;
private final Map<String, String> empresa = Map.of(
"nombre", "ImprimeLibros ERP",
@ -86,11 +88,12 @@ public class PdfService {
}
public PdfService(TemplateRegistry registry, PdfTemplateEngine engine, PdfRenderer renderer,
PresupuestoRepository presupuestoRepository) {
PresupuestoRepository presupuestoRepository, Utils utils) {
this.registry = registry;
this.engine = engine;
this.renderer = renderer;
this.presupuestoRepository = presupuestoRepository;
this.utils = utils;
}
private byte[] generate(DocumentSpec spec) {
@ -140,6 +143,9 @@ public class PdfService {
model.put("servicios", List.of(
Map.of("descripcion", "Transporte península", "unidades", 1, "precio", 90.00)));
Map<String, Object> specs = utils.getTextoPresupuesto(presupuesto, locale);
model.put("specs", specs);
Map<String, Object> pricing = new HashMap<>();
ObjectMapper mapper = new ObjectMapper();
@ -150,22 +156,22 @@ public class PdfService {
List<Integer> tiradas = snapshot.keySet().stream().toList();
pricing.put("tiradas", tiradas);
pricing.put("impresion", snapshot.values().stream()
.map(p -> formatCurrency(p.getPrecioTotalTirada(), locale))
.map(p -> Utils.formatCurrency(p.getPrecioTotalTirada(), locale))
.toList());
pricing.put("servicios", snapshot.values().stream()
.map(p -> formatCurrency(p.getServiciosTotal(), locale))
.map(p -> Utils.formatCurrency(p.getServiciosTotal(), locale))
.toList());
pricing.put("peso", snapshot.values().stream()
.map(p -> formatCurrency(p.getPeso(), locale))
.map(p -> Utils.formatCurrency(p.getPeso(), locale))
.toList());
pricing.put("iva_4", snapshot.values().stream()
.map(p -> formatCurrency(p.getIvaImporte4(), locale))
.map(p -> Utils.formatCurrency(p.getIvaImporte4(), locale))
.toList());
pricing.put("iva_21", snapshot.values().stream()
.map(p -> formatCurrency(p.getIvaImporte21(), locale))
.map(p -> Utils.formatCurrency(p.getIvaImporte21(), locale))
.toList());
pricing.put("total", snapshot.values().stream()
.map(p -> formatCurrency(p.getTotalConIva(), locale))
.map(p -> Utils.formatCurrency(p.getTotalConIva(), locale))
.toList());
pricing.put("show_iva_4", presupuesto.getIvaImporte4().floatValue() > 0);
pricing.put("show_iva_21", presupuesto.getIvaImporte21().floatValue() > 0);
@ -196,9 +202,4 @@ public class PdfService {
throw new RuntimeException("Error generando presupuesto PDF", e);
}
}
private static String formatCurrency(double value, Locale locale) {
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);
return currencyFormatter.format(value);
}
}

View File

@ -1,19 +1,15 @@
package com.imprimelibros.erp.presupuesto;
import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.datatables.*;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.JoinType;
import org.springframework.context.MessageSource;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
@ -71,7 +67,7 @@ public class PresupuestoDatatableService {
cb.coalesce(root.get("paginasNegro"), cb.literal(0))))
.add("estado", p -> msg(p.getEstado().getMessageKey(), locale))
.add("totalConIva", p -> formatCurrency(p.getTotalConIva(), locale))
.add("totalConIva", p -> Utils.formatCurrency(p.getTotalConIva(), locale))
.addIf(publico, "pais", Presupuesto::getPais)
.addIf(publico, "region", Presupuesto::getRegion)
.addIf(publico, "ciudad", Presupuesto::getCiudad)
@ -105,12 +101,6 @@ public class PresupuestoDatatableService {
return df.format(instant);
}
private String formatCurrency(BigDecimal value, Locale locale) {
if (value == null)
return "";
return NumberFormat.getCurrencyInstance(locale).format(value);
}
private String generarBotones(Presupuesto p) {
boolean borrador = p.getEstado() == Presupuesto.Estado.borrador;
String id = String.valueOf(p.getId());