mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-02-10 21:09:12 +00:00
terminando pdf de facturas
This commit is contained in:
@ -6,13 +6,18 @@ import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||
import com.imprimelibros.erp.facturacion.EstadoFactura;
|
||||
import com.imprimelibros.erp.facturacion.Factura;
|
||||
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.FacturaRepository;
|
||||
import com.imprimelibros.erp.facturacion.service.FacturacionService;
|
||||
import com.imprimelibros.erp.i18n.TranslationService;
|
||||
import com.imprimelibros.erp.pedidos.PedidoDireccion;
|
||||
import com.imprimelibros.erp.pedidos.PedidoService;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
@ -22,20 +27,20 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/facturas")
|
||||
@PreAuthorize("hasRole('SUPERADMIN') || hasRole('ADMIN')")
|
||||
public class FacturasController {
|
||||
|
||||
private final FacturacionService facturacionService;
|
||||
|
||||
private final FacturaRepository repo;
|
||||
private final TranslationService translationService;
|
||||
private final MessageSource messageSource;
|
||||
@ -45,11 +50,12 @@ public class FacturasController {
|
||||
FacturaRepository repo,
|
||||
TranslationService translationService,
|
||||
MessageSource messageSource,
|
||||
PedidoService pedidoService) {
|
||||
PedidoService pedidoService, FacturacionService facturacionService) {
|
||||
this.repo = repo;
|
||||
this.translationService = translationService;
|
||||
this.messageSource = messageSource;
|
||||
this.pedidoService = pedidoService;
|
||||
this.facturacionService = facturacionService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@ -75,12 +81,36 @@ public class FacturasController {
|
||||
PedidoDireccion direccionFacturacion = pedidoService
|
||||
.getPedidoDireccionFacturacionByPedidoId(factura.getPedidoId());
|
||||
|
||||
List<String> keys = List.of(
|
||||
"facturas.lineas.error.base",
|
||||
"facturas.lineas.delete.title",
|
||||
"facturas.lineas.delete.text",
|
||||
|
||||
"facturas.pagos.delete.title",
|
||||
"facturas.pagos.delete.text",
|
||||
"facturas.pagos.error.cantidad",
|
||||
"facturas.pagos.error.fecha",
|
||||
|
||||
"app.eliminar",
|
||||
"app.cancelar");
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
|
||||
model.addAttribute("direccionFacturacion", direccionFacturacion);
|
||||
model.addAttribute("factura", factura);
|
||||
|
||||
return "imprimelibros/facturas/facturas-form";
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/guardar")
|
||||
public ResponseEntity<?> guardarFacturaCabeceraYDireccion(
|
||||
@PathVariable Long id,
|
||||
@RequestBody @Valid FacturaGuardarDto payload) {
|
||||
facturacionService.guardarCabeceraYDireccionFacturacion(id, payload);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/container")
|
||||
public String facturaContainer(@PathVariable Long id, Model model, Locale locale) {
|
||||
Factura factura = repo.findById(id)
|
||||
@ -116,7 +146,8 @@ public class FacturasController {
|
||||
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada con ID: " + id));
|
||||
|
||||
if (factura.getEstado() != EstadoFactura.validada) {
|
||||
return ResponseEntity.badRequest().body("Solo se pueden marcar como borrador facturas en estado 'validada'.");
|
||||
return ResponseEntity.badRequest()
|
||||
.body("Solo se pueden marcar como borrador facturas en estado 'validada'.");
|
||||
}
|
||||
|
||||
factura.setEstado(EstadoFactura.borrador);
|
||||
@ -125,22 +156,75 @@ public class FacturasController {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{facturaId}/lineas")
|
||||
public ResponseEntity<?> createLinea(@PathVariable Long facturaId,
|
||||
@Valid @RequestBody FacturaLineaUpsertDto req) {
|
||||
facturacionService.createLinea(facturaId, req);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@PostMapping("/{facturaId}/lineas/{lineaId}")
|
||||
public ResponseEntity<?> updateLinea(@PathVariable Long facturaId,
|
||||
@PathVariable Long lineaId,
|
||||
@Valid @RequestBody FacturaLineaUpsertDto req) {
|
||||
facturacionService.upsertLinea(facturaId, req);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@PostMapping("/{facturaId}/lineas/{lineaId}/delete")
|
||||
public ResponseEntity<?> deleteLinea(@PathVariable Long facturaId,
|
||||
@PathVariable Long lineaId) {
|
||||
facturacionService.borrarLinea(facturaId, lineaId);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------
|
||||
* Pagos
|
||||
* --------------------------------
|
||||
*/
|
||||
@PostMapping("/{facturaId}/pagos")
|
||||
public ResponseEntity<?> createPago(
|
||||
@PathVariable Long facturaId,
|
||||
@Valid @RequestBody FacturaPagoUpsertDto req, Principal principal) {
|
||||
facturacionService.upsertPago(facturaId, req, principal);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@PostMapping("/{facturaId}/pagos/{pagoId}")
|
||||
public ResponseEntity<?> updatePago(
|
||||
@PathVariable Long facturaId,
|
||||
@PathVariable Long pagoId,
|
||||
@Valid @RequestBody FacturaPagoUpsertDto req,
|
||||
Principal principal) {
|
||||
// opcional: fuerza consistencia
|
||||
req.setId(pagoId);
|
||||
facturacionService.upsertPago(facturaId, req, principal);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@PostMapping("/{facturaId}/pagos/{pagoId}/delete")
|
||||
public ResponseEntity<?> deletePago(
|
||||
@PathVariable Long facturaId,
|
||||
@PathVariable Long pagoId, Principal principal) {
|
||||
facturacionService.borrarPago(facturaId, pagoId, principal);
|
||||
return ResponseEntity.ok(Map.of("ok", true));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/notas")
|
||||
public ResponseEntity<?> setNotas(
|
||||
@PathVariable Long id,
|
||||
@RequestBody Map<String, String> payload,
|
||||
Model model,
|
||||
Locale locale
|
||||
) {
|
||||
Locale locale) {
|
||||
Factura factura = repo.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada con ID: " + id));
|
||||
String notas = payload.get("notas");
|
||||
factura.setNotas(notas);
|
||||
repo.save(factura);
|
||||
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------
|
||||
// API: DataTables (server-side)
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
package com.imprimelibros.erp.facturacion.dto;
|
||||
|
||||
import com.imprimelibros.erp.pedidos.PedidoDireccion;
|
||||
|
||||
public class DireccionFacturacionDto {
|
||||
private String razonSocial;
|
||||
private String identificacionFiscal;
|
||||
private String direccion;
|
||||
private String cp;
|
||||
private String ciudad;
|
||||
private String provincia;
|
||||
private String paisKeyword;
|
||||
private String telefono;
|
||||
|
||||
public String getRazonSocial() {
|
||||
return razonSocial;
|
||||
}
|
||||
|
||||
public void setRazonSocial(String razonSocial) {
|
||||
this.razonSocial = razonSocial;
|
||||
}
|
||||
|
||||
public String getIdentificacionFiscal() {
|
||||
return identificacionFiscal;
|
||||
}
|
||||
|
||||
public void setIdentificacionFiscal(String identificacionFiscal) {
|
||||
this.identificacionFiscal = identificacionFiscal;
|
||||
}
|
||||
|
||||
public String getDireccion() {
|
||||
return direccion;
|
||||
}
|
||||
|
||||
public void setDireccion(String direccion) {
|
||||
this.direccion = direccion;
|
||||
}
|
||||
|
||||
public String getCp() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public void setCp(String cp) {
|
||||
this.cp = cp;
|
||||
}
|
||||
|
||||
public String getCiudad() {
|
||||
return ciudad;
|
||||
}
|
||||
|
||||
public void setCiudad(String ciudad) {
|
||||
this.ciudad = ciudad;
|
||||
}
|
||||
|
||||
public String getProvincia() {
|
||||
return provincia;
|
||||
}
|
||||
|
||||
public void setProvincia(String provincia) {
|
||||
this.provincia = provincia;
|
||||
}
|
||||
|
||||
public String getPaisKeyword() {
|
||||
return paisKeyword;
|
||||
}
|
||||
|
||||
public void setPaisKeyword(String paisKeyword) {
|
||||
this.paisKeyword = paisKeyword;
|
||||
}
|
||||
|
||||
public String getTelefono() {
|
||||
return telefono;
|
||||
}
|
||||
|
||||
public void setTelefono(String telefono) {
|
||||
this.telefono = telefono;
|
||||
}
|
||||
|
||||
public PedidoDireccion toPedidoDireccion() {
|
||||
PedidoDireccion pd = new PedidoDireccion();
|
||||
applyTo(pd);
|
||||
pd.setFacturacion(true);
|
||||
return pd;
|
||||
}
|
||||
|
||||
public void applyTo(PedidoDireccion pd) {
|
||||
pd.setRazonSocial(this.razonSocial);
|
||||
pd.setIdentificacionFiscal(this.identificacionFiscal);
|
||||
pd.setDireccion(this.direccion);
|
||||
|
||||
// CP robusto
|
||||
Integer cpInt = null;
|
||||
if (this.cp != null && !this.cp.isBlank()) {
|
||||
try {
|
||||
cpInt = Integer.valueOf(this.cp.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
// si quieres, lanza IllegalArgumentException para validarlo
|
||||
}
|
||||
}
|
||||
pd.setCp(cpInt);
|
||||
|
||||
pd.setCiudad(this.ciudad);
|
||||
pd.setProvincia(this.provincia);
|
||||
|
||||
pd.setPaisCode3(this.paisKeyword);
|
||||
|
||||
pd.setTelefono(this.telefono);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.imprimelibros.erp.facturacion.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class FacturaCabeceraDto {
|
||||
private Long serieId;
|
||||
private Long clienteId;
|
||||
private LocalDateTime fechaEmision;
|
||||
|
||||
public Long getSerieId() {
|
||||
return serieId;
|
||||
}
|
||||
|
||||
public void setSerieId(Long serieId) {
|
||||
this.serieId = serieId;
|
||||
}
|
||||
|
||||
public Long getClienteId() {
|
||||
return clienteId;
|
||||
}
|
||||
|
||||
public void setClienteId(Long clienteId) {
|
||||
this.clienteId = clienteId;
|
||||
}
|
||||
|
||||
public LocalDateTime getFechaEmision() {
|
||||
return fechaEmision;
|
||||
}
|
||||
|
||||
public void setFechaEmision(LocalDateTime fechaEmision) {
|
||||
this.fechaEmision = fechaEmision;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.imprimelibros.erp.facturacion.dto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
public class FacturaGuardarDto {
|
||||
@Valid private FacturaCabeceraDto cabecera;
|
||||
@Valid private DireccionFacturacionDto direccionFacturacion;
|
||||
|
||||
// getters/setters
|
||||
public FacturaCabeceraDto getCabecera() {
|
||||
return cabecera;
|
||||
}
|
||||
public void setCabecera(FacturaCabeceraDto cabecera) {
|
||||
this.cabecera = cabecera;
|
||||
}
|
||||
public DireccionFacturacionDto getDireccionFacturacion() {
|
||||
return direccionFacturacion;
|
||||
}
|
||||
public void setDireccionFacturacion(DireccionFacturacionDto direccionFacturacion) {
|
||||
this.direccionFacturacion = direccionFacturacion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,21 @@
|
||||
package com.imprimelibros.erp.facturacion.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class FacturaLineaUpsertDto {
|
||||
|
||||
private Long id; // null => nueva línea
|
||||
|
||||
@NotBlank
|
||||
private String descripcion;
|
||||
// Para update puedes mandarlo, pero realmente lo sacamos del path
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
private Integer cantidad;
|
||||
private String descripcion; // HTML
|
||||
|
||||
@NotNull
|
||||
private BigDecimal baseLinea; // base imponible de la línea (sin IVA)
|
||||
private BigDecimal base;
|
||||
|
||||
private boolean aplicaIva4;
|
||||
private boolean aplicaIva21;
|
||||
private BigDecimal iva4;
|
||||
private BigDecimal iva21;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
@ -27,15 +23,12 @@ public class FacturaLineaUpsertDto {
|
||||
public String getDescripcion() { return descripcion; }
|
||||
public void setDescripcion(String descripcion) { this.descripcion = descripcion; }
|
||||
|
||||
public Integer getCantidad() { return cantidad; }
|
||||
public void setCantidad(Integer cantidad) { this.cantidad = cantidad; }
|
||||
public BigDecimal getBase() { return base; }
|
||||
public void setBase(BigDecimal base) { this.base = base; }
|
||||
|
||||
public BigDecimal getBaseLinea() { return baseLinea; }
|
||||
public void setBaseLinea(BigDecimal baseLinea) { this.baseLinea = baseLinea; }
|
||||
public BigDecimal getIva4() { return iva4; }
|
||||
public void setIva4(BigDecimal iva4) { this.iva4 = iva4; }
|
||||
|
||||
public boolean isAplicaIva4() { return aplicaIva4; }
|
||||
public void setAplicaIva4(boolean aplicaIva4) { this.aplicaIva4 = aplicaIva4; }
|
||||
|
||||
public boolean isAplicaIva21() { return aplicaIva21; }
|
||||
public void setAplicaIva21(boolean aplicaIva21) { this.aplicaIva21 = aplicaIva21; }
|
||||
public BigDecimal getIva21() { return iva21; }
|
||||
public void setIva21(BigDecimal iva21) { this.iva21 = iva21; }
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@ import com.imprimelibros.erp.facturacion.FacturaLinea;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FacturaLineaRepository extends JpaRepository<FacturaLinea, Long> {
|
||||
List<FacturaLinea> findByFacturaId(Long facturaId);
|
||||
Optional<FacturaLinea> findByIdAndFacturaId(Long id, Long facturaId);
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@ import com.imprimelibros.erp.facturacion.FacturaPago;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FacturaPagoRepository extends JpaRepository<FacturaPago, Long> {
|
||||
List<FacturaPago> findByFacturaId(Long facturaId);
|
||||
List<FacturaPago> findByFacturaIdAndDeletedAtIsNullOrderByFechaPagoDescIdDesc(Long facturaId);
|
||||
Optional<FacturaPago> findByIdAndFacturaIdAndDeletedAtIsNull(Long id, Long facturaId);
|
||||
|
||||
}
|
||||
|
||||
@ -2,15 +2,20 @@ package com.imprimelibros.erp.facturacion.service;
|
||||
|
||||
import com.imprimelibros.erp.common.Utils;
|
||||
import com.imprimelibros.erp.facturacion.*;
|
||||
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.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.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 jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
@ -24,6 +29,7 @@ import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@ -34,26 +40,34 @@ public class FacturacionService {
|
||||
private final FacturaRepository facturaRepo;
|
||||
private final SerieFacturaRepository serieRepo;
|
||||
private final FacturaPagoRepository pagoRepo;
|
||||
private final FacturaLineaRepository lineaFacturaRepository;
|
||||
private final PedidoLineaRepository pedidoLineaRepo;
|
||||
private final UserService userService;
|
||||
private final Utils utils;
|
||||
private final MessageSource messageSource;
|
||||
private final PedidoService pedidoService;
|
||||
|
||||
public FacturacionService(
|
||||
FacturaRepository facturaRepo,
|
||||
FacturaLineaRepository lineaFacturaRepository,
|
||||
SerieFacturaRepository serieRepo,
|
||||
FacturaPagoRepository pagoRepo,
|
||||
PedidoLineaRepository pedidoLineaRepo,
|
||||
UserService userService,
|
||||
Utils utils,
|
||||
MessageSource messageSource) {
|
||||
MessageSource messageSource,
|
||||
PedidoService pedidoService) {
|
||||
this.facturaRepo = facturaRepo;
|
||||
this.lineaFacturaRepository = lineaFacturaRepository;
|
||||
this.serieRepo = serieRepo;
|
||||
this.pagoRepo = pagoRepo;
|
||||
this.pedidoLineaRepo = pedidoLineaRepo;
|
||||
this.userService = userService;
|
||||
this.utils = utils;
|
||||
this.messageSource = messageSource;
|
||||
this.pedidoService = pedidoService;
|
||||
}
|
||||
|
||||
|
||||
public SerieFactura getDefaultSerieFactura() {
|
||||
List<SerieFactura> series = serieRepo.findAll();
|
||||
if (series.isEmpty()) {
|
||||
@ -64,6 +78,11 @@ public class FacturacionService {
|
||||
return series.get(0);
|
||||
}
|
||||
|
||||
public Factura getFactura(Long facturaId) {
|
||||
return facturaRepo.findById(facturaId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada: " + facturaId));
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// Nueva factura
|
||||
// -----------------------
|
||||
@ -113,7 +132,7 @@ public class FacturacionService {
|
||||
|
||||
factura = facturaRepo.save(factura);
|
||||
|
||||
if(pedidoPendientePago) {
|
||||
if (pedidoPendientePago) {
|
||||
return factura;
|
||||
}
|
||||
FacturaPago pago = new FacturaPago();
|
||||
@ -149,6 +168,48 @@ public class FacturacionService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void guardarCabeceraYDireccionFacturacion(Long facturaId, FacturaGuardarDto dto) {
|
||||
Factura factura = getFactura(facturaId);
|
||||
|
||||
// ✅ Solo editable si borrador (tu regla actual para cabecera/dirección)
|
||||
if (factura.getEstado() != EstadoFactura.borrador) {
|
||||
throw new IllegalStateException("Solo se puede guardar cabecera/dirección en borrador.");
|
||||
}
|
||||
|
||||
// 1) Cabecera
|
||||
if (dto.getCabecera() != null) {
|
||||
var c = dto.getCabecera();
|
||||
|
||||
if (c.getSerieId() != null) {
|
||||
SerieFactura serie = serieRepo.findById(c.getSerieId())
|
||||
.orElseThrow(() -> new EntityNotFoundException("Serie no encontrada: " + c.getSerieId()));
|
||||
factura.setSerie(serie);
|
||||
}
|
||||
|
||||
if (c.getClienteId() != null) {
|
||||
User cliente = userService.findById(c.getClienteId());
|
||||
if(cliente == null){
|
||||
throw new EntityNotFoundException("Cliente no encontrado: " + c.getClienteId());
|
||||
}
|
||||
factura.setCliente(cliente);
|
||||
}
|
||||
|
||||
if (c.getFechaEmision() != null) {
|
||||
factura.setFechaEmision(c.getFechaEmision());
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Dirección de facturación del pedido asociado
|
||||
Long pedidoId = factura.getPedidoId();
|
||||
if (pedidoId != null && dto.getDireccionFacturacion() != null) {
|
||||
pedidoService.upsertDireccionFacturacion(pedidoId, dto.getDireccionFacturacion());
|
||||
|
||||
}
|
||||
|
||||
facturaRepo.save(factura);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Factura validarFactura(Long facturaId) {
|
||||
Factura factura = facturaRepo.findById(facturaId)
|
||||
@ -210,6 +271,20 @@ public class FacturacionService {
|
||||
// -----------------------
|
||||
// Líneas
|
||||
// -----------------------
|
||||
@Transactional
|
||||
public void createLinea(Long facturaId, FacturaLineaUpsertDto req) {
|
||||
Factura factura = this.getFactura(facturaId);
|
||||
|
||||
FacturaLinea lf = new FacturaLinea();
|
||||
lf.setFactura(factura);
|
||||
lf.setCantidad(1);
|
||||
|
||||
applyRequest(lf, req);
|
||||
|
||||
lineaFacturaRepository.save(lf);
|
||||
|
||||
this.recalcularTotales(factura);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Factura upsertLinea(Long facturaId, FacturaLineaUpsertDto dto) {
|
||||
@ -233,29 +308,15 @@ public class FacturacionService {
|
||||
}
|
||||
|
||||
linea.setDescripcion(dto.getDescripcion());
|
||||
linea.setCantidad(dto.getCantidad());
|
||||
|
||||
// Base por unidad o base total? Tu migración no define precio unitario.
|
||||
// Asumimos que baseLinea es TOTAL de línea (sin IVA) y cantidad informativa.
|
||||
linea.setBaseLinea(scale2(dto.getBaseLinea()));
|
||||
linea.setBaseLinea(scale2(dto.getBase()));
|
||||
|
||||
// Iva por checks: calculamos importes, no porcentajes
|
||||
BigDecimal iva4 = BigDecimal.ZERO;
|
||||
BigDecimal iva21 = BigDecimal.ZERO;
|
||||
linea.setIva4Linea(dto.getIva4());
|
||||
linea.setIva21Linea(dto.getIva21());
|
||||
|
||||
if (dto.isAplicaIva4() && dto.isAplicaIva21()) {
|
||||
throw new IllegalArgumentException("Una línea no puede tener IVA 4% y 21% a la vez.");
|
||||
}
|
||||
if (dto.isAplicaIva4()) {
|
||||
iva4 = scale2(linea.getBaseLinea().multiply(new BigDecimal("0.04")));
|
||||
}
|
||||
if (dto.isAplicaIva21()) {
|
||||
iva21 = scale2(linea.getBaseLinea().multiply(new BigDecimal("0.21")));
|
||||
}
|
||||
|
||||
linea.setIva4Linea(iva4);
|
||||
linea.setIva21Linea(iva21);
|
||||
linea.setTotalLinea(scale2(linea.getBaseLinea().add(iva4).add(iva21)));
|
||||
linea.setTotalLinea(scale2(linea.getBaseLinea()
|
||||
.add(nvl(linea.getIva4Linea()))
|
||||
.add(nvl(linea.getIva21Linea()))));
|
||||
|
||||
recalcularTotales(factura);
|
||||
return facturaRepo.save(factura);
|
||||
@ -284,7 +345,7 @@ public class FacturacionService {
|
||||
// -----------------------
|
||||
|
||||
@Transactional
|
||||
public Factura upsertPago(Long facturaId, FacturaPagoUpsertDto dto) {
|
||||
public Factura upsertPago(Long facturaId, FacturaPagoUpsertDto dto, Principal principal) {
|
||||
Factura factura = facturaRepo.findById(facturaId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada: " + facturaId));
|
||||
|
||||
@ -293,6 +354,8 @@ public class FacturacionService {
|
||||
if (dto.getId() == null) {
|
||||
pago = new FacturaPago();
|
||||
pago.setFactura(factura);
|
||||
pago.setCreatedBy(Utils.currentUser(principal));
|
||||
pago.setCreatedAt(Instant.now());
|
||||
factura.getPagos().add(pago);
|
||||
} else {
|
||||
pago = factura.getPagos().stream()
|
||||
@ -305,7 +368,8 @@ public class FacturacionService {
|
||||
pago.setCantidadPagada(scale2(dto.getCantidadPagada()));
|
||||
pago.setFechaPago(dto.getFechaPago() != null ? dto.getFechaPago() : LocalDateTime.now());
|
||||
pago.setNotas(dto.getNotas());
|
||||
|
||||
pago.setUpdatedAt(Instant.now());
|
||||
pago.setUpdatedBy(Utils.currentUser(principal));
|
||||
// El tipo_pago de la factura: si tiene un pago, lo reflejamos (último pago
|
||||
// manda)
|
||||
factura.setTipoPago(dto.getMetodoPago());
|
||||
@ -315,14 +379,18 @@ public class FacturacionService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Factura borrarPago(Long facturaId, Long pagoId) {
|
||||
public Factura borrarPago(Long facturaId, Long pagoId, Principal principal) {
|
||||
Factura factura = facturaRepo.findById(facturaId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Factura no encontrada: " + facturaId));
|
||||
|
||||
boolean removed = factura.getPagos().removeIf(p -> pagoId.equals(p.getId()));
|
||||
if (!removed) {
|
||||
throw new EntityNotFoundException("Pago no encontrado: " + pagoId);
|
||||
}
|
||||
FacturaPago pago = factura.getPagos().stream()
|
||||
.filter(p -> pagoId.equals(p.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new EntityNotFoundException("Pago no encontrado: " + pagoId));
|
||||
|
||||
// soft delete
|
||||
pago.setDeletedAt(Instant.now());
|
||||
pago.setDeletedBy(Utils.currentUser(principal));
|
||||
|
||||
recalcularTotales(factura);
|
||||
return facturaRepo.save(factura);
|
||||
@ -364,6 +432,8 @@ public class FacturacionService {
|
||||
BigDecimal pagado = BigDecimal.ZERO;
|
||||
if (factura.getPagos() != null) {
|
||||
for (FacturaPago p : factura.getPagos()) {
|
||||
if (p.getDeletedAt() != null)
|
||||
continue;
|
||||
pagado = pagado.add(nvl(p.getCantidadPagada()));
|
||||
}
|
||||
}
|
||||
@ -480,4 +550,20 @@ public class FacturacionService {
|
||||
.replace("'", "'");
|
||||
}
|
||||
|
||||
private void applyRequest(FacturaLinea lf, FacturaLineaUpsertDto req) {
|
||||
// HTML
|
||||
lf.setDescripcion(req.getDescripcion() == null ? "" : req.getDescripcion());
|
||||
|
||||
BigDecimal base = nvl(req.getBase());
|
||||
BigDecimal iva4 = nvl(req.getIva4());
|
||||
BigDecimal iva21 = nvl(req.getIva21());
|
||||
|
||||
lf.setBaseLinea(base);
|
||||
lf.setIva4Linea(iva4);
|
||||
lf.setIva21Linea(iva21);
|
||||
|
||||
// total de línea (por ahora)
|
||||
lf.setTotalLinea(base.add(iva4).add(iva21));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user