mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 08:58:48 +00:00
acabando presupuesto
This commit is contained in:
@ -1,5 +1,10 @@
|
||||
package com.imprimelibros.erp.pdf;
|
||||
|
||||
public enum DocumentType {
|
||||
PRESUPUESTO, PEDIDO, FACTURA
|
||||
}
|
||||
PRESUPUESTO, PEDIDO, FACTURA;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,47 @@
|
||||
package com.imprimelibros.erp.pdf;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/pdf")
|
||||
public class PdfController {
|
||||
private final PdfService pdfService;
|
||||
|
||||
public PdfController(PdfService pdfService) { this.pdfService = pdfService; }
|
||||
public PdfController(PdfService pdfService) {
|
||||
this.pdfService = pdfService;
|
||||
}
|
||||
|
||||
@PostMapping("/{type}/{templateId}")
|
||||
@GetMapping(value = "/{type}/{id}", produces = "application/pdf")
|
||||
public ResponseEntity<byte[]> generate(
|
||||
@PathVariable("type") DocumentType type,
|
||||
@PathVariable String templateId,
|
||||
@RequestBody Map<String,Object> model,
|
||||
@PathVariable("type") String type,
|
||||
@PathVariable String id,
|
||||
@RequestParam(defaultValue = "inline") String mode,
|
||||
Locale locale) {
|
||||
|
||||
var spec = new DocumentSpec(type, templateId, locale, model);
|
||||
var pdf = pdfService.generate(spec);
|
||||
if (type.equals(DocumentType.PRESUPUESTO.toString()) && id == null) {
|
||||
throw new IllegalArgumentException("Falta el ID del presupuesto para generar el PDF");
|
||||
}
|
||||
if (type.equals(DocumentType.PRESUPUESTO.toString())) {
|
||||
Long presupuestoId = Long.valueOf(id);
|
||||
byte[] pdf = pdfService.generaPresupuesto(presupuestoId, locale);
|
||||
var headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_PDF);
|
||||
headers.setContentDisposition(
|
||||
("download".equals(mode)
|
||||
? ContentDisposition.attachment()
|
||||
: ContentDisposition.inline()).filename("presupuesto-" + id + ".pdf").build());
|
||||
|
||||
return new ResponseEntity<>(pdf, headers, HttpStatus.OK);
|
||||
} else {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
|
||||
}
|
||||
|
||||
var fileName = type.name().toLowerCase() + "-" + templateId + ".pdf";
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", "application/pdf")
|
||||
.header("Content-Disposition", "inline; filename=\"" + fileName + "\"")
|
||||
.body(pdf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,99 @@
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.imprimelibros.erp.presupuesto.PresupuestoRepository;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
|
||||
@Service
|
||||
public class PdfService {
|
||||
private final TemplateRegistry registry;
|
||||
private final PdfTemplateEngine engine;
|
||||
private final PdfRenderer renderer;
|
||||
private final PresupuestoRepository presupuestoRepository;
|
||||
|
||||
public PdfService(TemplateRegistry registry, PdfTemplateEngine engine, PdfRenderer renderer) {
|
||||
private final Map<String, String> empresa = Map.of(
|
||||
"nombre", "ImprimeLibros ERP",
|
||||
"direccion", "C/ Dirección 123, 28000 Madrid",
|
||||
"telefono", "+34 600 000 000",
|
||||
"email", "info@imprimelibros.com",
|
||||
"cif", "B-12345678",
|
||||
"cp", "28000",
|
||||
"poblacion", "Madrid",
|
||||
"web", "www.imprimelibros.com");
|
||||
|
||||
|
||||
private static class PrecioTirada {
|
||||
private Double peso;
|
||||
@JsonProperty("iva_importe_4")
|
||||
private Double ivaImporte4;
|
||||
@JsonProperty("total_con_iva")
|
||||
private Double totalConIva;
|
||||
@JsonProperty("base_imponible")
|
||||
private Double baseImponible;
|
||||
@JsonProperty("iva_importe_21")
|
||||
private Double ivaImporte21;
|
||||
@JsonProperty("precio_unitario")
|
||||
private Double precioUnitario;
|
||||
@JsonProperty("servicios_total")
|
||||
private Double serviciosTotal;
|
||||
@JsonProperty("precio_total_tirada")
|
||||
private Double precioTotalTirada;
|
||||
|
||||
public Double getPeso() {
|
||||
return peso;
|
||||
}
|
||||
|
||||
public Double getIvaImporte4() {
|
||||
return ivaImporte4;
|
||||
}
|
||||
|
||||
public Double getTotalConIva() {
|
||||
return totalConIva;
|
||||
}
|
||||
|
||||
public Double getBaseImponible() {
|
||||
return baseImponible;
|
||||
}
|
||||
|
||||
public Double getIvaImporte21() {
|
||||
return ivaImporte21;
|
||||
}
|
||||
|
||||
public Double getPrecioUnitario() {
|
||||
return precioUnitario;
|
||||
}
|
||||
|
||||
public Double getServiciosTotal() {
|
||||
return serviciosTotal;
|
||||
}
|
||||
|
||||
public Double getPrecioTotalTirada() {
|
||||
return precioTotalTirada;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PdfService(TemplateRegistry registry, PdfTemplateEngine engine, PdfRenderer renderer,
|
||||
PresupuestoRepository presupuestoRepository) {
|
||||
this.registry = registry;
|
||||
this.engine = engine;
|
||||
this.renderer = renderer;
|
||||
this.presupuestoRepository = presupuestoRepository;
|
||||
}
|
||||
|
||||
public byte[] generate(DocumentSpec spec) {
|
||||
private byte[] generate(DocumentSpec spec) {
|
||||
var template = registry.resolve(spec.type(), spec.templateId());
|
||||
if (template == null) {
|
||||
throw new IllegalArgumentException("Plantilla no registrada: " + spec.type() + ":" + spec.templateId());
|
||||
@ -22,4 +101,104 @@ public class PdfService {
|
||||
var html = engine.render(template, spec.locale(), spec.model());
|
||||
return renderer.renderHtmlToPdf(html);
|
||||
}
|
||||
|
||||
public byte[] generaPresupuesto(Long presupuestoId, Locale locale) {
|
||||
|
||||
try {
|
||||
Presupuesto presupuesto = presupuestoRepository.findById(presupuestoId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Presupuesto no encontrado: " + presupuestoId));
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("numero", presupuesto.getId());
|
||||
model.put("fecha", presupuesto.getUpdatedAt());
|
||||
|
||||
model.put("empresa", empresa);
|
||||
|
||||
model.put("cliente", Map.of(
|
||||
"nombre", presupuesto.getUser().getFullName()));
|
||||
|
||||
model.put("titulo", presupuesto.getTitulo());
|
||||
|
||||
/*
|
||||
* Map<String, Object> resumen = presupuestoService.getTextosResumen(
|
||||
* presupuesto, null, model, model, null)
|
||||
*/
|
||||
model.put("lineas", List.of(
|
||||
Map.of("descripcion", "Impresión interior B/N offset 80 g",
|
||||
"meta", "300 páginas · tinta negra · papel 80 g",
|
||||
"uds", 1000,
|
||||
"precio", 2.15,
|
||||
"dto", 0,
|
||||
"importe", 2150.0),
|
||||
Map.of("descripcion", "Cubierta color 300 g laminado mate",
|
||||
"meta", "Lomo 15 mm · 4/0 · laminado mate",
|
||||
"uds", 1000,
|
||||
"precio", 0.38,
|
||||
"dto", 5.0,
|
||||
"importe", 361.0)));
|
||||
|
||||
model.put("servicios", List.of(
|
||||
Map.of("descripcion", "Transporte península", "unidades", 1, "precio", 90.00)));
|
||||
|
||||
Map<String, Object> pricing = new HashMap<>();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
// Si quieres parsear directamente a un Map:
|
||||
Map<Integer, PrecioTirada> snapshot = mapper.readValue(presupuesto.getPricingSnapshotJson(),
|
||||
mapper.getTypeFactory().constructMapType(Map.class, Integer.class, PrecioTirada.class));
|
||||
List<Integer> tiradas = snapshot.keySet().stream().toList();
|
||||
pricing.put("tiradas", tiradas);
|
||||
pricing.put("impresion", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getPrecioTotalTirada(), locale))
|
||||
.toList());
|
||||
pricing.put("servicios", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getServiciosTotal(), locale))
|
||||
.toList());
|
||||
pricing.put("peso", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getPeso(), locale))
|
||||
.toList());
|
||||
pricing.put("iva_4", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getIvaImporte4(), locale))
|
||||
.toList());
|
||||
pricing.put("iva_21", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getIvaImporte21(), locale))
|
||||
.toList());
|
||||
pricing.put("total", snapshot.values().stream()
|
||||
.map(p -> formatCurrency(p.getTotalConIva(), locale))
|
||||
.toList());
|
||||
pricing.put("show_iva_4", presupuesto.getIvaImporte4().floatValue() > 0);
|
||||
pricing.put("show_iva_21", presupuesto.getIvaImporte21().floatValue() > 0);
|
||||
model.put("pricing", pricing);
|
||||
|
||||
var spec = new DocumentSpec(
|
||||
DocumentType.PRESUPUESTO,
|
||||
"presupuesto-a4",
|
||||
Locale.forLanguageTag("es-ES"),
|
||||
model);
|
||||
|
||||
byte[] pdf = this.generate(spec);
|
||||
|
||||
// HTML
|
||||
// (Opcional) generar HTML de depuración con CSS incrustado
|
||||
try {
|
||||
String templateName = registry.resolve(DocumentType.PRESUPUESTO, "presupuesto-a4");
|
||||
String html = engine.render(templateName, Locale.forLanguageTag("es-ES"), model);
|
||||
String css = Files.readString(Path.of("src/main/resources/static/assets/css/presupuestopdf.css"));
|
||||
String htmlWithCss = html.replaceFirst("(?i)</head>", "<style>\n" + css + "\n</style>\n</head>");
|
||||
Path htmlPath = Path.of("target/presupuesto-test.html");
|
||||
Files.writeString(htmlPath, htmlWithCss, StandardCharsets.UTF_8);
|
||||
} catch (Exception ignore) {
|
||||
/* solo para depuración */ }
|
||||
|
||||
return pdf;
|
||||
} catch (Exception e) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user