package dev.lions.unionflow.server.entity; import dev.lions.unionflow.server.api.enums.comptabilite.TypeCompteComptable; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import jakarta.validation.constraints.*; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * Entité CompteComptable pour le plan comptable * * @author UnionFlow Team * @version 3.0 * @since 2025-01-29 */ @Entity @Table( name = "comptes_comptables", indexes = { @Index(name = "idx_compte_numero", columnList = "numero_compte", unique = true), @Index(name = "idx_compte_type", columnList = "type_compte"), @Index(name = "idx_compte_classe", columnList = "classe_comptable") }) @Data @NoArgsConstructor @AllArgsConstructor @Builder @EqualsAndHashCode(callSuper = true) public class CompteComptable extends BaseEntity { /** Numéro de compte unique (ex: 411000, 512000) */ @NotBlank @Column(name = "numero_compte", unique = true, nullable = false, length = 10) private String numeroCompte; /** Libellé du compte */ @NotBlank @Column(name = "libelle", nullable = false, length = 200) private String libelle; /** Type de compte */ @NotNull @Enumerated(EnumType.STRING) @Column(name = "type_compte", nullable = false, length = 30) private TypeCompteComptable typeCompte; /** Classe comptable (1-7) */ @NotNull @Min(value = 1, message = "La classe comptable doit être entre 1 et 9") @Max(value = 9, message = "La classe comptable doit être entre 1 et 9") @Column(name = "classe_comptable", nullable = false) private Integer classeComptable; /** Solde initial */ @Builder.Default @DecimalMin(value = "0.0", message = "Le solde initial doit être positif ou nul") @Digits(integer = 12, fraction = 2) @Column(name = "solde_initial", precision = 14, scale = 2) private BigDecimal soldeInitial = BigDecimal.ZERO; /** Solde actuel (calculé) */ @Builder.Default @Digits(integer = 12, fraction = 2) @Column(name = "solde_actuel", precision = 14, scale = 2) private BigDecimal soldeActuel = BigDecimal.ZERO; /** Compte collectif (regroupe plusieurs sous-comptes) */ @Builder.Default @Column(name = "compte_collectif", nullable = false) private Boolean compteCollectif = false; /** Compte analytique */ @Builder.Default @Column(name = "compte_analytique", nullable = false) private Boolean compteAnalytique = false; /** Description du compte */ @Column(name = "description", length = 500) private String description; /** Organisation propriétaire (null = compte standard global) */ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "organisation_id") private Organisation organisation; /** Lignes d'écriture associées */ @JsonIgnore @OneToMany(mappedBy = "compteComptable", cascade = CascadeType.ALL, fetch = FetchType.LAZY) @Builder.Default private List lignesEcriture = new ArrayList<>(); /** Méthode métier pour obtenir le numéro formaté */ public String getNumeroFormate() { return String.format("%-10s", numeroCompte); } /** Méthode métier pour vérifier si c'est un compte de trésorerie */ public boolean isTresorerie() { return TypeCompteComptable.TRESORERIE.equals(typeCompte); } /** Callback JPA avant la persistance */ @PrePersist protected void onCreate() { super.onCreate(); if (soldeInitial == null) { soldeInitial = BigDecimal.ZERO; } if (soldeActuel == null) { soldeActuel = soldeInitial; } if (compteCollectif == null) { compteCollectif = false; } if (compteAnalytique == null) { compteAnalytique = false; } } }