from pydantic import BaseModel, Field, field_validator from typing import List, Optional from datetime import datetime from enum import IntEnum class ModeReglement: """Modes de règlement Sage 100c""" VIREMENT = 1 CHEQUE = 2 TRAITE = 3 CARTE_BANCAIRE = 4 LCR = 5 PRELEVEMENT = 6 ESPECES = 7 LIBELLES = { 1: "Virement", 2: "Chèque", 3: "Traite", 4: "Carte bancaire", 5: "LCR", 6: "Prélèvement", 7: "Espèces", } @classmethod def get_libelle(cls, code: int) -> str: return cls.LIBELLES.get(code, f"Mode {code}") class ModeReglementEnum(IntEnum): """Modes de règlement Sage 100c""" VIREMENT = 1 CHEQUE = 2 TRAITE = 3 CARTE_BANCAIRE = 4 LCR = 5 PRELEVEMENT = 6 ESPECES = 7 class ReglementFactureRequest(BaseModel): """Requête de règlement d'une facture""" montant: float = Field(..., gt=0, description="Montant du règlement") mode_reglement: int = Field( default=2, ge=1, le=7, description="Mode de règlement (1=Virement, 2=Chèque, 3=Traite, 4=CB, 5=LCR, 6=Prélèvement, 7=Espèces)", ) date_reglement: Optional[datetime] = Field( default=None, description="Date du règlement (défaut: aujourd'hui)" ) reference: Optional[str] = Field( default="", max_length=35, description="Référence du règlement" ) libelle: Optional[str] = Field( default="", max_length=69, description="Libellé du règlement" ) code_journal: str = Field( default="BEU", max_length=6, description="Code journal comptable" ) devise_code: Optional[int] = Field(0) cours_devise: Optional[float] = Field(1.0) tva_encaissement: Optional[bool] = Field(False) compte_general: Optional[str] = Field(None) @field_validator("montant") def validate_montant(cls, v): if v <= 0: raise ValueError("Le montant doit être positif") return round(v, 2) class Config: json_schema_extra = { "example": { "montant": 375.12, "mode_reglement": 2, "date_reglement": "2026-01-06T00:00:00", "reference": "CHQ-001", "libelle": "Règlement facture", "code_journal": "BEU", } } class ReglementMultipleRequest(BaseModel): """Requête de règlement multiple pour un client""" client_code: str = Field(..., description="Code client") montant_total: float = Field(..., gt=0, description="Montant total à régler") mode_reglement: int = Field(default=2, ge=1, le=7) date_reglement: Optional[datetime] = None reference: Optional[str] = Field(default="", max_length=35) libelle: Optional[str] = Field(default="", max_length=69) code_journal: str = Field(default="BEU", max_length=6) numeros_factures: Optional[List[str]] = Field( default=None, description="Liste des factures à régler (sinon: plus anciennes d'abord)", ) devise_code: Optional[int] = Field(0) cours_devise: Optional[float] = Field(1.0) tva_encaissement: Optional[bool] = Field(False) @field_validator("client_code", mode="before") def strip_client_code(cls, v): return v.replace("\xa0", "").strip() if v else v @field_validator("montant_total") def validate_montant(cls, v): if v <= 0: raise ValueError("Le montant doit être positif") return round(v, 2) class Config: json_schema_extra = { "example": { "client_code": "CLI000001", "montant_total": 1000.00, "mode_reglement": 2, "reference": "VIR-MULTI-001", "numeros_factures": ["FA00081", "FA00082"], } } class ReglementResponse(BaseModel): """Réponse d'un règlement effectué""" numero_facture: str numero_reglement: Optional[str] montant_regle: float date_reglement: str mode_reglement: int mode_reglement_libelle: str reference: str libelle: str code_journal: str total_facture: float solde_restant: float facture_soldee: bool client_code: str class ReglementMultipleResponse(BaseModel): """Réponse d'un règlement multiple""" client_code: str montant_demande: float montant_effectif: float nb_factures_reglees: int nb_factures_soldees: int date_reglement: str mode_reglement: int mode_reglement_libelle: str reference: str reglements: List[ReglementResponse] class ReglementDetail(BaseModel): """Détail d'un règlement""" id: int date: Optional[str] montant: float reference: str libelle: str mode_reglement: int mode_reglement_libelle: str code_journal: str class ReglementsFactureResponse(BaseModel): """Réponse: tous les règlements d'une facture""" numero_facture: str client_code: str date_facture: Optional[str] reference: str total_ttc: float total_regle: float solde_restant: float est_soldee: bool nb_reglements: int reglements: List[ReglementDetail] class FactureAvecReglements(BaseModel): """Facture avec ses règlements""" numero_facture: str date_facture: Optional[str] total_ttc: float reference: str total_regle: float solde_restant: float est_soldee: bool nb_reglements: int reglements: List[ReglementDetail] class ReglementsClientResponse(BaseModel): """Réponse: tous les règlements d'un client""" client_code: str client_intitule: str nb_factures: int nb_factures_soldees: int nb_factures_en_cours: int total_factures: float total_regle: float solde_global: float factures: List[FactureAvecReglements] class ModeReglementInfo(BaseModel): """Information sur un mode de règlement""" code: int libelle: str class ModesReglementResponse(BaseModel): """Liste des modes de règlement disponibles""" modes: List[ModeReglementInfo] = [ ModeReglementInfo(code=1, libelle="Virement"), ModeReglementInfo(code=2, libelle="Chèque"), ModeReglementInfo(code=3, libelle="Traite"), ModeReglementInfo(code=4, libelle="Carte bancaire"), ModeReglementInfo(code=5, libelle="LCR"), ModeReglementInfo(code=6, libelle="Prélèvement"), ModeReglementInfo(code=7, libelle="Espèces"), ]