package com.imprimelibros.erp.presupuesto.dto; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import com.imprimelibros.erp.presupuesto.validation.ConsistentTiradas; import com.imprimelibros.erp.presupuesto.validation.Par; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; import com.imprimelibros.erp.presupuesto.validation.Tamanio; import com.imprimelibros.erp.common.HtmlStripConverter; import com.imprimelibros.erp.common.jpa.AbstractAuditedEntity; import jakarta.persistence.*; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.math.BigDecimal; import com.imprimelibros.erp.users.User; @ConsistentTiradas(groups = PresupuestoValidationGroups.DatosGenerales.class) @Tamanio(groups = PresupuestoValidationGroups.DatosGenerales.class) @EntityListeners(AuditingEntityListener.class) @Entity @Table(name = "presupuesto", indexes = { @Index(name = "idx_presupuesto_origen_estado", columnList = "origen, estado"), @Index(name = "idx_presupuesto_session", columnList = "session_id"), @Index(name = "idx_presupuesto_user", columnList = "user_id"), @Index(name = "idx_presupuesto_deleted", columnList = "deleted"), @Index(name = "idx_presupuesto_geo", columnList = "pais, region, ciudad") }) @SQLDelete(sql = "UPDATE presupuesto SET deleted = 1, deleted_at = NOW(3) WHERE id = ?") @SQLRestriction("deleted = 0") public class Presupuesto extends AbstractAuditedEntity implements Cloneable { public enum TipoEncuadernacion { fresado("presupuesto.fresado"), cosido("presupuesto.cosido"), grapado("presupuesto.grapado"), espiral("presupuesto.espiral"), wireo("presupuesto.wireo"); private final String messageKey; TipoEncuadernacion(String messageKey) { this.messageKey = messageKey; } public String getMessageKey() { return messageKey; } } public enum TipoImpresion { negro("presupuesto.blanco-negro"), negrohq("presupuesto.blanco-negro-premium"), color("presupuesto.color"), colorhq("presupuesto.color-premium"); private final String messageKey; TipoImpresion(String messageKey) { this.messageKey = messageKey; } public String getMessageKey() { return messageKey; } } public enum TipoCubierta { tapaBlanda("presupuesto.tapa-blanda"), tapaDura("presupuesto.tapa-dura"), tapaDuraLomoRedondo("presupuesto.tapa-dura-lomo-redondo"); private final String messageKey; TipoCubierta(String messageKey) { this.messageKey = messageKey; } public String getMessageKey() { return messageKey; } } public enum Origen { publico, privado } public enum Estado { borrador("presupuesto.estado.borrador"), aceptado("presupuesto.estado.aceptado"), modificado("presupuesto.estado.modificado"); private final String messageKey; Estado(String messageKey) { this.messageKey = messageKey; } public String getMessageKey() { return messageKey; } } public enum Entrega{ peninsula("presupuesto.entrega.peninsula"), canarias("presupuesto.entrega.canarias"), paises_ue("presupuesto.entrega.paises-ue"); private final String messageKey; Entrega(String messageKey) { this.messageKey = messageKey; } public String getMessageKey() { return messageKey; } } @Override public Presupuesto clone() { try { return (Presupuesto) super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } // ====== NUEVOS: Origen/Estado/Usuario/Session/Geo/IP/Totales/JSONs ====== @Enumerated(EnumType.STRING) @Column(name = "origen", nullable = false) private Origen origen = Origen.publico; @Enumerated(EnumType.STRING) @Column(name = "estado", nullable = false) private Estado estado = Estado.borrador; // Usuario autenticado (nullable en público) @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; // Continuidad en público @Column(name = "session_id", length = 64) private String sessionId; @Column(name = "visitor_id", length = 64) private String visitorId; // IP anonimizada / truncada + geolocalización resumida (para estadísticas) @Column(name = "ip_hash", length = 88) // SHA-256 base64 ≈ 44 chars; dejamos margen private String ipHash; @Column(name = "ip_trunc", length = 64) private String ipTrunc; // p.ej. "192.168.0.0" o "2a02:xxxx::" @Column(name = "pais", length = 64) private String pais; @Column(name = "region", length = 128) private String region; @Column(name = "ciudad", length = 128) private String ciudad; // Totales de la tirada seleccionada y del presupuesto @Column(name = "precio_unitario", precision = 12, scale = 4) private BigDecimal precioUnitario; @Column(name = "precio_total_tirada", precision = 12, scale = 2) private BigDecimal precioTotalTirada; @Column(name = "servicios_total", precision = 12, scale = 2) private BigDecimal serviciosTotal; @Column(name = "base_imponible", precision = 12, scale = 2) private BigDecimal baseImponible; @Column(name = "iva_reducido") private Boolean ivaReducido; @Column(name = "entrega_tipo") @Enumerated(EnumType.STRING) private Entrega entregaTipo; @Column(name = "iva_importe_4", precision = 12, scale = 2) private BigDecimal ivaImporte4; @Column(name = "iva_importe_21", precision = 12, scale = 2) private BigDecimal ivaImporte21; @Column(name = "total_con_iva", precision = 12, scale = 2) private BigDecimal totalConIva; // JSONs de apoyo (todas las tiradas, servicios, bloques de // maquetación/marcapáginas y snapshot) @Lob @Column(name = "precios_por_tirada_json", columnDefinition = "json") private String preciosPorTiradaJson; // [{tirada, precio_unitario, precio_total_tirada}, ...] @Lob @Column(name = "servicios_json", columnDefinition = "json") private String serviciosJson; @Lob @Column(name = "datos_maquetacion_json", columnDefinition = "json") private String datosMaquetacionJson; @Lob @Column(name = "datos_marcapaginas_json", columnDefinition = "json") private String datosMarcapaginasJson; @Lob @Column(name = "pricing_snapshot", columnDefinition = "json") private String pricingSnapshotJson; // ====== TUS CAMPOS ORIGINALES ====== @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @NotNull(message = "{presupuesto.errores.tipo-encuadernacion}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Enumerated(EnumType.STRING) @Column(name = "tipo_encuadernacion") private TipoEncuadernacion tipoEncuadernacion = TipoEncuadernacion.fresado; @Convert(converter = HtmlStripConverter.class) @NotBlank(message = "{presupuesto.errores.titulo}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "titulo") private String titulo; @Convert(converter = HtmlStripConverter.class) @Column(name = "autor") private String autor; @Convert(converter = HtmlStripConverter.class) @Column(name = "isbn") private String isbn; @NotNull(message = "{presupuesto.errores.tirada1}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "tirada1") private Integer tirada1; @Column(name = "tirada2") private Integer tirada2; @Column(name = "tirada3") private Integer tirada3; @Column(name = "tirada4") private Integer tirada4; @Column(name = "selected_tirada") private Integer selectedTirada; @NotNull(message = "{presupuesto.errores.ancho}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "ancho") private Integer ancho; @NotNull(message = "{presupuesto.errores.alto}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "alto") private Integer alto; @Column(name = "formatoPersonalizado") private Boolean formatoPersonalizado; @Par(message = "{presupuesto.errores.paginasNegro.par}", groups = PresupuestoValidationGroups.DatosGenerales.class) @NotNull(message = "{presupuesto.errores.paginasNegro.required}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "paginas_negro") private Integer paginasNegro; @Par(message = "{presupuesto.errores.paginasColor.par}", groups = PresupuestoValidationGroups.DatosGenerales.class) @NotNull(message = "{presupuesto.errores.paginasColor.required}", groups = PresupuestoValidationGroups.DatosGenerales.class) @Column(name = "paginas_color") private Integer paginasColor; @Column(name = "posicion_paginas_color") private String posicionPaginasColor; @Column(name = "paginas_color_total") private Integer paginasColorTotal; @NotNull(message = "{presupuesto.errores.tipo-impresion}", groups = PresupuestoValidationGroups.Interior.class) @Enumerated(EnumType.STRING) @Column(name = "tipo_impresion") private TipoImpresion tipoImpresion = TipoImpresion.negro; @NotNull(message = "{presupuesto.errores.papel-interior}", groups = PresupuestoValidationGroups.Interior.class) @Column(name = "papel_interior_id") private Integer papelInteriorId; @NotNull(message = "{presupuesto.errores.gramaje-interior}", groups = PresupuestoValidationGroups.Interior.class) @Column(name = "gramaje_interior") private Integer gramajeInterior; @NotNull(message = "{presupuesto.errores.tipo-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class) @Enumerated(EnumType.STRING) @Column(name = "tipo_cubierta") private TipoCubierta tipoCubierta = TipoCubierta.tapaBlanda; @Column(name = "solapas_cubierta") private Boolean solapasCubierta = false; @Column(name = "tamanio_solapas_cubierta") private Integer tamanioSolapasCubierta; @Column(name = "cubierta_caras") private Integer cubiertaCaras; @Column(name = "papel_guardas_id") private Integer papelGuardasId; @Column(name = "gramaje_guardas") private Integer gramajeGuardas; @Column(name = "guardas_impresas") private Integer guardasImpresas; @Column(name = "cabezada") private String cabezada; @NotNull(message = "{presupuesto.errores.papel-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class) @Column(name = "papel_cubierta_id") private Integer papelCubiertaId = 2; @NotNull(message = "{presupuesto.errores.gramaje-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class) @Column(name = "gramaje_cubierta") private Integer gramajeCubierta = 240; @NotNull(message = "{presupuesto.errores.acabado-cubierta}", groups = PresupuestoValidationGroups.Cubierta.class) @Column(name = "acabado") private Integer acabado = 1; @Column(name = "sobrecubierta") private Boolean sobrecubierta = false; @Column(name = "papel_sobrecubierta_id") private Integer papelSobrecubiertaId = 2; @Column(name = "gramaje_sobrecubierta") private Integer gramajeSobrecubierta = 170; @Column(name = "tamanio_solapas_sobrecubierta") private Integer tamanioSolapasSobrecubierta = 80; @Column(name = "acabado_sobrecubierta") private Integer acabadoSobrecubierta = 0; @Column(name = "faja") private Boolean faja = false; @Column(name = "papel_faja_id") private Integer papelFajaId = 2; @Column(name = "gramaje_faja") private Integer gramajeFaja = 170; @Column(name = "tamanio_solapas_faja") private Integer tamanioSolapasFaja = 80; @Column(name = "acabado_faja") private Integer acabadoFaja = 0; @Column(name = "alto_faja") private Integer altoFaja = 0; // ====== MÉTODOS AUX ====== public String resumenPresupuesto() { return String.format("%s - %s - %dx%d mm - %d Páginas (N:%d C:%d) - Tira:%d", this.titulo, this.tipoEncuadernacion, this.ancho, this.alto, (this.paginasNegro != null ? this.paginasNegro : 0) + (this.paginasColorTotal != null ? this.paginasColorTotal : 0), this.paginasNegro != null ? this.paginasNegro : 0, this.paginasColorTotal != null ? this.paginasColorTotal : 0, this.selectedTirada != null ? this.selectedTirada : 0); } public Integer[] getTiradas() { return new Integer[] { tirada1, tirada2, tirada3, tirada4 }; } // ====== GETTERS/SETTERS (incluye nuevos y existentes) ====== public Origen getOrigen() { return origen; } public void setOrigen(Origen origen) { this.origen = origen; } public Estado getEstado() { return estado; } public void setEstado(Estado estado) { this.estado = estado; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getSessionId() { return sessionId; } public void setSessionId(String sessionId) { this.sessionId = sessionId; } public String getVisitorId() { return visitorId; } public void setVisitorId(String visitorId) { this.visitorId = visitorId; } public String getIpHash() { return ipHash; } public void setIpHash(String ipHash) { this.ipHash = ipHash; } public String getIpTrunc() { return ipTrunc; } public void setIpTrunc(String ipTrunc) { this.ipTrunc = ipTrunc; } public String getPais() { return pais; } public void setPais(String pais) { this.pais = pais; } public String getRegion() { return region; } public void setRegion(String region) { this.region = region; } public String getCiudad() { return ciudad; } public void setCiudad(String ciudad) { this.ciudad = ciudad; } public BigDecimal getPrecioUnitario() { return precioUnitario; } public void setPrecioUnitario(BigDecimal precioUnitario) { this.precioUnitario = precioUnitario; } public BigDecimal getPrecioTotalTirada() { return precioTotalTirada; } public void setPrecioTotalTirada(BigDecimal precioTotalTirada) { this.precioTotalTirada = precioTotalTirada; } public BigDecimal getServiciosTotal() { return serviciosTotal; } public void setServiciosTotal(BigDecimal serviciosTotal) { this.serviciosTotal = serviciosTotal; } public BigDecimal getBaseImponible() { return baseImponible; } public void setBaseImponible(BigDecimal baseImponible) { this.baseImponible = baseImponible; } public Boolean getIvaReducido() { return ivaReducido; } public void setIvaReducido(Boolean ivaReducido) { this.ivaReducido = ivaReducido; } public Entrega getEntregaTipo() { return entregaTipo; } public void setEntregaTipo(Entrega entregaTipo) { this.entregaTipo = entregaTipo; } public BigDecimal getIvaImporte4() { return ivaImporte4; } public void setIvaImporte4(BigDecimal ivaImporte4) { this.ivaImporte4 = ivaImporte4; } public BigDecimal getIvaImporte21() { return ivaImporte21; } public void setIvaImporte21(BigDecimal ivaImporte21) { this.ivaImporte21 = ivaImporte21; } public BigDecimal getTotalConIva() { return totalConIva; } public void setTotalConIva(BigDecimal totalConIva) { this.totalConIva = totalConIva; } public String getPreciosPorTiradaJson() { return preciosPorTiradaJson; } public void setPreciosPorTiradaJson(String preciosPorTiradaJson) { this.preciosPorTiradaJson = preciosPorTiradaJson; } public String getServiciosJson() { return serviciosJson; } public void setServiciosJson(String serviciosJson) { this.serviciosJson = serviciosJson; } public String getDatosMaquetacionJson() { return datosMaquetacionJson; } public void setDatosMaquetacionJson(String datosMaquetacionJson) { this.datosMaquetacionJson = datosMaquetacionJson; } public String getDatosMarcapaginasJson() { return datosMarcapaginasJson; } public void setDatosMarcapaginasJson(String datosMarcapaginasJson) { this.datosMarcapaginasJson = datosMarcapaginasJson; } public String getPricingSnapshotJson() { return pricingSnapshotJson; } public void setPricingSnapshotJson(String pricingSnapshotJson) { this.pricingSnapshotJson = pricingSnapshotJson; } public TipoEncuadernacion getTipoEncuadernacion() { return tipoEncuadernacion; } public void setTipoEncuadernacion(TipoEncuadernacion tipoEncuadernacion) { this.tipoEncuadernacion = tipoEncuadernacion; } public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public String getAutor() { return autor; } public void setAutor(String autor) { this.autor = autor; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public Integer getTirada1() { return tirada1; } public void setTirada1(Integer tirada1) { this.tirada1 = tirada1; } public Integer getTirada2() { return tirada2; } public void setTirada2(Integer tirada2) { this.tirada2 = tirada2; } public Integer getTirada3() { return tirada3; } public void setTirada3(Integer tirada3) { this.tirada3 = tirada3; } public Integer getTirada4() { return tirada4; } public void setTirada4(Integer tirada4) { this.tirada4 = tirada4; } public Integer getSelectedTirada() { return selectedTirada; } public void setSelectedTirada(Integer selectedTirada) { this.selectedTirada = selectedTirada; } public Integer getAncho() { return ancho; } public void setAncho(Integer ancho) { this.ancho = ancho; } public Integer getAlto() { return alto; } public void setAlto(Integer alto) { this.alto = alto; } public Boolean getFormatoPersonalizado() { return formatoPersonalizado; } public void setFormatoPersonalizado(Boolean formatoPersonalizado) { this.formatoPersonalizado = formatoPersonalizado; } public Integer getPaginasNegro() { return paginasNegro; } public void setPaginasNegro(Integer paginasNegro) { this.paginasNegro = paginasNegro; } public Integer getPaginasColor() { return paginasColor; } public void setPaginasColor(Integer paginasColor) { this.paginasColor = paginasColor; } public String getPosicionPaginasColor() { return posicionPaginasColor; } public void setPosicionPaginasColor(String posicionPaginasColor) { this.posicionPaginasColor = posicionPaginasColor; } public Integer getPaginasColorTotal() { return paginasColorTotal; } public void setPaginasColorTotal(Integer paginasColorTotal) { this.paginasColorTotal = paginasColorTotal; } public TipoImpresion getTipoImpresion() { return tipoImpresion; } public void setTipoImpresion(TipoImpresion tipoImpresion) { this.tipoImpresion = tipoImpresion; } public Integer getPapelInteriorId() { return papelInteriorId; } public void setPapelInteriorId(Integer papelInteriorId) { this.papelInteriorId = papelInteriorId; } public Integer getGramajeInterior() { return gramajeInterior; } public void setGramajeInterior(Integer gramajeInterior) { this.gramajeInterior = gramajeInterior; } public TipoCubierta getTipoCubierta() { return tipoCubierta; } public void setTipoCubierta(TipoCubierta tipoCubierta) { this.tipoCubierta = tipoCubierta; } public Boolean getSolapasCubierta() { return solapasCubierta; } public void setSolapasCubierta(Boolean solapasCubierta) { this.solapasCubierta = solapasCubierta; } public Integer getTamanioSolapasCubierta() { return tamanioSolapasCubierta; } public void setTamanioSolapasCubierta(Integer tamanioSolapasCubierta) { this.tamanioSolapasCubierta = tamanioSolapasCubierta; } public Integer getCubiertaCaras() { return cubiertaCaras; } public void setCubiertaCaras(Integer cubiertaCaras) { this.cubiertaCaras = cubiertaCaras; } public Integer getPapelGuardasId() { return papelGuardasId; } public void setPapelGuardasId(Integer papelGuardasId) { this.papelGuardasId = papelGuardasId; } public Integer getGramajeGuardas() { return gramajeGuardas; } public void setGramajeGuardas(Integer gramajeGuardas) { this.gramajeGuardas = gramajeGuardas; } public Integer getGuardasImpresas() { return guardasImpresas; } public void setGuardasImpresas(Integer guardasImpresas) { this.guardasImpresas = guardasImpresas; } public String getCabezada() { return cabezada; } public void setCabezada(String cabezada) { this.cabezada = cabezada; } public Integer getPapelCubiertaId() { return papelCubiertaId; } public void setPapelCubiertaId(Integer papelCubiertaId) { this.papelCubiertaId = papelCubiertaId; } public Integer getGramajeCubierta() { return gramajeCubierta; } public void setGramajeCubierta(Integer gramajeCubierta) { this.gramajeCubierta = gramajeCubierta; } public Integer getAcabado() { return acabado; } public void setAcabado(Integer acabado) { this.acabado = acabado; } public Boolean getSobrecubierta() { return sobrecubierta; } public void setSobrecubierta(Boolean sobrecubierta) { this.sobrecubierta = sobrecubierta; } public Integer getPapelSobrecubiertaId() { return papelSobrecubiertaId; } public void setPapelSobrecubiertaId(Integer papelSobrecubiertaId) { this.papelSobrecubiertaId = papelSobrecubiertaId; } public Integer getGramajeSobrecubierta() { return gramajeSobrecubierta; } public void setGramajeSobrecubierta(Integer gramajeSobrecubierta) { this.gramajeSobrecubierta = gramajeSobrecubierta; } public Integer getTamanioSolapasSobrecubierta() { return tamanioSolapasSobrecubierta; } public void setTamanioSolapasSobrecubierta(Integer tamanioSolapasSobrecubierta) { this.tamanioSolapasSobrecubierta = tamanioSolapasSobrecubierta; } public Integer getAcabadoSobrecubierta() { return acabadoSobrecubierta; } public void setAcabadoSobrecubierta(Integer acabadoSobrecubierta) { this.acabadoSobrecubierta = acabadoSobrecubierta; } public Boolean getFaja() { return faja; } public void setFaja(Boolean faja) { this.faja = faja; } public Integer getPapelFajaId() { return papelFajaId; } public void setPapelFajaId(Integer papelFajaId) { this.papelFajaId = papelFajaId; } public Integer getGramajeFaja() { return gramajeFaja; } public void setGramajeFaja(Integer gramajeFaja) { this.gramajeFaja = gramajeFaja; } public Integer getTamanioSolapasFaja() { return tamanioSolapasFaja; } public void setTamanioSolapasFaja(Integer tamanioSolapasFaja) { this.tamanioSolapasFaja = tamanioSolapasFaja; } public Integer getAcabadoFaja() { return acabadoFaja; } public void setAcabadoFaja(Integer acabadoFaja) { this.acabadoFaja = acabadoFaja; } public Integer getAltoFaja() { return altoFaja; } public void setAltoFaja(Integer altoFaja) { this.altoFaja = altoFaja; } public Long getId(){ return id; } public void setId(Long id){ this.id = id; } }