terminado (provisional) modulo de facturas

This commit is contained in:
2026-01-07 21:21:33 +01:00
parent 292aebcf65
commit 8263d97bf7
27 changed files with 13608 additions and 720 deletions

View File

@ -3,20 +3,26 @@ package com.imprimelibros.erp.facturacion.service;
import com.imprimelibros.erp.common.Utils;
import com.imprimelibros.erp.configurationERP.VariableService;
import com.imprimelibros.erp.facturacion.*;
import com.imprimelibros.erp.facturacion.dto.DireccionFacturacionDto;
import com.imprimelibros.erp.facturacion.dto.FacturaDireccionMapper;
import com.imprimelibros.erp.facturacion.dto.FacturaGuardarDto;
import com.imprimelibros.erp.facturacion.dto.FacturaLineaUpsertDto;
import com.imprimelibros.erp.facturacion.dto.FacturaPagoUpsertDto;
import com.imprimelibros.erp.facturacion.repo.FacturaDireccionRepository;
import com.imprimelibros.erp.facturacion.repo.FacturaLineaRepository;
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.PedidoDireccion;
import com.imprimelibros.erp.pedidos.PedidoLinea;
import com.imprimelibros.erp.pedidos.PedidoLineaRepository;
import com.imprimelibros.erp.pedidos.PedidoService;
import com.imprimelibros.erp.presupuesto.dto.Presupuesto;
import com.imprimelibros.erp.users.User;
import com.imprimelibros.erp.users.UserService;
import com.imprimelibros.erp.direcciones.Direccion;
import com.imprimelibros.erp.direcciones.DireccionRepository;
import jakarta.persistence.EntityNotFoundException;
@ -25,12 +31,17 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.Locale;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.Principal;
import java.text.Collator;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -42,6 +53,7 @@ public class FacturacionService {
private final SerieFacturaRepository serieRepo;
private final FacturaPagoRepository pagoRepo;
private final FacturaLineaRepository lineaFacturaRepository;
private final DireccionRepository direccionRepo;
private final PedidoLineaRepository pedidoLineaRepo;
private final UserService userService;
private final Utils utils;
@ -54,6 +66,7 @@ public class FacturacionService {
FacturaLineaRepository lineaFacturaRepository,
SerieFacturaRepository serieRepo,
FacturaPagoRepository pagoRepo,
DireccionRepository direccionRepo,
PedidoLineaRepository pedidoLineaRepo,
UserService userService,
Utils utils,
@ -64,6 +77,7 @@ public class FacturacionService {
this.lineaFacturaRepository = lineaFacturaRepository;
this.serieRepo = serieRepo;
this.pagoRepo = pagoRepo;
this.direccionRepo = direccionRepo;
this.pedidoLineaRepo = pedidoLineaRepo;
this.userService = userService;
this.utils = utils;
@ -87,6 +101,14 @@ public class FacturacionService {
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada: " + facturaId));
}
public Long getFacturaIdFromPedidoId(Long pedidoId) {
Factura factura = facturaRepo.findByPedidoId(pedidoId);
if (factura == null) {
throw new EntityNotFoundException("Factura no encontrada para el pedido: " + pedidoId);
}
return factura.getId();
}
// -----------------------
// Nueva factura
// -----------------------
@ -147,6 +169,13 @@ public class FacturacionService {
lineaEnvio.setFactura(factura);
lineasFactura.add(lineaEnvio);
}
PedidoDireccion direccionPedido = pedidoService.getDireccionFacturacionPedido(pedido.getId());
if(direccionPedido == null){
throw new IllegalStateException("El pedido no tiene una dirección de facturación asociada.");
}
FacturaDireccion fd = FacturaDireccionMapper.fromPedidoDireccion(direccionPedido);
factura.addDireccion(fd);
factura.setLineas(lineasFactura);
factura = facturaRepo.save(factura);
@ -166,6 +195,46 @@ public class FacturacionService {
return factura;
}
@Transactional
public Factura crearNuevaFactura(Long userId, Long serieId, Long direccionId, Long facturaRectificadaId) {
User cliente = userService.findById(userId);
if (cliente == null) {
throw new EntityNotFoundException("Cliente no encontrado: " + userId);
}
SerieFactura serie = serieRepo.findById(serieId)
.orElseThrow(() -> new EntityNotFoundException("Serie no encontrada: " + serieId));
Factura factura = new Factura();
factura.setCliente(cliente);
factura.setPedidoId(null);
factura.setSerie(serie);
factura.setEstado(EstadoFactura.borrador);
factura.setEstadoPago(EstadoPagoFactura.pendiente);
factura.setFechaEmision(LocalDateTime.now());
factura.setCreatedAt(Instant.now());
factura.setUpdatedAt(Instant.now());
factura.setNumeroFactura(null);
factura.setBaseImponible(BigDecimal.ZERO);
factura.setIva4(BigDecimal.ZERO);
factura.setIva21(BigDecimal.ZERO);
factura.setTotalFactura(BigDecimal.ZERO);
factura.setTotalPagado(BigDecimal.ZERO);
factura.setLineas(new ArrayList<>());
factura.setPagos(new ArrayList<>());
Direccion direccion = direccionRepo.findById(direccionId)
.orElseThrow(() -> new EntityNotFoundException("Dirección de factura no encontrada: " + direccionId));
FacturaDireccion facturaDireccion = FacturaDireccionMapper.fromDireccion(direccion);
factura.addDireccion(facturaDireccion);
if(facturaRectificadaId != null){
Factura facturaRectificada = facturaRepo.findById(facturaRectificadaId)
.orElseThrow(() -> new EntityNotFoundException("Factura rectificada no encontrada: " + facturaRectificadaId));
factura.setFacturaRectificativa(facturaRectificada);
facturaRectificada.setFacturaRectificada(factura);
}
return facturaRepo.save(factura);
}
// -----------------------
// Estado / Numeración
// -----------------------
@ -225,6 +294,7 @@ public class FacturacionService {
pedidoService.upsertDireccionFacturacion(pedidoId, dto.getDireccionFacturacion());
}
upsertDireccionFacturacion(facturaId, dto.getDireccionFacturacion());
facturaRepo.save(factura);
}
@ -281,12 +351,79 @@ public class FacturacionService {
return facturaRepo.save(factura);
}
@Transactional
public Boolean upsertDireccionFacturacion(Long facturaId, DireccionFacturacionDto direccionData) {
try {
Factura factura = facturaRepo.findById(facturaId)
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada: " + facturaId));
// ✅ Solo editable si borrador (tu regla actual para cabecera/dirección)
if (factura.getEstado() != EstadoFactura.borrador) {
throw new IllegalStateException("Solo se puede guardar dirección en borrador.");
}
factura.getDirecciones().clear();
factura.addDireccion(direccionData.toFacturaDireccion());
facturaRepo.save(factura);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public Map<String, Object> getForSelectFacturasRectificables(String q1, String q2, Long userId) {
try {
String search = Optional.ofNullable(q1).orElse(q2);
if (search != null) {
search = search.trim();
}
final String q = (search == null || search.isEmpty())
? null
: search.toLowerCase();
List<Factura> all = facturaRepo.findByClienteIdAndEstadoAndEstadoPagoAndSerieId(
userId,
EstadoFactura.validada,
EstadoPagoFactura.pagada,
variableService.getValorEntero("serie_facturacion_default").longValue());
// Mapear a opciones id/text con i18n y filtrar por búsqueda si llega
List<Map<String, String>> options = all.stream()
.map(f -> {
String id = f.getId().toString();
String text = f.getNumeroFactura();
Map<String, String> m = new HashMap<>();
m.put("id", id); // lo normal en Select2: id = valor que guardarás (code3)
m.put("text", text); // texto mostrado, i18n con fallback a keyword
return m;
})
.filter(opt -> {
if (q == null || q.isEmpty())
return true;
String text = opt.get("text").toLowerCase();
return text.contains(q);
})
.sorted(Comparator.comparing(m -> m.get("text"), Collator.getInstance()))
.collect(Collectors.toList());
// Estructura Select2
Map<String, Object> resp = new HashMap<>();
resp.put("results", options);
return resp;
} catch (Exception e) {
e.printStackTrace();
return Map.of("results", List.of());
}
}
private String buildNumeroFactura(String prefijo, long numero) {
String pref = (prefijo == null) ? "" : prefijo.trim();
String num = String.format("%05d", numero);
return pref.isBlank() ? num : (pref + " " + num + "/" + LocalDate.now().getYear());
}
// -----------------------
// Líneas
// -----------------------