Browse Source

CHG Met a jour la structure des scripts

olivier.massot 7 years ago
parent
commit
faa752e999
12 changed files with 1850 additions and 1805 deletions
  1. 289 284
      analytique2facture.py
  2. 580 573
      ctrl2analytique.py
  3. 236 232
      gf2analytique.py
  4. 34 29
      gf2factures.py
  5. 76 69
      mails_rappel_ctrl.py
  6. 98 107
      pda2suiviactivite.py
  7. 164 159
      qgis_sync_compactage.py
  8. 71 67
      qgis_sync_etancheite.py
  9. 2 0
      qgis_sync_video.py
  10. 29 22
      qgis_sync_videores.py
  11. 1 2
      qgis_sync_wincan.py
  12. 270 261
      wincan2ctrl.py

+ 289 - 284
analytique2facture.py

@@ -30,291 +30,296 @@ logconf.start("analytique2facture", logging.DEBUG)
 
 ##-----------------------------------------------
 
-# Connexion à Analytique
-analytique_db = AnalytiqueDb(autocommit=False)
-
-# Connexion à Controles
-facture_db = FacturesDb(autocommit=False)
-
-# Connexion à CommunDb
-commun_db = CommunDb(autocommit=False)
-
-Sql = SqlFormatter()
-
-current = "{:%m/%Y}".format(datetime.now())
-
-mois_facturation = input("Veuillez renseigner le mois de facturation [defaut: {}] ('q' pour quitter): ".format(current))  # Format: voir avec jacky
-if mois_facturation == 'q':
-    sys.exit(1)
-
-sql = """SELECT * FROM tbl_Tarification
-        WHERE strStatut='A facturer'
-        ORDER BY DblAffaireId, DblTarifId DESC;
-        """
-
-# Liste les interventions à facturer par affaire
-a_facturer = {}
-for interv in analytique_db.read(sql):
-    if not interv.DblAffaireId in a_facturer.keys():
-        a_facturer[interv.DblAffaireId] = []
-    a_facturer[interv.DblAffaireId].append(interv)
-
-if not a_facturer:
-    logger.info("Aucune facture à créer - Opération annulée")
-    sys.exit(1)
-
-# Pour chaque facture, on va ajouter une ligne dans tblPieceEntete, et une ligne par intervention dans tblPieceLigne
-# > NB: On ne touche pas aux interventions de cette affaire qui ont déja été facturées
-
-for affaireId, interventions in a_facturer.items():
-
-    piece_id = facture_db.first("SELECT Max(lngpieceId) As Id FROM tblPieceEntete").Id + 1
-    if str(piece_id)[:2] != mois_facturation[-2:]:
-        # On démarre une nouvelle année
-        piece_id = 10000 * int(mois_facturation[:2]) + 1
-
-    affaire = analytique_db.first("SELECT * FROM tbl_Affaires WHERE dblAffaireId={}".format(affaireId))
-    if not affaire:
-        logger.error("Affaire {} - L'affaire n'existe pas dans tbl_Affaires".format(affaireId))
-        continue
-
-    if not affaire.strMOId:
-        logger.error("Affaire {} - Destinataire manquant dans tbl_Affaires".format(affaireId))
-        continue
-
-    destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId={}".format(affaire.strMOId))
-    if not destinataire:
-        logger.warning("Affaire {} - Destinataire introuvable dans tblTiers ('{}'), remplacé par tiers n°190".format(affaireId, affaire.strMOId))
-        destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId=190")
-
-    entete = EnTete()
-    entete.lngPieceId = piece_id
-
-    # on recupere les champs de tblTiers depuis le champ lngASTRE (champ n°3) jusqu'au champ strCodeProduit (champ n°26)
-    # ATTENTION: le champ 'strPaysIdIso3166-A2' n'etant pas un nom de propriété valide, il est renomme en strPaysIdIso3166 dans le modèle.
-    entete.lngTiersId = destinataire.lngTiersId
-    entete.lngASTRE = destinataire.lngASTRE
-    entete.strCodeProduit = destinataire.strCodeProduit
-    entete.bytTitreId = destinataire.bytTitreId
-    entete.strCodeAdresse = destinataire.strCodeAdresse
-    entete.strAdresse1 = destinataire.strAdresse1
-    entete.strAdresse2 = destinataire.strAdresse2
-    entete.strAdresse3 = destinataire.strAdresse3
-    entete.strAdresse4 = destinataire.strAdresse4
-    entete.strAdresse5 = destinataire.strAdresse5
-    entete.strPaysIdIso3166 = destinataire.field_11
-    entete.bytTVATiersId = destinataire.bytTVATiersId
-    entete.strTelephone = destinataire.strTelephone
-    entete.strTelecopie = destinataire.strTelecopie
-    entete.strPortable = destinataire.strPortable
-    entete.strEMail = destinataire.strEMail
-    entete.strWeb = destinataire.strWeb
-    entete.strLangueIdIso639 = destinataire.strLangueIdIso639
-    entete.strCompteComptable = destinataire.strCompteComptable
-    entete.strDeviseIdIso4217 = destinataire.strDeviseIdIso4217
-    entete.bytReglementId = destinataire.bytReglementId
-    entete.bytClasseTarifId = destinataire.bytClasseTarifId
-    entete.bytNbExFacture = destinataire.bytNbExFacture
-    entete.bytClasseRemiseTiersId = destinataire.bytClasseRemiseTiersId
-    entete.bytNbExFacture = destinataire.bytNbExFacture
-    entete.strStatTiers = destinataire.strStatTiers
-
-    # Valeurs fixes
-    entete.lngDocId = 0
-    entete.strLangueIdIso639 = "fr"
-    entete.strDeviseIdIso4217 = "EUR"
-    entete.bytReglementId = 1
-    entete.bytClasseTarifId = 1
-    entete.bytClasseRemiseTiersId = 1
-    entete.bytNbExFacture = 1
-    entete.strStatTiers = ""
-    entete.bytSituationIdPrincipale = 2
-    entete.bytSituationIdSecondaire = 41
-    entete.bytTypeDocumentId = 50
-    entete.strStatDocEntete = "PI00"
-    entete.dblCoursDevise = 1
-    entete.dtmCoursDevise = datetime(2002, 1, 1)
-    entete.bytTypeTarif = 1
-    entete.strReglement = "POUR ACQUITTER CET ETAT DE REDEVANCE,\r\n ATTENDEZ IMPERATIVEMENT DE RECEVOIR LE TITRE DE PERCEPTION EMANANT DE LA PAIERIE DEPARTEMENTALE"
-
-    # Commentaires
-    entete.memObsEntete = "[Mois : {mois_facturation}]\r\n" \
-                          "[Lieu de travail : ]{strLieux}\r\n" \
-                          "[V/Cde : ] {Ref} du {dtmCommande}\r\n" \
-                          "".format(mois_facturation=mois_facturation,
-                                    strLieux=affaire.strLieux,
-                                    Ref=affaire.Ref,
-                                    dtmCommande="{:%d/%m/%Y}".format(affaire.dtmCommande) if affaire.dtmCommande else "",
-                                    )
-
-    entete.memObsInterne = "N° Affaire analytique : {}".format(affaire.strLiaisonControle)
-
-    entete.strStatDocEntete = "PC00" if ("/" in affaire.strLiaisonControle or affaire.strLiaisonControle == "RDBOEC") else "PT00"
-
-    entete.strUserIdCreation = "script"
-    entete.strUserIdLastMod = "script"
-
-    i = 0
-    lignes = []
-    for interv in interventions:
-        i += 1
-
-        ligne = Ligne()
-        ligne.lngPieceId = piece_id
-        ligne.intLigneId = i
-        ligne.strArticleId = interv.strArticleId
-
-        article = commun_db.first("SELECT * FROM tblArticle WHERE strArticleId='{}'".format(interv.strArticleId))
-        ligne.strArticle = article.strArticle if article else ""
-        ligne.bytTVAArticleId = article.bytTVAArticleId if article else 0
-
-        tva = commun_db.first("SELECT bytTVAId FROM tblTVA WHERE bytTVATiersId={} AND bytTVAArticleId={}".format(destinataire.bytTVATiersId, ligne.bytTVAArticleId))
-        ligne.bytTVAId = tva.bytTVAId
-
-        taux = commun_db.first("SELECT dblTVATaux FROM tblTVATaux WHERE bytTVAId={}".format(ligne.bytTVAId))
-        ligne.dblTVATaux = taux.dblTVATaux
-
-        ligne.dblQte = interv.dblQuantite
-
-        if interv.strUnite:
-            unite = commun_db.first("SELECT * FROM tblUnite WHERE strUniteCourt='{}'".format(interv.strUnite))
-            ligne.bytUniteIdQuantite = unite.bytUniteId
-            ligne.bytUniteIdPrix = unite.bytUniteId
-        else:
-            ligne.bytUniteIdQuantite = 0
-            ligne.bytUniteIdPrix = 0
-        ligne.dblPUhtBrutDev = interv.dblPrixUnitaire
-        ligne.dblPUhtNetDev = interv.dblPrixUnitaire
-        ligne.dblPThtNetDev = interv.dblPrixTotal
-        ligne.blnPrestation = True
-        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
-        ligne.bytClasseRemiseArticleId = 1
-        ligne.dblPUSaisie = interv.dblPrixUnitaire
-        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
-    montant_ht = float(sum([ligne.dblPThtNetDev for ligne in lignes]))
-    taux_tva = float(lignes[0].dblTVATaux)
-    montant_tva = (0.01 * taux_tva) * montant_ht
-    montant_ttc = montant_ht + montant_tva
-
-    # Sous-total HT (ligne 32000)
-    ligne_total_1 = Ligne()
-    ligne_total_1.lngPieceId = piece_id
-    ligne_total_1.intLigneId = 32000
-    ligne_total_1.strArticleId = ""
-    ligne_total_1.strArticle = "Total H.T."
-    ligne_total_1.dblQte = 0
-    ligne_total_1.dblPThtNetDev = montant_ht
-    ligne_total_1.dblPTttcNetDev = montant_ttc
-    ligne_total_1.bytLigneSousTotal = 10
-    ligne_total_1.blnLigneVisible = False
-    ligne_total_1.dblPThtNet = montant_ht
-    ligne_total_1.dblPTttcNet = round(montant_ttc, 2)
-    ligne_total_1.dblPUSaisie = round(montant_ht, 2)
-    ligne_total_1.strPUAff = "{:03.02f} EUR".format(montant_ht)
-
-    lignes.append(ligne_total_1)
-
-    # Sous-total TVA (ligne 32001)
-    ligne_total_2 = Ligne()
-    ligne_total_2.lngPieceId = piece_id
-    ligne_total_2.intLigneId = 32001
-    ligne_total_2.strArticleId = ""
-    ligne_total_2.strArticle = """TVA 1 : {taux_tva}% H.T. : {montant_ht:03.02f} T.V.A. :
-                                 {montant_tva:03.02f} T.T.C. : {montant_ttc:03.02f}:""".format(taux_tva=taux_tva,
-                                                                                               montant_ht=montant_ht,
-                                                                                               montant_tva=montant_tva,
-                                                                                               montant_ttc=montant_ttc)
-    ligne_total_2.dblQte = 0
-    ligne_total_2.bytTVAId = lignes[0].bytTVAId
-    ligne_total_2.dblTVATaux = taux_tva
-    ligne_total_2.dblPThtNetDev = montant_ht
-    ligne_total_2.dblPTttcNetDev = montant_ttc
-    ligne_total_2.bytLigneSousTotal = 11
-    ligne_total_2.blnLigneVisible = False
-    ligne_total_2.dblPThtNet = montant_ht
-    ligne_total_2.dblPTttcNet = round(ligne_total_1.dblPTttcNetDev, 2)
-    ligne_total_2.dblPUSaisie = round(montant_tva, 2)
-    ligne_total_2.strPUAff = "{:03.02f} EUR".format(montant_tva)
-
-    lignes.append(ligne_total_2)
-
-    # Sous-total TTC (ligne 32500)
-    ligne_total_3 = Ligne()
-    ligne_total_3.lngPieceId = piece_id
-    ligne_total_3.intLigneId = 32500
-    ligne_total_3.strArticleId = ""
-    ligne_total_3.strArticle = "Total T.T.C."
-    ligne_total_3.dblQte = 0
-    ligne_total_3.dblPThtNetDev = montant_ht
-    ligne_total_3.dblPTttcNetDev = montant_ttc
-    ligne_total_3.bytLigneSousTotal = 12
-    ligne_total_3.blnLigneVisible = False
-    ligne_total_3.dblPThtNet = montant_ht
-    ligne_total_3.dblPTttcNet = round(montant_ttc, 2)
-    ligne_total_3.dblPUSaisie = round(montant_ttc, 2)
-    ligne_total_3.strPUAff = "{:03.02f} EUR".format(montant_ttc)
-
-    lignes.append(ligne_total_3)
-
-
-
-    logger.info("* Nouvelle facture: {}".format(entete.lngPieceId))
-
-    # Insertion de l'en-tête
-    sql = Sql.format("""
-            INSERT INTO tblPieceEntete ( lngPieceId, lngDocId, lngTiersId, lngASTRE, strCodeProduit, bytTitreId, strCodeAdresse, strAdresse1, strAdresse2,
-                                        strAdresse3, strAdresse4, strAdresse5, [strPaysIdIso3166-A2], bytTVATiersId, strTelephone, strTelecopie, strPortable,
-                                        strEMail, strWeb, strLangueIdIso639, strCompteComptable, strDeviseIdIso4217, bytReglementId, strReglement,
-                                        bytClasseTarifId, bytClasseRemiseTiersId, bytNbExFacture, strStatTiers, bytSituationIdPrincipale, bytSituationIdSecondaire,
-                                        memObsEntete, memObsInterne, bytTypeDocumentId, strStatDocEntete, dblCoursDevise, dtmCoursDevise,
-                                        strUserIdCreation, strUserIdLastMod )
-            VALUES ({entete.lngPieceId}, {entete.lngDocId}, {entete.lngTiersId}, {entete.lngASTRE}, {entete.strCodeProduit:text}, {entete.bytTitreId},
-                    {entete.strCodeAdresse:text}, {entete.strAdresse1:text}, {entete.strAdresse2:text}, {entete.strAdresse3:text}, {entete.strAdresse4:text}, {entete.strAdresse5:text},
-                    {entete.strPaysIdIso3166:text}, {entete.bytTVATiersId}, {entete.strTelephone:text}, {entete.strTelecopie:text}, {entete.strPortable:text}, {entete.strEMail:text},
-                    {entete.strWeb:text}, {entete.strLangueIdIso639:text}, {entete.strCompteComptable:text}, {entete.strDeviseIdIso4217:text}, {entete.bytReglementId},
-                    {entete.strReglement:text}, {entete.bytClasseTarifId}, {entete.bytClasseRemiseTiersId}, {entete.bytNbExFacture}, {entete.strStatTiers:text},
-                    {entete.bytSituationIdPrincipale}, {entete.bytSituationIdSecondaire}, {entete.memObsEntete:text}, {entete.memObsInterne:text},
-                    {entete.bytTypeDocumentId}, {entete.strStatDocEntete:text}, {entete.dblCoursDevise}, {entete.dtmCoursDevise:date},
-                    {entete.strUserIdCreation:text}, {entete.strUserIdLastMod:text})
-            """, entete=entete)
-    facture_db.execute(sql)
-
-    for ligne in lignes:
+def main():
+
+    # Connexion à Analytique
+    analytique_db = AnalytiqueDb(autocommit=False)
+
+    # Connexion à Controles
+    facture_db = FacturesDb(autocommit=False)
+
+    # Connexion à CommunDb
+    commun_db = CommunDb(autocommit=False)
+
+    Sql = SqlFormatter()
+
+    current = "{:%m/%Y}".format(datetime.now())
+
+    mois_facturation = input("Veuillez renseigner le mois de facturation [defaut: {}] ('q' pour quitter): ".format(current))  # Format: voir avec jacky
+    if mois_facturation == 'q':
+        sys.exit(1)
+
+    sql = """SELECT * FROM tbl_Tarification
+            WHERE strStatut='A facturer'
+            ORDER BY DblAffaireId, DblTarifId DESC;
+            """
+
+    # Liste les interventions à facturer par affaire
+    a_facturer = {}
+    for interv in analytique_db.read(sql):
+        if not interv.DblAffaireId in a_facturer.keys():
+            a_facturer[interv.DblAffaireId] = []
+        a_facturer[interv.DblAffaireId].append(interv)
+
+    if not a_facturer:
+        logger.info("Aucune facture à créer - Opération annulée")
+        sys.exit(1)
+
+    # Pour chaque facture, on va ajouter une ligne dans tblPieceEntete, et une ligne par intervention dans tblPieceLigne
+    # > NB: On ne touche pas aux interventions de cette affaire qui ont déja été facturées
+
+    for affaireId, interventions in a_facturer.items():
+
+        piece_id = facture_db.first("SELECT Max(lngpieceId) As Id FROM tblPieceEntete").Id + 1
+        if str(piece_id)[:2] != mois_facturation[-2:]:
+            # On démarre une nouvelle année
+            piece_id = 10000 * int(mois_facturation[:2]) + 1
+
+        affaire = analytique_db.first("SELECT * FROM tbl_Affaires WHERE dblAffaireId={}".format(affaireId))
+        if not affaire:
+            logger.error("Affaire {} - L'affaire n'existe pas dans tbl_Affaires".format(affaireId))
+            continue
+
+        if not affaire.strMOId:
+            logger.error("Affaire {} - Destinataire manquant dans tbl_Affaires".format(affaireId))
+            continue
+
+        destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId={}".format(affaire.strMOId))
+        if not destinataire:
+            logger.warning("Affaire {} - Destinataire introuvable dans tblTiers ('{}'), remplacé par tiers n°190".format(affaireId, affaire.strMOId))
+            destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId=190")
+
+        entete = EnTete()
+        entete.lngPieceId = piece_id
+
+        # on recupere les champs de tblTiers depuis le champ lngASTRE (champ n°3) jusqu'au champ strCodeProduit (champ n°26)
+        # ATTENTION: le champ 'strPaysIdIso3166-A2' n'etant pas un nom de propriété valide, il est renomme en strPaysIdIso3166 dans le modèle.
+        entete.lngTiersId = destinataire.lngTiersId
+        entete.lngASTRE = destinataire.lngASTRE
+        entete.strCodeProduit = destinataire.strCodeProduit
+        entete.bytTitreId = destinataire.bytTitreId
+        entete.strCodeAdresse = destinataire.strCodeAdresse
+        entete.strAdresse1 = destinataire.strAdresse1
+        entete.strAdresse2 = destinataire.strAdresse2
+        entete.strAdresse3 = destinataire.strAdresse3
+        entete.strAdresse4 = destinataire.strAdresse4
+        entete.strAdresse5 = destinataire.strAdresse5
+        entete.strPaysIdIso3166 = destinataire.field_11
+        entete.bytTVATiersId = destinataire.bytTVATiersId
+        entete.strTelephone = destinataire.strTelephone
+        entete.strTelecopie = destinataire.strTelecopie
+        entete.strPortable = destinataire.strPortable
+        entete.strEMail = destinataire.strEMail
+        entete.strWeb = destinataire.strWeb
+        entete.strLangueIdIso639 = destinataire.strLangueIdIso639
+        entete.strCompteComptable = destinataire.strCompteComptable
+        entete.strDeviseIdIso4217 = destinataire.strDeviseIdIso4217
+        entete.bytReglementId = destinataire.bytReglementId
+        entete.bytClasseTarifId = destinataire.bytClasseTarifId
+        entete.bytNbExFacture = destinataire.bytNbExFacture
+        entete.bytClasseRemiseTiersId = destinataire.bytClasseRemiseTiersId
+        entete.bytNbExFacture = destinataire.bytNbExFacture
+        entete.strStatTiers = destinataire.strStatTiers
+
+        # Valeurs fixes
+        entete.lngDocId = 0
+        entete.strLangueIdIso639 = "fr"
+        entete.strDeviseIdIso4217 = "EUR"
+        entete.bytReglementId = 1
+        entete.bytClasseTarifId = 1
+        entete.bytClasseRemiseTiersId = 1
+        entete.bytNbExFacture = 1
+        entete.strStatTiers = ""
+        entete.bytSituationIdPrincipale = 2
+        entete.bytSituationIdSecondaire = 41
+        entete.bytTypeDocumentId = 50
+        entete.strStatDocEntete = "PI00"
+        entete.dblCoursDevise = 1
+        entete.dtmCoursDevise = datetime(2002, 1, 1)
+        entete.bytTypeTarif = 1
+        entete.strReglement = "POUR ACQUITTER CET ETAT DE REDEVANCE,\r\n ATTENDEZ IMPERATIVEMENT DE RECEVOIR LE TITRE DE PERCEPTION EMANANT DE LA PAIERIE DEPARTEMENTALE"
+
+        # Commentaires
+        entete.memObsEntete = "[Mois : {mois_facturation}]\r\n" \
+                              "[Lieu de travail : ]{strLieux}\r\n" \
+                              "[V/Cde : ] {Ref} du {dtmCommande}\r\n" \
+                              "".format(mois_facturation=mois_facturation,
+                                        strLieux=affaire.strLieux,
+                                        Ref=affaire.Ref,
+                                        dtmCommande="{:%d/%m/%Y}".format(affaire.dtmCommande) if affaire.dtmCommande else "",
+                                        )
+
+        entete.memObsInterne = "N° Affaire analytique : {}".format(affaire.strLiaisonControle)
+
+        entete.strStatDocEntete = "PC00" if ("/" in affaire.strLiaisonControle or affaire.strLiaisonControle == "RDBOEC") else "PT00"
+
+        entete.strUserIdCreation = "script"
+        entete.strUserIdLastMod = "script"
+
+        i = 0
+        lignes = []
+        for interv in interventions:
+            i += 1
+
+            ligne = Ligne()
+            ligne.lngPieceId = piece_id
+            ligne.intLigneId = i
+            ligne.strArticleId = interv.strArticleId
+
+            article = commun_db.first("SELECT * FROM tblArticle WHERE strArticleId='{}'".format(interv.strArticleId))
+            ligne.strArticle = article.strArticle if article else ""
+            ligne.bytTVAArticleId = article.bytTVAArticleId if article else 0
+
+            tva = commun_db.first("SELECT bytTVAId FROM tblTVA WHERE bytTVATiersId={} AND bytTVAArticleId={}".format(destinataire.bytTVATiersId, ligne.bytTVAArticleId))
+            ligne.bytTVAId = tva.bytTVAId
+
+            taux = commun_db.first("SELECT dblTVATaux FROM tblTVATaux WHERE bytTVAId={}".format(ligne.bytTVAId))
+            ligne.dblTVATaux = taux.dblTVATaux
+
+            ligne.dblQte = interv.dblQuantite
+
+            if interv.strUnite:
+                unite = commun_db.first("SELECT * FROM tblUnite WHERE strUniteCourt='{}'".format(interv.strUnite))
+                ligne.bytUniteIdQuantite = unite.bytUniteId
+                ligne.bytUniteIdPrix = unite.bytUniteId
+            else:
+                ligne.bytUniteIdQuantite = 0
+                ligne.bytUniteIdPrix = 0
+            ligne.dblPUhtBrutDev = interv.dblPrixUnitaire
+            ligne.dblPUhtNetDev = interv.dblPrixUnitaire
+            ligne.dblPThtNetDev = interv.dblPrixTotal
+            ligne.blnPrestation = True
+            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
+            ligne.bytClasseRemiseArticleId = 1
+            ligne.dblPUSaisie = interv.dblPrixUnitaire
+            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
+        montant_ht = float(sum([ligne.dblPThtNetDev for ligne in lignes]))
+        taux_tva = float(lignes[0].dblTVATaux)
+        montant_tva = (0.01 * taux_tva) * montant_ht
+        montant_ttc = montant_ht + montant_tva
+
+        # Sous-total HT (ligne 32000)
+        ligne_total_1 = Ligne()
+        ligne_total_1.lngPieceId = piece_id
+        ligne_total_1.intLigneId = 32000
+        ligne_total_1.strArticleId = ""
+        ligne_total_1.strArticle = "Total H.T."
+        ligne_total_1.dblQte = 0
+        ligne_total_1.dblPThtNetDev = montant_ht
+        ligne_total_1.dblPTttcNetDev = montant_ttc
+        ligne_total_1.bytLigneSousTotal = 10
+        ligne_total_1.blnLigneVisible = False
+        ligne_total_1.dblPThtNet = montant_ht
+        ligne_total_1.dblPTttcNet = round(montant_ttc, 2)
+        ligne_total_1.dblPUSaisie = round(montant_ht, 2)
+        ligne_total_1.strPUAff = "{:03.02f} EUR".format(montant_ht)
+
+        lignes.append(ligne_total_1)
+
+        # Sous-total TVA (ligne 32001)
+        ligne_total_2 = Ligne()
+        ligne_total_2.lngPieceId = piece_id
+        ligne_total_2.intLigneId = 32001
+        ligne_total_2.strArticleId = ""
+        ligne_total_2.strArticle = """TVA 1 : {taux_tva}% H.T. : {montant_ht:03.02f} T.V.A. :
+                                     {montant_tva:03.02f} T.T.C. : {montant_ttc:03.02f}:""".format(taux_tva=taux_tva,
+                                                                                                   montant_ht=montant_ht,
+                                                                                                   montant_tva=montant_tva,
+                                                                                                   montant_ttc=montant_ttc)
+        ligne_total_2.dblQte = 0
+        ligne_total_2.bytTVAId = lignes[0].bytTVAId
+        ligne_total_2.dblTVATaux = taux_tva
+        ligne_total_2.dblPThtNetDev = montant_ht
+        ligne_total_2.dblPTttcNetDev = montant_ttc
+        ligne_total_2.bytLigneSousTotal = 11
+        ligne_total_2.blnLigneVisible = False
+        ligne_total_2.dblPThtNet = montant_ht
+        ligne_total_2.dblPTttcNet = round(ligne_total_1.dblPTttcNetDev, 2)
+        ligne_total_2.dblPUSaisie = round(montant_tva, 2)
+        ligne_total_2.strPUAff = "{:03.02f} EUR".format(montant_tva)
+
+        lignes.append(ligne_total_2)
+
+        # Sous-total TTC (ligne 32500)
+        ligne_total_3 = Ligne()
+        ligne_total_3.lngPieceId = piece_id
+        ligne_total_3.intLigneId = 32500
+        ligne_total_3.strArticleId = ""
+        ligne_total_3.strArticle = "Total T.T.C."
+        ligne_total_3.dblQte = 0
+        ligne_total_3.dblPThtNetDev = montant_ht
+        ligne_total_3.dblPTttcNetDev = montant_ttc
+        ligne_total_3.bytLigneSousTotal = 12
+        ligne_total_3.blnLigneVisible = False
+        ligne_total_3.dblPThtNet = montant_ht
+        ligne_total_3.dblPTttcNet = round(montant_ttc, 2)
+        ligne_total_3.dblPUSaisie = round(montant_ttc, 2)
+        ligne_total_3.strPUAff = "{:03.02f} EUR".format(montant_ttc)
+
+        lignes.append(ligne_total_3)
+
+
+
+        logger.info("* Nouvelle facture: {}".format(entete.lngPieceId))
+
+        # Insertion de l'en-tête
         sql = Sql.format("""
-                INSERT INTO tblPieceLigne ( lngPieceId, intLigneId, strArticleId, strArticle, bytTVAArticleId, bytTVAId, dblTVATaux, dblQte, bytUniteIdQuantite,
-                                             bytNbDecQuantite, bytUniteIdPrix, dblCnvUniteCoef, bytNbDecQuantiteConvertie, dblPUhtBrutDev, dblPUttcBrutDev, dblPUhtNetDev,
-                                             dblPUttcNetDev, bytNbDecPU, dblPThtBrutDev, dblPTttcBrutDev, dblPThtNetDev, dblPTttcNetDev, strStatArticle, strStatDocLigne,
-                                             dblTauxRemise1, dblTauxRemise2, dblTauxRemise3, blnPrestation, strCompteComptable, memObs, memObsInterne, bytLigneSousTotal,
-                                             intLigneIdRattachement, blnLigneVisible, blnLigneGeneree, bytGenerateurId, dblPUhtBrut, dblPUttcBrut, dblPUhtNet, dblPUttcNet,
-                                             dblPThtBrut, dblPTttcBrut, dblPThtNet, dblPTttcNet, bytClasseRemiseArticleId, dblPUSaisie, strPUAff, strQteAff )
-                VALUES ({ligne.lngPieceId}, {ligne.intLigneId}, {ligne.strArticleId:text}, {ligne.strArticle:text}, {ligne.bytTVAArticleId}, {ligne.bytTVAId}, {ligne.dblTVATaux},
-                        {ligne.dblQte}, {ligne.bytUniteIdQuantite}, {ligne.bytNbDecQuantite}, {ligne.bytUniteIdPrix}, {ligne.dblCnvUniteCoef}, {ligne.bytNbDecQuantiteConvertie},
-                        {ligne.dblPUhtBrutDev}, {ligne.dblPUttcBrutDev}, {ligne.dblPUhtNetDev}, {ligne.dblPUttcNetDev}, {ligne.bytNbDecPU}, {ligne.dblPThtBrutDev},
-                        {ligne.dblPTttcBrutDev}, {ligne.dblPThtNetDev}, {ligne.dblPTttcNetDev}, {ligne.strStatArticle:text}, {ligne.strStatDocLigne:text},
-                        {ligne.dblTauxRemise1}, {ligne.dblTauxRemise2}, {ligne.dblTauxRemise3}, {ligne.blnPrestation}, {ligne.strCompteComptable:text},
-                        {ligne.memObs:text}, {ligne.memObsInterne:text}, {ligne.bytLigneSousTotal}, {ligne.intLigneIdRattachement}, {ligne.blnLigneVisible},
-                        {ligne.blnLigneGeneree}, {ligne.bytGenerateurId}, {ligne.dblPUhtBrut}, {ligne.dblPUttcBrut}, {ligne.dblPUhtNet}, {ligne.dblPUttcNet},
-                        {ligne.dblPThtBrut}, {ligne.dblPTttcBrut}, {ligne.dblPThtNet}, {ligne.dblPTttcNet}, {ligne.bytClasseRemiseArticleId}, {ligne.dblPUSaisie},
-                        {ligne.strPUAff:text}, {ligne.strQteAff:text})
-                """, ligne=ligne)
+                INSERT INTO tblPieceEntete ( lngPieceId, lngDocId, lngTiersId, lngASTRE, strCodeProduit, bytTitreId, strCodeAdresse, strAdresse1, strAdresse2,
+                                            strAdresse3, strAdresse4, strAdresse5, [strPaysIdIso3166-A2], bytTVATiersId, strTelephone, strTelecopie, strPortable,
+                                            strEMail, strWeb, strLangueIdIso639, strCompteComptable, strDeviseIdIso4217, bytReglementId, strReglement,
+                                            bytClasseTarifId, bytClasseRemiseTiersId, bytNbExFacture, strStatTiers, bytSituationIdPrincipale, bytSituationIdSecondaire,
+                                            memObsEntete, memObsInterne, bytTypeDocumentId, strStatDocEntete, dblCoursDevise, dtmCoursDevise,
+                                            strUserIdCreation, strUserIdLastMod )
+                VALUES ({entete.lngPieceId}, {entete.lngDocId}, {entete.lngTiersId}, {entete.lngASTRE}, {entete.strCodeProduit:text}, {entete.bytTitreId},
+                        {entete.strCodeAdresse:text}, {entete.strAdresse1:text}, {entete.strAdresse2:text}, {entete.strAdresse3:text}, {entete.strAdresse4:text}, {entete.strAdresse5:text},
+                        {entete.strPaysIdIso3166:text}, {entete.bytTVATiersId}, {entete.strTelephone:text}, {entete.strTelecopie:text}, {entete.strPortable:text}, {entete.strEMail:text},
+                        {entete.strWeb:text}, {entete.strLangueIdIso639:text}, {entete.strCompteComptable:text}, {entete.strDeviseIdIso4217:text}, {entete.bytReglementId},
+                        {entete.strReglement:text}, {entete.bytClasseTarifId}, {entete.bytClasseRemiseTiersId}, {entete.bytNbExFacture}, {entete.strStatTiers:text},
+                        {entete.bytSituationIdPrincipale}, {entete.bytSituationIdSecondaire}, {entete.memObsEntete:text}, {entete.memObsInterne:text},
+                        {entete.bytTypeDocumentId}, {entete.strStatDocEntete:text}, {entete.dblCoursDevise}, {entete.dtmCoursDevise:date},
+                        {entete.strUserIdCreation:text}, {entete.strUserIdLastMod:text})
+                """, entete=entete)
         facture_db.execute(sql)
 
-    facture_db.commit()
-    analytique_db.commit()
-
-
+        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,
+                                                 dblPUttcNetDev, bytNbDecPU, dblPThtBrutDev, dblPTttcBrutDev, dblPThtNetDev, dblPTttcNetDev, strStatArticle, strStatDocLigne,
+                                                 dblTauxRemise1, dblTauxRemise2, dblTauxRemise3, blnPrestation, strCompteComptable, memObs, memObsInterne, bytLigneSousTotal,
+                                                 intLigneIdRattachement, blnLigneVisible, blnLigneGeneree, bytGenerateurId, dblPUhtBrut, dblPUttcBrut, dblPUhtNet, dblPUttcNet,
+                                                 dblPThtBrut, dblPTttcBrut, dblPThtNet, dblPTttcNet, bytClasseRemiseArticleId, dblPUSaisie, strPUAff, strQteAff )
+                    VALUES ({ligne.lngPieceId}, {ligne.intLigneId}, {ligne.strArticleId:text}, {ligne.strArticle:text}, {ligne.bytTVAArticleId}, {ligne.bytTVAId}, {ligne.dblTVATaux},
+                            {ligne.dblQte}, {ligne.bytUniteIdQuantite}, {ligne.bytNbDecQuantite}, {ligne.bytUniteIdPrix}, {ligne.dblCnvUniteCoef}, {ligne.bytNbDecQuantiteConvertie},
+                            {ligne.dblPUhtBrutDev}, {ligne.dblPUttcBrutDev}, {ligne.dblPUhtNetDev}, {ligne.dblPUttcNetDev}, {ligne.bytNbDecPU}, {ligne.dblPThtBrutDev},
+                            {ligne.dblPTttcBrutDev}, {ligne.dblPThtNetDev}, {ligne.dblPTttcNetDev}, {ligne.strStatArticle:text}, {ligne.strStatDocLigne:text},
+                            {ligne.dblTauxRemise1}, {ligne.dblTauxRemise2}, {ligne.dblTauxRemise3}, {ligne.blnPrestation}, {ligne.strCompteComptable:text},
+                            {ligne.memObs:text}, {ligne.memObsInterne:text}, {ligne.bytLigneSousTotal}, {ligne.intLigneIdRattachement}, {ligne.blnLigneVisible},
+                            {ligne.blnLigneGeneree}, {ligne.bytGenerateurId}, {ligne.dblPUhtBrut}, {ligne.dblPUttcBrut}, {ligne.dblPUhtNet}, {ligne.dblPUttcNet},
+                            {ligne.dblPThtBrut}, {ligne.dblPTttcBrut}, {ligne.dblPThtNet}, {ligne.dblPTttcNet}, {ligne.bytClasseRemiseArticleId}, {ligne.dblPUSaisie},
+                            {ligne.strPUAff:text}, {ligne.strQteAff:text})
+                    """, ligne=ligne)
+            facture_db.execute(sql)
+
+        facture_db.commit()
+        analytique_db.commit()
+
+if __name__ == "__main__":
+    main()
+
+    logger.info("-- Fin --")
 

