Tried to set hours manually

This commit is contained in:
fanilo 2026-01-07 01:53:29 +01:00
parent fd0385d417
commit 4a642fa654
9 changed files with 193 additions and 108 deletions

24
main.py
View file

@ -204,8 +204,8 @@ def creer_devis(req: DevisRequest):
try: try:
devis_data = { devis_data = {
"client": {"code": req.client_id, "intitule": ""}, "client": {"code": req.client_id, "intitule": ""},
"date_devis": req.date_devis or date.today(), "date_devis": req.date_devis or datetime.now(),
"date_livraison": req.date_livraison or date.today(), "date_livraison": req.date_livraison or datetime.now(),
"reference": req.reference, "reference": req.reference,
"lignes": req.lignes, "lignes": req.lignes,
} }
@ -687,8 +687,8 @@ def creer_commande_endpoint(req: CommandeCreate):
try: try:
commande_data = { commande_data = {
"client": {"code": req.client_id, "intitule": ""}, "client": {"code": req.client_id, "intitule": ""},
"date_commande": req.date_commande or date.today(), "date_commande": req.date_commande or datetime.now(),
"date_livraison": req.date_livraison or date.today(), "date_livraison": req.date_livraison or datetime.now(),
"reference": req.reference, "reference": req.reference,
"lignes": req.lignes, "lignes": req.lignes,
} }
@ -727,8 +727,8 @@ def creer_livraison_endpoint(req: LivraisonCreate):
livraison_data = { livraison_data = {
"client": {"code": req.client_id, "intitule": ""}, "client": {"code": req.client_id, "intitule": ""},
"date_livraison": req.date_livraison or date.today(), "date_livraison": req.date_livraison or datetime.now(),
"date_livraison_prevue": req.date_livraison or date.today(), "date_livraison_prevue": req.date_livraison or datetime.now(),
"reference": req.reference, "reference": req.reference,
"lignes": req.lignes, "lignes": req.lignes,
} }
@ -767,8 +767,8 @@ def creer_avoir_endpoint(req: AvoirCreate):
avoir_data = { avoir_data = {
"client": {"code": req.client_id, "intitule": ""}, "client": {"code": req.client_id, "intitule": ""},
"date_avoir": req.date_avoir or date.today(), "date_avoir": req.date_avoir or datetime.now(),
"date_livraison": req.date_livraison or date.today(), "date_livraison": req.date_livraison or datetime.now(),
"reference": req.reference, "reference": req.reference,
"lignes": req.lignes, "lignes": req.lignes,
} }
@ -810,8 +810,8 @@ def creer_facture_endpoint(req: FactureCreate):
facture_data = { facture_data = {
"client": {"code": req.client_id, "intitule": ""}, "client": {"code": req.client_id, "intitule": ""},
"date_facture": req.date_facture or date.today(), "date_facture": req.date_facture or datetime.now(),
"date_livraison": req.date_livraison or date.today(), "date_livraison": req.date_livraison or datetime.now(),
"reference": req.reference, "reference": req.reference,
"lignes": req.lignes, "lignes": req.lignes,
} }
@ -1177,7 +1177,7 @@ def creer_entree_stock(req: EntreeStock):
) )
entree_data = { entree_data = {
"date_mouvement": req.date_entree or date.today(), "date_mouvement": req.date_entree or datetime.now(),
"reference": req.reference, "reference": req.reference,
"depot_code": req.depot_code, "depot_code": req.depot_code,
"lignes": [ligne.dict() for ligne in req.lignes], "lignes": [ligne.dict() for ligne in req.lignes],
@ -1207,7 +1207,7 @@ def creer_sortie_stock(req: SortieStock):
) )
sortie_data = { sortie_data = {
"date_mouvement": req.date_sortie or date.today(), "date_mouvement": req.date_sortie or datetime.now(),
"reference": req.reference, "reference": req.reference,
"depot_code": req.depot_code, "depot_code": req.depot_code,
"lignes": [ligne.dict() for ligne in req.lignes], "lignes": [ligne.dict() for ligne in req.lignes],

View file

@ -1,14 +1,14 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import date from datetime import datetime
class AvoirCreate(BaseModel): class AvoirCreate(BaseModel):
"""Création d'un avoir côté gateway""" """Création d'un avoir côté gateway"""
client_id: str client_id: str
date_avoir: Optional[date] = None date_avoir: Optional[datetime] = None
date_livraison: Optional[date] = None date_livraison: Optional[datetime] = None
lignes: List[Dict] lignes: List[Dict]
reference: Optional[str] = None reference: Optional[str] = None

View file

@ -1,14 +1,14 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import date from datetime import datetime
class CommandeCreate(BaseModel): class CommandeCreate(BaseModel):
"""Création d'une commande""" """Création d'une commande"""
client_id: str client_id: str
date_commande: Optional[date] = None date_commande: Optional[datetime] = None
date_livraison: Optional[date] = None date_livraison: Optional[datetime] = None
reference: Optional[str] = None reference: Optional[str] = None
lignes: List[Dict] lignes: List[Dict]

View file

@ -1,12 +1,12 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import date from datetime import datetime
class DevisRequest(BaseModel): class DevisRequest(BaseModel):
client_id: str client_id: str
date_devis: Optional[date] = None date_devis: Optional[datetime] = None
date_livraison: Optional[date] = None date_livraison: Optional[datetime] = None
reference: Optional[str] = None reference: Optional[str] = None
lignes: List[Dict] lignes: List[Dict]

View file

@ -1,14 +1,14 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import date from datetime import datetime
class FactureCreate(BaseModel): class FactureCreate(BaseModel):
"""Création d'une facture côté gateway""" """Création d'une facture côté gateway"""
client_id: str client_id: str
date_facture: Optional[date] = None date_facture: Optional[datetime] = None
date_livraison: Optional[date] = None date_livraison: Optional[datetime] = None
lignes: List[Dict] lignes: List[Dict]
reference: Optional[str] = None reference: Optional[str] = None

View file

@ -1,14 +1,14 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import date from datetime import datetime
class LivraisonCreate(BaseModel): class LivraisonCreate(BaseModel):
"""Création d'une livraison côté gateway""" """Création d'une livraison côté gateway"""
client_id: str client_id: str
date_livraison: Optional[date] = None date_livraison: Optional[datetime] = None
date_livraison_prevue: Optional[date] = None date_livraison_prevue: Optional[datetime] = None
lignes: List[Dict] lignes: List[Dict]
reference: Optional[str] = None reference: Optional[str] = None

View file

@ -5,6 +5,7 @@ from utils.functions.functions import (
_convertir_type_depuis_sql, _convertir_type_depuis_sql,
_convertir_type_pour_sql, _convertir_type_pour_sql,
_safe_strip, _safe_strip,
_combiner_date_heure,
) )
@ -106,7 +107,8 @@ def _lire_document_sql(cursor, numero: str, type_doc: int):
d.DO_Tarif, d.DO_TypeFrais, d.DO_ValFrais, d.DO_Tarif, d.DO_TypeFrais, d.DO_ValFrais,
d.DO_TypeFranco, d.DO_ValFranco, d.DO_TypeFranco, d.DO_ValFranco,
c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal, c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal,
c.CT_Ville, c.CT_Telephone, c.CT_EMail c.CT_Ville, c.CT_Telephone, c.CT_EMail,
d.DO_Heure
FROM F_DOCENTETE d FROM F_DOCENTETE d
LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num
WHERE d.DO_Piece = ? AND d.DO_Type = ? WHERE d.DO_Piece = ? AND d.DO_Type = ?
@ -119,19 +121,19 @@ def _lire_document_sql(cursor, numero: str, type_doc: int):
if not row: if not row:
logger.warning( logger.warning(
f"[SQL READ] Document {numero} (type={type_doc}) INTROUVABLE dans F_DOCENTETE" f"[SQL READ] Document {numero} (type={type_doc}) INTROUVABLE"
) )
return None return None
numero_piece = _safe_strip(row[0]) numero_piece = _safe_strip(row[0])
logger.info(f"[SQL READ] Document trouvé: {numero_piece}") logger.info(f"[SQL READ] Document trouvé: {numero_piece}")
doc = { doc = {
"numero": numero_piece, "numero": numero_piece,
"reference": _safe_strip(row[2]), "reference": _safe_strip(row[2]),
"date": str(row[1]) if row[1] else "", "date": _combiner_date_heure(row[1], row[45]),
"date_livraison": (str(row[7]) if row[7] else ""), "date_livraison": str(row[7]) if row[7] else "",
"date_expedition": (str(row[8]) if row[8] else ""), "date_expedition": str(row[8]) if row[8] else "",
"client_code": _safe_strip(row[6]), "client_code": _safe_strip(row[6]),
"client_intitule": _safe_strip(row[39]), "client_intitule": _safe_strip(row[39]),
"client_adresse": _safe_strip(row[40]), "client_adresse": _safe_strip(row[40]),
@ -144,9 +146,9 @@ def _lire_document_sql(cursor, numero: str, type_doc: int):
"total_ht_net": float(row[10]) if row[10] else 0.0, "total_ht_net": float(row[10]) if row[10] else 0.0,
"total_ttc": float(row[4]) if row[4] else 0.0, "total_ttc": float(row[4]) if row[4] else 0.0,
"net_a_payer": float(row[11]) if row[11] else 0.0, "net_a_payer": float(row[11]) if row[11] else 0.0,
"montant_regle": (float(row[12]) if row[12] else 0.0), "montant_regle": float(row[12]) if row[12] else 0.0,
"reliquat": float(row[13]) if row[13] else 0.0, "reliquat": float(row[13]) if row[13] else 0.0,
"taux_escompte": (float(row[14]) if row[14] else 0.0), "taux_escompte": float(row[14]) if row[14] else 0.0,
"escompte": float(row[15]) if row[15] else 0.0, "escompte": float(row[15]) if row[15] else 0.0,
"taxe1": float(row[16]) if row[16] else 0.0, "taxe1": float(row[16]) if row[16] else 0.0,
"taxe2": float(row[17]) if row[17] else 0.0, "taxe2": float(row[17]) if row[17] else 0.0,
@ -155,26 +157,22 @@ def _lire_document_sql(cursor, numero: str, type_doc: int):
"code_taxe2": _safe_strip(row[20]), "code_taxe2": _safe_strip(row[20]),
"code_taxe3": _safe_strip(row[21]), "code_taxe3": _safe_strip(row[21]),
"statut": int(row[5]) if row[5] is not None else 0, "statut": int(row[5]) if row[5] is not None else 0,
"statut_estatut": ( "statut_estatut": int(row[22]) if row[22] is not None else 0,
int(row[22]) if row[22] is not None else 0
),
"imprime": int(row[23]) if row[23] is not None else 0, "imprime": int(row[23]) if row[23] is not None else 0,
"valide": int(row[24]) if row[24] is not None else 0, "valide": int(row[24]) if row[24] is not None else 0,
"cloture": int(row[25]) if row[25] is not None else 0, "cloture": int(row[25]) if row[25] is not None else 0,
"transfere": (int(row[26]) if row[26] is not None else 0), "transfere": int(row[26]) if row[26] is not None else 0,
"souche": int(row[27]) if row[27] is not None else 0, "souche": int(row[27]) if row[27] is not None else 0,
"piece_origine": _safe_strip(row[28]), "piece_origine": _safe_strip(row[28]),
"guid": _safe_strip(row[29]), "guid": _safe_strip(row[29]),
"ca_num": _safe_strip(row[30]), "ca_num": _safe_strip(row[30]),
"cg_num": _safe_strip(row[31]), "cg_num": _safe_strip(row[31]),
"expedition": (int(row[32]) if row[32] is not None else 1), "expedition": int(row[32]) if row[32] is not None else 1,
"condition": (int(row[33]) if row[33] is not None else 1), "condition": int(row[33]) if row[33] is not None else 1,
"tarif": int(row[34]) if row[34] is not None else 1, "tarif": int(row[34]) if row[34] is not None else 1,
"type_frais": (int(row[35]) if row[35] is not None else 0), "type_frais": int(row[35]) if row[35] is not None else 0,
"valeur_frais": float(row[36]) if row[36] else 0.0, "valeur_frais": float(row[36]) if row[36] else 0.0,
"type_franco": ( "type_franco": int(row[37]) if row[37] is not None else 0,
int(row[37]) if row[37] is not None else 0
),
"valeur_franco": float(row[38]) if row[38] else 0.0, "valeur_franco": float(row[38]) if row[38] else 0.0,
} }
@ -386,7 +384,8 @@ def _lister_documents_avec_lignes_sql(
d.CA_Num, d.CG_Num, d.DO_Expedit, d.DO_Condition, d.DO_Tarif, d.CA_Num, d.CG_Num, d.DO_Expedit, d.DO_Condition, d.DO_Tarif,
d.DO_TypeFrais, d.DO_ValFrais, d.DO_TypeFranco, d.DO_ValFranco, d.DO_TypeFrais, d.DO_ValFrais, d.DO_TypeFranco, d.DO_ValFranco,
c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal, c.CT_Intitule, c.CT_Adresse, c.CT_CodePostal,
c.CT_Ville, c.CT_Telephone, c.CT_EMail c.CT_Ville, c.CT_Telephone, c.CT_EMail,
d.DO_Heure
FROM F_DOCENTETE d FROM F_DOCENTETE d
LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num LEFT JOIN F_COMPTET c ON d.DO_Tiers = c.CT_Num
WHERE d.DO_Type = ? WHERE d.DO_Type = ?
@ -400,7 +399,7 @@ def _lister_documents_avec_lignes_sql(
) )
params.extend([f"%{filtre}%", f"%{filtre}%", f"%{filtre}%"]) params.extend([f"%{filtre}%", f"%{filtre}%", f"%{filtre}%"])
query += " ORDER BY d.DO_Date DESC" query += " ORDER BY d.DO_Date DESC, d.DO_Heure DESC"
if limit: if limit:
query = f"SELECT TOP ({limit}) * FROM ({query}) AS subquery" query = f"SELECT TOP ({limit}) * FROM ({query}) AS subquery"
@ -455,15 +454,13 @@ def _lister_documents_avec_lignes_sql(
"numero": numero, "numero": numero,
"type": type_doc_depuis_sql, "type": type_doc_depuis_sql,
"reference": _safe_strip(entete.DO_Ref), "reference": _safe_strip(entete.DO_Ref),
"date": str(entete.DO_Date) if entete.DO_Date else "", "date": _combiner_date_heure(entete.DO_Date, entete.DO_Heure),
"date_livraison": ( "date_livraison": str(entete.DO_DateLivr)
str(entete.DO_DateLivr) if entete.DO_DateLivr else "" if entete.DO_DateLivr
), else "",
"date_expedition": ( "date_expedition": str(entete.DO_DateExpedition)
str(entete.DO_DateExpedition) if entete.DO_DateExpedition
if entete.DO_DateExpedition else "",
else ""
),
"client_code": _safe_strip(entete.DO_Tiers), "client_code": _safe_strip(entete.DO_Tiers),
"client_intitule": _safe_strip(entete.CT_Intitule), "client_intitule": _safe_strip(entete.CT_Intitule),
"client_adresse": _safe_strip(entete.CT_Adresse), "client_adresse": _safe_strip(entete.CT_Adresse),

View file

@ -47,9 +47,19 @@ def creer_document_vente(
logger.info(f"✓ Document {config.nom_document} créé") logger.info(f"✓ Document {config.nom_document} créé")
# ===== DATES ===== # ===== DATES =====
doc.DO_Date = pywintypes.Time( date_principale = normaliser_date(
normaliser_date(doc_data.get(config.champ_date_principale)) doc_data.get(config.champ_date_principale)
) )
doc.DO_Date = pywintypes.Time(date_principale)
# Heure - même datetime, Sage extrait la composante horaire
try:
doc.DO_Heure = pywintypes.Time(date_principale)
logger.debug(
f"DO_Heure défini: {date_principale.strftime('%H:%M:%S')}"
)
except Exception as e:
logger.debug(f"DO_Heure non défini: {e}")
# Date secondaire (livraison, etc.) # Date secondaire (livraison, etc.)
if config.champ_date_secondaire and doc_data.get( if config.champ_date_secondaire and doc_data.get(
@ -407,10 +417,6 @@ def _relire_document_final(
def modifier_document_vente( def modifier_document_vente(
self, numero: str, doc_data: dict, type_document: TypeDocumentVente self, numero: str, doc_data: dict, type_document: TypeDocumentVente
) -> Dict: ) -> Dict:
"""
Méthode unifiée de modification de documents de vente
RÉUTILISE les mêmes sous-méthodes que la création
"""
if not self.cial: if not self.cial:
raise RuntimeError("Connexion Sage non établie") raise RuntimeError("Connexion Sage non établie")
@ -546,9 +552,14 @@ def modifier_document_vente(
logger.info("📝 Modifications simples...") logger.info("📝 Modifications simples...")
if modif_date: if modif_date:
doc.DO_Date = pywintypes.Time( date_principale = normaliser_date(
normaliser_date(doc_data_temp.get(config.champ_date_principale)) doc_data_temp.get(config.champ_date_principale)
) )
doc.DO_Date = pywintypes.Time(date_principale)
try:
doc.DO_Heure = pywintypes.Time(date_principale)
except Exception:
pass
champs_modifies.append(config.champ_date_principale) champs_modifies.append(config.champ_date_principale)
if modif_date_sec: if modif_date_sec:

View file

@ -110,20 +110,96 @@ def _normaliser_type_document(type_doc: int) -> int:
def normaliser_date(valeur): def normaliser_date(valeur):
if isinstance(valeur, str): """Parse flexible des dates - supporte ISO, datetime, YYYY-MM-DD HH:MM:SS"""
try: if valeur is None:
return datetime.fromisoformat(valeur) return datetime.now()
except ValueError:
return datetime.now()
elif isinstance(valeur, date): if isinstance(valeur, datetime):
return datetime.combine(valeur, datetime.min.time())
elif isinstance(valeur, datetime):
return valeur return valeur
else: if isinstance(valeur, date):
return datetime.now() return datetime.combine(valeur, datetime.min.time())
if isinstance(valeur, str):
valeur = valeur.strip()
if not valeur:
return datetime.now()
if valeur.endswith("Z"):
valeur = valeur[:-1] + "+00:00"
formats = [
"%Y-%m-%dT%H:%M:%S.%f%z",
"%Y-%m-%dT%H:%M:%S%z",
"%Y-%m-%dT%H:%M:%S.%f",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d %H:%M:%S.%f",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M",
"%Y-%m-%d",
"%d/%m/%Y %H:%M:%S",
"%d/%m/%Y %H:%M",
"%d/%m/%Y",
]
for fmt in formats:
try:
dt = datetime.strptime(valeur, fmt)
return dt.replace(tzinfo=None) if dt.tzinfo else dt
except ValueError:
continue
try:
if "+" in valeur:
valeur = valeur.split("+")[0]
dt = datetime.fromisoformat(valeur)
return dt.replace(tzinfo=None) if dt.tzinfo else dt
except ValueError:
pass
return datetime.now()
def _parser_heure_sage(do_heure) -> str:
"""Parse DO_Heure format Sage (HHMMSS stocké en entier)"""
if not do_heure:
return "00:00:00"
try:
# Convertir en entier pour éliminer les zéros de padding SQL
heure_int = int(str(do_heure).strip())
# Formatter en string 6 caractères (HHMMSS)
heure_str = str(heure_int).zfill(6)
hh = int(heure_str[0:2])
mm = int(heure_str[2:4])
ss = int(heure_str[4:6])
if 0 <= hh <= 23 and 0 <= mm <= 59 and 0 <= ss <= 59:
return f"{hh:02d}:{mm:02d}:{ss:02d}"
except (ValueError, TypeError):
pass
return "00:00:00"
def _combiner_date_heure(do_date, do_heure) -> str:
"""Combine DO_Date et DO_Heure en datetime string"""
if not do_date:
return ""
try:
date_str = (
do_date.strftime("%Y-%m-%d")
if hasattr(do_date, "strftime")
else str(do_date)[:10]
)
heure_str = _parser_heure_sage(do_heure)
return f"{date_str} {heure_str}"
except Exception:
return str(do_date) if do_date else ""
__all__ = [ __all__ = [
@ -136,4 +212,5 @@ __all__ = [
"_convertir_type_depuis_sql", "_convertir_type_depuis_sql",
"_convertir_type_pour_sql", "_convertir_type_pour_sql",
"normaliser_date", "normaliser_date",
"_combiner_date_heure",
] ]