Sage100-ws/utils/article_fields.py

170 lines
6.4 KiB
Python

from typing import Dict, Any, Optional
import logging
logger = logging.getLogger(__name__)
CHAMPS_ASSIGNABLES_CREATION = {
"AR_Design": {"max_length": 69, "required": True, "description": "Désignation"},
"AR_PrixVen": {"type": float, "min": 0, "description": "Prix de vente HT"},
"AR_PrixAch": {"type": float, "min": 0, "description": "Prix achat HT"},
"AR_PrixAchat": {"type": float, "min": 0, "description": "Prix achat HT (alias)"},
"AR_CodeBarre": {"max_length": 13, "description": "Code-barres EAN"},
"AR_Commentaire": {"max_length": 255, "description": "Description/Commentaire"},
"AR_UniteVen": {"max_length": 10, "description": "Unité de vente"},
"AR_CodeFiscal": {"max_length": 10, "description": "Code fiscal/TVA"},
"AR_Pays": {"max_length": 3, "description": "Pays d'origine"},
"AR_Garantie": {"type": int, "min": 0, "description": "Garantie en mois"},
"AR_Delai": {"type": int, "min": 0, "description": "Délai livraison jours"},
"AR_Coef": {"type": float, "min": 0, "description": "Coefficient"},
"AR_PoidsNet": {"type": float, "min": 0, "description": "Poids net kg"},
"AR_PoidsBrut": {"type": float, "min": 0, "description": "Poids brut kg"},
"AR_Stat01": {"max_length": 20, "description": "Statistique 1"},
"AR_Stat02": {"max_length": 20, "description": "Statistique 2"},
"AR_Stat03": {"max_length": 20, "description": "Statistique 3"},
"AR_Stat04": {"max_length": 20, "description": "Statistique 4"},
"AR_Stat05": {"max_length": 20, "description": "Statistique 5"},
"AR_Escompte": {"type": bool, "description": "Soumis à escompte"},
"AR_Publie": {"type": bool, "description": "Publié web/catalogue"},
"AR_Sommeil": {"type": int, "values": [0, 1], "description": "Actif/Sommeil"},
}
CHAMPS_ASSIGNABLES_MODIFICATION = {
**CHAMPS_ASSIGNABLES_CREATION,
"AR_Stock": {"type": float, "min": 0, "description": "Stock réel"},
"AR_StockMini": {"type": float, "min": 0, "description": "Stock minimum"},
"AR_StockMaxi": {"type": float, "min": 0, "description": "Stock maximum"},
}
CHAMPS_OBJETS_SPECIAUX = {
"Unite": {"description": "Unité de vente (objet)", "copie_modele": True},
"Famille": {"description": "Famille article (objet)", "validation_sql": True},
}
CHAMPS_STOCK_INITIAL = {
"stock_reel": {"type": float, "min": 0, "description": "Stock initial"},
"stock_mini": {"type": float, "min": 0, "description": "Stock minimum"},
"stock_maxi": {"type": float, "min": 0, "description": "Stock maximum"},
}
def valider_champ(
nom_champ: str, valeur: Any, config: Dict
) -> tuple[bool, Optional[str]]:
if valeur is None:
if config.get("required"):
return False, f"Le champ {nom_champ} est obligatoire"
return True, None
if "type" in config:
expected_type = config["type"]
try:
if expected_type is float:
valeur = float(valeur)
elif expected_type is int:
valeur = int(valeur)
elif expected_type is bool:
valeur = bool(valeur)
except (ValueError, TypeError):
return (
False,
f"Le champ {nom_champ} doit être de type {expected_type.__name__}",
)
if "min" in config:
if isinstance(valeur, (int, float)) and valeur < config["min"]:
return False, f"Le champ {nom_champ} doit être >= {config['min']}"
if "max_length" in config:
if isinstance(valeur, str) and len(valeur) > config["max_length"]:
return (
False,
f"Le champ {nom_champ} ne peut dépasser {config['max_length']} caractères",
)
if "values" in config:
if valeur not in config["values"]:
return False, f"Le champ {nom_champ} doit être parmi {config['values']}"
return True, None
def valider_donnees_creation(data: Dict) -> tuple[bool, Optional[str]]:
if "reference" not in data or not data["reference"]:
return False, "Le champ 'reference' est obligatoire"
if len(str(data["reference"])) > 18:
return False, "La référence ne peut dépasser 18 caractères"
if "designation" not in data or not data["designation"]:
return False, "Le champ 'designation' est obligatoire"
for champ, valeur in data.items():
if champ in CHAMPS_ASSIGNABLES_CREATION:
valide, erreur = valider_champ(
champ, valeur, CHAMPS_ASSIGNABLES_CREATION[champ]
)
if not valide:
return False, erreur
return True, None
def valider_donnees_modification(data: Dict) -> tuple[bool, Optional[str]]:
if not data:
return False, "Aucun champ à modifier"
for champ, valeur in data.items():
if champ in ["famille", "stock_reel", "stock_mini", "stock_maxi"]:
continue
if champ in CHAMPS_ASSIGNABLES_MODIFICATION:
valide, erreur = valider_champ(
champ, valeur, CHAMPS_ASSIGNABLES_MODIFICATION[champ]
)
if not valide:
return False, erreur
return True, None
def mapper_champ_api_vers_sage(champ_api: str) -> Optional[str]:
mapping = {
"designation": "AR_Design",
"prix_vente": "AR_PrixVen",
"prix_achat": "AR_PrixAch",
"code_ean": "AR_CodeBarre",
"code_barre": "AR_CodeBarre",
"description": "AR_Commentaire",
"unite_vente": "AR_UniteVen",
"code_fiscal": "AR_CodeFiscal",
"tva_code": "AR_CodeFiscal",
"pays": "AR_Pays",
"garantie": "AR_Garantie",
"delai": "AR_Delai",
"coef": "AR_Coef",
"coefficient": "AR_Coef",
"poids_net": "AR_PoidsNet",
"poids_brut": "AR_PoidsBrut",
"stat_01": "AR_Stat01",
"stat_02": "AR_Stat02",
"stat_03": "AR_Stat03",
"stat_04": "AR_Stat04",
"stat_05": "AR_Stat05",
"soumis_escompte": "AR_Escompte",
"publie": "AR_Publie",
"en_sommeil": "AR_Sommeil",
"stock_reel": "AR_Stock",
"stock_mini": "AR_StockMini",
"stock_maxi": "AR_StockMaxi",
}
return mapping.get(champ_api, champ_api)
def obtenir_champs_assignables() -> Dict[str, Any]:
return {
"creation": list(CHAMPS_ASSIGNABLES_CREATION.keys()),
"modification": list(CHAMPS_ASSIGNABLES_MODIFICATION.keys()),
"objets_speciaux": list(CHAMPS_OBJETS_SPECIAUX.keys()),
"stock_initial": list(CHAMPS_STOCK_INITIAL.keys()),
}