Updated client's field
This commit is contained in:
parent
215763b679
commit
f8ea7b48b9
2 changed files with 268 additions and 58 deletions
295
api.py
295
api.py
|
|
@ -400,54 +400,275 @@ class BaremeRemiseResponse(BaseModel):
|
|||
message: str
|
||||
|
||||
|
||||
class ClientCreateAPIRequest(BaseModel):
|
||||
"""Modèle pour création d'un nouveau client"""
|
||||
class ClientCreateRequest(BaseModel):
|
||||
"""Modèle complet pour la création d'un client Sage avec tous les champs disponibles"""
|
||||
|
||||
intitule: str = Field(
|
||||
..., min_length=1, max_length=69, description="Raison sociale ou Nom complet"
|
||||
)
|
||||
compte_collectif: str = Field(
|
||||
"411000", description="Compte comptable (411000 par défaut)"
|
||||
)
|
||||
num: Optional[str] = Field(
|
||||
None, max_length=17, description="Code client souhaité (auto si vide)"
|
||||
)
|
||||
# ========================================
|
||||
# CHAMPS OBLIGATOIRES
|
||||
# ========================================
|
||||
intitule: str = Field(..., max_length=69, description="Nom du client (CT_Intitule)")
|
||||
|
||||
# Adresse
|
||||
adresse: Optional[str] = Field(None, max_length=35)
|
||||
code_postal: Optional[str] = Field(None, max_length=9)
|
||||
ville: Optional[str] = Field(None, max_length=35)
|
||||
pays: Optional[str] = Field(None, max_length=35)
|
||||
# ========================================
|
||||
# IDENTIFICATION & CLASSIFICATION
|
||||
# ========================================
|
||||
num: Optional[str] = Field(None, max_length=17, description="Numéro client (auto si vide)")
|
||||
compte_collectif: str = Field("411000", max_length=13, description="Compte général (CG_NumPrinc)")
|
||||
qualite: str = Field("CLI", max_length=3, description="CLI/FOU/SAL/DIV/AUT")
|
||||
classement: Optional[str] = Field(None, max_length=17, description="Code de classement")
|
||||
raccourci: Optional[str] = Field(None, max_length=17, description="Code abrégé")
|
||||
|
||||
# Contact
|
||||
email: Optional[EmailStr] = None
|
||||
telephone: Optional[str] = Field(None, max_length=21, description="Téléphone fixe")
|
||||
portable: Optional[str] = Field(None, max_length=21, description="Téléphone mobile")
|
||||
# ========================================
|
||||
# ADRESSE PRINCIPALE
|
||||
# ========================================
|
||||
contact: Optional[str] = Field(None, max_length=69, description="Nom du contact principal")
|
||||
adresse: Optional[str] = Field(None, max_length=35, description="Adresse ligne 1")
|
||||
complement: Optional[str] = Field(None, max_length=35, description="Adresse ligne 2")
|
||||
code_postal: Optional[str] = Field(None, max_length=9, description="Code postal")
|
||||
ville: Optional[str] = Field(None, max_length=35, description="Ville")
|
||||
code_region: Optional[str] = Field(None, max_length=25, description="Code région/département")
|
||||
pays: Optional[str] = Field(None, max_length=35, description="Pays")
|
||||
|
||||
# Juridique
|
||||
forme_juridique: Optional[str] = Field(
|
||||
None, max_length=50, description="SARL, SA, SAS, EI, etc."
|
||||
)
|
||||
siret: Optional[str] = Field(None, max_length=14)
|
||||
tva_intra: Optional[str] = Field(None, max_length=25)
|
||||
# ========================================
|
||||
# CONTACT & COMMUNICATION
|
||||
# ========================================
|
||||
telephone: Optional[str] = Field(None, max_length=21, description="Téléphone principal")
|
||||
telecopie: Optional[str] = Field(None, max_length=21, description="Fax")
|
||||
email: Optional[str] = Field(None, max_length=69, description="Email principal")
|
||||
site: Optional[str] = Field(None, max_length=69, description="Site web")
|
||||
facebook: Optional[str] = Field(None, max_length=100, description="URL Facebook")
|
||||
linkedin: Optional[str] = Field(None, max_length=100, description="URL LinkedIn")
|
||||
|
||||
# ========================================
|
||||
# IDENTIFIANTS LÉGAUX & FISCAUX
|
||||
# ========================================
|
||||
siret: Optional[str] = Field(None, max_length=14, description="SIRET (14 chiffres)")
|
||||
tva_intra: Optional[str] = Field(None, max_length=25, description="TVA intracommunautaire (CT_Identifiant)")
|
||||
ape: Optional[str] = Field(None, max_length=5, description="Code APE/NAF")
|
||||
type_nif: Optional[int] = Field(None, description="Type de NIF (0-10)")
|
||||
|
||||
# ========================================
|
||||
# BANQUE & DEVISE
|
||||
# ========================================
|
||||
banque_num: Optional[str] = Field(None, max_length=13, description="Code banque (BT_Num)")
|
||||
devise: Optional[int] = Field(0, description="Code devise (N_Devise, 0=EUR)")
|
||||
|
||||
# ========================================
|
||||
# CATÉGORIES & CLASSIFICATIONS
|
||||
# ========================================
|
||||
cat_tarif: int = Field(1, ge=1, description="Catégorie tarifaire (N_CatTarif)")
|
||||
cat_compta: int = Field(1, ge=1, description="Catégorie comptable (N_CatCompta)")
|
||||
period: int = Field(1, ge=1, description="Période de règlement (N_Period)")
|
||||
expedition: int = Field(1, ge=1, description="Mode d'expédition (N_Expedition)")
|
||||
condition: int = Field(1, ge=1, description="Condition de livraison (N_Condition)")
|
||||
risque: int = Field(1, ge=1, description="Niveau de risque (N_Risque)")
|
||||
|
||||
# ========================================
|
||||
# TAUX PERSONNALISÉS
|
||||
# ========================================
|
||||
taux01: Optional[Decimal] = Field(None, description="Taux personnalisé 1 (CT_Taux01)")
|
||||
taux02: Optional[Decimal] = Field(None, description="Taux personnalisé 2 (CT_Taux02)")
|
||||
taux03: Optional[Decimal] = Field(None, description="Taux personnalisé 3 (CT_Taux03)")
|
||||
taux04: Optional[Decimal] = Field(None, description="Taux personnalisé 4 (CT_Taux04)")
|
||||
|
||||
# ========================================
|
||||
# GESTION COMMERCIALE
|
||||
# ========================================
|
||||
encours: Optional[Decimal] = Field(None, description="Encours autorisé (CT_Encours)")
|
||||
assurance: Optional[Decimal] = Field(None, description="Plafond assurance (CT_Assurance)")
|
||||
num_payeur: Optional[str] = Field(None, max_length=17, description="Numéro client payeur")
|
||||
langue: Optional[int] = Field(None, description="Code langue (0-25)")
|
||||
langue_iso2: Optional[str] = Field(None, max_length=2, description="Code ISO langue (FR, EN, etc)")
|
||||
|
||||
# ========================================
|
||||
# PARAMÈTRES FACTURATION
|
||||
# ========================================
|
||||
facture: int = Field(1, description="Type facturation (0=aucune, 1=normale, 2=regroupée)")
|
||||
bl_fact: Optional[int] = Field(None, description="BL en facture (0/1)")
|
||||
saut: Optional[int] = Field(None, description="Saut de page (0/1)")
|
||||
lettrage: bool = Field(True, description="Lettrage auto (CT_Lettrage)")
|
||||
valid_ech: Optional[int] = Field(None, description="Validation échéance (0/1)")
|
||||
control_enc: Optional[int] = Field(None, description="Contrôle encours (0/1)")
|
||||
not_rappel: Optional[int] = Field(None, description="Pas de relance (0/1)")
|
||||
not_penal: Optional[int] = Field(None, description="Pas de pénalités (0/1)")
|
||||
|
||||
# ========================================
|
||||
# LIVRAISON & LOGISTIQUE
|
||||
# ========================================
|
||||
priorite_livr: Optional[int] = Field(None, description="Priorité livraison (0-5)")
|
||||
livr_partielle: Optional[int] = Field(None, description="Livraison partielle autorisée (0/1)")
|
||||
delai_transport: Optional[int] = Field(None, description="Délai transport (jours)")
|
||||
delai_appro: Optional[int] = Field(None, description="Délai approvisionnement (jours)")
|
||||
|
||||
# JOURS DE COMMANDE (0=non, 1=oui)
|
||||
order_day_lundi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_mardi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_mercredi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_jeudi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_vendredi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_samedi: Optional[int] = Field(None, ge=0, le=1)
|
||||
order_day_dimanche: Optional[int] = Field(None, ge=0, le=1)
|
||||
|
||||
# JOURS DE LIVRAISON (0=non, 1=oui)
|
||||
delivery_day_lundi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_mardi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_mercredi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_jeudi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_vendredi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_samedi: Optional[int] = Field(None, ge=0, le=1)
|
||||
delivery_day_dimanche: Optional[int] = Field(None, ge=0, le=1)
|
||||
|
||||
# ========================================
|
||||
# DATES FERMETURE
|
||||
# ========================================
|
||||
date_ferme_debut: Optional[date] = Field(None, description="Début période fermeture")
|
||||
date_ferme_fin: Optional[date] = Field(None, description="Fin période fermeture")
|
||||
|
||||
# ========================================
|
||||
# STATISTIQUES PERSONNALISÉES (10 champs)
|
||||
# ========================================
|
||||
statistique01: Optional[str] = Field(None, max_length=69)
|
||||
statistique02: Optional[str] = Field(None, max_length=69)
|
||||
statistique03: Optional[str] = Field(None, max_length=69)
|
||||
statistique04: Optional[str] = Field(None, max_length=69)
|
||||
statistique05: Optional[str] = Field(None, max_length=69)
|
||||
statistique06: Optional[str] = Field(None, max_length=69)
|
||||
statistique07: Optional[str] = Field(None, max_length=69)
|
||||
statistique08: Optional[str] = Field(None, max_length=69)
|
||||
statistique09: Optional[str] = Field(None, max_length=69)
|
||||
statistique10: Optional[str] = Field(None, max_length=69)
|
||||
|
||||
# ========================================
|
||||
# COMMENTAIRE
|
||||
# ========================================
|
||||
commentaire: Optional[str] = Field(None, description="Commentaire libre (CT_Commentaire, jusqu'à 2Go théorique)")
|
||||
|
||||
# ========================================
|
||||
# ÉTAT & STATUT
|
||||
# ========================================
|
||||
sommeil: bool = Field(False, description="Compte en sommeil")
|
||||
prospect: bool = Field(False, description="Prospect (pas encore client)")
|
||||
bon_a_payer: Optional[int] = Field(None, description="Bon à payer (0/1)")
|
||||
|
||||
# ========================================
|
||||
# ANALYTIQUE
|
||||
# ========================================
|
||||
section_analytique: Optional[str] = Field(None, max_length=13, description="Section analytique (CA_Num)")
|
||||
section_analytique_ifrs: Optional[str] = Field(None, max_length=13, description="Section IFRS (CA_NumIFRS)")
|
||||
plan_analytique: Optional[int] = Field(None, description="Plan analytique (N_Analytique)")
|
||||
plan_analytique_ifrs: Optional[int] = Field(None, description="Plan IFRS (N_AnalytiqueIFRS)")
|
||||
|
||||
# ========================================
|
||||
# COLLABORATEUR & DÉPÔT
|
||||
# ========================================
|
||||
collaborateur: Optional[str] = Field(None, max_length=4, description="Code collaborateur (CO_No)")
|
||||
depot: Optional[str] = Field(None, max_length=13, description="Dépôt par défaut (DE_No)")
|
||||
etablissement: Optional[str] = Field(None, max_length=13, description="Établissement (EB_No)")
|
||||
mode_regl: Optional[str] = Field(None, max_length=3, description="Mode règlement (MR_No)")
|
||||
|
||||
# ========================================
|
||||
# CENTRALE D'ACHAT
|
||||
# ========================================
|
||||
num_centrale: Optional[str] = Field(None, max_length=17, description="Numéro centrale d'achat")
|
||||
|
||||
# ========================================
|
||||
# SURVEILLANCE COFACE
|
||||
# ========================================
|
||||
coface: Optional[str] = Field(None, max_length=25, description="Code Coface")
|
||||
surveillance: Optional[int] = Field(None, description="Surveillance activée (0/1)")
|
||||
sv_date_create: Optional[date] = Field(None, description="Date création entreprise")
|
||||
sv_forme_juri: Optional[str] = Field(None, max_length=50, description="Forme juridique")
|
||||
sv_effectif: Optional[int] = Field(None, description="Effectif")
|
||||
sv_ca: Optional[Decimal] = Field(None, description="Chiffre d'affaires")
|
||||
sv_resultat: Optional[Decimal] = Field(None, description="Résultat")
|
||||
sv_incident: Optional[int] = Field(None, description="Incidents (0/1)")
|
||||
sv_date_incid: Optional[date] = Field(None, description="Date dernier incident")
|
||||
sv_privil: Optional[int] = Field(None, description="Privilèges (0/1)")
|
||||
sv_regul: Optional[int] = Field(None, description="Régularité (0/1)")
|
||||
sv_cotation: Optional[str] = Field(None, max_length=25, description="Cotation")
|
||||
sv_date_maj: Optional[date] = Field(None, description="Date MAJ surveillance")
|
||||
sv_objet_maj: Optional[str] = Field(None, max_length=35, description="Objet MAJ")
|
||||
sv_date_bilan: Optional[date] = Field(None, description="Date dernier bilan")
|
||||
sv_nb_mois_bilan: Optional[int] = Field(None, description="Nb mois bilan")
|
||||
|
||||
# ========================================
|
||||
# FACTURATION ÉLECTRONIQUE
|
||||
# ========================================
|
||||
facture_elec: Optional[int] = Field(None, description="Facturation électronique (0/1)")
|
||||
edi_code_type: Optional[int] = Field(None, description="Type code EDI")
|
||||
edi_code: Optional[str] = Field(None, max_length=35, description="Code EDI")
|
||||
edi_code_sage: Optional[str] = Field(None, max_length=35, description="Code EDI Sage")
|
||||
fe_assujetti: Optional[int] = Field(None, description="Assujetti facture électronique")
|
||||
fe_autre_identif_type: Optional[str] = Field(None, max_length=10)
|
||||
fe_autre_identif_val: Optional[str] = Field(None, max_length=50)
|
||||
fe_entite_type: Optional[int] = Field(None)
|
||||
fe_emission: Optional[int] = Field(None)
|
||||
fe_application: Optional[int] = Field(None)
|
||||
|
||||
# ========================================
|
||||
# ÉCHANGES & INTÉGRATION
|
||||
# ========================================
|
||||
echange_rappro: Optional[int] = Field(None, description="Échange rapprochement")
|
||||
echange_cr: Optional[int] = Field(None, description="Échange compte rendu")
|
||||
annulation_cr: Optional[int] = Field(None, description="Annulation CR")
|
||||
profil_soc: Optional[int] = Field(None, description="Profil société")
|
||||
statut_contrat: Optional[int] = Field(None, description="Statut contrat")
|
||||
|
||||
# ========================================
|
||||
# RGPD & CONFIDENTIALITÉ
|
||||
# ========================================
|
||||
gdpr: Optional[int] = Field(None, description="Consentement RGPD (0/1)")
|
||||
exclure_trait: Optional[int] = Field(None, description="Exclure des traitements (0/1)")
|
||||
|
||||
# ========================================
|
||||
# REPRÉSENTANT FISCAL
|
||||
# ========================================
|
||||
represent_int: Optional[int] = Field(None, description="Représentant intracommunautaire")
|
||||
represent_nif: Optional[str] = Field(None, max_length=25, description="NIF représentant")
|
||||
|
||||
# ========================================
|
||||
# CHAMPS PERSONNALISÉS (exemples)
|
||||
# ========================================
|
||||
date_creation_societe: Optional[date] = Field(None, description="Date création de la société")
|
||||
capital_social: Optional[Decimal] = Field(None, description="Capital social")
|
||||
actionnaire_principal: Optional[str] = Field(None, max_length=100, description="Actionnaire principal")
|
||||
score_banque_france: Optional[str] = Field(None, max_length=10, description="Score BDF")
|
||||
|
||||
# FIDÉLITÉ
|
||||
total_points_fidelite: Optional[int] = Field(None, description="Total points fidélité")
|
||||
points_fidelite_restants: Optional[int] = Field(None, description="Points restants")
|
||||
fin_validite_carte: Optional[date] = Field(None, description="Fin validité carte")
|
||||
|
||||
# ========================================
|
||||
# AUTRES
|
||||
# ========================================
|
||||
calendrier: Optional[str] = Field(None, max_length=13, description="Code calendrier (CAL_No)")
|
||||
mode_test: Optional[int] = Field(None, description="Mode test (0/1)")
|
||||
confiance: Optional[int] = Field(None, description="Niveau de confiance")
|
||||
|
||||
@field_validator('siret')
|
||||
@classmethod
|
||||
def validate_siret(cls, v):
|
||||
if v and len(v.replace(' ', '')) != 14:
|
||||
raise ValueError('Le SIRET doit contenir 14 chiffres')
|
||||
return v.replace(' ', '') if v else v
|
||||
|
||||
@field_validator('email')
|
||||
@classmethod
|
||||
def validate_email(cls, v):
|
||||
if v and '@' not in v:
|
||||
raise ValueError('Format email invalide')
|
||||
return v
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"intitule": "SARL NOUVELLE ENTREPRISE",
|
||||
"forme_juridique": "SARL",
|
||||
"adresse": "10 Avenue des Champs",
|
||||
"code_postal": "75008",
|
||||
"ville": "Paris",
|
||||
"telephone": "0123456789",
|
||||
"portable": "0612345678",
|
||||
"email": "contact@nouvelle-entreprise.fr",
|
||||
"siret": "12345678901234",
|
||||
"tva_intra": "FR12345678901",
|
||||
"intitule": "ENTREPRISE EXEMPLE SARL",
|
||||
"num": "CLI00123",
|
||||
"compte_collectif": "411000",
|
||||
"qualite": "CLI"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ClientUpdateRequest(BaseModel):
|
||||
"""Modèle pour modification d'un client existant"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Queue d'envoi d'emails avec threading et génération PDF
|
||||
Version VPS Linux - utilise sage_client pour récupérer les données
|
||||
"""
|
||||
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
|
|
@ -17,6 +11,10 @@ from email.mime.text import MIMEText
|
|||
from email.mime.application import MIMEApplication
|
||||
from config import settings
|
||||
import logging
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.units import cm
|
||||
from io import BytesIO
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -202,15 +200,6 @@ class EmailQueue:
|
|||
await asyncio.to_thread(self._send_smtp, msg)
|
||||
|
||||
def _generate_pdf(self, doc_id: str, type_doc: int) -> bytes:
|
||||
"""
|
||||
Génération PDF via ReportLab + sage_client
|
||||
|
||||
⚠️ Cette méthode est appelée depuis un thread worker
|
||||
"""
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.units import cm
|
||||
from io import BytesIO
|
||||
|
||||
if not self.sage_client:
|
||||
logger.error("❌ sage_client non configuré")
|
||||
|
|
|
|||
Loading…
Reference in a new issue