mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-22 00:30:23 +00:00
trabajando en el formulario de la factura
This commit is contained in:
@ -1,17 +1,31 @@
|
||||
package com.imprimelibros.erp.facturacion.service;
|
||||
|
||||
import com.imprimelibros.erp.common.Utils;
|
||||
import com.imprimelibros.erp.facturacion.*;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
|
||||
import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto;
|
||||
import com.imprimelibros.erp.facturacion.repo.FacturaPagoRepository;
|
||||
import com.imprimelibros.erp.facturacion.repo.FacturaRepository;
|
||||
import com.imprimelibros.erp.facturacion.repo.SerieFacturaRepository;
|
||||
import com.imprimelibros.erp.pedidos.Pedido;
|
||||
import com.imprimelibros.erp.pedidos.PedidoLinea;
|
||||
import com.imprimelibros.erp.pedidos.PedidoLineaRepository;
|
||||
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
@ -20,21 +34,121 @@ public class FacturacionService {
|
||||
private final FacturaRepository facturaRepo;
|
||||
private final SerieFacturaRepository serieRepo;
|
||||
private final FacturaPagoRepository pagoRepo;
|
||||
private final PedidoLineaRepository pedidoLineaRepo;
|
||||
private final Utils utils;
|
||||
private final MessageSource messageSource;
|
||||
|
||||
public FacturacionService(
|
||||
FacturaRepository facturaRepo,
|
||||
SerieFacturaRepository serieRepo,
|
||||
FacturaPagoRepository pagoRepo
|
||||
) {
|
||||
FacturaPagoRepository pagoRepo,
|
||||
PedidoLineaRepository pedidoLineaRepo,
|
||||
Utils utils,
|
||||
MessageSource messageSource) {
|
||||
this.facturaRepo = facturaRepo;
|
||||
this.serieRepo = serieRepo;
|
||||
this.pagoRepo = pagoRepo;
|
||||
this.pedidoLineaRepo = pedidoLineaRepo;
|
||||
this.utils = utils;
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
|
||||
public SerieFactura getDefaultSerieFactura() {
|
||||
List<SerieFactura> series = serieRepo.findAll();
|
||||
if (series.isEmpty()) {
|
||||
throw new IllegalStateException("No hay ninguna serie de facturación configurada.");
|
||||
}
|
||||
// Aquí simplemente devolvemos la primera. Puedes implementar lógica más
|
||||
// compleja si es necesario.
|
||||
return series.get(0);
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// Nueva factura
|
||||
// -----------------------
|
||||
@Transactional
|
||||
public Factura crearNuevaFacturaAuto(Pedido pedido, SerieFactura serie, TipoPago tipoPago, Locale locale) {
|
||||
|
||||
Factura factura = new Factura();
|
||||
factura.setCliente(pedido.getCreatedBy());
|
||||
factura.setCreatedAt(Instant.now());
|
||||
factura.setUpdatedAt(Instant.now());
|
||||
Boolean pedidoPendientePago = false;
|
||||
List<PedidoLinea> lineasPedido = pedidoLineaRepo.findByPedidoId(pedido.getId());
|
||||
for (PedidoLinea lineaPedido : lineasPedido) {
|
||||
if (lineaPedido.getEstado() == PedidoLinea.Estado.pendiente_pago) {
|
||||
pedidoPendientePago = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
factura.setEstado(pedidoPendientePago ? EstadoFactura.borrador : EstadoFactura.validada);
|
||||
factura.setEstadoPago(pedidoPendientePago ? EstadoPagoFactura.pendiente : EstadoPagoFactura.pagada);
|
||||
factura.setTipoPago(pedidoPendientePago ? TipoPago.otros : tipoPago);
|
||||
factura.setPedidoId(pedido.getId());
|
||||
factura.setSerie(serie);
|
||||
factura.setNumeroFactura(this.getNumberFactura(serie));
|
||||
factura.setFechaEmision(LocalDateTime.now());
|
||||
factura.setBaseImponible(BigDecimal.valueOf(pedido.getBase()).setScale(2, RoundingMode.HALF_UP));
|
||||
factura.setIva4(BigDecimal.valueOf(pedido.getIva4()).setScale(2, RoundingMode.HALF_UP));
|
||||
factura.setIva21(BigDecimal.valueOf(pedido.getIva21()).setScale(2, RoundingMode.HALF_UP));
|
||||
factura.setTotalFactura(BigDecimal.valueOf(pedido.getTotal()).setScale(2, RoundingMode.HALF_UP));
|
||||
factura.setTotalPagado(BigDecimal.valueOf(pedido.getTotal()).setScale(2, RoundingMode.HALF_UP));
|
||||
// rellenar lineas
|
||||
List<FacturaLinea> lineasFactura = new ArrayList<>();
|
||||
for (PedidoLinea lineaPedido : lineasPedido) {
|
||||
Presupuesto p = lineaPedido.getPresupuesto();
|
||||
FacturaLinea lineaFactura = new FacturaLinea();
|
||||
lineaFactura.setDescripcion(this.obtenerLineaFactura(lineaPedido, locale));
|
||||
lineaFactura.setCantidad(p.getSelectedTirada());
|
||||
lineaFactura.setBaseLinea(p.getBaseImponible());
|
||||
lineaFactura.setIva4Linea(p.getIvaImporte4());
|
||||
lineaFactura.setIva21Linea(p.getIvaImporte21());
|
||||
lineaFactura.setTotalLinea(p.getTotalConIva());
|
||||
lineaFactura.setCreatedBy(p.getUser());
|
||||
lineaFactura.setFactura(factura);
|
||||
lineasFactura.add(lineaFactura);
|
||||
}
|
||||
factura.setLineas(lineasFactura);
|
||||
|
||||
factura = facturaRepo.save(factura);
|
||||
|
||||
if(pedidoPendientePago) {
|
||||
return factura;
|
||||
}
|
||||
FacturaPago pago = new FacturaPago();
|
||||
pago.setMetodoPago(tipoPago);
|
||||
pago.setCantidadPagada(factura.getTotalFactura());
|
||||
pago.setFechaPago(LocalDateTime.now());
|
||||
pago.setFactura(factura);
|
||||
pago.setCreatedBy(pedido.getCreatedBy());
|
||||
pago.setCreatedAt(Instant.now());
|
||||
pagoRepo.save(pago);
|
||||
|
||||
return factura;
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// Estado / Numeración
|
||||
// -----------------------
|
||||
|
||||
@Transactional
|
||||
public String getNumberFactura(SerieFactura serie) {
|
||||
|
||||
try {
|
||||
long next = (serie.getNumeroActual() == null) ? 1L : serie.getNumeroActual();
|
||||
String numeroFactura = buildNumeroFactura(serie.getPrefijo(), next);
|
||||
|
||||
// Incrementar contador para la siguiente
|
||||
serie.setNumeroActual(next + 1);
|
||||
serieRepo.save(serie);
|
||||
|
||||
return numeroFactura;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Factura validarFactura(Long facturaId) {
|
||||
Factura factura = facturaRepo.findById(facturaId)
|
||||
@ -56,7 +170,8 @@ public class FacturacionService {
|
||||
// Si ya tiene numero_factura, no reservamos otro
|
||||
if (factura.getNumeroFactura() == null || factura.getNumeroFactura().isBlank()) {
|
||||
SerieFactura serieLocked = serieRepo.findByIdForUpdate(factura.getSerie().getId())
|
||||
.orElseThrow(() -> new EntityNotFoundException("Serie no encontrada: " + factura.getSerie().getId()));
|
||||
.orElseThrow(
|
||||
() -> new EntityNotFoundException("Serie no encontrada: " + factura.getSerie().getId()));
|
||||
|
||||
long next = (serieLocked.getNumeroActual() == null) ? 1L : serieLocked.getNumeroActual();
|
||||
String numeroFactura = buildNumeroFactura(serieLocked.getPrefijo(), next);
|
||||
@ -89,7 +204,7 @@ public class FacturacionService {
|
||||
private String buildNumeroFactura(String prefijo, long numero) {
|
||||
String pref = (prefijo == null) ? "" : prefijo.trim();
|
||||
String num = String.format("%07d", numero);
|
||||
return pref.isBlank() ? num : (pref + "-" + num);
|
||||
return pref.isBlank() ? num : (pref + " " + num + "/" + LocalDate.now().getYear());
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
@ -191,7 +306,8 @@ public class FacturacionService {
|
||||
pago.setFechaPago(dto.getFechaPago() != null ? dto.getFechaPago() : LocalDateTime.now());
|
||||
pago.setNotas(dto.getNotas());
|
||||
|
||||
// El tipo_pago de la factura: si tiene un pago, lo reflejamos (último pago manda)
|
||||
// El tipo_pago de la factura: si tiene un pago, lo reflejamos (último pago
|
||||
// manda)
|
||||
factura.setTipoPago(dto.getMetodoPago());
|
||||
|
||||
recalcularTotales(factura);
|
||||
@ -254,7 +370,8 @@ public class FacturacionService {
|
||||
factura.setTotalPagado(scale2(pagado));
|
||||
|
||||
// estado_pago
|
||||
// - cancelada: si la factura está marcada como cancelada manualmente (aquí NO lo hacemos automático)
|
||||
// - cancelada: si la factura está marcada como cancelada manualmente (aquí NO
|
||||
// lo hacemos automático)
|
||||
// - pagada: si total_pagado >= total_factura y total_factura > 0
|
||||
// - pendiente: resto
|
||||
if (factura.getEstadoPago() == EstadoPagoFactura.cancelada) {
|
||||
@ -277,4 +394,90 @@ public class FacturacionService {
|
||||
private static BigDecimal scale2(BigDecimal v) {
|
||||
return (v == null ? BigDecimal.ZERO : v).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private String obtenerLineaFactura(PedidoLinea lineaPedido, Locale locale) {
|
||||
|
||||
Map<String, Object> specs = utils.getTextoPresupuesto(lineaPedido.getPresupuesto(), locale);
|
||||
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<div class=\"specs-wrapper align-with-text \">")
|
||||
.append("<div class=\"specs\">");
|
||||
|
||||
if (specs == null) {
|
||||
return "<div></div>";
|
||||
}
|
||||
|
||||
// 1) Líneas del presupuesto (HTML)
|
||||
Object lineasObj = specs.get("lineas");
|
||||
if (lineasObj instanceof List<?> lineasList) {
|
||||
for (Object o : lineasList) {
|
||||
if (!(o instanceof Map<?, ?> m))
|
||||
continue;
|
||||
|
||||
Object descObj = m.get("descripcion");
|
||||
String descripcionHtml = descObj != null ? descObj.toString() : "";
|
||||
if (descripcionHtml.isBlank())
|
||||
continue;
|
||||
|
||||
html.append("<div class=\"spec-row mb-1\">")
|
||||
.append("<span class=\"spec-label\">")
|
||||
.append(descripcionHtml) // OJO: esto es HTML (como th:utext)
|
||||
.append("</span>")
|
||||
.append("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Servicios adicionales (texto)
|
||||
Object serviciosObj = specs.get("servicios");
|
||||
String servicios = (serviciosObj != null) ? serviciosObj.toString().trim() : "";
|
||||
if (!servicios.isBlank()) {
|
||||
String label = messageSource.getMessage("pdf.servicios-adicionales", null, "Servicios adicionales", locale);
|
||||
html.append("<div class=\"spec-row mb-1\">")
|
||||
.append("<span>").append(escapeHtml(label)).append("</span>")
|
||||
.append("<span class=\"spec-label\">").append(escapeHtml(servicios)).append("</span>")
|
||||
.append("</div>");
|
||||
}
|
||||
|
||||
// 3) Datos de maquetación (HTML)
|
||||
Object datosMaqObj = specs.get("datosMaquetacion");
|
||||
if (datosMaqObj != null && !datosMaqObj.toString().isBlank()) {
|
||||
String label = messageSource.getMessage("pdf.datos-maquetacion", null, "Datos de maquetación:", locale);
|
||||
html.append("<div class=\"spec-row mb-1\">")
|
||||
.append("<span>").append(escapeHtml(label)).append("</span>")
|
||||
.append("<span class=\"spec-label\">")
|
||||
.append(datosMaqObj) // HTML (como th:utext)
|
||||
.append("</span>")
|
||||
.append("</div>");
|
||||
}
|
||||
|
||||
// 4) Datos de marcapáginas (HTML)
|
||||
Object datosMarcaObj = specs.get("datosMarcapaginas");
|
||||
if (datosMarcaObj != null && !datosMarcaObj.toString().isBlank()) {
|
||||
String label = messageSource.getMessage("pdf.datos-marcapaginas", null, "Datos de marcapáginas:", locale);
|
||||
html.append("<div class=\"spec-row mb-1\">")
|
||||
.append("<span>").append(escapeHtml(label)).append("</span>")
|
||||
.append("<span class=\"spec-label\">")
|
||||
.append(datosMarcaObj) // HTML (como th:utext)
|
||||
.append("</span>")
|
||||
.append("</div>");
|
||||
}
|
||||
|
||||
html.append("</div></div>");
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape mínimo para texto plano (equivalente a th:text).
|
||||
* No lo uses para fragmentos que ya son HTML (th:utext).
|
||||
*/
|
||||
private static String escapeHtml(String s) {
|
||||
if (s == null)
|
||||
return "";
|
||||
return s.replace("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("\"", """)
|
||||
.replace("'", "'");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user