Browse Source

NEW Youpi! Nouveaux scripts testés et validés.

olivier.massot 7 years ago
parent
commit
7540cf821b
8 changed files with 351 additions and 194 deletions
  1. 1 0
      .gitignore
  2. 11 15
      analytique2facture.py
  3. 50 3
      core/model.py
  4. 179 100
      core/pde.py
  5. 32 0
      core/sqlformatter.py
  6. 75 74
      ctrl2analytique.py
  7. 2 2
      logging.yaml
  8. 1 0
      requirements.txt

+ 1 - 0
.gitignore

@@ -5,4 +5,5 @@
 Output/
 htmlcov/
 *.log
+*.log.1
 work/*

+ 11 - 15
analytique2facture.py

@@ -22,10 +22,10 @@ logconf.start("analytique2facture", logging.DEBUG)
 # # POUR TESTER, décommenter les lignes suivantes
 ##-----------------------------------------------
 
-logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
-AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
-FacturesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Facture_data.mdb")
-CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
+# logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
+# AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
+# FacturesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Facture_data.mdb")
+# CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
 
 ##-----------------------------------------------
 
@@ -179,7 +179,7 @@ for affaireId, interventions in a_facturer.items():
         ligne.dblPUhtNetDev = interv.dblPrixUnitaire
         ligne.dblPThtNetDev = interv.dblPrixTotal
         ligne.blnPrestation = True
-        ligne.memObs = "Intervention le {}{}".format(interv.dtmFin, "\nRapport no : {}".format(interv.strRapportId) if interv.strRapportId else "")
+        ligne.memObs = "Intervention le {:%d/%m/%Y}{}".format(interv.dtmFin, "\r\nRapport n° : {}".format(interv.strRapportId) if interv.strRapportId else "")
         ligne.blnLigneGeneree = True
         ligne.bytGenerateurId = 1
         ligne.dblPThtNet = interv.dblPrixTotal
@@ -188,6 +188,12 @@ for affaireId, interventions in a_facturer.items():
         ligne.strPUAff = "{:03.02f} EUR/{}".format(interv.dblPrixUnitaire, interv.strUnite)
         ligne.strQteAff = "{:03.02f} {}".format(interv.dblQuantite, interv.strUnite)
 
+        # on met a jour tbl_Tarification.strNumFacture et strStatut pour marquer la ligne comme facturée.
+        # !! Cette opération ne sera committée qu'à la fin du traitement de cette intervention
+        sql = Sql.format(""" UPDATE tbl_Tarification SET strStatut = 'Facturée', strNumFacture = {:text}
+                              WHERE DblTarifId={}""", piece_id, interv.DblTarifId)
+        analytique_db.execute(sql)
+
         lignes.append(ligne)
 
     # Toutes les lignes ont été ajoutées: on créé maintenant les lignes de totaux
@@ -277,12 +283,9 @@ for affaireId, interventions in a_facturer.items():
                     {entete.bytTypeDocumentId}, {entete.strStatDocEntete:text}, {entete.dblCoursDevise}, {entete.dtmCoursDevise:date},
                     {entete.strUserIdCreation:text}, {entete.strUserIdLastMod:text})
             """, entete=entete)
-
-    logger.debug(sql)
     facture_db.execute(sql)
 
     for ligne in lignes:
-
         sql = Sql.format("""
                 INSERT INTO tblPieceLigne ( lngPieceId, intLigneId, strArticleId, strArticle, bytTVAArticleId, bytTVAId, dblTVATaux, dblQte, bytUniteIdQuantite,
                                              bytNbDecQuantite, bytUniteIdPrix, dblCnvUniteCoef, bytNbDecQuantiteConvertie, dblPUhtBrutDev, dblPUttcBrutDev, dblPUhtNetDev,
@@ -300,15 +303,8 @@ for affaireId, interventions in a_facturer.items():
                         {ligne.dblPThtBrut}, {ligne.dblPTttcBrut}, {ligne.dblPThtNet}, {ligne.dblPTttcNet}, {ligne.bytClasseRemiseArticleId}, {ligne.dblPUSaisie},
                         {ligne.strPUAff:text}, {ligne.strQteAff:text})
                 """, ligne=ligne)
-        logger.debug(sql)
         facture_db.execute(sql)
 
-        # Maj tbl_Tarification.strNumFacture et strStatut pour marquer la ligne comme facturée.
-        sql = Sql.format(""" UPDATE tbl_Tarification SET strStatut = 'Facturée', strNumFacture = '{}'
-                              WHERE DblTarifId={}; """, piece_id, interv.DblTarifId)
-        logger.debug(sql)
-        analytique_db.execute(sql)
-
     facture_db.commit()
     analytique_db.commit()
 

+ 50 - 3
core/model.py

@@ -3,14 +3,59 @@
 
     @author: olivier.massot, févr. 2018
 '''
+from datetime import datetime
 import logging
 
+import dateutil.parser
+
+from core.sqlformatter import SqlFormatter
+
+
 logger = logging.getLogger("model")
 
+Sql = SqlFormatter()
 
 class Model():
     _mapping = {}
 
+    def __setattr__(self, name, value):
+
+        try:
+            val, type_ = value
+            if not type_ in (int, float, bool, str, datetime):
+                raise TypeError("Type de donnée invalide pour une propriété de 'Model' ('{}')".format(type(val)))
+            self.__class__._mapping[name] = type_
+        except (TypeError, ValueError):
+            val = value
+            if val is None:
+                super().__setattr__(name, val)
+                return
+            try:
+                type_ = self.__class__._mapping[name]
+            except KeyError:
+                super().__setattr__(name, val)
+                return
+
+        val = Model._cast(val, type_)
+
+        super().__setattr__(name, val)
+
+    @staticmethod
+    def _cast(value, type_):
+        if value is None:
+            return value
+        if type_ == datetime:
+            if type(value) is str:
+                return dateutil.parser.parse(value)
+            elif type(value) is datetime:
+                return value
+            else:
+                raise ValueError("'{}' ne peut pas être converti en date".format(value))
+        if type_ is bool and value in ("True", "False"):
+            return (value == "True")
+        else:
+            return type_(value)
+
     @property
     def _fields(self):
         return list(self.__dict__.keys())
@@ -31,9 +76,11 @@ class Model():
         return model
 
     @classmethod
-    def _map_type(cls, field, value):
+    def _parse(cls, field, value):
+        if value == 'None':
+            value = None
         try:
-            return cls._mapping[field](value)
+            return Model._cast(value, cls._mapping[field])
         except KeyError:
             return value
 
@@ -58,5 +105,5 @@ class Model():
         with open(path) as f:
             fields = next(f).split("\t")
             for line in f:
-                data = {key: cls._map_type(key, value) for key, value in zip(fields, line.split("\t"))}
+                data = {key: cls._parse(key, value) for key, value in zip(fields, line.split("\t"))}
                 yield(cls.from_dict(data))

+ 179 - 100
core/pde.py

@@ -86,114 +86,193 @@ class PdaDb(ParcDb):
 
 # ## Modèles
 
+class Affaire(Model):
+    """ Modèle de données d'une affaire Analytique """
+    def __init__(self):
+        self.DblAffaireId = None, int
+        self.strMOId = None, str
+        self.strMOeId = None, str
+        self.strEntrepriseId = None, str
+        self.strCommneId = None, str
+        self.strLieux = None, str
+        self.strTypeId = None, str
+        self.dtmCommande = None, datetime
+        self.Ref = None, str
+        self.blnMarche = None, bool
+        self.dblMarche = 0, float
+        self.intTypeContrat = None, int
+        self.strCT = None, str
+        self.strAvancement = None, str
+        self.strLiaisonControle = None, str
+        self.intDevisId = None, int
+        self.blnTarification = False, bool
+        self.blnAnalyse = False, bool
+        self.remarques = None, str
+        self.strSituation = None, str
+        self.dtmFin = None, datetime
+        self.intCoefFG = 0, float
+
+class Interv(Model):
+    """ Modèle de données d'une intervention de contrôle réseaux """
+    def __init__(self):
+        self.dblInterventionId = None, int
+        self.dblAffaireId = 0, int
+        self.strEquipeId = None, str
+        self.strEnginId = None, str
+        self.strRapportId = None, str
+        self.strCatégorieInterventionId = None, str
+        self.strTypeInterventionId = None, str
+        self.dblquantite = 0, float
+        self.strunite = None, str
+        self.dtmIntervention = None, datetime
+        self.dtmDureeIntervention = None, datetime
+        self.dtmDureeInstallation = None, datetime
+        self.strcaracteristique1 = None, str
+        self.strgrandeur1 = None, str
+        self.strunite1 = None, str
+        self.strcaracteristique2 = None, str
+        self.strgrandeur2 = None, str
+        self.strunite2 = None, str
+        self.strcaracteristique3 = None, str
+        self.strgrandeur3 = None, str
+        self.strunite3 = None, str
+        self.strLiaisonControle = None, str
+        self.strarticleId = None, str
+        self.intPeriode = 0, int
+        self.blnTarification = False, bool
+        self.blnAnalyse = False, bool
+        self.blnFacturer = None, bool
+        self.remarques = None, str
+        self.blnPeriode = None, bool
+        self.dtnPeriodeDebut = None, datetime
+        self.dtmImportation = None, datetime
+        self.blnVerifFacture = None, bool
+        self.strTest = None, str
+
+class Tarification(Model):
+    """ Modèle de donnée d'une ligne de tarification """
+    def __init__(self):
+        self.DblTarifId = None, int
+        self.DblAffaireId = 0, int
+        self.strRapportId = None, int
+        self.strArticleId = None, str
+        self.dblQuantite = 0, float
+        self.strUnite = None, str
+        self.dtmDebut = None, datetime
+        self.dtmFin = None, datetime
+        self.bytPeriode = 0, int
+        self.dblPrixUnitaire = 0, float
+        self.dblPrixTotal = 0, float
+        self.bytCodeTiers = 0, int
+        self.dblTauxTVA = 0, float
+        self.dblPrixTVA = 0, float
+        self.strStatut = None, str
+        self.strNumFacture = None, str
+
 class EnTete(Model):
     """ En-tête d'une facture dans FacturesDb """
     def __init__(self):
-        self.lngPieceId = 0
-        self.lngDocId = 0
-        self.lngTiersId = 0
-        self.lngASTRE = None
-        self.strCodeProduit = None
-        self.bytTitreId = 0
-        self.strCodeAdresse = None
-        self.strAdresse1 = None
-        self.strAdresse2 = None
-        self.strAdresse3 = None
-        self.strAdresse4 = None
-        self.strAdresse5 = None
-        self.strPaysIdIso3166 = None  # (!) le nom réel du champ est '[strPaysIdIso3166-A2]'
-        self.bytTVATiersId = 0
-        self.strTelephone = None
-        self.strTelecopie = None
-        self.strPortable = None
-        self.strEMail = None
-        self.strWeb = None
-        self.strLangueIdIso639 = None
-        self.strCompteComptable = None
-        self.strDeviseIdIso4217 = None
-        self.bytReglementId = 0
-        self.strReglement = None
-        self.bytClasseTarifId = 0
-        self.bytClasseRemiseTiersId = 0
-        self.bytNbExFacture = 1
-        self.strStatTiers = None
-        self.bytSituationIdPrincipale = 0
-        self.bytSituationIdSecondaire = 0
-        self.memObsEntete = None
-        self.memObsPied = None
-        self.memObsInterne = None
-        self.bytTypeDocumentId = 0
-        self.strStatDocEntete = None
-        self.dblCoursDevise = 1
-        self.dtmCoursDevise = datetime.now()
-        self.dtmCreation = datetime.now()
-        self.strUserIdCreation = None
+        self.lngPieceId = 0, int
+        self.lngDocId = 0, int
+        self.lngTiersId = 0, int
+        self.lngASTRE = None, int
+        self.strCodeProduit = None, str
+        self.bytTitreId = 0, int
+        self.strCodeAdresse = None, str
+        self.strAdresse1 = None, str
+        self.strAdresse2 = None, str
+        self.strAdresse3 = None, str
+        self.strAdresse4 = None, str
+        self.strAdresse5 = None, str
+        self.strPaysIdIso3166 = None , str  # (!) le nom réel du champ est '[strPaysIdIso3166-A2]'
+        self.bytTVATiersId = 0, int
+        self.strTelephone = None, str
+        self.strTelecopie = None, str
+        self.strPortable = None, str
+        self.strEMail = None, str
+        self.strWeb = None, str
+        self.strLangueIdIso639 = None, str
+        self.strCompteComptable = None, str
+        self.strDeviseIdIso4217 = None, str
+        self.bytReglementId = 0, int
+        self.strReglement = None, str
+        self.bytClasseTarifId = 0, int
+        self.bytClasseRemiseTiersId = 0, int
+        self.bytNbExFacture = 1, int
+        self.strStatTiers = None, str
+        self.bytSituationIdPrincipale = 0, int
+        self.bytSituationIdSecondaire = 0, int
+        self.memObsEntete = None, str
+        self.memObsPied = None, str
+        self.memObsInterne = None, str
+        self.bytTypeDocumentId = 0, int
+        self.strStatDocEntete = None, str
+        self.dblCoursDevise = 1, int
+        self.dtmCoursDevise = datetime.now(), datetime
+        self.dtmCreation = datetime.now(), datetime
+        self.strUserIdCreation = None, str
         self.dtmLastMod = datetime.now()
-        self.strUserIdLastMod = None
-        self.dtmPiece = datetime.now()
-        self.dtmFirstPrint = None
-        self.dtmLastPrint = None
-        self.dtmCompta = None
-        self.dtmLivraison = None
-        self.dtmEcheance = None
-        self.bytTypeTarif = 1
-        self.idEtatEmission = None
+        self.strUserIdLastMod = None, str
+        self.dtmPiece = datetime.now(), datetime
+        self.dtmFirstPrint = None, datetime
+        self.dtmLastPrint = None, datetime
+        self.dtmCompta = None, datetime
+        self.dtmLivraison = None, datetime
+        self.dtmEcheance = None, datetime
+        self.bytTypeTarif = 1, int
+        self.idEtatEmission = None, str
 
 class Ligne(Model):
     """ Ligne d'une facture dans FacturesDb """
     def __init__(self):
-        self.lngPieceId = 0
-        self.intLigneId = 0
-        self.strArticleId = None
-        self.strArticle = None
-        self.bytTVAArticleId = 0
-        self.bytTVAId = 0
-        self.dblTVATaux = 0
-        self.dblQte = 0
-        self.bytUniteIdQuantite = 0
-        self.bytNbDecQuantite = 2
-        self.bytUniteIdPrix = 0
-        self.dblCnvUniteCoef = 1
-        self.bytNbDecQuantiteConvertie = 2
-        self.dblPUhtBrutDev = 0
-        self.dblPUttcBrutDev = 0
-        self.dblPUhtNetDev = 0
-        self.dblPUttcNetDev = 0
-        self.bytNbDecPU = 2
-        self.dblPThtBrutDev = 0
-        self.dblPTttcBrutDev = 0
-        self.dblPThtNetDev = 0
-        self.dblPTttcNetDev = 0
-        self.strStatArticle = None
-        self.strStatDocLigne = None
-        self.dblTauxRemise1 = 0
-        self.dblTauxRemise2 = 0
-        self.dblTauxRemise3 = 0
-        self.blnPrestation = False
-        self.strCompteComptable = None
-        self.memObs = None
-        self.memObsInterne = None
-        self.bytLigneSousTotal = 1
-        self.intLigneIdRattachement = 0
-        self.blnLigneVisible = True
-        self.blnLigneGeneree = False
-        self.bytGenerateurId = 0
-        self.dblPUhtBrut = 0
-        self.dblPUttcBrut = 0
-        self.dblPUhtNet = 0
-        self.dblPUttcNet = 0
-        self.dblPThtBrut = 0
-        self.dblPTttcBrut = 0
-        self.dblPThtNet = 0
-        self.dblPTttcNet = 0
-        self.bytClasseRemiseArticleId = 0
-        self.dblPUSaisie = 0
-        self.strPUAff = ""
-        self.strQteAff = ""
-
-
-
-
+        self.lngPieceId = 0, int
+        self.intLigneId = 0, int
+        self.strArticleId = None, str
+        self.strArticle = None, str
+        self.bytTVAArticleId = 0, int
+        self.bytTVAId = 0, int
+        self.dblTVATaux = 0, float
+        self.dblQte = 0, float
+        self.bytUniteIdQuantite = 0, int
+        self.bytNbDecQuantite = 2, int
+        self.bytUniteIdPrix = 0, int
+        self.dblCnvUniteCoef = 1.0, float
+        self.bytNbDecQuantiteConvertie = 2, int
+        self.dblPUhtBrutDev = 0, float
+        self.dblPUttcBrutDev = 0, float
+        self.dblPUhtNetDev = 0, float
+        self.dblPUttcNetDev = 0, float
+        self.bytNbDecPU = 2, int
+        self.dblPThtBrutDev = 0, float
+        self.dblPTttcBrutDev = 0, float
+        self.dblPThtNetDev = 0, float
+        self.dblPTttcNetDev = 0, float
+        self.strStatArticle = None, str
+        self.strStatDocLigne = None, str
+        self.dblTauxRemise1 = 0, float
+        self.dblTauxRemise2 = 0, float
+        self.dblTauxRemise3 = 0, float
+        self.blnPrestation = False, bool
+        self.strCompteComptable = None, str
+        self.memObs = None, str
+        self.memObsInterne = None, str
+        self.bytLigneSousTotal = 1, int
+        self.intLigneIdRattachement = 0, int
+        self.blnLigneVisible = True, bool
+        self.blnLigneGeneree = False, bool
+        self.bytGenerateurId = 0, int
+        self.dblPUhtBrut = 0, float
+        self.dblPUttcBrut = 0, float
+        self.dblPUhtNet = 0, float
+        self.dblPUttcNet = 0, float
+        self.dblPThtBrut = 0, float
+        self.dblPTttcBrut = 0, float
+        self.dblPThtNet = 0, float
+        self.dblPTttcNet = 0, float
+        self.bytClasseRemiseArticleId = 0, int
+        self.dblPUSaisie = 0, float
+        self.strPUAff = "", str
+        self.strQteAff = "", str
 
 
 

+ 32 - 0
core/sqlformatter.py

@@ -0,0 +1,32 @@
+'''
+
+    Formatter special pour les requetes SQL
+
+    usage:
+
+        Sql = SqlFormatter()
+
+        my_query = Sql.format("SELECT {} FROM {}", "my_field", "tblname")
+
+@author: olivier.massot
+'''
+import string
+
+
+class SqlFormatter(string.Formatter):
+
+    def format_field(self, value, format_spec):
+
+        if value is None:
+            return "Null"
+
+        if format_spec == "date":
+            return "#{:%Y-%m-%d %H:%M:%S}#".format(value)
+        elif format_spec == "text":
+            return "'{}'".format(SqlFormatter._escape(str(value)))
+        else:
+            return SqlFormatter._escape(value.__format__(format_spec))
+
+    @staticmethod
+    def _escape(instr):
+            return instr.replace("'", "''").replace("\"", "''").replace("\t", " ")

+ 75 - 74
ctrl2analytique.py

@@ -16,8 +16,9 @@ from path import Path  # @UnusedImport
 
 from core import logconf
 from core.db import AccessSqlHelper
-from core.model import Model
-from core.pde import ControlesDb, AnalytiqueDb, mk_workdir, CommunDb
+from core.pde import ControlesDb, AnalytiqueDb, mk_workdir, CommunDb, Affaire, \
+    Interv, Tarification
+from core.sqlformatter import SqlFormatter
 
 
 logger = logging.getLogger("ctrl2analytique")
@@ -27,10 +28,10 @@ logconf.start("ctrl2analytique", logging.DEBUG)
 # > Lancer le script /resources/test_ctrl2analytique.py pour reinitialiser les données de la base de test
 ##-----------------------------------------------
 
-logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
-ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
-AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
-CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
+# logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
+# ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
+# AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
+# CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
 
 ##-----------------------------------------------
 
@@ -38,6 +39,8 @@ CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
 # #########    INITIALISATION    ##########
 logger.info("Initialisation...")
 
+Sql = SqlFormatter()
+
 no_prompt = ("-n" in sys.argv)
 if no_prompt:
     logger.info("> Lancé en mode automatique (sans interruption)")
@@ -63,20 +66,11 @@ for file in (affaires_file, intervs_file):
         file.remove()
 
 
-class Affaire(Model):
-    """ Modèle de données d'une affaire Analytique """
-    pass
-
-class Interv(Model):
-    """ Modèle de données d'une intervention de contrôle réseaux """
-    pass
-
-class Tarification(Model):
-    """ Modèle de donnée d'une ligne de tarification """
-    pass
-
 sqlHelper = AccessSqlHelper
 
+# date zéro pour Access
+date_zero = datetime(1899, 12, 30, 0, 0, 0)
+
 def get_type_id(lngChantierId, bytCommandeId):
     """ Recupère le type de chantier.
     'ZP': Chantier de contrôle d'étanchéité
@@ -105,7 +99,7 @@ def get_coeff_k(lngChantierId):
     """ Récupère le coefficient de calcul des frais généraux (batiments, frais administratifs...Etc.) """
     # On déduit l'année du chantier à partir du code chantier
     annee = "20" + str(lngChantierId)[:2] if len(str(lngChantierId)) == 6 else "200" + str(lngChantierId)[:1]
-    return analytique_db.first("SELECT [COEFFG] FROM tbl_COEFFG WHERE [ANNEE] = {}".format(annee)).COEFFG / 100
+    return analytique_db.first(Sql.format("SELECT [COEFFG] FROM tbl_COEFFG WHERE [ANNEE] = {}", annee)).COEFFG / 100
 
 
 
@@ -135,6 +129,7 @@ for data in controles_db.read(sql):
     affaire.Ref = data.strRefCommande
     affaire.blnMarche = data.blnMarche
     affaire.dblMarche = data.dblMtMarche
+    affaire.intDevisId = data.strdevis if data.strdevis else 0
     affaire.intTypeContrat = 1
     affaire.strCT = '1'
     affaire.strTypeId = get_type_id(data.lngChantierId, data.bytCommandeId)
@@ -161,13 +156,15 @@ logger.info("> {} affaires ajoutées au fichier".format(compteur))
 
 def engin_existe(strEnginId):
     """ retourne True si le code de l'engin existe dans la table tbl_Engin """
-    return analytique_db.exists("SELECT strEnginId FROM tbl_Engin WHERE strEnginId='{}'".format(strEnginId))
+    return analytique_db.exists(Sql.format("SELECT strEnginId FROM tbl_Engin WHERE strEnginId={:text}", strEnginId))
 
 def get_periode_validite(date_interv):
     """ retourne la préiode comptable correspondant à la date de l'intervention """
-    sql = """SELECT intPeriodeValiditeId FROM tblTarifValidite
-            WHERE [dtmValiditeDebut] <= #{date_interv}# AND [dtmValiditeFin] > #{date_interv}# AND [bytClasseTarifId]=1
-    """.format(date_interv=date_interv)
+    if not date_interv:
+        return None
+    sql = Sql.format("""SELECT intPeriodeValiditeId FROM tblTarifValidite
+                        WHERE [dtmValiditeDebut] <= {date_interv:date} AND [dtmValiditeFin] > {date_interv:date} AND [bytClasseTarifId]=1
+                    """, date_interv=date_interv)
     return commun_db.first(sql).intPeriodeValiditeId
 
 compteur = 0
@@ -213,7 +210,7 @@ for data in controles_db.read(sql):
     interv.strunite = "u"
     interv.dtmIntervention = data.dtmEssai
     interv.dtmDureeIntervention = data.dtmDuree
-    interv.dtmDureeInstallation = 0  # Les temps d'installation seront calculés en fin de traitement
+    interv.dtmDureeInstallation = date_zero  # Les temps d'installation seront calculés en fin de traitement
     interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
     interv.strArticleId = data.strEnginId
     interv.intPeriode = get_periode_validite(data.dtmEssai)
@@ -283,7 +280,7 @@ for data in controles_db.read(sql):
     interv.strunite = "u"
     interv.dtmIntervention = data.dtmEssai
     interv.dtmDureeIntervention = data.dtmDuree
-    interv.dtmDureeInstallation = 0  # Les temps d'installation seront recalculés en fin de traitement
+    interv.dtmDureeInstallation = date_zero  # Les temps d'installation seront recalculés en fin de traitement
     interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
     interv.strArticleId = interv.strEnginId
     interv.intPeriode = get_periode_validite(data.dtmEssai)
@@ -347,7 +344,7 @@ for data in controles_db.read(sql):
     interv.strunite = "j"
     interv.dtmIntervention = data.dtmIntervDu
     interv.dtmDureeIntervention = data.dtmDuree
-    interv.dtmDureeInstallation = 0  # Les temps d'installation seront recalculés en fin de traitement
+    interv.dtmDureeInstallation = date_zero  # Les temps d'installation seront recalculés en fin de traitement
     interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
     interv.strArticleId = data.strEnginId
     interv.intPeriode = get_periode_validite(data.dtmIntervDu)
@@ -398,31 +395,32 @@ while errors:
         if not affaire.strMOId:
             errors.append(prefix + "MO manquant")
         else:
-            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOId)):
+            if not commun_db.exists(Sql.format("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}", affaire.strMOId)):
                 errors.append(prefix + "Le MO {} n'existe pas dans tblTiers".format(affaire.strMOId))
         if not affaire.strMOeId:
             errors.append(prefix + "MOe manquant")
         else:
-            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOeId)):
+            if not commun_db.exists(Sql.format("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}", affaire.strMOeId)):
                 errors.append(prefix + "Le MOe {} n'existe pas dans tblTiers".format(affaire.strMOeId))
         if not affaire.strEntrepriseId:
             errors.append(prefix + "Entreprise manquante")
         else:
-            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strEntrepriseId)):
+            if not commun_db.exists(Sql.format("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}", affaire.strEntrepriseId)):
                 errors.append(prefix + "L'entreprise {} n'existe pas dans tblTiers".format(affaire.strEntrepriseId))
         if not affaire.strCommneId:
             errors.append(prefix + "Commune manquante")
         else:
-            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strCommneId)):
+            if not commun_db.exists(Sql.format("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}", affaire.strCommneId)):
                 errors.append(prefix + "La commune {} n'existe pas dans tblTiers".format(affaire.strCommneId))
         if not affaire.strTypeId:
             errors.append(prefix + "Type d'affaire manquant")
         if not affaire.dtmCommande:
             errors.append(prefix + "Date de commande manquante")
-        if affaire.blnMarche == True and not affaire.intDevisId:
-            errors.append(prefix + "Numéro de devis manquant")
+        if affaire.blnMarche == True:
+            if not affaire.intDevisId:
+                errors.append(prefix + "Numéro de devis manquant")
 
-        if analytique_db.exists("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)):
+        if analytique_db.exists(Sql.format("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'", affaire.strLiaisonControle)):
             errors.append(prefix + "Une affaire portant ce code existe déjà: {}".format(affaire.strLiaisonControle))
 
     for interv in Interv.load_csv(intervs_file):
@@ -485,13 +483,13 @@ for affaire in affaires:
 
     # insertion dans tbl_Affaires
 
-    sql = """ INSERT INTO tbl_Affaires ( strMOId, strMOeId, strEntrepriseId, strCommneId, strLieux, strTypeId, dtmCommande, Ref,
+    sql = Sql.format(""" INSERT INTO tbl_Affaires ( strMOId, strMOeId, strEntrepriseId, strCommneId, strLieux, strTypeId, dtmCommande, Ref,
                                         blnMarche, dblMarche, intTypeContrat, strCT, strLiaisonControle, blnTarification,
                                         blnAnalyse, strSituation, intCoefFG )
-       VALUES ('{affaire.strMOId}', '{affaire.strMOeId}', '{affaire.strEntrepriseId}', '{affaire.strCommneId}', '{affaire.strLieux}', '{affaire.strTypeId}',
-       #{affaire.dtmCommande}#, '{affaire.Ref}', {affaire.blnMarche}, {affaire.dblMarche}, {affaire.intTypeContrat}, '{affaire.strCT}',
-       '{affaire.strLiaisonControle}', True, False, '{affaire.strSituation}', {affaire.intCoefFG})
-       """.format(affaire=affaire)
+       VALUES ({affaire.strMOId:text}, {affaire.strMOeId:text}, {affaire.strEntrepriseId:text}, {affaire.strCommneId:text}, {affaire.strLieux:text}, {affaire.strTypeId:text},
+       {affaire.dtmCommande:date}, {affaire.Ref:text}, {affaire.blnMarche}, {affaire.dblMarche}, {affaire.intTypeContrat}, {affaire.strCT:text},
+       {affaire.strLiaisonControle:text}, True, False, {affaire.strSituation:text}, {affaire.intCoefFG})
+       """, affaire=affaire)
 
     analytique_db.execute(sql)
 
@@ -501,24 +499,26 @@ for affaire in affaires:
 # On insère les interventions dans tbl_Intervention
 for interv in intervs:
 
-    affaire = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(interv.LienAff))
+    affaire = analytique_db.first(Sql.format("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'", interv.LienAff))
     if not affaire:
         logger.error("Intervention {} : Impossible de trouver l'affaire {}".format(interv.strTest, interv.LienAff))
         continue
 
     interv.dblAffaireId = affaire.DblAffaireId
+    if not interv.intPeriode:
+        interv.intPeriode = get_periode_validite(data.dtmIntervDu)  # Si la date d'interv manquait avant la validation, la periode n'a pa été mise à jour
 
-    sql = """INSERT INTO tbl_Intervention ( DblAffaireId, strEquipeId, strEnginId, strRapportId, strCatégorieInterventionId, strTypeInterventionId,
+    sql = Sql.format("""INSERT INTO tbl_Intervention ( DblAffaireId, strEquipeId, strEnginId, strRapportId, strCatégorieInterventionId, strTypeInterventionId,
               dblquantite, strunite, dtmIntervention, dtmDureeIntervention, dtmDureeInstallation, strcaracteristique1, strgrandeur1, strunite1,
               strcaracteristique2, strgrandeur2, strunite2, strcaracteristique3, strgrandeur3, strunite3, strLiaisonControle, strarticleId,
               intPeriode, blnTarification, blnAnalyse, blnFacturer, remarques, blnPeriode, dtmImportation, strTest )
-      VALUES ({interv.dblAffaireId}, '{interv.strEquipeId}', '{interv.strEnginId}', '{interv.strRapportId}', '{interv.strCatégorieInterventionId}',
-          '{interv.strTypeInterventionId}', {interv.dblquantite}, '{interv.strunite}', #{interv.dtmIntervention}#, #{interv.dtmDureeIntervention}#, #1899-12-30 00:00:00#,
-          '{interv.strcaracteristique1}', '{interv.strgrandeur1}', '{interv.strunite1}', '{interv.strcaracteristique2}',
-          '{interv.strgrandeur2}', '{interv.strunite2}', '{interv.strcaracteristique3}', '{interv.strgrandeur3}', '{interv.strunite3}', '{interv.strLiaisonControle}',
-          '{interv.strArticleId}', {interv.intPeriode}, True, False, False, '{interv.remarques}',
-          False, #{interv.dtmImportation}#, '{interv.strTest}')
-      """.format(interv=interv)
+      VALUES ({interv.dblAffaireId}, {interv.strEquipeId:text}, {interv.strEnginId:text}, {interv.strRapportId:text}, {interv.strCatégorieInterventionId:text},
+          {interv.strTypeInterventionId:text}, {interv.dblquantite}, {interv.strunite:text}, {interv.dtmIntervention:date}, {interv.dtmDureeIntervention:date}, {date_zero:date},
+          {interv.strcaracteristique1:text}, {interv.strgrandeur1:text}, {interv.strunite1:text}, {interv.strcaracteristique2:text},
+          {interv.strgrandeur2:text}, {interv.strunite2:text}, {interv.strcaracteristique3:text}, {interv.strgrandeur3:text}, {interv.strunite3:text},
+          {interv.strLiaisonControle:text}, {interv.strArticleId:text}, {interv.intPeriode}, True, False, False, {interv.remarques:text},
+          False, {interv.dtmImportation:date}, {interv.strTest:text})
+      """, interv=interv, date_zero=date_zero)
 
     analytique_db.execute(sql)
 
@@ -536,11 +536,15 @@ for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId
     tarif.intervs = [interv for interv in intervs if interv.strRapportId == strRapportId and interv.strArticleId == strArticleId]
 
     # recupere le prix unitaire de l'engin
-    prix_unitaire = commun_db.first("""SELECT dblPU FROM tblTarif WHERE [strArticleId]='{}' AND [intPeriodeValiditeId]={}
-                                    """.format(strArticleId, tarif.intervs[0].intPeriode)).dblPU
+    tarif_engin = commun_db.first(Sql.format("""SELECT dblPU FROM tblTarif WHERE [strArticleId]={:text} AND [intPeriodeValiditeId]={}
+                                             """, strArticleId, get_periode_validite(intervs[0].dtmIntervention)))
+    if not tarif_engin:
+        logger.error("Aucun tarif trouvé dans tblTarif pour l'article {}, periode {}".format(strArticleId, tarif.intervs[0].intPeriode))
+    prix_unitaire = tarif_engin.dblPU
+
     # recupere le taux de tva applicable à l'engin
-    taux_tva = commun_db.first("""SELECT tblTVATaux.dblTVATaux FROM tblArticle INNER JOIN tblTVATaux ON tblArticle.bytTVAArticleId = tblTVATaux.bytTVAId
-                                  WHERE (((tblArticle.strArticleId)='{}'));""".format(strArticleId)).dblTVATaux
+    taux_tva = commun_db.first(Sql.format("""SELECT tblTVATaux.dblTVATaux FROM tblArticle INNER JOIN tblTVATaux ON tblArticle.bytTVAArticleId = tblTVATaux.bytTVAId
+                                             WHERE tblArticle.strArticleId={:text};""", strArticleId)).dblTVATaux
 
     tarif.DblAffaireId = tarif.intervs[0].dblAffaireId
     tarif.strRapportId = strRapportId
@@ -556,12 +560,12 @@ for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId
     tarif.dblPrixTVA = tarif.dblPrixTotal * (0.01 * tarif.dblTauxTVA)
     tarif.strStatut = 'A facturer'
 
-    sql = """ INSERT INTO tbl_Tarification ( DblAffaireId, strRapportId, strArticleId, dblQuantite, strUnite, dtmDebut, dtmFin, bytPeriode,
+    sql = Sql.format(""" INSERT INTO tbl_Tarification ( DblAffaireId, strRapportId, strArticleId, dblQuantite, strUnite, dtmDebut, dtmFin, bytPeriode,
                                              dblPrixUnitaire, dblPrixTotal, dblTauxTVA, dblPrixTVA, strStatut )
-              VALUES ({tarif.DblAffaireId}, '{tarif.strRapportId}', '{tarif.strArticleId}', {tarif.dblQuantite}, '{tarif.strUnite}', #{tarif.dtmDebut}#,
-                       #{tarif.dtmFin}#, {tarif.bytPeriode}, {tarif.dblPrixUnitaire}, {tarif.dblPrixTotal},
-                       {tarif.dblTauxTVA}, {tarif.dblPrixTVA}, '{tarif.strStatut}')
-            """.format(tarif=tarif)
+              VALUES ({tarif.DblAffaireId}, {tarif.strRapportId:text}, {tarif.strArticleId:text}, {tarif.dblQuantite}, {tarif.strUnite:text}, {tarif.dtmDebut:date},
+                       {tarif.dtmFin:date}, {tarif.bytPeriode}, {tarif.dblPrixUnitaire}, {tarif.dblPrixTotal},
+                       {tarif.dblTauxTVA}, {tarif.dblPrixTVA}, {tarif.strStatut:text})
+            """, tarif=tarif)
     analytique_db.execute(sql)
 
     logger.info("> Génération d'une ligne de tarification pour l'affaire {} (rapport {}, article: {})".format(tarif.intervs[0].LienAff, strRapportId, strArticleId))
@@ -570,13 +574,11 @@ for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId
 # Maj champs MOS
 # Ces champs sont utilisés dans les tables Controles pour savoir si une ligne a déjà été importée
 for affaire in affaires:
-    dblAffaireId = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)).DblAffaireId
+    dblAffaireId = analytique_db.first(Sql.format("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]={:text}", affaire.strLiaisonControle)).DblAffaireId
 
-    sql = """UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
+    sql = Sql.format("""UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
             WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId}
-            """.format(DblAffaireId=dblAffaireId,
-                       lngChantierId=affaire.lngChantierId,
-                       bytCommandeId=affaire.bytCommandeId)
+            """, DblAffaireId=dblAffaireId, lngChantierId=affaire.lngChantierId, bytCommandeId=affaire.bytCommandeId)
     controles_db.execute(sql)
 
 for interv in intervs:
@@ -589,13 +591,13 @@ for interv in intervs:
     else:
         continue
 
-    sql = """UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
+    sql = Sql.format("""UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
             WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId} AND [bytIntervId]={bytIntervId}
-            """.format(tbl=tbl,
-                       DblAffaireId=interv.dblAffaireId,
-                       lngChantierId=interv.lngChantierId,
-                       bytCommandeId=interv.bytCommandeId,
-                       bytIntervId=interv.bytIntervId)
+            """, tbl=tbl,
+                   DblAffaireId=interv.dblAffaireId,
+                   lngChantierId=interv.lngChantierId,
+                   bytCommandeId=interv.bytCommandeId,
+                   bytIntervId=interv.bytIntervId)
     controles_db.execute(sql)
 
 logger.info("> Mise à jour des champs MOS")
@@ -614,14 +616,13 @@ analytique_db.commit()
 
 logger.info("Mise à jour des temps d'installation...")
 
-# date zéro pour Access
-date0 = datetime(1899, 12, 30, 0, 0, 0)
+
 
 # On parcourt les interventions.
 # Lorsque le temps d'intervention total d'une même équipe un même jour est inférieur à 8h,
 # On affecte la différence de temps à la première intervention en tant que 'temps d'installation'
 
-sql = """SELECT First(tbl_Intervention.dblInterventionId) AS dblInterventionId, tbl_Intervention.strEquipeId,
+sql = Sql.format("""SELECT First(tbl_Intervention.dblInterventionId) AS dblInterventionId, tbl_Intervention.strEquipeId,
             tbl_Intervention.dtmIntervention, CDate(Sum(tbl_Intervention.dtmDureeIntervention)) AS SD
         FROM tbl_Intervention
         WHERE tbl_Intervention.strLiaisonControle Like '%/%'
@@ -630,12 +631,12 @@ sql = """SELECT First(tbl_Intervention.dblInterventionId) AS dblInterventionId,
             AND tbl_Intervention.strEquipeId Is Not Null
         GROUP BY tbl_Intervention.strEquipeId, tbl_Intervention.dtmIntervention
         HAVING (((CDate(Sum(tbl_Intervention.dtmDureeIntervention)))<#1899/12/30 8:0:0#))
-        """.format(datetime.now().year - 1)
+        """, datetime.now().year - 1)
 
 for interv in analytique_db.read_all(sql):
-    tps_install = (date0 + timedelta(hours=8) - interv.SD)
-    sql = """UPDATE tbl_Intervention SET tbl_Intervention.dtmDureeInstallation = #{}#
-             WHERE (((tbl_Intervention.dblInterventionId)={}));""".format(date0 + tps_install, interv.dblInterventionId)
+    tps_install = (date_zero + timedelta(hours=8) - interv.SD)
+    sql = Sql.format("""UPDATE tbl_Intervention SET dtmDureeInstallation = #{}#
+                         WHERE dblInterventionId={}""", date_zero + tps_install, interv.dblInterventionId)
     analytique_db.execute(sql)
 
     logger.debug("* Mise à jour du temps d'installation de l'intervention {}".format(interv.dblInterventionId))

+ 2 - 2
logging.yaml

@@ -30,7 +30,7 @@ handlers:
         formatter: complete
         mailhost: smtp.bas-rhin.fr
         fromaddr: log@bas-rhin.fr
-        toaddrs: [olivier.massot@bas-rhin.fr] #, jacky.klein@bas-rhin.fr
+        toaddrs: [olivier.massot@bas-rhin.fr, jacky.klein@bas-rhin.fr]
         subject: log
         capacity: 100000000
         
@@ -59,7 +59,7 @@ loggers:
         level: DEBUG
         handlers: [console, file,mail]
         propagate: no
-          
+        
 root:
     level: DEBUG
     handlers: [console]

+ 1 - 0
requirements.txt

@@ -1,3 +1,4 @@
 pypyodbc
 path.py
 lxml
+python-dateutil