+ 580 - 573
ctrl2analytique.py

@@ -36,627 +36,634 @@ logconf.start("ctrl2analytique", logging.DEBUG)
 
 ##-----------------------------------------------
 
+def main():
+    # #########    INITIALISATION    ##########
+    logger.info("Initialisation...")
 
-# #########    INITIALISATION    ##########
-logger.info("Initialisation...")
+    Sql = SqlFormatter()
 
-Sql = SqlFormatter()
+    no_prompt = ("-n" in sys.argv)
+    if no_prompt:
+        logger.info("> Lancé en mode automatique (sans interruption)")
 
-no_prompt = ("-n" in sys.argv)
-if no_prompt:
-    logger.info("> Lancé en mode automatique (sans interruption)")
+    # Connexion à Analytique
+    analytique_db = AnalytiqueDb(autocommit=False)
 
-# Connexion à Analytique
-analytique_db = AnalytiqueDb(autocommit=False)
+    # Connexion à Controles
+    controles_db = ControlesDb(autocommit=False)
 
-# Connexion à Controles
-controles_db = ControlesDb(autocommit=False)
+    # Connexion à CommunDb
+    commun_db = CommunDb(autocommit=False)
 
-# Connexion à CommunDb
-commun_db = CommunDb(autocommit=False)
+    # Créé le répertoire de travail
+    workdir = mk_workdir("ctrl2analytique")
+    affaires_file = workdir / "affaires.csv"
+    intervs_file = workdir / "intervs.csv"
 
-# Créé le répertoire de travail
-workdir = mk_workdir("ctrl2analytique")
-affaires_file = workdir / "affaires.csv"
-intervs_file = workdir / "intervs.csv"
+    # > Supprime les fichiers d'import s'il existent
+    for file in (affaires_file, intervs_file):
+        if file.exists():
+            logger.debug("Supprime le fichier %s", file)
+            file.remove()
 
-# > Supprime les fichiers d'import s'il existent
-for file in (affaires_file, intervs_file):
-    if file.exists():
-        logger.debug("Supprime le fichier %s", file)
-        file.remove()
+    sqlHelper = AccessSqlHelper
 
-sqlHelper = AccessSqlHelper
+    # date zéro pour Access
+    date_zero = datetime(1899, 12, 30, 0, 0, 0)
 
-# 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é
+        'ZC': Chantier de contrôle du compactage
+        'ZI': Chantier d'inspection vidéo
+        'ZZ': Chantier mixte.
+        '': Inconnu
+        """
+        sql = """SELECT lngChantierId, 'ZP' as type FROM tblEtancheiteBases WHERE [lngChantierId] = {chantier} AND [bytCommandeId] = {commande}
+                UNION
+                SELECT lngChantierId, 'ZC' as type FROM tblCompactageBases WHERE [lngChantierId] = {chantier}
+                UNION
+                SELECT lngChantierId, 'ZI' as type FROM tblVideoBases WHERE [lngChantierId] = {chantier};
+                """.format(chantier=lngChantierId,
+                           commande=bytCommandeId)
+        res = controles_db.read_all(sql)
+
+        if len(res) == 0:
+            return ""
+        elif len(res) == 1:
+            return res[0].type
+        else:
+            return "ZZ"
 
-def get_type_id(lngChantierId, bytCommandeId):
-    """ Recupère le type de chantier.
-    'ZP': Chantier de contrôle d'étanchéité
-    'ZC': Chantier de contrôle du compactage
-    'ZI': Chantier d'inspection vidéo
-    'ZZ': Chantier mixte.
-    '': Inconnu
-    """
-    sql = """SELECT lngChantierId, 'ZP' as type FROM tblEtancheiteBases WHERE [lngChantierId] = {chantier} AND [bytCommandeId] = {commande}
-            UNION
-            SELECT lngChantierId, 'ZC' as type FROM tblCompactageBases WHERE [lngChantierId] = {chantier}
-            UNION
-            SELECT lngChantierId, 'ZI' as type FROM tblVideoBases WHERE [lngChantierId] = {chantier};
-            """.format(chantier=lngChantierId,
-                       commande=bytCommandeId)
-    res = controles_db.read_all(sql)
+    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(Sql.format("SELECT [COEFFG] FROM tbl_COEFFG WHERE [ANNEE] = {}", annee)).COEFFG / 100
 
-    if len(res) == 0:
-        return ""
-    elif len(res) == 1:
-        return res[0].type
-    else:
-        return "ZZ"
 
-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(Sql.format("SELECT [COEFFG] FROM tbl_COEFFG WHERE [ANNEE] = {}", annee)).COEFFG / 100
 
 
+    # ##########   IMPORT DES AFFAIRES    ##########
 
+    # Parcourt les chantiers de contrôle pour lesquels aucune affaire n'a été créée, et les ajoute au fichier affaire.csv
 
-# ##########   IMPORT DES AFFAIRES    ##########
+    compteur = 0
+    sql = """ SELECT tblCommandes.lngChantierId, tblCommandes.bytCommandeId, tblChantiers.strSubdivisionId, tblChantiers.strCollectiviteId as ChantierCollectiviteId, tblChantiers.strLocChantier,
+              tblChantiers.strEntrepriseId, tblCommandes.strCollectiviteId as CommandeCollectiviteId, tblCommandes.dtmCommande, tblCommandes.strRefCommande, tblCommandes.blnMarche, tblCommandes.dblMtMarche, tblCommandes.strdevis
+              FROM tblChantiers INNER JOIN tblCommandes ON tblChantiers.lngChantierId = tblCommandes.lngChantierId
+              WHERE (((tblCommandes.sngAffaireIdMos) Is Null Or (tblCommandes.sngAffaireIdMos)=0))
+              """
 
-# Parcourt les chantiers de contrôle pour lesquels aucune affaire n'a été créée, et les ajoute au fichier affaire.csv
+    for data in controles_db.read(sql):
 
-compteur = 0
-sql = """ SELECT tblCommandes.lngChantierId, tblCommandes.bytCommandeId, tblChantiers.strSubdivisionId, tblChantiers.strCollectiviteId as ChantierCollectiviteId, tblChantiers.strLocChantier,
-          tblChantiers.strEntrepriseId, tblCommandes.strCollectiviteId as CommandeCollectiviteId, tblCommandes.dtmCommande, tblCommandes.strRefCommande, tblCommandes.blnMarche, tblCommandes.dblMtMarche, tblCommandes.strdevis
-          FROM tblChantiers INNER JOIN tblCommandes ON tblChantiers.lngChantierId = tblCommandes.lngChantierId
-          WHERE (((tblCommandes.sngAffaireIdMos) Is Null Or (tblCommandes.sngAffaireIdMos)=0))
-          """
+        # Création de l'affaire
+        affaire = Affaire()
+        affaire.strLiaisonControle = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
+        affaire.strMOeId = data.strSubdivisionId
+        affaire.strCommneId = data.ChantierCollectiviteId
+        affaire.strLieux = data.strLocChantier
+        affaire.strEntrepriseId = data.strEntrepriseId
+        affaire.strMOId = data.CommandeCollectiviteId
+        affaire.dtmCommande = data.dtmCommande
+        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)
+        affaire.intCoefFG = get_coeff_k(data.lngChantierId)
+        affaire.strSituation = "En cours"
 
-for data in controles_db.read(sql):
-
-    # Création de l'affaire
-    affaire = Affaire()
-    affaire.strLiaisonControle = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
-    affaire.strMOeId = data.strSubdivisionId
-    affaire.strCommneId = data.ChantierCollectiviteId
-    affaire.strLieux = data.strLocChantier
-    affaire.strEntrepriseId = data.strEntrepriseId
-    affaire.strMOId = data.CommandeCollectiviteId
-    affaire.dtmCommande = data.dtmCommande
-    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)
-    affaire.intCoefFG = get_coeff_k(data.lngChantierId)
-    affaire.strSituation = "En cours"
-
-    # pour garder le lien avec la donnée d'origine:
-    affaire.lngChantierId = data.lngChantierId
-    affaire.bytCommandeId = data.bytCommandeId
-
-    # Créé la ligne dans le fichier affaires.csv
-    affaire.dump_to_csv(affaires_file)
-
-    compteur += 1
-
-logger.info("> {} affaires ajoutées au fichier".format(compteur))
-
-
-
-
-# ########## IMPORT DES INTERVENTIONS DE COMPACTAGE ##########
-
-# Importe les interventions de contrôle du compactage dans le fichier intervs.csv
-
-def engin_existe(strEnginId):
-    """ retourne True si le code de l'engin existe dans la table tbl_Engin """
-    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 """
-    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
-
-sql = """SELECT tblCompactageIntervs.lngChantierId, tblCompactageIntervs.bytCommandeId, tblCompactageIntervs.bytIntervId, tblCompactageIntervs.strEquipeId,
-            tblCompactageEngins.strEnginId, tblCompactageIntervs.lngRapportId, tblCompactageBases.memTravaux, tblCompactageResultats.dtmEssai, tblCompactageResultats.dtmDuree,
-            tblCompactagePartChantiers.strTrcRegard, tblMateriaux.strMatériau AS str_materiau_remblai, tblMateriaux_1.strMatériau AS str_materiau_enrobage,
-            tblMateriaux_2.strMatériau AS str_materiau_lit, tblCompactageResultats.bytPartChantierId, tblCompactageIntervs.sngIntervIdMos
-        FROM ((tblMateriaux RIGHT JOIN ((((tblCompactageIntervs LEFT JOIN tblCompactageEngins ON tblCompactageIntervs.strEquipeId = tblCompactageEngins.strEquipeId)
-            INNER JOIN tblCompactageResultats ON (tblCompactageIntervs.lngChantierId = tblCompactageResultats.lngChantierId) AND
-            (tblCompactageIntervs.bytIntervId = tblCompactageResultats.bytIntervId)) INNER JOIN tblCompactagePartChantiers ON
-            (tblCompactageResultats.lngChantierId = tblCompactagePartChantiers.lngChantierId) AND
-            (tblCompactageResultats.bytPartChantierId = tblCompactagePartChantiers.bytPartChantierId))
-            INNER JOIN tblCompactageBases ON tblCompactageIntervs.lngChantierId = tblCompactageBases.lngChantierId)
-            ON tblMateriaux.strMateriauId = tblCompactagePartChantiers.strMateriauRemblaiId) LEFT JOIN tblMateriaux AS tblMateriaux_1
-            ON tblCompactagePartChantiers.strMateriauEnrobageId = tblMateriaux_1.strMateriauId) LEFT JOIN tblMateriaux AS tblMateriaux_2
-            ON tblCompactagePartChantiers.strMateriauLitId = tblMateriaux_2.strMateriauId
-        WHERE (((tblCompactageIntervs.sngIntervIdMos)=0 Or (tblCompactageIntervs.sngIntervIdMos) Is Null))
-      """
-
-def get_type_compactage_interv(observation):
-    """ retourne le sous-type d'intervention à partir du commentaire associé """
-    if "ASSAINISEMENT" or "ASSAINISEMENT" in observation:
-        return "CC3"
-    elif "CABLE" in observation:
-        return "CC1"
-    elif "A.E.P" in observation:
-        return "CC2"
-    elif "GAZ" in observation:
-        return "CC4"
-    else:
-        return "CC3"
-
-for data in controles_db.read(sql):
-    interv = Interv()
-
-    interv.strEquipeId = "C{}".format(data.strEquipeId)
-    interv.strEnginId = data.strEnginId
-    interv.strRapportId = data.lngRapportId
-    interv.strTypeInterventionId = get_type_compactage_interv(data.memTravaux)
-    interv.strCatégorieInterventionId = "CC"
-    interv.dblquantite = 1.0
-    interv.strunite = "u"
-    interv.dtmIntervention = data.dtmEssai
-    interv.dtmDureeIntervention = data.dtmDuree
-    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)
-    interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
-    interv.strgrandeur1 = data.str_materiau_remblai
-    interv.strgrandeur2 = data.str_materiau_lit
-    interv.strgrandeur3 = data.str_materiau_enrobage
-    interv.strcaracteristique1 = "Matériau remblai"
-    interv.strcaracteristique2 = "Matériau lit de pose"
-    interv.strcaracteristique3 = "Matériau enrobage"
-    interv.strunite1 = ""
-    interv.strunite2 = ""
-    interv.strunite3 = ""
-    interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
-    interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
-    interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
-
-    # pour garder le lien avec la donnée d'origine:
-    interv.lngChantierId = data.lngChantierId
-    interv.bytCommandeId = data.bytCommandeId
-    interv.bytIntervId = data.bytIntervId
-
-    # Créé la ligne dans le fichier intervs.csv
-    interv.dump_to_csv(intervs_file)
-
-    compteur += 1
-
-logger.info("> {} interventions Compactage ajoutées au fichier".format(compteur))
-
-
-
-# ########## IMPORT DES INTERVENTIONS D'ETANCHEITE ##########
-
-# Importe les interventions de contrôle d'étanchéité dans le fichier intervs.csv
-
-compteur = 0
-
-sql = """SELECT tblEtancheiteIntervs.lngChantierId, tblEtancheiteIntervs.bytCommandeId, tblEtancheiteIntervs.bytIntervId, tblEtancheiteIntervs.strEquipeId,
-            tblEtancheiteIntervs.lngRapportId, tblEtancheitePartChantiers.bytTypeEssai, tblMateriaux.strMateriauId, tblMateriaux.strMatériau,
-            tblEtancheitePartChantiers.intDiametre, tblEtancheitePartChantiers.sngLgHt, tblEtancheitePartChantiers.intNbJoint, tblEtancheiteResultats.dtmDuree,
-            tblEtancheiteResultats.dtmEssai, tblEtancheitePartChantiers.strTrcRegard, tblEtancheiteResultats.bytPartChantierId
-        FROM ((tblEtancheiteIntervs INNER JOIN tblEtancheiteResultats ON (tblEtancheiteIntervs.lngChantierId = tblEtancheiteResultats.lngChantierId)
-            AND (tblEtancheiteIntervs.bytIntervId = tblEtancheiteResultats.bytIntervId)) INNER JOIN tblEtancheitePartChantiers
-            ON (tblEtancheiteResultats.lngChantierId = tblEtancheitePartChantiers.lngChantierId)
-            AND (tblEtancheiteResultats.bytPartChantierId = tblEtancheitePartChantiers.bytPartChantierId)) INNER JOIN tblMateriaux
-            ON tblEtancheitePartChantiers.strMateriauId = tblMateriaux.strMateriauId
-        WHERE (((tblEtancheiteIntervs.sngIntervIdMos)=0 Or (tblEtancheiteIntervs.sngIntervIdMos) Is Null));
-      """
-
-def get_engin_etancheite(equipe, diametre, materiau, type_essai):
-    """ retourne l'engin correspondant à l'essai en fonction eds caractéristiques de l'essai """
-    sql = """SELECT strEnginId FROM tblEtancheiteEngins
-            WHERE ([strEquipeId] = '{}') AND ([intDiametre] = {}) AND ([strMateriauId] = '{}') AND ([bytTypeEssaiId] ={})
-            """.format(equipe, diametre, materiau, type_essai)
-    row = controles_db.first(sql)
-    return row.strEnginId if row else ""
-
-for data in controles_db.read(sql):
-    interv = Interv()
-
-    interv.strEquipeId = "C{}".format(data.strEquipeId)
-    interv.strEnginId = get_engin_etancheite(data.strEquipeId, data.intDiametre, data.strMateriauId, data.bytTypeEssai)
-    interv.strRapportId = data.lngRapportId
-    interv.strTypeInterventionId = "CE{}".format(data.bytTypeEssai)
-    interv.strCatégorieInterventionId = "CE"
-    interv.dblquantite = float(data.intNbJoint)
-    interv.strunite = "u"
-    interv.dtmIntervention = data.dtmEssai
-    interv.dtmDureeIntervention = data.dtmDuree
-    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)
-    interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
-    interv.strgrandeur1 = data.strMatériau
-    interv.strgrandeur2 = data.intDiametre
-    interv.strgrandeur3 = data.sngLgHt
-    interv.strcaracteristique1 = "Matériau"
-    interv.strcaracteristique2 = "Diamètre"
-    interv.strcaracteristique3 = "Longueur"
-    interv.strunite1 = ""
-    interv.strunite2 = "mm"
-    interv.strunite3 = "m"
-    interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
-    interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
-    interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
-
-    # pour garder le lien avec la donnée d'origine:
-    interv.lngChantierId = data.lngChantierId
-    interv.bytCommandeId = data.bytCommandeId
-    interv.bytIntervId = data.bytIntervId
-
-    # Créé la ligne dans le fichier intervs.csv
-    interv.dump_to_csv(intervs_file)
-
-    compteur += 1
-
-logger.info("> {} interventions Etanchéité ajoutées au fichier".format(compteur))
-
-
-
-# ########## IMPORT DES INTERVENTIONS D'INSPECTION VIDEO ##########
-
-
-# Importe les interventions d'inspection vidéo dans le fichier intervs.csv
-
-compteur = 0
-
-sql = """SELECT tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
-        tblVideoEngins.strEnginId, tblVideoIntervs.lngRapportId, First(tblso_rate_Analyse.MateriauCourt) AS strmateriau, tblVideoIntervs.lngTroncon,
-        tblVideoIntervs.sngNbJourFact, First(tblso_rate_Analyse.MaxDeDiametre) AS diam, tblVideoIntervs.dtmDuree, tblVideoIntervs.dtmIntervDu,
-        First(tblVideoIntervs.memObservation) AS memObservation, tblChantiers.strEntrepriseId
-        FROM ((tblVideoEngins RIGHT JOIN tblVideoIntervs ON tblVideoEngins.strEquipeId = tblVideoIntervs.strEquipeId) INNER JOIN tblso_rate_Analyse ON
-        (tblVideoIntervs.lngChantierId = tblso_rate_Analyse.lngChantierId) AND (tblVideoIntervs.bytIntervId = tblso_rate_Analyse.bytIntervId)) INNER JOIN
-        tblChantiers ON tblVideoIntervs.lngChantierId = tblChantiers.lngChantierId
-        WHERE (((tblVideoIntervs.sngIntervIdMos) Is Null Or (tblVideoIntervs.sngIntervIdMos)=0))
-        GROUP BY tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
-        tblVideoIntervs.lngRapportId, tblVideoIntervs.lngTroncon, tblVideoIntervs.sngNbJourFact, tblVideoIntervs.dtmDuree,
-        tblVideoIntervs.dtmIntervDu, tblVideoEngins.strEnginId, tblChantiers.strEntrepriseId
-        """
+        # pour garder le lien avec la donnée d'origine:
+        affaire.lngChantierId = data.lngChantierId
+        affaire.bytCommandeId = data.bytCommandeId
 
-for data in controles_db.read(sql):
-    interv = Interv()
+        # Créé la ligne dans le fichier affaires.csv
+        affaire.dump_to_csv(affaires_file)
 
-    interv.strEquipeId = "C{}".format(data.strEquipeId)
-    interv.strEnginId = data.strEnginId
-    interv.strRapportId = data.lngRapportId
-    interv.strTypeInterventionId = "CI1" if data.strEntrepriseId != 195 else "CI2"
-    interv.strCatégorieInterventionId = "CI"
-    interv.dblquantite = float(data.sngNbJourFact)
-    interv.strunite = "j"
-    interv.dtmIntervention = data.dtmIntervDu
-    interv.dtmDureeIntervention = data.dtmDuree
-    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)
-    interv.remarques = data.memObservation if data.memObservation else "-"
-    interv.strgrandeur1 = data.strmateriau
-    interv.strgrandeur2 = data.diam
-    interv.strgrandeur3 = data.lngTroncon
-    interv.strcaracteristique1 = "Matériau"
-    interv.strcaracteristique2 = "Diamètre"
-    interv.strcaracteristique3 = "Longueur inspectée"
-    interv.strunite1 = ""
-    interv.strunite2 = "mm"
-    interv.strunite3 = "m"
-    interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
-    interv.strTest = "{}/{}/{}/1".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
-    interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
+        compteur += 1
 
-    # pour garder le lien avec la donnée d'origine:
-    interv.lngChantierId = data.lngChantierId
-    interv.bytCommandeId = data.bytCommandeId
-    interv.bytIntervId = data.bytIntervId
+    logger.info("> {} affaires ajoutées au fichier".format(compteur))
 
-    # Créé la ligne dans le fichier intervs.csv
-    interv.dump_to_csv(intervs_file)
 
-    compteur += 1
 
-logger.info("> {} interventions ITV ajoutées au fichier".format(compteur))
 
+    # ########## IMPORT DES INTERVENTIONS DE COMPACTAGE ##########
 
-logging.info("Les données à importer ont été ajoutées aux fichiers '{}' et '{}'".format(affaires_file, intervs_file))
-logging.info("Ces fichiers sont au format CSV (séparateur: tabulation)")
+    # Importe les interventions de contrôle du compactage dans le fichier intervs.csv
 
+    def engin_existe(strEnginId):
+        """ retourne True si le code de l'engin existe dans la table tbl_Engin """
+        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 """
+        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
 
+    sql = """SELECT tblCompactageIntervs.lngChantierId, tblCompactageIntervs.bytCommandeId, tblCompactageIntervs.bytIntervId, tblCompactageIntervs.strEquipeId,
+                tblCompactageEngins.strEnginId, tblCompactageIntervs.lngRapportId, tblCompactageBases.memTravaux, tblCompactageResultats.dtmEssai, tblCompactageResultats.dtmDuree,
+                tblCompactagePartChantiers.strTrcRegard, tblMateriaux.strMatériau AS str_materiau_remblai, tblMateriaux_1.strMatériau AS str_materiau_enrobage,
+                tblMateriaux_2.strMatériau AS str_materiau_lit, tblCompactageResultats.bytPartChantierId, tblCompactageIntervs.sngIntervIdMos
+            FROM ((tblMateriaux RIGHT JOIN ((((tblCompactageIntervs LEFT JOIN tblCompactageEngins ON tblCompactageIntervs.strEquipeId = tblCompactageEngins.strEquipeId)
+                INNER JOIN tblCompactageResultats ON (tblCompactageIntervs.lngChantierId = tblCompactageResultats.lngChantierId) AND
+                (tblCompactageIntervs.bytIntervId = tblCompactageResultats.bytIntervId)) INNER JOIN tblCompactagePartChantiers ON
+                (tblCompactageResultats.lngChantierId = tblCompactagePartChantiers.lngChantierId) AND
+                (tblCompactageResultats.bytPartChantierId = tblCompactagePartChantiers.bytPartChantierId))
+                INNER JOIN tblCompactageBases ON tblCompactageIntervs.lngChantierId = tblCompactageBases.lngChantierId)
+                ON tblMateriaux.strMateriauId = tblCompactagePartChantiers.strMateriauRemblaiId) LEFT JOIN tblMateriaux AS tblMateriaux_1
+                ON tblCompactagePartChantiers.strMateriauEnrobageId = tblMateriaux_1.strMateriauId) LEFT JOIN tblMateriaux AS tblMateriaux_2
+                ON tblCompactagePartChantiers.strMateriauLitId = tblMateriaux_2.strMateriauId
+            WHERE (((tblCompactageIntervs.sngIntervIdMos)=0 Or (tblCompactageIntervs.sngIntervIdMos) Is Null))
+          """
 
-# ########## CONTROLE ET CORRECTION DES DONNEES ##########
+    def get_type_compactage_interv(observation):
+        """ retourne le sous-type d'intervention à partir du commentaire associé """
+        if "ASSAINISEMENT" or "ASSAINISEMENT" in observation:
+            return "CC3"
+        elif "CABLE" in observation:
+            return "CC1"
+        elif "A.E.P" in observation:
+            return "CC2"
+        elif "GAZ" in observation:
+            return "CC4"
+        else:
+            return "CC3"
+
+    for data in controles_db.read(sql):
+        interv = Interv()
+
+        interv.strEquipeId = "C{}".format(data.strEquipeId)
+        interv.strEnginId = data.strEnginId
+        interv.strRapportId = data.lngRapportId
+        interv.strTypeInterventionId = get_type_compactage_interv(data.memTravaux)
+        interv.strCatégorieInterventionId = "CC"
+        interv.dblquantite = 1.0
+        interv.strunite = "u"
+        interv.dtmIntervention = data.dtmEssai
+        interv.dtmDureeIntervention = data.dtmDuree
+        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)
+        interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
+        interv.strgrandeur1 = data.str_materiau_remblai
+        interv.strgrandeur2 = data.str_materiau_lit
+        interv.strgrandeur3 = data.str_materiau_enrobage
+        interv.strcaracteristique1 = "Matériau remblai"
+        interv.strcaracteristique2 = "Matériau lit de pose"
+        interv.strcaracteristique3 = "Matériau enrobage"
+        interv.strunite1 = ""
+        interv.strunite2 = ""
+        interv.strunite3 = ""
+        interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
+        interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
+        interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
+
+        # pour garder le lien avec la donnée d'origine:
+        interv.lngChantierId = data.lngChantierId
+        interv.bytCommandeId = data.bytCommandeId
+        interv.bytIntervId = data.bytIntervId
+
+        # Créé la ligne dans le fichier intervs.csv
+        interv.dump_to_csv(intervs_file)
+
+        compteur += 1
+
+    logger.info("> {} interventions Compactage ajoutées au fichier".format(compteur))
+
+
+
+    # ########## IMPORT DES INTERVENTIONS D'ETANCHEITE ##########
+
+    # Importe les interventions de contrôle d'étanchéité dans le fichier intervs.csv
+
+    compteur = 0
+
+    sql = """SELECT tblEtancheiteIntervs.lngChantierId, tblEtancheiteIntervs.bytCommandeId, tblEtancheiteIntervs.bytIntervId, tblEtancheiteIntervs.strEquipeId,
+                tblEtancheiteIntervs.lngRapportId, tblEtancheitePartChantiers.bytTypeEssai, tblMateriaux.strMateriauId, tblMateriaux.strMatériau,
+                tblEtancheitePartChantiers.intDiametre, tblEtancheitePartChantiers.sngLgHt, tblEtancheitePartChantiers.intNbJoint, tblEtancheiteResultats.dtmDuree,
+                tblEtancheiteResultats.dtmEssai, tblEtancheitePartChantiers.strTrcRegard, tblEtancheiteResultats.bytPartChantierId
+            FROM ((tblEtancheiteIntervs INNER JOIN tblEtancheiteResultats ON (tblEtancheiteIntervs.lngChantierId = tblEtancheiteResultats.lngChantierId)
+                AND (tblEtancheiteIntervs.bytIntervId = tblEtancheiteResultats.bytIntervId)) INNER JOIN tblEtancheitePartChantiers
+                ON (tblEtancheiteResultats.lngChantierId = tblEtancheitePartChantiers.lngChantierId)
+                AND (tblEtancheiteResultats.bytPartChantierId = tblEtancheitePartChantiers.bytPartChantierId)) INNER JOIN tblMateriaux
+                ON tblEtancheitePartChantiers.strMateriauId = tblMateriaux.strMateriauId
+            WHERE (((tblEtancheiteIntervs.sngIntervIdMos)=0 Or (tblEtancheiteIntervs.sngIntervIdMos) Is Null));
+          """
 
-errors = -1
+    def get_engin_etancheite(equipe, diametre, materiau, type_essai):
+        """ retourne l'engin correspondant à l'essai en fonction eds caractéristiques de l'essai """
+        sql = """SELECT strEnginId FROM tblEtancheiteEngins
+                WHERE ([strEquipeId] = '{}') AND ([intDiametre] = {}) AND ([strMateriauId] = '{}') AND ([bytTypeEssaiId] ={})
+                """.format(equipe, diametre, materiau, type_essai)
+        row = controles_db.first(sql)
+        return row.strEnginId if row else ""
+
+    for data in controles_db.read(sql):
+        interv = Interv()
+
+        interv.strEquipeId = "C{}".format(data.strEquipeId)
+        interv.strEnginId = get_engin_etancheite(data.strEquipeId, data.intDiametre, data.strMateriauId, data.bytTypeEssai)
+        interv.strRapportId = data.lngRapportId
+        interv.strTypeInterventionId = "CE{}".format(data.bytTypeEssai)
+        interv.strCatégorieInterventionId = "CE"
+        interv.dblquantite = float(data.intNbJoint)
+        interv.strunite = "u"
+        interv.dtmIntervention = data.dtmEssai
+        interv.dtmDureeIntervention = data.dtmDuree
+        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)
+        interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
+        interv.strgrandeur1 = data.strMatériau
+        interv.strgrandeur2 = data.intDiametre
+        interv.strgrandeur3 = data.sngLgHt
+        interv.strcaracteristique1 = "Matériau"
+        interv.strcaracteristique2 = "Diamètre"
+        interv.strcaracteristique3 = "Longueur"
+        interv.strunite1 = ""
+        interv.strunite2 = "mm"
+        interv.strunite3 = "m"
+        interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
+        interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
+        interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
+
+        # pour garder le lien avec la donnée d'origine:
+        interv.lngChantierId = data.lngChantierId
+        interv.bytCommandeId = data.bytCommandeId
+        interv.bytIntervId = data.bytIntervId
+
+        # Créé la ligne dans le fichier intervs.csv
+        interv.dump_to_csv(intervs_file)
+
+        compteur += 1
+
+    logger.info("> {} interventions Etanchéité ajoutées au fichier".format(compteur))
+
+
+
+    # ########## IMPORT DES INTERVENTIONS D'INSPECTION VIDEO ##########
+
+
+    # Importe les interventions d'inspection vidéo dans le fichier intervs.csv
+
+    compteur = 0
+
+    sql = """SELECT tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
+            tblVideoEngins.strEnginId, tblVideoIntervs.lngRapportId, First(tblso_rate_Analyse.MateriauCourt) AS strmateriau, tblVideoIntervs.lngTroncon,
+            tblVideoIntervs.sngNbJourFact, First(tblso_rate_Analyse.MaxDeDiametre) AS diam, tblVideoIntervs.dtmDuree, tblVideoIntervs.dtmIntervDu,
+            First(tblVideoIntervs.memObservation) AS memObservation, tblChantiers.strEntrepriseId
+            FROM ((tblVideoEngins RIGHT JOIN tblVideoIntervs ON tblVideoEngins.strEquipeId = tblVideoIntervs.strEquipeId) INNER JOIN tblso_rate_Analyse ON
+            (tblVideoIntervs.lngChantierId = tblso_rate_Analyse.lngChantierId) AND (tblVideoIntervs.bytIntervId = tblso_rate_Analyse.bytIntervId)) INNER JOIN
+            tblChantiers ON tblVideoIntervs.lngChantierId = tblChantiers.lngChantierId
+            WHERE (((tblVideoIntervs.sngIntervIdMos) Is Null Or (tblVideoIntervs.sngIntervIdMos)=0))
+            GROUP BY tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
+            tblVideoIntervs.lngRapportId, tblVideoIntervs.lngTroncon, tblVideoIntervs.sngNbJourFact, tblVideoIntervs.dtmDuree,
+            tblVideoIntervs.dtmIntervDu, tblVideoEngins.strEnginId, tblChantiers.strEntrepriseId
+            """
+
+    for data in controles_db.read(sql):
+        interv = Interv()
+
+        interv.strEquipeId = "C{}".format(data.strEquipeId)
+        interv.strEnginId = data.strEnginId
+        interv.strRapportId = data.lngRapportId
+        interv.strTypeInterventionId = "CI1" if data.strEntrepriseId != 195 else "CI2"
+        interv.strCatégorieInterventionId = "CI"
+        interv.dblquantite = float(data.sngNbJourFact)
+        interv.strunite = "j"
+        interv.dtmIntervention = data.dtmIntervDu
+        interv.dtmDureeIntervention = data.dtmDuree
+        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)
+        interv.remarques = data.memObservation if data.memObservation else "-"
+        interv.strgrandeur1 = data.strmateriau
+        interv.strgrandeur2 = data.diam
+        interv.strgrandeur3 = data.lngTroncon
+        interv.strcaracteristique1 = "Matériau"
+        interv.strcaracteristique2 = "Diamètre"
+        interv.strcaracteristique3 = "Longueur inspectée"
+        interv.strunite1 = ""
+        interv.strunite2 = "mm"
+        interv.strunite3 = "m"
+        interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
+        interv.strTest = "{}/{}/{}/1".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
+        interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
+
+        # pour garder le lien avec la donnée d'origine:
+        interv.lngChantierId = data.lngChantierId
+        interv.bytCommandeId = data.bytCommandeId
+        interv.bytIntervId = data.bytIntervId
+
+        # Créé la ligne dans le fichier intervs.csv
+        interv.dump_to_csv(intervs_file)
+
+        compteur += 1
+
+    logger.info("> {} interventions ITV ajoutées au fichier".format(compteur))
+
+
+    logging.info("Les données à importer ont été ajoutées aux fichiers '{}' et '{}'".format(affaires_file, intervs_file))
+    logging.info("Ces fichiers sont au format CSV (séparateur: tabulation)")
+
+
+
+
+
+    # ########## CONTROLE ET CORRECTION DES DONNEES ##########
+
+    errors = -1
+
+    while errors:
+        errors = []
+
+        for affaire in Affaire.load_csv(affaires_file):
+
+            prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
+            if not affaire.strMOId:
+                errors.append(prefix + "MO manquant")
+            else:
+                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(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(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(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:
+                if not affaire.intDevisId:
+                    errors.append(prefix + "Numéro de devis manquant")
+
+            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):
+
+            prefix = "Intervention {}: ".format(interv.strTest)
+            if not interv.strEquipeId:
+                errors.append(prefix + "Equipe manquante")
+            if not interv.strEnginId:
+                errors.append(prefix + "Engin manquant")
+            if not interv.strRapportId:
+                errors.append(prefix + "Rapport manquant")
+            if not interv.strCatégorieInterventionId:
+                errors.append(prefix + "Catégorie de l'intervention manquante")
+            if not interv.strTypeInterventionId:
+                errors.append(prefix + "Type d'intervention manquant")
+            if not interv.dblquantite:
+                errors.append(prefix + "Quantité nulle")
+            if not interv.strunite:
+                errors.append(prefix + "Unité non renseignée")
+            if not interv.dtmIntervention:
+                errors.append(prefix + "Erreur : date d'intervention")
+            if not interv.dtmDureeIntervention or interv.dtmDureeIntervention == date_zero:
+                errors.append(prefix + "Durée d'intervention nulle")
+            if not interv.strunite:
+                errors.append(prefix + "Unité non renseignée")
+
+            if not engin_existe(interv.strEnginId):
+                errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
+
+        # *** 6- Interruption pour corection manuelle des données (si nécessaire)
+        if errors:
+            logging.error("<!> Des erreurs ont été détectées dans les données à importer. <!>")
+            for msg in errors:
+                logging.error(msg)
+            if no_prompt:
+                logger.info("# Annulation de l'import")
+                sys.exit(1)
+        else:
+            logging.info("Aucune erreur n'a été détectée dans les données.")
+            if no_prompt:
+                break
 
-while errors:
-    errors = []
+        # Même si aucune erreur n'a été détectée, on demande un controle visuel.
+        prompt = ""
+        while prompt != "v":
+            logger.info(">> Veuillez contrôler les données, puis taper: \n\t'v' pour continuer\n\t'f' pour forcer le traitement à se poursuivre\n\t'q' pour annuler")
+            try:
+                from core import tsv_editor
+                tsv_editor.exec_(affaires_file.abspath())
+                tsv_editor.exec_(intervs_file.abspath())
+            except:
+                logger.error("Erreur à l'ouverture du fichier %s", affaires_file)
+                logger.error("Erreur à l'ouverture du fichier %s", intervs_file)
+
+            prompt = input("")
+            if prompt == "f":
+                break
+            if prompt == "q":
+                logger.info("# Annulation de l'import")
+                sys.exit(1)
+
+    # ########## MISE A JOUR DE LA BASE DE DONNEES ANALYTIQUE ##########
+
+    # On charge en mémoire les affaires et les interventions
+    logger.info("# Mise à jour de la base Analytique")
+    logger.info("> NB: Les modifications ne seront appliquées à la base que si toutes les opérations se déroulent normalement.")
+
+    affaires = list(Affaire.load_csv(affaires_file))
+    intervs = list(Interv.load_csv(intervs_file))
+
+    # On insère les affaires, interventions dans Analytique, et on génère la ou les lignes de tarification associées
+
+    for affaire in affaires:
+
+        # insertion dans tbl_Affaires
+
+        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: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)
+
+        logger.info("> Ajout de l'affaire: {}".format(affaire.strLiaisonControle))
+
+
+    # On insère les interventions dans tbl_Intervention
+    for interv in intervs:
+
+        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 = 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: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)
+
+        logger.info("> Ajout de l'intervention: {}".format(interv.strTest))
+
+
+
+    # Calcul de la tarification et ajout à tbl_Tarification
+
+    # > On va créer une ligne de tarification pour chaque groupe d'interventions
+    # > partageant le même lngRapportid et strArticleId (cad le même engin)
+    for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId) for interv in intervs]):
+
+        tarif = Tarification()
+        tarif.intervs = [interv for interv in intervs if interv.strRapportId == strRapportId and interv.strArticleId == strArticleId]
+
+        # recupere le prix unitaire de l'engin
+        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(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
+        tarif.strArticleId = strArticleId
+        tarif.dblQuantite = sum([float(interv.dblquantite) for interv in tarif.intervs])
+        tarif.strUnite = tarif.intervs[0].strunite
+        tarif.dtmDebut = min([interv.dtmIntervention for interv in tarif.intervs])
+        tarif.dtmFin = max([interv.dtmIntervention for interv in tarif.intervs])
+        tarif.bytPeriode = tarif.intervs[0].intPeriode
+        tarif.dblPrixUnitaire = prix_unitaire
+        tarif.dblPrixTotal = tarif.dblQuantite * tarif.dblPrixUnitaire
+        tarif.dblTauxTVA = taux_tva
+        tarif.dblPrixTVA = tarif.dblPrixTotal * (0.01 * tarif.dblTauxTVA)
+        tarif.strStatut = 'A facturer'
+
+        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: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)
 
-    for affaire in Affaire.load_csv(affaires_file):
+        logger.info("> Génération d'une ligne de tarification pour l'affaire {} (rapport {}, article: {})".format(tarif.intervs[0].LienAff, strRapportId, strArticleId))
 
-        prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
-        if not affaire.strMOId:
-            errors.append(prefix + "MO manquant")
-        else:
-            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(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")
+
+    # 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(Sql.format("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]={:text}", affaire.strLiaisonControle)).DblAffaireId
+
+        sql = Sql.format("""UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
+                WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId}
+                """, DblAffaireId=dblAffaireId, lngChantierId=affaire.lngChantierId, bytCommandeId=affaire.bytCommandeId)
+        controles_db.execute(sql)
+
+    for interv in intervs:
+        if interv.strCatégorieInterventionId == "CC":
+            tbl = "tblCompactageIntervs"
+        elif interv.strCatégorieInterventionId == "CE":
+            tbl = "tblEtancheiteIntervs"
+        elif interv.strCatégorieInterventionId == "CI":
+            tbl = "tblVideoIntervs"
         else:
-            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(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:
-            if not affaire.intDevisId:
-                errors.append(prefix + "Numéro de devis manquant")
-
-        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):
-
-        prefix = "Intervention {}: ".format(interv.strTest)
-        if not interv.strEquipeId:
-            errors.append(prefix + "Equipe manquante")
-        if not interv.strEnginId:
-            errors.append(prefix + "Engin manquant")
-        if not interv.strRapportId:
-            errors.append(prefix + "Rapport manquant")
-        if not interv.strCatégorieInterventionId:
-            errors.append(prefix + "Catégorie de l'intervention manquante")
-        if not interv.strTypeInterventionId:
-            errors.append(prefix + "Type d'intervention manquant")
-        if not interv.dblquantite:
-            errors.append(prefix + "Quantité nulle")
-        if not interv.strunite:
-            errors.append(prefix + "Unité non renseignée")
-        if not interv.dtmIntervention:
-            errors.append(prefix + "Erreur : date d'intervention")
-        if not interv.dtmDureeIntervention or interv.dtmDureeIntervention == date_zero:
-            errors.append(prefix + "Durée d'intervention nulle")
-        if not interv.strunite:
-            errors.append(prefix + "Unité non renseignée")
-
-        if not engin_existe(interv.strEnginId):
-            errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
-
-    # *** 6- Interruption pour corection manuelle des données (si nécessaire)
-    if errors:
-        logging.error("<!> Des erreurs ont été détectées dans les données à importer. <!>")
-        for msg in errors:
-            logging.error(msg)
-        if no_prompt:
-            logger.info("# Annulation de l'import")
-            sys.exit(1)
-    else:
-        logging.info("Aucune erreur n'a été détectée dans les données.")
-        if no_prompt:
-            break
-
-    # Même si aucune erreur n'a été détectée, on demande un controle visuel.
-    prompt = ""
-    while prompt != "v":
-        logger.info(">> Veuillez contrôler les données, puis taper: \n\t'v' pour continuer\n\t'f' pour forcer le traitement à se poursuivre\n\t'q' pour annuler")
-        try:
-            from core import tsv_editor
-            tsv_editor.exec_(affaires_file.abspath())
-            tsv_editor.exec_(intervs_file.abspath())
-        except:
-            logger.error("Erreur à l'ouverture du fichier %s", affaires_file)
-            logger.error("Erreur à l'ouverture du fichier %s", intervs_file)
-
-        prompt = input("")
-        if prompt == "f":
-            break
-        if prompt == "q":
-            logger.info("# Annulation de l'import")
-            sys.exit(1)
-
-# ########## MISE A JOUR DE LA BASE DE DONNEES ANALYTIQUE ##########
-
-# On charge en mémoire les affaires et les interventions
-logger.info("# Mise à jour de la base Analytique")
-logger.info("> NB: Les modifications ne seront appliquées à la base que si toutes les opérations se déroulent normalement.")
-
-affaires = list(Affaire.load_csv(affaires_file))
-intervs = list(Interv.load_csv(intervs_file))
-
-# On insère les affaires, interventions dans Analytique, et on génère la ou les lignes de tarification associées
-
-for affaire in affaires:
-
-    # insertion dans tbl_Affaires
-
-    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: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)
-
-    logger.info("> Ajout de l'affaire: {}".format(affaire.strLiaisonControle))
-
-
-# On insère les interventions dans tbl_Intervention
-for interv in intervs:
-
-    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 = 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: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)
-
-    logger.info("> Ajout de l'intervention: {}".format(interv.strTest))
-
-
-
-# Calcul de la tarification et ajout à tbl_Tarification
-
-# > On va créer une ligne de tarification pour chaque groupe d'interventions
-# > partageant le même lngRapportid et strArticleId (cad le même engin)
-for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId) for interv in intervs]):
-
-    tarif = Tarification()
-    tarif.intervs = [interv for interv in intervs if interv.strRapportId == strRapportId and interv.strArticleId == strArticleId]
-
-    # recupere le prix unitaire de l'engin
-    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(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
-    tarif.strArticleId = strArticleId
-    tarif.dblQuantite = sum([float(interv.dblquantite) for interv in tarif.intervs])
-    tarif.strUnite = tarif.intervs[0].strunite
-    tarif.dtmDebut = min([interv.dtmIntervention for interv in tarif.intervs])
-    tarif.dtmFin = max([interv.dtmIntervention for interv in tarif.intervs])
-    tarif.bytPeriode = tarif.intervs[0].intPeriode
-    tarif.dblPrixUnitaire = prix_unitaire
-    tarif.dblPrixTotal = tarif.dblQuantite * tarif.dblPrixUnitaire
-    tarif.dblTauxTVA = taux_tva
-    tarif.dblPrixTVA = tarif.dblPrixTotal * (0.01 * tarif.dblTauxTVA)
-    tarif.strStatut = 'A facturer'
+            continue
+
+        sql = Sql.format("""UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
+                WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId} AND [bytIntervId]={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")
+
+    # On commit les modifications
+    logger.info("Commit des modifications...")
+    analytique_db.commit()
+
+
+
+    # ########## MISE A JOUR DES TEMPS D'INSTALLATION ##########
+
+    # > Le temps d'installation est le temps passé par chaque agent en transport, préparation, reporting...etc.
+    # > C'est donc le temps de travail théorique, moins le temps d'intervention.
+    # > pour des raisons de performances, on ne commence le traitement qu'à partir de l'année N-1
+
+    logger.info("Mise à jour des temps d'installation...")
 
-    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: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))
 
+    # 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'
 
-# 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(Sql.format("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]={:text}", affaire.strLiaisonControle)).DblAffaireId
+    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 '%/%'
+                AND Year([dtmIntervention])>={}
+                AND tbl_Intervention.dtmDureeIntervention > 0
+                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#))
+            """, datetime.now().year - 1)
 
-    sql = Sql.format("""UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
-            WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId}
-            """, DblAffaireId=dblAffaireId, lngChantierId=affaire.lngChantierId, bytCommandeId=affaire.bytCommandeId)
-    controles_db.execute(sql)
-
-for interv in intervs:
-    if interv.strCatégorieInterventionId == "CC":
-        tbl = "tblCompactageIntervs"
-    elif interv.strCatégorieInterventionId == "CE":
-        tbl = "tblEtancheiteIntervs"
-    elif interv.strCatégorieInterventionId == "CI":
-        tbl = "tblVideoIntervs"
-    else:
-        continue
-
-    sql = Sql.format("""UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
-            WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId} AND [bytIntervId]={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")
-
-# On commit les modifications
-logger.info("Commit des modifications...")
-analytique_db.commit()
+    for interv in analytique_db.read_all(sql):
+        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))
+    logger.info("Commit des modifications...")
+    analytique_db.commit()
 
 
-# ########## MISE A JOUR DES TEMPS D'INSTALLATION ##########
-
-# > Le temps d'installation est le temps passé par chaque agent en transport, préparation, reporting...etc.
-# > C'est donc le temps de travail théorique, moins le temps d'intervention.
-# > pour des raisons de performances, on ne commence le traitement qu'à partir de l'année N-1
 
-logger.info("Mise à jour des temps d'installation...")
+    logger.info("# Import terminé")
 
 
+if __name__ == "__main__":
 
-# 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'
+    main()
 
-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 '%/%'
-            AND Year([dtmIntervention])>={}
-            AND tbl_Intervention.dtmDureeIntervention > 0
-            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#))
-        """, datetime.now().year - 1)
-
-for interv in analytique_db.read_all(sql):
-    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))
-logger.info("Commit des modifications...")
-analytique_db.commit()
-
-
-
-logger.info("# Import terminé")
+    logger.info("-- Fin --")

+ 236 - 232
gf2analytique.py

@@ -38,258 +38,262 @@ logconf.start("gf2analytique", logging.DEBUG)
 
 ##-----------------------------------------------
 
-# *** Initialisation
-logger.info("Initialisation...")
-
-no_prompt = ("-n" in sys.argv)
-if no_prompt:
-    logger.info("> Lancé en mode automatique (sans interruption)")
-
-# Connect to factures.mdb
-analytique_db = AnalytiqueDb(autocommit=False)
-
-# Connect to the astre gf webservice
-ws = GfWebservice("GetPDEFactures")
-
-# Make the working directory
-workdir = mk_workdir("gf2analytique")
-importfile = workdir / "import.csv"
-
-# > Supprime le fichier d'import s'il existe, et le recréé avec la ligne d'en-tête
-if importfile.exists():
-    logger.debug("Supprime le fichier %s", importfile)
-    importfile.remove()
-
-class CsvFormatError(Exception):
-    pass
-
-class Facture(Model):
-    """ Modèle de données d'une facture """
-    _FIELDS = ["numExBudget", "codeColl", "codeBudg", "numEnv", "codeSection", "typeMvt", "numMandat", "numLiqMandat",
-                 "numLigneMandat", "codeAxe", "libAxe", "codeCout", "libCout", "dateMandat", "numBj", "numTiers",
-                 "libRai", "refIntMandat", "codePeriode", "dateDepDelai", "typeNomencMarche", "mntTtcMandat",
-                 "mntTvaMandat", "mntVent"]
-
-    def __init__(self):
-        super(Facture, self).__init__()
-
-    def est_importee(self):
-        """ Renvoie True si la facture a déjà été importée dans Analytique
-        ATTENTION: en l'absence d'identifiants uniques, il est difficile de contrôler de manière certaine si une ligne a déjà été importée.
-        C'est pour cette raison que les données sont importées 'par blocs' """
-
-        sql = """SELECT dblFactureId FROM tbl_Factures
-                WHERE intExercice = {} AND strLiquidation = '{}' AND strEngagement = '{}' AND strService='7710'
-              """.format(self.numExBudget, self.numLiqMandat, self.numMandat)
-        return analytique_db.exists(sql)
-
-    def autocorrection(self):
-        """ Procède à certaines corrections automatiques sur les données de la facture """
-        # correction auto des codes chantiers
-        if self.codeAxe == "AFFAI" and re.match(r"\d{2}5\d{3}", self.codeCout):
-            self.codeCout += "/1"
-
-        # echappe les apostrophes
-        self.libRai = self.libRai.replace("'", "''")
-
-        # renomme automatiquement les noms de materiels
-        if self.codeAxe == "ENGIN":
-            row = analytique_db.first("""SELECT txtMateriel FROM tbl_materiel
-                                            WHERE txtMateriel='{codeCout}' or txtMateriel='ZZ {codeCout}'
-                                            """.format(codeCout=self.codeCout))
-            if row:
-                self.codeCout = row.txtMateriel
-
-    def is_valid(self):
-        """ controle la validité des données d'une facture """
-        for field in self._FIELDS:
-            if not hasattr(self, field):
-                raise CsvFormatError("Un ou plusieurs champs sont manquants dans le fichier '{}'".format(importfile))
-
-        errors = False
-        if not int(self.numExBudget) > 2000:
-            logger.error("Exercice budgetaire invalide: %s", self.numExBudget)
-            errors = True
-        if self.codeColl != "CG67":
-            logger.error("Code collectivité invalide: %s", self.codeColl)
-            errors = True
-        if self.codeBudg != "02":
-            logger.error("Code budgetaire invalide: %s", self.codeBudg)
-            errors = True
-        if self.codeAxe == "ENGIN":
-            # Controle l'existence du materiel
-            if not analytique_db.first("SELECT intlMaterielID FROM tbl_materiel WHERE txtMateriel='{}'".format(self.codeCout)):
-                logger.error("Le materiel n'existe pas: %s", self.codeCout)
+
+def main():
+    # *** Initialisation
+    logger.info("Initialisation...")
+
+    no_prompt = ("-n" in sys.argv)
+    if no_prompt:
+        logger.info("> Lancé en mode automatique (sans interruption)")
+
+    # Connect to factures.mdb
+    analytique_db = AnalytiqueDb(autocommit=False)
+
+    # Connect to the astre gf webservice
+    ws = GfWebservice("GetPDEFactures")
+
+    # Make the working directory
+    workdir = mk_workdir("gf2analytique")
+    importfile = workdir / "import.csv"
+
+    # > Supprime le fichier d'import s'il existe, et le recréé avec la ligne d'en-tête
+    if importfile.exists():
+        logger.debug("Supprime le fichier %s", importfile)
+        importfile.remove()
+
+    class CsvFormatError(Exception):
+        pass
+
+    class Facture(Model):
+        """ Modèle de données d'une facture """
+        _FIELDS = ["numExBudget", "codeColl", "codeBudg", "numEnv", "codeSection", "typeMvt", "numMandat", "numLiqMandat",
+                     "numLigneMandat", "codeAxe", "libAxe", "codeCout", "libCout", "dateMandat", "numBj", "numTiers",
+                     "libRai", "refIntMandat", "codePeriode", "dateDepDelai", "typeNomencMarche", "mntTtcMandat",
+                     "mntTvaMandat", "mntVent"]
+
+        def __init__(self):
+            super(Facture, self).__init__()
+
+        def est_importee(self):
+            """ Renvoie True si la facture a déjà été importée dans Analytique
+            ATTENTION: en l'absence d'identifiants uniques, il est difficile de contrôler de manière certaine si une ligne a déjà été importée.
+            C'est pour cette raison que les données sont importées 'par blocs' """
+
+            sql = """SELECT dblFactureId FROM tbl_Factures
+                    WHERE intExercice = {} AND strLiquidation = '{}' AND strEngagement = '{}' AND strService='7710'
+                  """.format(self.numExBudget, self.numLiqMandat, self.numMandat)
+            return analytique_db.exists(sql)
+
+        def autocorrection(self):
+            """ Procède à certaines corrections automatiques sur les données de la facture """
+            # correction auto des codes chantiers
+            if self.codeAxe == "AFFAI" and re.match(r"\d{2}5\d{3}", self.codeCout):
+                self.codeCout += "/1"
+
+            # echappe les apostrophes
+            self.libRai = self.libRai.replace("'", "''")
+
+            # renomme automatiquement les noms de materiels
+            if self.codeAxe == "ENGIN":
+                row = analytique_db.first("""SELECT txtMateriel FROM tbl_materiel
+                                                WHERE txtMateriel='{codeCout}' or txtMateriel='ZZ {codeCout}'
+                                                """.format(codeCout=self.codeCout))
+                if row:
+                    self.codeCout = row.txtMateriel
+
+        def is_valid(self):
+            """ controle la validité des données d'une facture """
+            for field in self._FIELDS:
+                if not hasattr(self, field):
+                    raise CsvFormatError("Un ou plusieurs champs sont manquants dans le fichier '{}'".format(importfile))
+
+            errors = False
+            if not int(self.numExBudget) > 2000:
+                logger.error("Exercice budgetaire invalide: %s", self.numExBudget)
+                errors = True
+            if self.codeColl != "CG67":
+                logger.error("Code collectivité invalide: %s", self.codeColl)
                 errors = True
-        elif self.codeAxe == "AFFAI":
-            # Controle l'existence de l'affaire
-            if not analytique_db.first("SELECT dblAffaireId FROM tbl_Affaires WHERE strLiaisonControle='{}'".format(self.codeCout)):
-                logger.error("L'affaire n'existe pas: %s", self.codeCout)
+            if self.codeBudg != "02":
+                logger.error("Code budgetaire invalide: %s", self.codeBudg)
                 errors = True
-        else:
-            # CodeAxe invalide
-            logger.error("Code axe inconnu: %s", self.codeAxe)
-            errors = True
-        return (not errors)
+            if self.codeAxe == "ENGIN":
+                # Controle l'existence du materiel
+                if not analytique_db.first("SELECT intlMaterielID FROM tbl_materiel WHERE txtMateriel='{}'".format(self.codeCout)):
+                    logger.error("Le materiel n'existe pas: %s", self.codeCout)
+                    errors = True
+            elif self.codeAxe == "AFFAI":
+                # Controle l'existence de l'affaire
+                if not analytique_db.first("SELECT dblAffaireId FROM tbl_Affaires WHERE strLiaisonControle='{}'".format(self.codeCout)):
+                    logger.error("L'affaire n'existe pas: %s", self.codeCout)
+                    errors = True
+            else:
+                # CodeAxe invalide
+                logger.error("Code axe inconnu: %s", self.codeAxe)
+                errors = True
+            return (not errors)
+
+
+    # *** 1- Parcourt les factures renvoyées par le webservice, et stoque toutes les lignes non-importées dans Analytique dans un fichier import.csv
+    logger.info("Parcourt les données fournies par le webservice")
+    logger.info("(les ligne à importer sont ajoutées au fichier %s)", importfile)
+
+    for data in ws:
+        # Génère la facture à partir des données fournies par le web-service
+        facture = Facture.from_dict(data)
+
+        # Contrôle si la facture est déjà importée. Si c'est le cas, passe à la facture suivante.
+        if facture.est_importee():
+            continue
 
+        logger.info("* Facture %s/%s/%s: import", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
 
-# *** 1- Parcourt les factures renvoyées par le webservice, et stoque toutes les lignes non-importées dans Analytique dans un fichier import.csv
-logger.info("Parcourt les données fournies par le webservice")
-logger.info("(les ligne à importer sont ajoutées au fichier %s)", importfile)
+        # procède à une auto-correction des données
+        facture.autocorrection()
 
-for data in ws:
-    # Génère la facture à partir des données fournies par le web-service
-    facture = Facture.from_dict(data)
+        # Ajoute les données au format CSV au fichier d'import
+        facture.dump_to_csv(importfile)
 
-    # Contrôle si la facture est déjà importée. Si c'est le cas, passe à la facture suivante.
-    if facture.est_importee():
-        continue
+    if not importfile.exists():
+        logger.info("** Aucune nouvelle facture à importer **")
+        sys.exit(0)
 
-    logger.info("* Facture %s/%s/%s: import", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
+    # *** 2- Contrôle les données. En cas d'erreur, le script est interrompu et la position et la description des erreurs sont loggés.
+    errors = -1
 
-    # procède à une auto-correction des données
-    facture.autocorrection()
+    while errors:
+        errors = 0
+        logger.info("Contrôle des données")
 
-    # Ajoute les données au format CSV au fichier d'import
-    facture.dump_to_csv(importfile)
+        # Parcourt les lignes du fichier d'import, et teste la validité de chacune.
+        for facture in Facture.load_csv(importfile):
+            if not facture.is_valid():
+                errors += 1
 
-if not importfile.exists():
-    logger.info("** Aucune nouvelle facture à importer **")
-    sys.exit(0)
+        if errors:
+            logger.error("<!> Une ou plusieurs erreurs ont été détectées, voir le fichier de log pour plus d'information <!>")
+            if no_prompt:
+                logger.info("Veuillez executer le script manuellement: %s", Path(__file__).abspath())
+            else:
+                logger.info("Veuillez corriger les données du fichier %s", importfile)
 
-# *** 2- Contrôle les données. En cas d'erreur, le script est interrompu et la position et la description des erreurs sont loggés.
-errors = -1
+            # En cas d'erreur(s), deux possibilités:
+            # - Le script a été lancé en mode sans interruption avec l'option '-n', on interrompt le script.
+            # - Le script a été lancé normalement, sans option: on attend une correction manuelle de l'utilisateur.
+            if no_prompt:
+                sys.exit(errors)
+            else:
+                try:
+                    from core import tsv_editor
+                    tsv_editor.exec_(importfile.abspath())
+                except:
+                    logger.error("Erreur à l'ouverture du fichier %s", importfile)
+                    input("Presser une touche pour continuer...")
 
-while errors:
-    errors = 0
-    logger.info("Contrôle des données")
+    logger.info("Les données sont valides.")
+
+    # 3- Si toutes les données sont valides, parcourt les lignes du fichier import.csv et les insère dans la table tbl_Facture.
+    logger.info("Mise à jour des tables de %s", AnalytiqueDb._path)
 
-    # Parcourt les lignes du fichier d'import, et teste la validité de chacune.
     for facture in Facture.load_csv(importfile):
-        if not facture.is_valid():
-            errors += 1
-
-    if errors:
-        logger.error("<!> Une ou plusieurs erreurs ont été détectées, voir le fichier de log pour plus d'information <!>")
-        if no_prompt:
-            logger.info("Veuillez executer le script manuellement: %s", Path(__file__).abspath())
-        else:
-            logger.info("Veuillez corriger les données du fichier %s", importfile)
-
-        # En cas d'erreur(s), deux possibilités:
-        # - Le script a été lancé en mode sans interruption avec l'option '-n', on interrompt le script.
-        # - Le script a été lancé normalement, sans option: on attend une correction manuelle de l'utilisateur.
-        if no_prompt:
-            sys.exit(errors)
-        else:
-            try:
-                from core import tsv_editor
-                tsv_editor.exec_(importfile.abspath())
-            except:
-                logger.error("Erreur à l'ouverture du fichier %s", importfile)
-                input("Presser une touche pour continuer...")
-
-logger.info("Les données sont valides.")
-
-# 3- Si toutes les données sont valides, parcourt les lignes du fichier import.csv et les insère dans la table tbl_Facture.
-logger.info("Mise à jour des tables de %s", AnalytiqueDb._path)
-
-for facture in Facture.load_csv(importfile):
-
-    logger.info("* Facture %s/%s/%s: traitement", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
-    # NB: les données ne sont committées qu'aprés l'exécution de toutes les requêtes suivantes
-
-    logger.info("> mise à jour de tbl_Factures")
-
-    # Insère les données dans la table tbl_Factures
-    sql = """INSERT INTO tbl_Factures ( intExercice, strLiquidation, intLiquidationLigne, strEngagement,
-                                        strEnveloppe, strService, strTiers, strTiersLibelle, strMotsClefs,
-                                        dtmDeb, intOperation, strNomenclature0, strAXE, strCentreCout,
-                                        strObjet, dblMontantTotal, dblMontantTVA, strORIGINE_DONNEES
-                                    )
-              VALUES ({intExercice}, '{strLiquidation}', {intLiquidationLigne}, '{strEngagement}',
-                      '{strEnveloppe}', '{strService}', '{strTiers}', '{strTiersLibelle}', '{strMotsClefs}',
-                      #{dtmDeb}#, {intOperation}, '{strNomenclature0}', '{strAxe}', '{strCentreCout}',
-                      '{strObjet}', {dblMontantTotal}, {dblMontantTVA}, '{strORIGINE_DONNEES}')
-          """.format(
-                     intExercice=facture.numExBudget,
-                     strLiquidation=facture.numLiqMandat,
-                     intLiquidationLigne=facture.numLigneMandat,
-                     strEngagement=facture.numMandat,
-                     strEnveloppe=facture.numEnv,
-                     strService='7710',
-                     strTiers=facture.numTiers,
-                     strTiersLibelle=facture.libRai,
-                     strMotsClefs=AnalytiqueDb.nz(facture.refIntMandat),
-                     dtmDeb=AnalytiqueDb.format_date(facture.dateDepDelai),
-                     intOperation=AnalytiqueDb.nz(facture.codePeriode, "Null"),
-                     strNomenclature0=facture.typeNomencMarche,
-                     strAxe=facture.codeAxe,
-                     strCentreCout=facture.codeCout,
-                     strObjet=AnalytiqueDb.format_date(facture.dateMandat, out_format="%d/%m/%Y"),
-                     dblMontantTVA=facture.mntTvaMandat,
-                     dblMontantTotal=facture.mntVent,
-                     strORIGINE_DONNEES='ASTRE'
-                     )
-    logger.debug("> %s", sql)
-    analytique_db.execute(sql)
-
-    facture.factureId = analytique_db.first("SELECT TOP 1 dblFactureId FROM tbl_Factures ORDER BY dblFactureId DESC").dblFactureId
-
-
-    if facture.codeAxe == "ENGIN":
-        # La ligne concerne un engin: insère les données dans la table tbl_Facture_Engin
-        logger.info("> mise à jour de tbl_Facture_Engin")
-
-        materiel = analytique_db.first("SELECT intlMaterielID FROM tbl_Materiel WHERE [txtMateriel]='{}'".format(facture.codeCout))
-        materielId = materiel.intlMaterielID if materiel else '859'
-        logger.debug("retrieve intlMaterielID: %s", materielId)
-
-        sql = """INSERT INTO tbl_Facture_Engin ( intlMaterielID, txtMateriel, dblFactureId, strLibelle, dblMontant, strType )
-                VALUES ({}, '{}', {}, '{}', {}, '{}')
-        """.format(materielId,
-                   facture.codeCout,
-                   facture.factureId,
-                   AnalytiqueDb.nz(facture.libCout),
-                   facture.mntVent,
-                   facture.libRai
-                   )
-        logger.debug("> %s", sql)
-        analytique_db.execute(sql)
 
-    elif facture.codeAxe == "AFFAI":
-        # La ligne concerne une affaire: insère les données dans la table tbl_Facture_Affaire
-        logger.info("> mise à jour de tbl_Facture_Affaire")
-
-        sql = """INSERT INTO tbl_Facture_Affaire ( strAffaireId, dblFactureId, strLibelle, dblMontant, strType )
-              VALUES ('{}', {}, '{}', {}, '{}')
-              """.format(facture.codeCout,
-                         facture.factureId,
-                         facture.libRai ,
-                         facture.mntVent,
-                         AnalytiqueDb.nz(facture.libCout),
+        logger.info("* Facture %s/%s/%s: traitement", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
+        # NB: les données ne sont committées qu'aprés l'exécution de toutes les requêtes suivantes
+
+        logger.info("> mise à jour de tbl_Factures")
+
+        # Insère les données dans la table tbl_Factures
+        sql = """INSERT INTO tbl_Factures ( intExercice, strLiquidation, intLiquidationLigne, strEngagement,
+                                            strEnveloppe, strService, strTiers, strTiersLibelle, strMotsClefs,
+                                            dtmDeb, intOperation, strNomenclature0, strAXE, strCentreCout,
+                                            strObjet, dblMontantTotal, dblMontantTVA, strORIGINE_DONNEES
+                                        )
+                  VALUES ({intExercice}, '{strLiquidation}', {intLiquidationLigne}, '{strEngagement}',
+                          '{strEnveloppe}', '{strService}', '{strTiers}', '{strTiersLibelle}', '{strMotsClefs}',
+                          #{dtmDeb}#, {intOperation}, '{strNomenclature0}', '{strAxe}', '{strCentreCout}',
+                          '{strObjet}', {dblMontantTotal}, {dblMontantTVA}, '{strORIGINE_DONNEES}')
+              """.format(
+                         intExercice=facture.numExBudget,
+                         strLiquidation=facture.numLiqMandat,
+                         intLiquidationLigne=facture.numLigneMandat,
+                         strEngagement=facture.numMandat,
+                         strEnveloppe=facture.numEnv,
+                         strService='7710',
+                         strTiers=facture.numTiers,
+                         strTiersLibelle=facture.libRai,
+                         strMotsClefs=AnalytiqueDb.nz(facture.refIntMandat),
+                         dtmDeb=AnalytiqueDb.format_date(facture.dateDepDelai),
+                         intOperation=AnalytiqueDb.nz(facture.codePeriode, "Null"),
+                         strNomenclature0=facture.typeNomencMarche,
+                         strAxe=facture.codeAxe,
+                         strCentreCout=facture.codeCout,
+                         strObjet=AnalytiqueDb.format_date(facture.dateMandat, out_format="%d/%m/%Y"),
+                         dblMontantTVA=facture.mntTvaMandat,
+                         dblMontantTotal=facture.mntVent,
+                         strORIGINE_DONNEES='ASTRE'
                          )
         logger.debug("> %s", sql)
         analytique_db.execute(sql)
 
+        facture.factureId = analytique_db.first("SELECT TOP 1 dblFactureId FROM tbl_Factures ORDER BY dblFactureId DESC").dblFactureId
+
+
+        if facture.codeAxe == "ENGIN":
+            # La ligne concerne un engin: insère les données dans la table tbl_Facture_Engin
+            logger.info("> mise à jour de tbl_Facture_Engin")
+
+            materiel = analytique_db.first("SELECT intlMaterielID FROM tbl_Materiel WHERE [txtMateriel]='{}'".format(facture.codeCout))
+            materielId = materiel.intlMaterielID if materiel else '859'
+            logger.debug("retrieve intlMaterielID: %s", materielId)
+
+            sql = """INSERT INTO tbl_Facture_Engin ( intlMaterielID, txtMateriel, dblFactureId, strLibelle, dblMontant, strType )
+                    VALUES ({}, '{}', {}, '{}', {}, '{}')
+            """.format(materielId,
+                       facture.codeCout,
+                       facture.factureId,
+                       AnalytiqueDb.nz(facture.libCout),
+                       facture.mntVent,
+                       facture.libRai
+                       )
+            logger.debug("> %s", sql)
+            analytique_db.execute(sql)
+
+        elif facture.codeAxe == "AFFAI":
+            # La ligne concerne une affaire: insère les données dans la table tbl_Facture_Affaire
+            logger.info("> mise à jour de tbl_Facture_Affaire")
+
+            sql = """INSERT INTO tbl_Facture_Affaire ( strAffaireId, dblFactureId, strLibelle, dblMontant, strType )
+                  VALUES ('{}', {}, '{}', {}, '{}')
+                  """.format(facture.codeCout,
+                             facture.factureId,
+                             facture.libRai ,
+                             facture.mntVent,
+                             AnalytiqueDb.nz(facture.libCout),
+                             )
+            logger.debug("> %s", sql)
+            analytique_db.execute(sql)
+
+
+        logger.info("> mise à jour de tbl_Mandatement")
+
+        # Insère les données dans la table tbl_Mandatement
+        sql = """INSERT INTO tbl_Mandatement ( dblFacture, strNumMandat, dtmMandat, strBordereau )
+              VALUES ({}, '{}', #{}#, '{}')
+              """.format(facture.factureId,
+                         facture.numMandat,
+                         AnalytiqueDb.format_date(facture.dateMandat),
+                         facture.numBj
+                       )
+        logger.debug("> %s", sql)
+        analytique_db.execute(sql)
 
-    logger.info("> mise à jour de tbl_Mandatement")
-
-    # Insère les données dans la table tbl_Mandatement
-    sql = """INSERT INTO tbl_Mandatement ( dblFacture, strNumMandat, dtmMandat, strBordereau )
-          VALUES ({}, '{}', #{}#, '{}')
-          """.format(facture.factureId,
-                     facture.numMandat,
-                     AnalytiqueDb.format_date(facture.dateMandat),
-                     facture.numBj
-                   )
-    logger.debug("> %s", sql)
-    analytique_db.execute(sql)
-
-    # Commit les insertions dans la base
-    analytique_db.commit()
+        # Commit les insertions dans la base
+        analytique_db.commit()
 
-    logger.info("Facture %s : ok", facture.factureId)
+        logger.info("Facture %s : ok", facture.factureId)
 
 
-logging.shutdown()
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 34 - 29
gf2factures.py

@@ -27,48 +27,53 @@ logconf.start("gf2factures", logging.INFO)
 
 ##-----------------------------------------------
 
-logger.info("Initialization")
+def main():
+    logger.info("Initialization")
 
-# Connect to factures.mdb
-factures_db = FacturesDb(autocommit=True)
+    # Connect to factures.mdb
+    factures_db = FacturesDb(autocommit=True)
 
-# Connect to the astre gf webservice
-ws = GfWebservice("GetPDETitres")
+    # Connect to the astre gf webservice
+    ws = GfWebservice("GetPDETitres")
 
-analysed = 0
-updated = 0
+    analysed = 0
+    updated = 0
 
-logger.info("Mise à jour de FacturesDb")
-for titre in ws:
-    if not titre:
-        continue
+    logger.info("Mise à jour de FacturesDb")
+    for titre in ws:
+        if not titre:
+            continue
 
-    analysed += 1
+        analysed += 1
 
-    strfilter = "[lngDocId]={} AND [bytTypeDocumentId]=50".format(titre["docId"])
+        strfilter = "[lngDocId]={} AND [bytTypeDocumentId]=50".format(titre["docId"])
 
-    record = factures_db.first("SELECT memobsinterne FROM tblPieceEntete WHERE {}".format(strfilter))
-    if not record:
-        logger.warning("(!) no record found where '{}'".format(strfilter))
-        continue
+        record = factures_db.first("SELECT memobsinterne FROM tblPieceEntete WHERE {}".format(strfilter))
+        if not record:
+            logger.warning("(!) no record found where '{}'".format(strfilter))
+            continue
 
-    memobs = record.memobsinterne if record.memobsinterne else ""
+        memobs = record.memobsinterne if record.memobsinterne else ""
 
-    if "Titre n° : {}".format(titre["titreId"]) in memobs:
-        # already imported
-        continue
+        if "Titre n° : {}".format(titre["titreId"]) in memobs:
+            # already imported
+            continue
 
-    logger.info("Mise à jour de : {}".format(titre["docId"]))
+        logger.info("Mise à jour de : {}".format(titre["docId"]))
 
-    if memobs:
-        memobs += "\r\n"
+        if memobs:
+            memobs += "\r\n"
 
-    dt = datetime.strptime(titre["dateTitre"][:10], "%Y-%m-%d").strftime("%d/%m/%Y")
+        dt = datetime.strptime(titre["dateTitre"][:10], "%Y-%m-%d").strftime("%d/%m/%Y")
 
-    memobs += r"Titre n° : {} le {}".format(titre["titreId"], dt)
+        memobs += r"Titre n° : {} le {}".format(titre["titreId"], dt)
 
-    factures_db.execute("UPDATE tblPieceEntete SET [memObsInterne]='{}' WHERE {}".format(memobs, strfilter))
+        factures_db.execute("UPDATE tblPieceEntete SET [memObsInterne]='{}' WHERE {}".format(memobs, strfilter))
 
-    updated += 1
+        updated += 1
 
-logger.info("Operation ended: {} lines analysed, {} updated".format(analysed, updated))
+    logger.info("{} lines analysées, {} mises à jour".format(analysed, updated))
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 76 - 69
mails_rappel_ctrl.py

@@ -66,72 +66,79 @@ CONTENT = """<p>Bonjour,</p>
 
 ##-----------------------------------------------
 
-# #     INITIALISATION
-
-db = ControlesDb()
-
-# Sous requête: liste les chantiers compactage/étanchéite/video.
-# si le chantier est en état A1, a1_status est Vrai
-subsql = """SELECT tblCompactageBases.lngChantierId,
-                (tblCompactageBases.bytStatus=45 Or tblCompactageBases.bytStatus=46 Or tblCompactageBases.bytStatus=47) AS a1_status,
-                tblCompactageBases.dtmStatus AS since
-            FROM tblCompactageBases
-            UNION
-            SELECT tblEtancheiteBases.lngChantierId,
-                (tblEtancheiteBases.bytStatus=45 Or tblEtancheiteBases.bytStatus=46 Or tblEtancheiteBases.bytStatus=47) AS a1_status,
-                tblEtancheiteBases.dtmStatus AS since
-            FROM tblEtancheiteBases
-            UNION
-            SELECT tblVideoBases.lngChantierId,
-                (tblVideoBases.bytStatus=45 Or tblVideoBases.bytStatus=46 Or tblVideoBases.bytStatus=47) AS a1_status,
-                tblVideoBases.dtmStatus AS since
-            FROM tblVideoBases
-            """
-
-# Selectionne les chantiers pour lesquels un mail doit être envoyé
-sql = """SELECT tblChantiers.lngChantierId, tblChantiers.mailContact, tblChantiers.strInterlEntreprise,
-            tblChantiers.stopMails, statuts.a1_status, statuts.since
-        FROM tblChantiers INNER JOIN ({subsql}) as statuts ON tblChantiers.lngChantierId = statuts.lngChantierId
-        WHERE statuts.a1_status=True
-            AND tblChantiers.lngChantierId>={depart}
-            AND statuts.since<=(Date()-{seuil})
-            ;
-        """.format(subsql=subsql,
-                   seuil=SEUIL_DUREE,
-                   depart=CHANTIER_DEPART)
-
-# #    PROCESS
-qry = db.execute(sql)
-
-for row in qry:
-    chantier_id, mail_to, nom_dest, stop_mails, a1, since = row
-    if DEBUG:
-        mail_to = "olivier.massot@bas-rhin.fr"
-
-    if stop_mails:
-        logger.info("X Chantier %s: l'envoi de mail est bloqué", chantier_id)
-        continue
-
-    if not mail_to:
-        logger.warning("X Chantier %s: pas d'adresse de contact", chantier_id)
-        continue
-
-    logger.info("> Chantier %s: envoi d'un mail à %s", chantier_id, mail_to)
-
-    data = {
-            "Application": "scripts-pde",
-            "Sujet": "Rappel Chantier {chantier_id}".format(chantier_id=chantier_id),
-            "MessageURL": TEMPLATE_URL,
-            "Message": html.escape(CONTENT.format(chantier_id=chantier_id, date_status=since, contact=CONTACT), quote=False),
-            "Email": mail_to,
-            "NomExpediteur": "Script - Parc Départemental d'Erstein",
-            "NomDestinataire": nom_dest
-           }
-
-    r = requests.post("http://t-referentiel.bas-rhin.fr/Facteur/Poste/Contenu", auth=AUTH, data=data)
-
-    logger.info("Transmission du mail au serveur: %s %s", r.status_code, r.text)
-    r.raise_for_status()
-
-    if DEBUG:
-        break
+
+def main():
+
+    # #     INITIALISATION
+    db = ControlesDb()
+
+    # Sous requête: liste les chantiers compactage/étanchéite/video.
+    # si le chantier est en état A1, a1_status est Vrai
+    subsql = """SELECT tblCompactageBases.lngChantierId,
+                    (tblCompactageBases.bytStatus=45 Or tblCompactageBases.bytStatus=46 Or tblCompactageBases.bytStatus=47) AS a1_status,
+                    tblCompactageBases.dtmStatus AS since
+                FROM tblCompactageBases
+                UNION
+                SELECT tblEtancheiteBases.lngChantierId,
+                    (tblEtancheiteBases.bytStatus=45 Or tblEtancheiteBases.bytStatus=46 Or tblEtancheiteBases.bytStatus=47) AS a1_status,
+                    tblEtancheiteBases.dtmStatus AS since
+                FROM tblEtancheiteBases
+                UNION
+                SELECT tblVideoBases.lngChantierId,
+                    (tblVideoBases.bytStatus=45 Or tblVideoBases.bytStatus=46 Or tblVideoBases.bytStatus=47) AS a1_status,
+                    tblVideoBases.dtmStatus AS since
+                FROM tblVideoBases
+                """
+
+    # Selectionne les chantiers pour lesquels un mail doit être envoyé
+    sql = """SELECT tblChantiers.lngChantierId, tblChantiers.mailContact, tblChantiers.strInterlEntreprise,
+                tblChantiers.stopMails, statuts.a1_status, statuts.since
+            FROM tblChantiers INNER JOIN ({subsql}) as statuts ON tblChantiers.lngChantierId = statuts.lngChantierId
+            WHERE statuts.a1_status=True
+                AND tblChantiers.lngChantierId>={depart}
+                AND statuts.since<=(Date()-{seuil})
+                ;
+            """.format(subsql=subsql,
+                       seuil=SEUIL_DUREE,
+                       depart=CHANTIER_DEPART)
+
+    # #    PROCESS
+    qry = db.execute(sql)
+
+    for row in qry:
+        chantier_id, mail_to, nom_dest, stop_mails, _, since = row
+        if DEBUG:
+            mail_to = "olivier.massot@bas-rhin.fr"
+
+        if stop_mails:
+            logger.info("X Chantier %s: l'envoi de mail est bloqué", chantier_id)
+            continue
+
+        if not mail_to:
+            logger.warning("X Chantier %s: pas d'adresse de contact", chantier_id)
+            continue
+
+        logger.info("> Chantier %s: envoi d'un mail à %s", chantier_id, mail_to)
+
+        data = {
+                "Application": "scripts-pde",
+                "Sujet": "Rappel Chantier {chantier_id}".format(chantier_id=chantier_id),
+                "MessageURL": TEMPLATE_URL,
+                "Message": html.escape(CONTENT.format(chantier_id=chantier_id, date_status=since, contact=CONTACT), quote=False),
+                "Email": mail_to,
+                "NomExpediteur": "Script - Parc Départemental d'Erstein",
+                "NomDestinataire": nom_dest
+               }
+
+        r = requests.post("http://t-referentiel.bas-rhin.fr/Facteur/Poste/Contenu", auth=AUTH, data=data)
+
+        logger.info("Transmission du mail au serveur: %s %s", r.status_code, r.text)
+        r.raise_for_status()
+
+        if DEBUG:
+            break
+
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 98 - 107
pda2suiviactivite.py

@@ -20,8 +20,6 @@ from core.pde import PdaDb, PDA_FILES_DEST
 logger = logging.getLogger("pda2suiviactivite")
 logconf.start("pda2suiviactivite", logging.DEBUG)
 
-logger.info("Initialization")
-
 
 # # POUR TESTER, décommenter les lignes suivantes
 ##-----------------------------------------------
@@ -33,108 +31,101 @@ logger.info("Initialization")
 
 ##-----------------------------------------------
 
-
-# Connect to db_PDA.mdb
-pda_db = PdaDb(autocommit=True)
-
-
-# Explication du mapping ci dessous:
-    # (
-    #  nom de la table,
-    #  {champ: noeud),
-    #  nom des nodes contenant chaque enregistrement,
-    #  nom du fichier cible
-    # Order by
-    # )
-
-class PdaXmlFile():
-    def __init__(self):
-        self.source_table = ""
-        self.fields_mapping = {}
-        self.root_node = ""
-        self.item_node = ""
-        self.file_name = ""
-        self.order_by = []
-        self.nsmap = {'xsd': "http://www.w3.org/2001/XMLSchema",
-                      'xsi': "http://www.w3.org/2001/XMLSchema-instance"}
-
-    def create(self):
-        maker = builder.ElementMaker(nsmap=self.nsmap)
-
-        nodes = []
-
-        sql = "SELECT {mapping} FROM {tbl} ORDER BY {order}".format(
-                                                                mapping=",".join(["[{}].[{}] as {}".format(self.source_table, key, val) for key, val in self.fields_mapping.items()]),
-                                                                tbl=self.source_table,
-                                                                order=",".join(self.order_by)
-                                                                )
-        data = [record._asdict() for record in pda_db.read_all(sql)]
-
-        for record in data:
-            node = maker.__call__(self.item_node, *[maker.__call__(field, str(value)) for field, value in record.items()])
-            nodes.append(node)
-
-        root = maker.__call__(self.root_node, *nodes)
-
-        with open(PDA_FILES_DEST / self.file_name, "wb") as f:
-            f.write(etree.tostring(root, xml_declaration=True, encoding='utf-8', pretty_print=True))
-        logger.info("> Créé: {}".format(self.file_name))
-
-
-xml = PdaXmlFile()
-xml.source_table = "pdaEngin"
-xml.fields_mapping = {"strEnginId": "Id", "strEnginLibelleLong": "Nom"}
-xml.root_node = "ArrayOfAttelage"
-xml.item_node = "Attelage"
-xml.file_name = "attelages.xml"
-xml.order_by = ["strEnginLibelleLong"]
-xml.create()
-
-xml = PdaXmlFile()
-xml.source_table = "pdaorigine"
-xml.fields_mapping = {"lngorigine": "Id", "strorigine": "Nom"}
-xml.root_node = "ArrayOfDepart"
-xml.item_node = "Depart"
-xml.file_name = "depart.xml"
-xml.order_by = ["strorigine"]
-xml.create()
-
-xml = PdaXmlFile()
-xml.source_table = "pdaHeures"
-xml.fields_mapping = {"annee": "annee", "mois": "mois", "heures": "heures"}
-xml.root_node = "dataroot"
-xml.item_node = "pdaHeures"
-xml.file_name = "heures.xml"
-xml.order_by = ["annee", "mois"]
-xml.create()
-
-xml = PdaXmlFile()
-xml.source_table = "pdaEquip"
-xml.fields_mapping = {"strEquipesId": "Id", "strEquipesLibelle": "Nom"}
-xml.root_node = "ArrayOfEquipe"
-xml.item_node = "Equipe"
-xml.file_name = "equipes.xml"
-xml.order_by = ["strEquipesLibelle"]
-xml.create()
-
-xml = PdaXmlFile()
-xml.source_table = "pdalocalisation"
-xml.fields_mapping = {"lngTiersId": "Id", "strTiersMnemo": "Nom"}
-xml.root_node = "ArrayOfLocalisation"
-xml.item_node = "Localisation"
-xml.file_name = "localisation.xml"
-xml.order_by = ["strTiersMnemo"]
-xml.create()
-
-xml = PdaXmlFile()
-xml.source_table = "pdaNatInterv"
-xml.fields_mapping = {"strCategorieInterventioinId": "Id", "strCategorieInterventioinLibelle": "Nom"}
-xml.root_node = "ArrayOfNatureRealisation"
-xml.item_node = "NatureRealisation"
-xml.file_name = "naturesinterventions.xml"
-xml.order_by = ["strCategorieInterventioinLibelle"]
-xml.create()
-
-logger.info("Export termine")
-
-
+def main():
+    logger.info("Initialization")
+
+    # Connect to db_PDA.mdb
+    pda_db = PdaDb(autocommit=True)
+
+    class PdaXmlFile():
+        def __init__(self):
+            self.source_table = ""
+            self.fields_mapping = {}
+            self.root_node = ""
+            self.item_node = ""
+            self.file_name = ""
+            self.order_by = []
+            self.nsmap = {'xsd': "http://www.w3.org/2001/XMLSchema",
+                          'xsi': "http://www.w3.org/2001/XMLSchema-instance"}
+
+        def create(self):
+            maker = builder.ElementMaker(nsmap=self.nsmap)
+
+            nodes = []
+
+            sql = "SELECT {mapping} FROM {tbl} ORDER BY {order}".format(
+                                                                    mapping=",".join(["[{}].[{}] as {}".format(self.source_table, key, val) for key, val in self.fields_mapping.items()]),
+                                                                    tbl=self.source_table,
+                                                                    order=",".join(self.order_by)
+                                                                    )
+            data = [record._asdict() for record in pda_db.read_all(sql)]
+
+            for record in data:
+                node = maker.__call__(self.item_node, *[maker.__call__(field, str(value)) for field, value in record.items()])
+                nodes.append(node)
+
+            root = maker.__call__(self.root_node, *nodes)
+
+            with open(PDA_FILES_DEST / self.file_name, "wb") as f:
+                f.write(etree.tostring(root, xml_declaration=True, encoding='utf-8', pretty_print=True))
+            logger.info("> Créé: {}".format(self.file_name))
+
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdaEngin"
+    xml.fields_mapping = {"strEnginId": "Id", "strEnginLibelleLong": "Nom"}
+    xml.root_node = "ArrayOfAttelage"
+    xml.item_node = "Attelage"
+    xml.file_name = "attelages.xml"
+    xml.order_by = ["strEnginLibelleLong"]
+    xml.create()
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdaorigine"
+    xml.fields_mapping = {"lngorigine": "Id", "strorigine": "Nom"}
+    xml.root_node = "ArrayOfDepart"
+    xml.item_node = "Depart"
+    xml.file_name = "depart.xml"
+    xml.order_by = ["strorigine"]
+    xml.create()
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdaHeures"
+    xml.fields_mapping = {"annee": "annee", "mois": "mois", "heures": "heures"}
+    xml.root_node = "dataroot"
+    xml.item_node = "pdaHeures"
+    xml.file_name = "heures.xml"
+    xml.order_by = ["annee", "mois"]
+    xml.create()
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdaEquip"
+    xml.fields_mapping = {"strEquipesId": "Id", "strEquipesLibelle": "Nom"}
+    xml.root_node = "ArrayOfEquipe"
+    xml.item_node = "Equipe"
+    xml.file_name = "equipes.xml"
+    xml.order_by = ["strEquipesLibelle"]
+    xml.create()
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdalocalisation"
+    xml.fields_mapping = {"lngTiersId": "Id", "strTiersMnemo": "Nom"}
+    xml.root_node = "ArrayOfLocalisation"
+    xml.item_node = "Localisation"
+    xml.file_name = "localisation.xml"
+    xml.order_by = ["strTiersMnemo"]
+    xml.create()
+
+    xml = PdaXmlFile()
+    xml.source_table = "pdaNatInterv"
+    xml.fields_mapping = {"strCategorieInterventioinId": "Id", "strCategorieInterventioinLibelle": "Nom"}
+    xml.root_node = "ArrayOfNatureRealisation"
+    xml.item_node = "NatureRealisation"
+    xml.file_name = "naturesinterventions.xml"
+    xml.order_by = ["strCategorieInterventioinLibelle"]
+    xml.create()
+
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 164 - 159
qgis_sync_compactage.py

@@ -38,167 +38,172 @@ logconf.start("qgis_sync_compactage", logging.DEBUG)
 
 ##-----------------------------------------------
 
-Sql = SqlFormatter()
+def main():
 
-# Connexion à ControlesSig (postgres)
-csig_db = CSigDb(autocommit=False)
-
-# Connexion à Controles
-controles_db = ControlesDb(autocommit=False)
-
-# Regex pour parser les noms de repertoires
-rxi = re.compile(r"^(\d{5,6})([-_]S?\d*)?[\s_]?(.*)$")  # non importés
-
-a_importer = [subdir for subdir in COMPACTAGE_DIR.dirs() if rxi.search(subdir.name)]
-
-if a_importer:
-    a_importer = select_list_dialog.exec_(a_importer, lambda x: x.name, "Sélectionnez les chantiers à importer")
-
-if not a_importer:
-    logger.info("Aucun nouveau dossier à importer")
-    sys.exit()
-
-chantiers = []
-
-# ** Read the data in the shapefiles and store it in memory **
-for chantier_dir_path in a_importer:
-    logger.info("** %s", chantier_dir_path)
-
-    # instanciate a new Chantier
-    chantier = QGisChantier()
-    chantier.dir_path = chantier_dir_path
-
-    logger.debug("> parse path")
-    parsed = rxi.search(chantier_dir_path.name)
-    chantier.number = parsed.group(1)
-    chantier.complement = parsed.group(2).replace("_", "-") if parsed.group(2) else ""
-    logger.debug("> number: %s, compl: %s", chantier.number, chantier.complement)
-
-    # query for name in ControlesDb
-    logger.debug("> Query ControlesDb for chantier's name")
-    row = controles_db.first("""SELECT tblChantiers.lngChantierId, tblCollectivites.strNom
-                                      FROM tblChantiers INNER JOIN tblCollectivites
-                                      ON tblChantiers.strCollectiviteId = tblCollectivites.strCollectiviteId
-                                      WHERE lngChantierId = {lngChantierId};""".format(lngChantierId=chantier.number))
-
-    chantier.name = "{}{} {}".format(chantier.number, chantier.complement, row.strNom)
-    logger.debug("> {}".format(chantier.name))
-
-    # importe le fichier shape dans une couche temp
-    shp_path = chantier_dir_path / "{}_p_PointCompactage.shp".format(chantier_dir_path.name)
-
-    logger.debug("Read the shapefile: %s", shp_path)
-
-    sf = shapefile.Reader(shp_path)
-
-    # should we check? :
-    if sf.shapeType != 1:
-        logger.error("Le fichier shapefile n'est pas de type POINT")
-        sys.exit(1)
-
-    sh_points = sf.shapeRecords()
-    if not sh_points:
-        logger.error("Le fichier shapefile ne contient aucune donnees")
-        sys.exit(1)
-
-    chantier.points = []
-    for sh_point in sh_points:
-
-        # create the Point instance
-        point = QGisPoint()
-        point.number = sh_point.record[0]
-        point.name = sh_point.record[1]
-        point.x, point.y = sh_point.shape.points[0]
-        logger.debug("> {}".format(point))
-        chantier.points.append(point)
-
-    del sf, sh_points
-
-    # compute the chantier's rect coordinates
-    logger.debug("Compute the chantier's rect coordinates")
-    chantier.x0 = min([point.x for point in chantier.points]) - 5
-    chantier.x1 = max([point.x for point in chantier.points]) + 5
-    chantier.y0 = min([point.y for point in chantier.points]) - 5
-    chantier.y1 = max([point.y for point in chantier.points]) + 5
-    logger.debug("> ({}, {}, {}, {})".format(chantier.x0, chantier.x1, chantier.y0, chantier.y1))
-
-    chantiers.append(chantier)
-
-# ** Insère les chantiers dans la base **
-
-for chantier in chantiers:
-    logger.debug("** Chantier {}: {} points".format(chantier.name, len(chantier.points)))
-
-    # Contrôle si un chantier de compactage portant ce nom n'existe pas déjà dans la base
-    qry = csig_db.read(u"SELECT id FROM t_chantiers WHERE nom = '{}' AND id_type_chantier=2;".format(chantier.name))
-    for row in qry:
-        logger.warning("Un chantier de compactage portant ce nom existe déjà '{}' (id {})".format(chantier.name, row.id))
-        if input("Voulez-vous le remplacer? (o/n)") == "o":
-            # delete the old chantier
-            csig_db.execute("""DELETE FROM t_points_compactage
-                              WHERE id_chantier = {};""".format(row.id))
-            csig_db.execute("""DELETE FROM t_chantiers
-                              WHERE id = {};""".format(row.id))
-            logger.info("> L'ancien chantier a été supprimé".format(chantier.name, row.id))
-        else:
-            logger.warning("Import du chantier annulé")
-            continue
-
-    # Créé le chantier
-    q = csig_db.first("""INSERT INTO t_chantiers(id_type_chantier, numero, nom, geom, archive)
-                           VALUES ({chantier_type}, {number}, '{name}', {geom}, {archive})
-                           RETURNING id;
-                        """.format(
-                            chantier_type=2,
-                            number=chantier.number,
-                            name=chantier.name,
-                            geom="ST_GeomFromText('POLYGON(({x0} {y0}, \
-                            {x0} {y1}, {x1} {y1}, {x1} {y0}, {x0} {y0}))', {srid})".format(x0=chantier.x0,
-                                                                                           x1=chantier.x1,
-                                                                                           y0=chantier.y0,
-                                                                                           y1=chantier.y1,
-                                                                                           srid=SRID),
-                            archive="FALSE"
-                            )
-                       )
-
-    # get its postgis ID
-    logger.debug("Getting newly created ID")
-    chantier.pgid = q.id
-    logger.debug("> {}".format(chantier.pgid))
-    q = None
-
-    # create the points
-    for point in chantier.points:
-        csig_db.execute("""INSERT INTO t_points_compactage(numero, nom, id_chantier, geom, archive)
-                           VALUES ({number}, '{name}', {chantier_id}, ST_GeomFromText('POINT({x} {y})', {srid}), False);
-                        """.format(
-                            number=point.number,
-                            name=point.name,
-                            chantier_id=chantier.pgid,
-                            x=point.x,
-                            y=point.y,
-                            srid=SRID
-                            )
-                       )
-
-    csig_db.commit()
-
-    # rename the directory to mark it as imported ('I_')
-    new_path = r"{}\I_{}".format(chantier.dir_path.parent, chantier.dir_path.name)
-    logger.debug("Rename {} to {}".format(chantier.dir_path, new_path))
-
-    try:
-        chantier.dir_path.rename(new_path)
-    except:
-        logger.error("Impossible de renommer le dossier")
-
-    logger.info("Le chantier %s a été importé", chantier.name)
-
-
-csig_db.close()
-controles_db.close()
+    Sql = SqlFormatter()
 
+    # Connexion à ControlesSig (postgres)
+    csig_db = CSigDb(autocommit=False)
 
+    # Connexion à Controles
+    controles_db = ControlesDb(autocommit=False)
+
+    # Regex pour parser les noms de repertoires
+    rxi = re.compile(r"^(\d{5,6})([-_]S?\d*)?[\s_]?(.*)$")  # non importés
+
+    a_importer = [subdir for subdir in COMPACTAGE_DIR.dirs() if rxi.search(subdir.name)]
+
+    if a_importer:
+        a_importer = select_list_dialog.exec_(a_importer, lambda x: x.name, "Sélectionnez les chantiers à importer")
+
+    if not a_importer:
+        logger.info("Aucun nouveau dossier à importer")
+        sys.exit()
+
+    chantiers = []
+
+    # ** Read the data in the shapefiles and store it in memory **
+    for chantier_dir_path in a_importer:
+        logger.info("** %s", chantier_dir_path)
+
+        # instanciate a new Chantier
+        chantier = QGisChantier()
+        chantier.dir_path = chantier_dir_path
+
+        logger.debug("> parse path")
+        parsed = rxi.search(chantier_dir_path.name)
+        chantier.number = parsed.group(1)
+        chantier.complement = parsed.group(2).replace("_", "-") if parsed.group(2) else ""
+        logger.debug("> number: %s, compl: %s", chantier.number, chantier.complement)
+
+        # query for name in ControlesDb
+        logger.debug("> Query ControlesDb for chantier's name")
+        row = controles_db.first("""SELECT tblChantiers.lngChantierId, tblCollectivites.strNom
+                                          FROM tblChantiers INNER JOIN tblCollectivites
+                                          ON tblChantiers.strCollectiviteId = tblCollectivites.strCollectiviteId
+                                          WHERE lngChantierId = {lngChantierId};""".format(lngChantierId=chantier.number))
+
+        chantier.name = "{}{} {}".format(chantier.number, chantier.complement, row.strNom)
+        logger.debug("> {}".format(chantier.name))
+
+        # importe le fichier shape dans une couche temp
+        shp_path = chantier_dir_path / "{}_p_PointCompactage.shp".format(chantier_dir_path.name)
+
+        logger.debug("Read the shapefile: %s", shp_path)
+
+        sf = shapefile.Reader(shp_path)
+
+        # should we check? :
+        if sf.shapeType != 1:
+            logger.error("Le fichier shapefile n'est pas de type POINT")
+            sys.exit(1)
+
+        sh_points = sf.shapeRecords()
+        if not sh_points:
+            logger.error("Le fichier shapefile ne contient aucune donnees")
+            sys.exit(1)
+
+        chantier.points = []
+        for sh_point in sh_points:
+
+            # create the Point instance
+            point = QGisPoint()
+            point.number = sh_point.record[0]
+            point.name = sh_point.record[1]
+            point.x, point.y = sh_point.shape.points[0]
+            logger.debug("> {}".format(point))
+            chantier.points.append(point)
+
+        del sf, sh_points
+
+        # compute the chantier's rect coordinates
+        logger.debug("Compute the chantier's rect coordinates")
+        chantier.x0 = min([point.x for point in chantier.points]) - 5
+        chantier.x1 = max([point.x for point in chantier.points]) + 5
+        chantier.y0 = min([point.y for point in chantier.points]) - 5
+        chantier.y1 = max([point.y for point in chantier.points]) + 5
+        logger.debug("> ({}, {}, {}, {})".format(chantier.x0, chantier.x1, chantier.y0, chantier.y1))
+
+        chantiers.append(chantier)
+
+    # ** Insère les chantiers dans la base **
+
+    for chantier in chantiers:
+        logger.debug("** Chantier {}: {} points".format(chantier.name, len(chantier.points)))
+
+        # Contrôle si un chantier de compactage portant ce nom n'existe pas déjà dans la base
+        qry = csig_db.read(u"SELECT id FROM t_chantiers WHERE nom = '{}' AND id_type_chantier=2;".format(chantier.name))
+        for row in qry:
+            logger.warning("Un chantier de compactage portant ce nom existe déjà '{}' (id {})".format(chantier.name, row.id))
+            if input("Voulez-vous le remplacer? (o/n)") == "o":
+                # delete the old chantier
+                csig_db.execute("""DELETE FROM t_points_compactage
+                                  WHERE id_chantier = {};""".format(row.id))
+                csig_db.execute("""DELETE FROM t_chantiers
+                                  WHERE id = {};""".format(row.id))
+                logger.info("> L'ancien chantier a été supprimé".format(chantier.name, row.id))
+            else:
+                logger.warning("Import du chantier annulé")
+                continue
+
+        # Créé le chantier
+        q = csig_db.first("""INSERT INTO t_chantiers(id_type_chantier, numero, nom, geom, archive)
+                               VALUES ({chantier_type}, {number}, '{name}', {geom}, {archive})
+                               RETURNING id;
+                            """.format(
+                                chantier_type=2,
+                                number=chantier.number,
+                                name=chantier.name,
+                                geom="ST_GeomFromText('POLYGON(({x0} {y0}, \
+                                {x0} {y1}, {x1} {y1}, {x1} {y0}, {x0} {y0}))', {srid})".format(x0=chantier.x0,
+                                                                                               x1=chantier.x1,
+                                                                                               y0=chantier.y0,
+                                                                                               y1=chantier.y1,
+                                                                                               srid=SRID),
+                                archive="FALSE"
+                                )
+                           )
+
+        # get its postgis ID
+        logger.debug("Getting newly created ID")
+        chantier.pgid = q.id
+        logger.debug("> {}".format(chantier.pgid))
+        q = None
+
+        # create the points
+        for point in chantier.points:
+            csig_db.execute("""INSERT INTO t_points_compactage(numero, nom, id_chantier, geom, archive)
+                               VALUES ({number}, '{name}', {chantier_id}, ST_GeomFromText('POINT({x} {y})', {srid}), False);
+                            """.format(
+                                number=point.number,
+                                name=point.name,
+                                chantier_id=chantier.pgid,
+                                x=point.x,
+                                y=point.y,
+                                srid=SRID
+                                )
+                           )
+
+        csig_db.commit()
+
+        # rename the directory to mark it as imported ('I_')
+        new_path = r"{}\I_{}".format(chantier.dir_path.parent, chantier.dir_path.name)
+        logger.debug("Rename {} to {}".format(chantier.dir_path, new_path))
+
+        try:
+            chantier.dir_path.rename(new_path)
+        except:
+            logger.error("Impossible de renommer le dossier")
+
+        logger.info("Le chantier %s a été importé", chantier.name)
+
+
+    csig_db.close()
+    controles_db.close()
+
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")
 
 

+ 71 - 67
qgis_sync_etancheite.py

@@ -39,94 +39,98 @@ IMPORT_DEPUIS = 24  # Ne cherche des données à importer que sur les X derniers
 
 ##-----------------------------------------------
 
-# Connexion à ControlesSig (postgres)
-csig_db = CSigDb(autocommit=False)
+def main():
 
-# Connexion à Controles
-controles_db = ControlesDb(autocommit=False)
+    # Connexion à ControlesSig (postgres)
+    csig_db = CSigDb(autocommit=False)
 
-# Connexion à Wincan
-wincan_db = WincanDb(autocommit=False)
+    # Connexion à Controles
+    controles_db = ControlesDb(autocommit=False)
 
+    # Connexion à Wincan
+    wincan_db = WincanDb(autocommit=False)
 
-# filter results on the last X months, depending on corresponding parameter
-date_min = datetime.today() - relativedelta(months=IMPORT_DEPUIS) if IMPORT_DEPUIS >= 0 else datetime(1899, 12, 30, 0, 0, 0)
 
+    # filter results on the last X months, depending on corresponding parameter
+    date_min = datetime.today() - relativedelta(months=IMPORT_DEPUIS) if IMPORT_DEPUIS >= 0 else datetime(1899, 12, 30, 0, 0, 0)
 
-logger.info("Loading data")
 
-# NB: utiliser une erquête access pré-enregistrée permet d'améliorer les perfs (la requete est pré-compilée)
-qessais = controles_db.read(Sql.format("""SELECT tblEtancheiteResultats.lngChantierId, tblEtancheitePartChantiers.strTrcRegard, tblTypeEssais.strObjetEssai, tblEtancheiteResultats.strResSigne, tblEtancheiteResultats.dtmEssai
-                                            FROM ((tblTypeEssais INNER JOIN tblEtancheitePartChantiers ON tblTypeEssais.bytTypeEssaiId = tblEtancheitePartChantiers.bytTypeEssai) INNER JOIN
-                                                   tblEtancheiteResultats ON (tblEtancheitePartChantiers.bytPartChantierId = tblEtancheiteResultats.bytPartChantierId) AND
-                                                   (tblEtancheitePartChantiers.lngChantierId = tblEtancheiteResultats.lngChantierId)) INNER JOIN (SELECT tblEtancheiteResultats.lngChantierId, Max(tblEtancheiteResultats.bytIntervId) AS MaxDebytIntervId, tblEtancheiteResultats.bytPartChantierId
-                                            FROM tblEtancheiteResultats
-                                            GROUP BY tblEtancheiteResultats.lngChantierId, tblEtancheiteResultats.bytPartChantierId) as filter_intervs ON
-                                                   (tblEtancheiteResultats.bytPartChantierId = filter_intervs.bytPartChantierId) AND
-                                                   (tblEtancheiteResultats.bytIntervId = filter_intervs.MaxDebytIntervId) AND
-                                                   (tblEtancheiteResultats.lngChantierId = filter_intervs.lngChantierId)
-                                            WHERE tblEtancheiteResultats.dtmEssai>{:date};
-                                            """, date_min))
+    logger.info("Loading data")
 
-nb_r, nb_t = 0, 0
+    # NB: utiliser une erquête access pré-enregistrée permet d'améliorer les perfs (la requete est pré-compilée)
+    qessais = controles_db.read(Sql.format("""SELECT tblEtancheiteResultats.lngChantierId, tblEtancheitePartChantiers.strTrcRegard, tblTypeEssais.strObjetEssai, tblEtancheiteResultats.strResSigne, tblEtancheiteResultats.dtmEssai
+                                                FROM ((tblTypeEssais INNER JOIN tblEtancheitePartChantiers ON tblTypeEssais.bytTypeEssaiId = tblEtancheitePartChantiers.bytTypeEssai) INNER JOIN
+                                                       tblEtancheiteResultats ON (tblEtancheitePartChantiers.bytPartChantierId = tblEtancheiteResultats.bytPartChantierId) AND
+                                                       (tblEtancheitePartChantiers.lngChantierId = tblEtancheiteResultats.lngChantierId)) INNER JOIN (SELECT tblEtancheiteResultats.lngChantierId, Max(tblEtancheiteResultats.bytIntervId) AS MaxDebytIntervId, tblEtancheiteResultats.bytPartChantierId
+                                                FROM tblEtancheiteResultats
+                                                GROUP BY tblEtancheiteResultats.lngChantierId, tblEtancheiteResultats.bytPartChantierId) as filter_intervs ON
+                                                       (tblEtancheiteResultats.bytPartChantierId = filter_intervs.bytPartChantierId) AND
+                                                       (tblEtancheiteResultats.bytIntervId = filter_intervs.MaxDebytIntervId) AND
+                                                       (tblEtancheiteResultats.lngChantierId = filter_intervs.lngChantierId)
+                                                WHERE tblEtancheiteResultats.dtmEssai>{:date};
+                                                """, date_min))
 
-for essai in qessais:
+    nb_r, nb_t = 0, 0
 
-    # L'essai concerne un regard
-    if essai.strObjetEssai == "R":
-        # # Cherche le regard correspondant dans qregards
+    for essai in qessais:
 
-        regard = csig_db.first(Sql.format("""SELECT t_regards.id, t_regards.res_ce
-                                    FROM t_regards INNER JOIN t_chantiers ON t_regards.id_chantier = t_chantiers.id
-                                    WHERE t_regards.nom={:text} AND t_chantiers.numero={} AND t_regards.archive=False
-                                    """, essai.strTrcRegard, essai.lngChantierId))
+        # L'essai concerne un regard
+        if essai.strObjetEssai == "R":
+            # # Cherche le regard correspondant dans qregards
 
-        if not regard:
-            continue
+            regard = csig_db.first(Sql.format("""SELECT t_regards.id, t_regards.res_ce
+                                        FROM t_regards INNER JOIN t_chantiers ON t_regards.id_chantier = t_chantiers.id
+                                        WHERE t_regards.nom={:text} AND t_chantiers.numero={} AND t_regards.archive=False
+                                        """, essai.strTrcRegard, essai.lngChantierId))
 
-        if regard.res_ce != essai.strResSigne:
-            q = csig_db.execute(Sql.format("""UPDATE t_regards
-                                              SET res_ce = {res_ce:text}
-                                              WHERE id = {pgid}
-                                              """, res_ce=essai.strResSigne, pgid=regard.id))
-            nb_r += 1
-            logger.info("Résultat mis à jour: %s, %s > %s", essai.lngChantierId, essai.strTrcRegard, essai.strResSigne)
+            if not regard:
+                continue
 
+            if regard.res_ce != essai.strResSigne:
+                q = csig_db.execute(Sql.format("""UPDATE t_regards
+                                                  SET res_ce = {res_ce:text}
+                                                  WHERE id = {pgid}
+                                                  """, res_ce=essai.strResSigne, pgid=regard.id))
+                nb_r += 1
+                logger.info("Résultat mis à jour: %s, %s > %s", essai.lngChantierId, essai.strTrcRegard, essai.strResSigne)
 
-    # L'essai concerne un tronçon
-    elif essai.strObjetEssai == "T":
 
-        # Parse le nom de tronçon
-        regex = re.search(r"^(\S*)\s?-\s?(\S*)$", essai.strTrcRegard)
-        if not regex:
-            logger.error(u"Nom de tronçon invalide: '{}' (chantier: {})".format(essai.strTrcRegard, essai.lngChantierId))
-            continue
-        r1, r2 = regex.group(1), regex.group(2)
+        # L'essai concerne un tronçon
+        elif essai.strObjetEssai == "T":
 
-        troncon = csig_db.first(Sql.format("""SELECT t_troncons.id, t_chantiers.numero, t_regards_1.nom AS r1,
-                                    t_regards.nom AS r2, t_troncons.res_ce, t_troncons.nom as nom
-                                    FROM ((t_troncons INNER JOIN t_chantiers ON t_troncons.id_chantier = t_chantiers.id)
-                                    INNER JOIN t_regards AS t_regards_1 ON t_troncons.id_regard_depart = t_regards_1.id)
-                                    INNER JOIN t_regards ON t_troncons.id_regard_fin = t_regards.id
-                                    WHERE ((t_regards_1.nom={r1:text} AND t_regards.nom={r2:text})
-                                        OR (t_regards_1.nom={r2:text} AND t_regards.nom={r1:text}))
-                                        AND t_chantiers.numero={num_chantier} AND t_troncons.archive=False
-                                    """, r1=r1, r2=r2, num_chantier=essai.lngChantierId))
+            # Parse le nom de tronçon
+            regex = re.search(r"^(\S*)\s?-\s?(\S*)$", essai.strTrcRegard)
+            if not regex:
+                logger.error(u"Nom de tronçon invalide: '{}' (chantier: {})".format(essai.strTrcRegard, essai.lngChantierId))
+                continue
+            r1, r2 = regex.group(1), regex.group(2)
 
-        if not troncon:
-            continue
+            troncon = csig_db.first(Sql.format("""SELECT t_troncons.id, t_chantiers.numero, t_regards_1.nom AS r1,
+                                        t_regards.nom AS r2, t_troncons.res_ce, t_troncons.nom as nom
+                                        FROM ((t_troncons INNER JOIN t_chantiers ON t_troncons.id_chantier = t_chantiers.id)
+                                        INNER JOIN t_regards AS t_regards_1 ON t_troncons.id_regard_depart = t_regards_1.id)
+                                        INNER JOIN t_regards ON t_troncons.id_regard_fin = t_regards.id
+                                        WHERE ((t_regards_1.nom={r1:text} AND t_regards.nom={r2:text})
+                                            OR (t_regards_1.nom={r2:text} AND t_regards.nom={r1:text}))
+                                            AND t_chantiers.numero={num_chantier} AND t_troncons.archive=False
+                                        """, r1=r1, r2=r2, num_chantier=essai.lngChantierId))
 
-        if troncon.res_ce != essai.strResSigne:
-            q = csig_db.execute(Sql.format("""UPDATE t_troncons
-                                              SET res_ce = {res_ce:text}
-                                              WHERE id = {pgid};""", res_ce=essai.strResSigne, pgid=troncon.id))
-            nb_t += 1
-            logger.info("Résultat mis à jour: %s, %s > %s", essai.lngChantierId, essai.strTrcRegard, essai.strResSigne)
+            if not troncon:
+                continue
 
-    csig_db.commit()
+            if troncon.res_ce != essai.strResSigne:
+                csig_db.execute(Sql.format("""UPDATE t_troncons
+                                                  SET res_ce = {res_ce:text}
+                                                  WHERE id = {pgid};""", res_ce=essai.strResSigne, pgid=troncon.id))
+                nb_t += 1
+                logger.info("Résultat mis à jour: %s, %s > %s", essai.lngChantierId, essai.strTrcRegard, essai.strResSigne)
 
-logger.info("- Opération terminée -")
-logger.info("%s regards et %s tronçons mis à jour", nb_r, nb_t)
+        csig_db.commit()
 
+    logger.info("%s regards et %s tronçons mis à jour", nb_r, nb_t)
 
 
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 2 - 0
qgis_sync_video.py

@@ -263,3 +263,5 @@ if __name__ == "__main__":
     qgis_sync_wincan.main()
 
     main()
+
+    logger.info("-- Fin --")

+ 29 - 22
qgis_sync_videores.py

@@ -35,32 +35,39 @@ IMPORT_DEPUIS = 24  # Ne cherche des données à importer que sur les X derniers
 
 ##-----------------------------------------------
 
-# Connexion à ControlesSig (postgres)
-csig_db = CSigDb(autocommit=False)
+def main():
 
-# Connexion à Controles
-controles_db = ControlesDb(autocommit=False)
+    # Connexion à ControlesSig (postgres)
+    csig_db = CSigDb(autocommit=False)
 
-# filter results on the last X months, depending on corresponding parameter
-date_min = datetime.today() - relativedelta(months=IMPORT_DEPUIS) if IMPORT_DEPUIS >= 0 else datetime(1899, 12, 30, 0, 0, 0)
+    # Connexion à Controles
+    controles_db = ControlesDb(autocommit=False)
 
-logger.info("Chargement des données")
-qessais = controles_db.read(Sql.format("SELECT lngChantierId, SI_AutoNumber, DG FROM csig_itv_results WHERE SI_Date > {:date}", date_min))
+    # filter results on the last X months, depending on corresponding parameter
+    date_min = datetime.today() - relativedelta(months=IMPORT_DEPUIS) if IMPORT_DEPUIS >= 0 else datetime(1899, 12, 30, 0, 0, 0)
 
-for essai in qessais:
+    logger.info("Chargement des données")
+    qessais = controles_db.read(Sql.format("SELECT lngChantierId, SI_AutoNumber, DG FROM csig_itv_results WHERE SI_Date > {:date}", date_min))
 
-    troncon = csig_db.first(Sql.format("""SELECT t_chantiers.numero, t_troncons.si_autonumber, t_troncons.res_itv
-                                          FROM (t_troncons INNER JOIN t_chantiers ON t_troncons.id_chantier = t_chantiers.id)
-                                          WHERE t_troncons.si_autonumber={}
-                                          """, essai.SI_AutoNumber))
-    if not troncon:
-        logger.warning("Le tronçon n'existe pas dans ControlesSIG (si_autonumber=%s)", essai.SI_AutoNumber)
-        continue
+    for essai in qessais:
 
-    if troncon.res_itv != essai.DG:
-        logger.info("Mise à jour du tronçon si_autonumber=%s", essai.SI_AutoNumber)
-        csig_db.execute(Sql.format("""UPDATE t_troncons
-                                      SET res_itv = {dg:text}
-                                      WHERE si_autonumber = {si_autonumber}""", dg=essai.DG, si_autonumber=essai.SI_AutoNumber))
+        troncon = csig_db.first(Sql.format("""SELECT t_chantiers.numero, t_troncons.si_autonumber, t_troncons.res_itv
+                                              FROM (t_troncons INNER JOIN t_chantiers ON t_troncons.id_chantier = t_chantiers.id)
+                                              WHERE t_troncons.si_autonumber={}
+                                              """, essai.SI_AutoNumber))
+        if not troncon:
+            logger.warning("Le tronçon n'existe pas dans ControlesSIG (si_autonumber=%s)", essai.SI_AutoNumber)
+            continue
 
-csig_db.commit()
+        if troncon.res_itv != essai.DG:
+            logger.info("Mise à jour du tronçon si_autonumber=%s", essai.SI_AutoNumber)
+            csig_db.execute(Sql.format("""UPDATE t_troncons
+                                          SET res_itv = {dg:text}
+                                          WHERE si_autonumber = {si_autonumber}""", dg=essai.DG, si_autonumber=essai.SI_AutoNumber))
+
+    csig_db.commit()
+
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")

+ 1 - 2
qgis_sync_wincan.py

@@ -156,8 +156,7 @@ def main():
         except:
             logger.error("Impossible de renommer le dossier")
 
-    logger.info("-- Fin --")
-
 if __name__ == "__main__":
     main()
 
+    logger.info("-- Fin --")

+ 270 - 261
wincan2ctrl.py

@@ -41,281 +41,290 @@ logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
 
 ##-----------------------------------------------
 
-logger.info("Initialisation...")
 
-Sql = SqlFormatter()
+def main():
 
-# Connexion à Controles
-controles_db = ControlesDb(autocommit=False)
+    logger.info("Initialisation...")
 
-# Connexion à Wincan
-wincan_db = WincanDb(autocommit=False)
+    Sql = SqlFormatter()
 
-# Connexion à CommunDb
-piraca_db = PiracaDb(autocommit=False)
+    # Connexion à Controles
+    controles_db = ControlesDb(autocommit=False)
 
+    # Connexion à Wincan
+    wincan_db = WincanDb(autocommit=False)
 
-def get_lib(lib_id, tbl_id):
-    row = piraca_db.first(Sql.format("""SELECT LIB_ID, LIB_LIB, TLB_ID
-                                            FROM LISTE_LIBELLE_WINCAN
-                                            WHERE TLB_ID='{}' AND LIB_ID='{}'""", tbl_id, lib_id))
-    if not row:
-        logger.error("Aucun libelle trouvé dans la table LISTE_LIBELLE_WINCAN pour %s, id=%s", tbl_id, lib_id)
-        raise KeyError()
-    return row.LIB_LIB
+    # Connexion à CommunDb
+    piraca_db = PiracaDb(autocommit=False)
 
 
-def get_materiau(wincan_id):
-    return controles_db.first(Sql.format("""SELECT strMateriauCourt
-                                            FROM tblMateriaux
-                                            WHERE strWincanId='{}'""", wincan_id)).strMateriauCourt
+    def get_lib(lib_id, tbl_id):
+        row = piraca_db.first(Sql.format("""SELECT LIB_ID, LIB_LIB, TLB_ID
+                                                FROM LISTE_LIBELLE_WINCAN
+                                                WHERE TLB_ID='{}' AND LIB_ID='{}'""", tbl_id, lib_id))
+        if not row:
+            logger.error("Aucun libelle trouvé dans la table LISTE_LIBELLE_WINCAN pour %s, id=%s", tbl_id, lib_id)
+            raise KeyError()
+        return row.LIB_LIB
 
-# Parse les arguments
-args = docopt(__doc__, help=False)
-chantier_id = args["-c"] if args["-c"] else 0
-inspname = args["-i"] if args["-i"] else ""
 
-# Demande à l'utilisateur de saisir les informations qui n'ont pas été passées en argument
-if not inspname:
-    # Propose une liste d'inspections possibles
-    sql = """SELECT SI_InspName
-                FROM SI_T
-                WHERE SI_Spare1 Is Null OR Len([SI_Spare1])=0
-                GROUP BY SI_InspName
-            """
-    candidats = wincan_db.read_all(sql)
+    def get_materiau(wincan_id):
+        return controles_db.first(Sql.format("""SELECT strMateriauCourt
+                                                FROM tblMateriaux
+                                                WHERE strWincanId='{}'""", wincan_id)).strMateriauCourt
 
-    print("Veuillez choisir une inspection Wincan à traiter en saisissant son numéro:")
-    for i, candidat in enumerate(candidats):
-        print("[{}] - {}".format(i, candidat.SI_InspName))
+    # Parse les arguments
+    args = docopt(__doc__, help=False)
+    chantier_id = args["-c"] if args["-c"] else 0
+    inspname = args["-i"] if args["-i"] else ""
 
-    while not inspname:
-        reponse = input("> ")
-        try:
-            inspname = candidats[int(reponse)].SI_InspName
-        except ValueError:
-            print("Valeur invalide!")
-
-if not chantier_id:
-    try:
-        default = int(inspname[:6])
-        chantier_id = input("Veuillez saisir le code chantier [{}]: ".format(default))
-        if not chantier_id:
-            chantier_id = default
-    except ValueError:
-        chantier_id = input("Veuillez saisir le code chantier: ")
-        if not chantier_id:
-            logger.error("Code chantier invalide")
-            sys.exit(1)
-
-# Calcul du numéro d'intervention
-last_interv_id = controles_db.first("SELECT max(bytIntervId) as last_interv_id FROM tblVideoIntervs WHERE lngChantierId={}".format(chantier_id)).last_interv_id
-interv_id = last_interv_id + 1 if last_interv_id else 1
-
-# Récupération du numéro de commande en cours
-commande_id = controles_db.first("SELECT bytCommandeId FROM tblVideoBases WHERE lngChantierId={}".format(chantier_id)).bytCommandeId
-
-
-# Affiche les infos et demande confirmation avant ed lancer le traitement
-logger.info("## Traitement de l'inspection Wincan")
-logger.info("Nom chantier: %s", inspname)
-logger.info("Code chantier: %s", chantier_id)
-logger.info("Numero d'intervention: %s", interv_id)
-logger.info("Numero de commande: %s", commande_id)
-if not args["-o"]:
-    if not input("Voulez-vous continuer? (o/n)") == 'o':
-        if input("Etes-vous sûr de vouloir annuler l'opération? (o/n)") == 'o':
-            logger.info("Opération annulée par l'utilisateur")
-            sys.exit(1)
-
-
-# Recuperation des données de l'intervention
-sql = """SELECT SI_T.SI_InspName, Sum(S_T.S_Sectionlength) AS Long_insp, Min(SI_T.SI_Date) AS mindate, Max(SI_T.SI_Date) AS maxdate,
-        First(SI_T.SI_Operator) AS Equipe, First(SI_T.SI_Vehicle) AS Mat, First(SI_T.SI_InspMethod) AS InspMethod,
-        First(SI_T.SI_ReasonOfInspection) AS SI_ReasonOfInspection, First(S_T.S_SectionPurpose) AS nature,
-        First(S_T.S_SectionUse) AS Fonction, First(S_T.S_SectionType) AS Type
-        FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
-        WHERE (((SI_T.[SI_InspName])='{}'))
-        GROUP BY SI_T.SI_InspName
-        HAVING Sum(S_T.S_Sectionlength) Is Not Null AND First(SI_T.SI_Operator) Is Not Null AND First(SI_T.SI_Vehicle) Is Not Null AND First(SI_T.SI_InspMethod) Is Not Null
-        """.format(inspname)
-try:
-    inspection = next(wincan_db.read(sql))
-except StopIteration:
-    logger.error("Aucune inspection trouvée pour le nom '%s'", inspname)
-    raise
-
-# Insère l'inspection dans la table tblVideoIntervs de Controles
-logger.info("Création de l'intervention dans tblVideoIntervs")
-
-interv = InterventionITV()
-
-interv.lngChantierId = chantier_id
-interv.bytIntervId = interv_id
-interv.dtmIntervDu = inspection.mindate
-interv.dtmIntervAu = inspection.maxdate
-interv.strEquipeId = inspection.Equipe
-interv.intlMaterielID = inspection.Mat
-interv.bytCommandeId = commande_id
-interv.lngTroncon = inspection.Long_insp
-interv.SI_InspMethod = inspection.InspMethod
-interv.SI_ReasonOfInspection = get_lib(inspection.SI_ReasonOfInspection, "SI_REASONOfINSPECTION")
-
-sql = Sql.format("""INSERT INTO tblVideoIntervs ( lngChantierId, bytIntervId, dtmIntervDu, dtmIntervAu, strEquipeId,
-                                       intlMaterielID, bytCommandeId, lngTroncon, SI_InspMethod, SI_ReasonOfInspection )
-                    VALUES ({interv.lngChantierId}, {interv.bytIntervId}, {interv.dtmIntervDu:date}, {interv.dtmIntervAu:date}, {interv.strEquipeId:text},
-                            {interv.intlMaterielID}, {interv.bytCommandeId}, {interv.lngTroncon}, {interv.SI_InspMethod:text}, {interv.SI_ReasonOfInspection:text})
-                    """, interv=interv)
-controles_db.execute(sql)
-
-# Met a jour les champs SI_Spare1 et SI_Spare2 de la table Wincan en retour
-logger.info("Mise à jour en retour de SI_T")
-sql = """UPDATE SI_T
-         SET SI_Spare1='{}', SI_Spare2='{}'
-         WHERE SI_InspName='{}'
-         """.format(chantier_id, interv_id, inspection.SI_InspName)
-wincan_db.execute(sql)
-
-# Met a jour la table tbl_so_rate de Controles
-logger.info("Traitement des inspections")
-
-# Extrait les données des tronçons
-
-sql = """SELECT SI_T.SI_ID, S_T.S_ID, SI_T.SI_InspName, SI_T.SI_Spare1, SI_T.SI_Spare2, SI_T.SI_AutoNumber, SI_T.SI_MediaNumber1, S_T.S_SectionFlow,
-            S_T.S_StartNode, S_T.S_EndNode, S_T.S_StartNodeType, S_T.S_EndNodeType, S_T.S_StartNodeCoord_Z, S_T.S_EndNodeCoord_Z,
-            S_T.S_Sectionlength, S_T.S_SectionPurpose, S_T.S_SectionUse, S_T.S_SectionType, S_T.S_PipeMaterial, S_T.S_YearLayed,
-            S_T.S_PipeDia, S_T.S_Situation, S_T.S_Spare1, S_T.S_PipeShape, S_T.S_Pipelength, S_T.S_Spare3,
-            SI_T.SI_Spare0, SI_T.SI_Date
-        FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
-        WHERE SI_T.SI_Spare1='{}' AND SI_T.SI_Spare2='{}'
-        """.format(chantier_id, interv_id)
-
-for data in wincan_db.read(sql):
-
-    logger.info("* Traitement de %s (S_ID: %s)", data.SI_InspName, data.S_ID)
-
-    inspection = InspectionTronconWincan()
-
-    inspection.s_guid = data.S_ID[2:-1] if (data.S_ID[:2] == "b'" and data.S_ID[-1:] == "'") else data.S_ID
-    inspection.si_guid = data.SI_ID[2:-1] if (data.SI_ID[:2] == "b'" and data.SI_ID[-1:] == "'") else data.SI_ID
-    inspection.nom_chantier = data.SI_InspName
-    inspection.lng_chantier_id = data.SI_Spare1
-    inspection.byt_interv_id = data.SI_Spare2
-    inspection.si_autonumber = data.SI_AutoNumber
-    inspection.classement_troncons = data.SI_MediaNumber1
-    inspection.nom_troncon = "{r1}-{r2}".format(r1=data.S_StartNode, r2=data.S_EndNode) if data.S_SectionFlow == '2' else "{r2}-{r1}".format(r1=data.S_StartNode, r2=data.S_EndNode)
-    inspection.startnode_type = get_lib(data.S_StartNodeType, "S_StartNodeType")
-    inspection.endnode_type = get_lib(data.S_StartNodeType, "S_EndNodeType")
-    inspection.sens_ecoul = ">>" if data.S_SectionFlow == '1' else ('<<' if data.S_SectionFlow == '2' else '')
-    inspection.startnode_z = data.S_StartNodeCoord_Z
-    inspection.endnode_z = data.S_EndNodeCoord_Z
-    inspection.section_length = data.S_Sectionlength
-    inspection.section_purpose = data.S_SectionPurpose
-    inspection.section_use = data.S_SectionUse
-    inspection.section_type = data.S_SectionType
-    inspection.materiau = get_materiau(data.S_PipeMaterial)
-    inspection.annee_pose = data.S_YearLayed
-    inspection.diametre = get_lib(int(data.S_PipeDia), "S_PipeDia")
-    inspection.route = get_lib(data.S_Situation, "S_Situation")
-    inspection.n_route = data.S_Spare1
-    inspection.pipe_shape = data.S_PipeShape
-    inspection.pipe_length = data.S_Pipelength
-    if data.S_Spare3:
-        inspection.arbres = get_lib(data.S_Spare3, "S_Spare3")
-    inspection.test_ecoulement = data.SI_Spare0
-    inspection.si_date = data.SI_Date
-
-    sql = """ SELECT SO_T.SO_ID, SO_T.SO_Rate, SO_T.SO_Photonumber1, SO_T.SO_Photonumber2
-                FROM SO_T
-                WHERE SO_T.SO_Inspecs_ID='{}'
-            """.format(inspection.si_guid)
-
-    # Parcours les opérations réalisées au cours de l'inspection du tronçon
-    for opdata in wincan_db.read(sql):
-        inspection.nb_ops += 1
-
-        if opdata.SO_Rate == 1:
-            inspection.rate_1 = inspection.rate_1 + 1 if inspection.rate_1 else 1
-        elif opdata.SO_Rate == 2:
-            inspection.rate_2 = inspection.rate_2 + 1 if inspection.rate_2 else 1
-        elif opdata.SO_Rate == 3:
-            inspection.rate_3 = inspection.rate_3 + 1 if inspection.rate_3 else 1
-        elif opdata.SO_Rate == 4:
-            inspection.rate_4 = inspection.rate_4 + 1 if inspection.rate_4 else 1
-        elif opdata.SO_Rate == 5:
-            inspection.rate_5 = inspection.rate_5 + 1 if inspection.rate_5 else 1
-        elif not opdata.SO_Rate:
-            pass
-        elif opdata.SO_Rate >= 6:
-            logger.error("Attention: une valeur de [SO_Rate] supérieure à 5 a été enregistrée (SO_ID: %s)", opdata.SO_ID)
-
-        if opdata.SO_Photonumber1:
-            inspection.nb_photos += 1
-        if opdata.SO_Photonumber2:
-            inspection.nb_photos += 1
-
-    if not any([inspection.rate_1, inspection.rate_2, inspection.rate_3, inspection.rate_4, inspection.rate_5]):
-        inspection.DG = 'ABS'  # Absence de défauts
-    elif not any([inspection.rate_1, inspection.rate_2, inspection.rate_3]):
-        inspection.DG = 'ACC'  # Défauts acceptables
-    else:
-        inspection.DG = 'INT'  # Défauts non-acceptables
-
-    logger.info("\t- Mise à jour de tblso_Rate_Analyse")
-    sql = Sql.format("""INSERT INTO tblso_Rate_Analyse (
-                    lngChantierId, bytIntervId, SI_InspName, SI_AutoNumber,
-                    [Classement tronons], Nom_troncon, S_StartNodeType,
-                    Sens_ecoul, S_EndNodeType, S_PipeShape, MateriauCourt,
-                    SI_Date, nb_Arbres, ANNEE_POSE,
-                    Route, NRoute, Test_ecoulement, MaxDeS_StartNodeCoord_Z, MaxDeS_EndNodeCoord_Z,
-                    MaxDeS_Sectionlength, MaxDeS_Pipelength, MaxDeDiametre, cpt_Photos, [Total de SO_ID],
-                    1, 2, 3, 4, 5, DG
-                    )
-            VALUES ({inspection.lng_chantier_id}, {inspection.byt_interv_id}, {inspection.nom_chantier:text}, {inspection.si_autonumber},
-                    {inspection.classement_troncons:text}, {inspection.nom_troncon:text}, {inspection.startnode_type:text},
-                    {inspection.sens_ecoul:text}, {inspection.endnode_type:text}, {inspection.pipe_shape:text}, {inspection.materiau:text},
-                    {inspection.si_date:date}, {inspection.arbres:text}, {inspection.annee_pose},
-                    {inspection.route:text}, {inspection.n_route:text}, {inspection.test_ecoulement:text}, {inspection.startnode_z}, {inspection.endnode_z},
-                    {inspection.section_length}, {inspection.pipe_length}, {inspection.diametre:text}, {inspection.nb_photos}, {inspection.nb_ops},
-                    {inspection.rate_1}, {inspection.rate_2}, {inspection.rate_3}, {inspection.rate_4}, {inspection.rate_5}, {inspection.DG:text}
-                    )
-            """, inspection=inspection)
-    controles_db.execute(sql)
+    # Demande à l'utilisateur de saisir les informations qui n'ont pas été passées en argument
+    if not inspname:
+        # Propose une liste d'inspections possibles
+        sql = """SELECT SI_InspName
+                    FROM SI_T
+                    WHERE SI_Spare1 Is Null OR Len([SI_Spare1])=0
+                    GROUP BY SI_InspName
+                """
+        candidats = wincan_db.read_all(sql)
 
-    # Met à jour tblvideointervs.strResGlobal avec le resultat global
-    #  le resultat global vaut '-' si un de ces trois champs n'est pas nul: tbl_so_rate.1, tbl_so_rate.2, tbl_so_rate.3
-    # >> On peut peut-être rassembler cette partie et l'insertion dans cette table au debut?
-    logger.info("\t- Mise à jour du resultat global dans tblVideoIntervs")
-    sql = """UPDATE tblVideoIntervs
-             SET strResGlobal='{}'
-             WHERE lngChantierId= {} AND bytIntervId= {}
-            """.format('-' if inspection.DG == 'INT' else '+',
-                       chantier_id,
-                       interv_id)
-    controles_db.execute(sql)
+        print("Veuillez choisir une inspection Wincan à traiter en saisissant son numéro:")
+        for i, candidat in enumerate(candidats):
+            print("[{}] - {}".format(i, candidat.SI_InspName))
 
-    # Met à jour la table tblVideoBases pour marquer le chantier comme traité
-    logger.info("\t- Mise à jour de tblVideoBases")
-    sql = """UPDATE tblVideoBases
-             SET blnWincan=True,bytNbInterv={}
-             WHERE lngChantierId={}
-             """.format(inspection.byt_interv_id,
-                        inspection.lng_chantier_id)
-    controles_db.execute(sql)
+        while not inspname:
+            reponse = input("> ")
+            try:
+                inspname = candidats[int(reponse)].SI_InspName
+            except ValueError:
+                print("Valeur invalide!")
 
-    # Met à jour les données du réseau dans tblChantiers
-    logger.info("\t- Mise à jour des données du réseau dans tblChantiers")
-    sql = """UPDATE tblChantiers
-             SET  bytFoncReseauId ={} , bytNatureReseauId={}, bytTypeReseauId={}
-             WHERE lngChantierId={}
-             """.format(inspection.section_use,
-                        inspection.section_purpose,
-                        inspection.section_type,
-                        inspection.lng_chantier_id)
+    if not chantier_id:
+        try:
+            default = int(inspname[:6])
+            chantier_id = input("Veuillez saisir le code chantier [{}]: ".format(default))
+            if not chantier_id:
+                chantier_id = default
+        except ValueError:
+            chantier_id = input("Veuillez saisir le code chantier: ")
+            if not chantier_id:
+                logger.error("Code chantier invalide")
+                sys.exit(1)
+
+    # Calcul du numéro d'intervention
+    last_interv_id = controles_db.first("SELECT max(bytIntervId) as last_interv_id FROM tblVideoIntervs WHERE lngChantierId={}".format(chantier_id)).last_interv_id
+    interv_id = last_interv_id + 1 if last_interv_id else 1
+
+    # Récupération du numéro de commande en cours
+    commande_id = controles_db.first("SELECT bytCommandeId FROM tblVideoBases WHERE lngChantierId={}".format(chantier_id)).bytCommandeId
+
+
+    # Affiche les infos et demande confirmation avant ed lancer le traitement
+    logger.info("## Traitement de l'inspection Wincan")
+    logger.info("Nom chantier: %s", inspname)
+    logger.info("Code chantier: %s", chantier_id)
+    logger.info("Numero d'intervention: %s", interv_id)
+    logger.info("Numero de commande: %s", commande_id)
+    if not args["-o"]:
+        if not input("Voulez-vous continuer? (o/n)") == 'o':
+            if input("Etes-vous sûr de vouloir annuler l'opération? (o/n)") == 'o':
+                logger.info("Opération annulée par l'utilisateur")
+                sys.exit(1)
+
+
+    # Recuperation des données de l'intervention
+    sql = """SELECT SI_T.SI_InspName, Sum(S_T.S_Sectionlength) AS Long_insp, Min(SI_T.SI_Date) AS mindate, Max(SI_T.SI_Date) AS maxdate,
+            First(SI_T.SI_Operator) AS Equipe, First(SI_T.SI_Vehicle) AS Mat, First(SI_T.SI_InspMethod) AS InspMethod,
+            First(SI_T.SI_ReasonOfInspection) AS SI_ReasonOfInspection, First(S_T.S_SectionPurpose) AS nature,
+            First(S_T.S_SectionUse) AS Fonction, First(S_T.S_SectionType) AS Type
+            FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+            WHERE (((SI_T.[SI_InspName])='{}'))
+            GROUP BY SI_T.SI_InspName
+            HAVING Sum(S_T.S_Sectionlength) Is Not Null AND First(SI_T.SI_Operator) Is Not Null AND First(SI_T.SI_Vehicle) Is Not Null AND First(SI_T.SI_InspMethod) Is Not Null
+            """.format(inspname)
+    try:
+        inspection = next(wincan_db.read(sql))
+    except StopIteration:
+        logger.error("Aucune inspection trouvée pour le nom '%s'", inspname)
+        raise
+
+    # Insère l'inspection dans la table tblVideoIntervs de Controles
+    logger.info("Création de l'intervention dans tblVideoIntervs")
+
+    interv = InterventionITV()
+
+    interv.lngChantierId = chantier_id
+    interv.bytIntervId = interv_id
+    interv.dtmIntervDu = inspection.mindate
+    interv.dtmIntervAu = inspection.maxdate
+    interv.strEquipeId = inspection.Equipe
+    interv.intlMaterielID = inspection.Mat
+    interv.bytCommandeId = commande_id
+    interv.lngTroncon = inspection.Long_insp
+    interv.SI_InspMethod = inspection.InspMethod
+    interv.SI_ReasonOfInspection = get_lib(inspection.SI_ReasonOfInspection, "SI_REASONOfINSPECTION")
+
+    sql = Sql.format("""INSERT INTO tblVideoIntervs ( lngChantierId, bytIntervId, dtmIntervDu, dtmIntervAu, strEquipeId,
+                                           intlMaterielID, bytCommandeId, lngTroncon, SI_InspMethod, SI_ReasonOfInspection )
+                        VALUES ({interv.lngChantierId}, {interv.bytIntervId}, {interv.dtmIntervDu:date}, {interv.dtmIntervAu:date}, {interv.strEquipeId:text},
+                                {interv.intlMaterielID}, {interv.bytCommandeId}, {interv.lngTroncon}, {interv.SI_InspMethod:text}, {interv.SI_ReasonOfInspection:text})
+                        """, interv=interv)
     controles_db.execute(sql)
 
-
-
-logger.info("Commit des modifications")
-controles_db.commit()
-wincan_db.commit()
+    # Met a jour les champs SI_Spare1 et SI_Spare2 de la table Wincan en retour
+    logger.info("Mise à jour en retour de SI_T")
+    sql = """UPDATE SI_T
+             SET SI_Spare1='{}', SI_Spare2='{}'
+             WHERE SI_InspName='{}'
+             """.format(chantier_id, interv_id, inspection.SI_InspName)
+    wincan_db.execute(sql)
+
+    # Met a jour la table tbl_so_rate de Controles
+    logger.info("Traitement des inspections")
+
+    # Extrait les données des tronçons
+
+    sql = """SELECT SI_T.SI_ID, S_T.S_ID, SI_T.SI_InspName, SI_T.SI_Spare1, SI_T.SI_Spare2, SI_T.SI_AutoNumber, SI_T.SI_MediaNumber1, S_T.S_SectionFlow,
+                S_T.S_StartNode, S_T.S_EndNode, S_T.S_StartNodeType, S_T.S_EndNodeType, S_T.S_StartNodeCoord_Z, S_T.S_EndNodeCoord_Z,
+                S_T.S_Sectionlength, S_T.S_SectionPurpose, S_T.S_SectionUse, S_T.S_SectionType, S_T.S_PipeMaterial, S_T.S_YearLayed,
+                S_T.S_PipeDia, S_T.S_Situation, S_T.S_Spare1, S_T.S_PipeShape, S_T.S_Pipelength, S_T.S_Spare3,
+                SI_T.SI_Spare0, SI_T.SI_Date
+            FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+            WHERE SI_T.SI_Spare1='{}' AND SI_T.SI_Spare2='{}'
+            """.format(chantier_id, interv_id)
+
+    for data in wincan_db.read(sql):
+
+        logger.info("* Traitement de %s (S_ID: %s)", data.SI_InspName, data.S_ID)
+
+        inspection = InspectionTronconWincan()
+
+        inspection.s_guid = data.S_ID[2:-1] if (data.S_ID[:2] == "b'" and data.S_ID[-1:] == "'") else data.S_ID
+        inspection.si_guid = data.SI_ID[2:-1] if (data.SI_ID[:2] == "b'" and data.SI_ID[-1:] == "'") else data.SI_ID
+        inspection.nom_chantier = data.SI_InspName
+        inspection.lng_chantier_id = data.SI_Spare1
+        inspection.byt_interv_id = data.SI_Spare2
+        inspection.si_autonumber = data.SI_AutoNumber
+        inspection.classement_troncons = data.SI_MediaNumber1
+        inspection.nom_troncon = "{r1}-{r2}".format(r1=data.S_StartNode, r2=data.S_EndNode) if data.S_SectionFlow == '2' else "{r2}-{r1}".format(r1=data.S_StartNode, r2=data.S_EndNode)
+        inspection.startnode_type = get_lib(data.S_StartNodeType, "S_StartNodeType")
+        inspection.endnode_type = get_lib(data.S_StartNodeType, "S_EndNodeType")
+        inspection.sens_ecoul = ">>" if data.S_SectionFlow == '1' else ('<<' if data.S_SectionFlow == '2' else '')
+        inspection.startnode_z = data.S_StartNodeCoord_Z
+        inspection.endnode_z = data.S_EndNodeCoord_Z
+        inspection.section_length = data.S_Sectionlength
+        inspection.section_purpose = data.S_SectionPurpose
+        inspection.section_use = data.S_SectionUse
+        inspection.section_type = data.S_SectionType
+        inspection.materiau = get_materiau(data.S_PipeMaterial)
+        inspection.annee_pose = data.S_YearLayed
+        inspection.diametre = get_lib(int(data.S_PipeDia), "S_PipeDia")
+        inspection.route = get_lib(data.S_Situation, "S_Situation")
+        inspection.n_route = data.S_Spare1
+        inspection.pipe_shape = data.S_PipeShape
+        inspection.pipe_length = data.S_Pipelength
+        if data.S_Spare3:
+            inspection.arbres = get_lib(data.S_Spare3, "S_Spare3")
+        inspection.test_ecoulement = data.SI_Spare0
+        inspection.si_date = data.SI_Date
+
+        sql = """ SELECT SO_T.SO_ID, SO_T.SO_Rate, SO_T.SO_Photonumber1, SO_T.SO_Photonumber2
+                    FROM SO_T
+                    WHERE SO_T.SO_Inspecs_ID='{}'
+                """.format(inspection.si_guid)
+
+        # Parcours les opérations réalisées au cours de l'inspection du tronçon
+        for opdata in wincan_db.read(sql):
+            inspection.nb_ops += 1
+
+            if opdata.SO_Rate == 1:
+                inspection.rate_1 = inspection.rate_1 + 1 if inspection.rate_1 else 1
+            elif opdata.SO_Rate == 2:
+                inspection.rate_2 = inspection.rate_2 + 1 if inspection.rate_2 else 1
+            elif opdata.SO_Rate == 3:
+                inspection.rate_3 = inspection.rate_3 + 1 if inspection.rate_3 else 1
+            elif opdata.SO_Rate == 4:
+                inspection.rate_4 = inspection.rate_4 + 1 if inspection.rate_4 else 1
+            elif opdata.SO_Rate == 5:
+                inspection.rate_5 = inspection.rate_5 + 1 if inspection.rate_5 else 1
+            elif not opdata.SO_Rate:
+                pass
+            elif opdata.SO_Rate >= 6:
+                logger.error("Attention: une valeur de [SO_Rate] supérieure à 5 a été enregistrée (SO_ID: %s)", opdata.SO_ID)
+
+            if opdata.SO_Photonumber1:
+                inspection.nb_photos += 1
+            if opdata.SO_Photonumber2:
+                inspection.nb_photos += 1
+
+        if not any([inspection.rate_1, inspection.rate_2, inspection.rate_3, inspection.rate_4, inspection.rate_5]):
+            inspection.DG = 'ABS'  # Absence de défauts
+        elif not any([inspection.rate_1, inspection.rate_2, inspection.rate_3]):
+            inspection.DG = 'ACC'  # Défauts acceptables
+        else:
+            inspection.DG = 'INT'  # Défauts non-acceptables
+
+        logger.info("\t- Mise à jour de tblso_Rate_Analyse")
+        sql = Sql.format("""INSERT INTO tblso_Rate_Analyse (
+                        lngChantierId, bytIntervId, SI_InspName, SI_AutoNumber,
+                        [Classement tronons], Nom_troncon, S_StartNodeType,
+                        Sens_ecoul, S_EndNodeType, S_PipeShape, MateriauCourt,
+                        SI_Date, nb_Arbres, ANNEE_POSE,
+                        Route, NRoute, Test_ecoulement, MaxDeS_StartNodeCoord_Z, MaxDeS_EndNodeCoord_Z,
+                        MaxDeS_Sectionlength, MaxDeS_Pipelength, MaxDeDiametre, cpt_Photos, [Total de SO_ID],
+                        1, 2, 3, 4, 5, DG
+                        )
+                VALUES ({inspection.lng_chantier_id}, {inspection.byt_interv_id}, {inspection.nom_chantier:text}, {inspection.si_autonumber},
+                        {inspection.classement_troncons:text}, {inspection.nom_troncon:text}, {inspection.startnode_type:text},
+                        {inspection.sens_ecoul:text}, {inspection.endnode_type:text}, {inspection.pipe_shape:text}, {inspection.materiau:text},
+                        {inspection.si_date:date}, {inspection.arbres:text}, {inspection.annee_pose},
+                        {inspection.route:text}, {inspection.n_route:text}, {inspection.test_ecoulement:text}, {inspection.startnode_z}, {inspection.endnode_z},
+                        {inspection.section_length}, {inspection.pipe_length}, {inspection.diametre:text}, {inspection.nb_photos}, {inspection.nb_ops},
+                        {inspection.rate_1}, {inspection.rate_2}, {inspection.rate_3}, {inspection.rate_4}, {inspection.rate_5}, {inspection.DG:text}
+                        )
+                """, inspection=inspection)
+        controles_db.execute(sql)
+
+        # Met à jour tblvideointervs.strResGlobal avec le resultat global
+        #  le resultat global vaut '-' si un de ces trois champs n'est pas nul: tbl_so_rate.1, tbl_so_rate.2, tbl_so_rate.3
+        # >> On peut peut-être rassembler cette partie et l'insertion dans cette table au debut?
+        logger.info("\t- Mise à jour du resultat global dans tblVideoIntervs")
+        sql = """UPDATE tblVideoIntervs
+                 SET strResGlobal='{}'
+                 WHERE lngChantierId= {} AND bytIntervId= {}
+                """.format('-' if inspection.DG == 'INT' else '+',
+                           chantier_id,
+                           interv_id)
+        controles_db.execute(sql)
+
+        # Met à jour la table tblVideoBases pour marquer le chantier comme traité
+        logger.info("\t- Mise à jour de tblVideoBases")
+        sql = """UPDATE tblVideoBases
+                 SET blnWincan=True,bytNbInterv={}
+                 WHERE lngChantierId={}
+                 """.format(inspection.byt_interv_id,
+                            inspection.lng_chantier_id)
+        controles_db.execute(sql)
+
+        # Met à jour les données du réseau dans tblChantiers
+        logger.info("\t- Mise à jour des données du réseau dans tblChantiers")
+        sql = """UPDATE tblChantiers
+                 SET  bytFoncReseauId ={} , bytNatureReseauId={}, bytTypeReseauId={}
+                 WHERE lngChantierId={}
+                 """.format(inspection.section_use,
+                            inspection.section_purpose,
+                            inspection.section_type,
+                            inspection.lng_chantier_id)
+        controles_db.execute(sql)
+
+
+
+    logger.info("Commit des modifications")
+    controles_db.commit()
+    wincan_db.commit()
+
+
+
+if __name__ == "__main__":
+    main()
+    logger.info("-- Fin --")