gf2analytique.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. '''
  2. Script d'import des données de facturation depuis
  3. la base de données ASTRE-GF vers les tables de la base Analytique
  4. du Parc Départemental d'Erstein
  5. L'import se déroule en trois étapes:
  6. 1- chargement des données issues de Astre (via le web service CG67.AstreGf) dans le fichier /work/gf2analytique/import.csv
  7. 2- Contrôle de la validité des données, prompt éventuel pour une correction des donneés
  8. 3- Une fois les données valides, import dans Analytique
  9. **IMPORTANT**: pour lancer le script sans interaction avec l'utilisateur
  10. (par ex, dans le cas d'une tâche planifiée), appeller le script avec l'option '--auto'.
  11. '''
  12. from datetime import datetime
  13. import logging
  14. import re
  15. import sys
  16. from path import Path # @UnusedImport
  17. from core import logconf
  18. from core.model import Sql
  19. from core.pde import AnalytiqueDb, FactureGf
  20. from core.webservice import GfWebservice
  21. logger = logging.getLogger("gf2analytique")
  22. logconf.start("gf2analytique", logging.DEBUG)
  23. # # POUR TESTER, décommenter les lignes suivantes
  24. ##-----------------------------------------------
  25. # GfWebservice._url = r"http://webservices-t.bas-rhin.fr/CG67.AstreGF.WebServices/public/WsPDE.asmx"
  26. # AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
  27. # logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)]
  28. # logger.warning("<<<<<<<<<<<<<< Mode TEST >>>>>>>>>>>>>>>>>")
  29. ##-----------------------------------------------
  30. def main():
  31. # *** Initialisation
  32. logger.info("Initialisation...")
  33. no_prompt = ("--auto" in sys.argv)
  34. if no_prompt:
  35. logger.info("> Lancé en mode automatique (sans interruption)")
  36. # Connect to factures.mdb
  37. analytique_db = AnalytiqueDb(autocommit=False)
  38. # Connect to the astre gf webservice
  39. ws = GfWebservice("GetPDEFactures")
  40. ws.parse()
  41. # *** 1- Parcourt les factures renvoyées par le webservice, et stoque toutes les lignes non-importées dans Analytique dans un fichier import.csv
  42. logger.info("Parcourt les données fournies par le webservice")
  43. factures = []
  44. for data in ws:
  45. # Génère la facture à partir des données fournies par le web-service
  46. facture = FactureGf.from_dict(data)
  47. # Contrôle si la facture est déjà importée. Si c'est le cas, passe à la facture suivante.
  48. # 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.
  49. # C'est pour cette raison que les données sont importées 'par blocs'
  50. if analytique_db.exists(Sql.format("""SELECT dblFactureId FROM tbl_Factures
  51. WHERE intExercice = {} AND strLiquidation = {:text} AND strEngagement = {:text} AND strService='7710'
  52. """, facture.numExBudget, facture.numLiqMandat, facture.numMandat)):
  53. continue
  54. logger.info("* La facture %s/%s/%s sera importée", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
  55. # # Auto-correction des données
  56. # correction auto des codes chantiers
  57. if facture.codeAxe == "AFFAI" and re.match(r"\d{2}5\d{3}", facture.codeCout):
  58. facture.codeCout += "/1"
  59. # echappe les apostrophes
  60. facture.libRai = facture.libRai.replace("'", "''")
  61. # corrige automatiquement les noms de materiels
  62. if facture.codeAxe == "ENGIN":
  63. row = analytique_db.first(Sql.format("""SELECT txtMateriel FROM tbl_materiel
  64. WHERE txtMateriel={codeCout:text} or txtMateriel='ZZ {codeCout}'
  65. """, codeCout=facture.codeCout))
  66. if row:
  67. facture.codeCout = row.txtMateriel
  68. factures.append(facture)
  69. if not factures:
  70. logger.info("** Aucune nouvelle facture à importer **")
  71. return
  72. # *** 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.
  73. errors = -1
  74. while errors:
  75. errors = 0
  76. logger.info("# Contrôle des données")
  77. for facture in factures:
  78. prefix = "* Facture {}/{}/{}: ".format(facture.numExBudget, facture.numMandat, facture.numLiqMandat)
  79. if not int(facture.numExBudget) > 2000:
  80. logger.error(prefix + "Exercice budgetaire invalide: %s", facture.numExBudget)
  81. errors += 1
  82. if not no_prompt:
  83. facture.numExBudget = input("Saisir la nouvelle valeur:")
  84. if facture.codeColl != "CG67":
  85. logger.error(prefix + "Code collectivité invalide: %s", facture.codeColl)
  86. errors += 1
  87. if not no_prompt:
  88. facture.codeColl = input("Saisir la nouvelle valeur:")
  89. if facture.codeBudg != "02":
  90. logger.error(prefix + "Code budgetaire invalide: %s", facture.codeBudg)
  91. errors += 1
  92. if not no_prompt:
  93. facture.codeBudg = input("Saisir la nouvelle valeur:")
  94. if facture.codeAxe == "ENGIN":
  95. # Controle l'existence du materiel
  96. if not analytique_db.exists(Sql.format("SELECT intlMaterielID FROM tbl_materiel WHERE txtMateriel={:text}", facture.codeCout)):
  97. logger.error(prefix + "Le materiel n'existe pas: %s", facture.codeCout)
  98. errors += 1
  99. if not no_prompt:
  100. facture.codeCout = input("Saisir la nouvelle valeur:")
  101. elif facture.codeAxe == "AFFAI":
  102. # Controle l'existence de l'affaire
  103. if not analytique_db.exists(Sql.format("SELECT dblAffaireId FROM tbl_Affaires WHERE strLiaisonControle={:text}", facture.codeCout)):
  104. logger.error(prefix + "L'affaire n'existe pas: %s", facture.codeCout)
  105. errors += 1
  106. if not no_prompt:
  107. facture.codeCout = input("Saisir la nouvelle valeur:")
  108. else:
  109. # CodeAxe invalide
  110. logger.error(prefix + "Code axe inconnu: %s", facture.codeAxe)
  111. errors += 1
  112. if not no_prompt:
  113. facture.codeAxe = input("Saisir la nouvelle valeur:")
  114. if no_prompt:
  115. if errors:
  116. logger.error("<!> Une ou plusieurs erreurs ont été détectées, voir le fichier de log pour plus d'information <!>")
  117. logger.info("* Opération annulée *")
  118. return
  119. logger.info("Les données sont valides.")
  120. # 3- Si toutes les données sont valides, parcourt les lignes du fichier import.csv et les insère dans la table tbl_Facture.
  121. logger.info("Mise à jour des tables de la base Analytique")
  122. for facture in factures:
  123. logger.info("* Facture %s/%s/%s: traitement", facture.numExBudget, facture.numMandat, facture.numLiqMandat)
  124. # NB: les données ne sont committées qu'aprés l'exécution de toutes les requêtes suivantes
  125. logger.info("> mise à jour de tbl_Factures")
  126. # Insère les données dans la table tbl_Factures
  127. sql = Sql.format("""INSERT INTO tbl_Factures ( intExercice, strLiquidation, intLiquidationLigne, strEngagement,
  128. strEnveloppe, strService, strTiers, strTiersLibelle, strMotsClefs,
  129. dtmDeb, intOperation, strNomenclature0, strAXE, strCentreCout,
  130. strObjet, dblMontantTotal, dblMontantTVA, strORIGINE_DONNEES
  131. )
  132. VALUES ({intExercice}, {strLiquidation:text}, {intLiquidationLigne}, {strEngagement:text},
  133. {strEnveloppe:text}, {strService:text}, {strTiers:text}, {strTiersLibelle:text}, {strMotsClefs:text},
  134. {dtmDeb:date}, {intOperation}, {strNomenclature0:text}, {strAxe:text}, {strCentreCout:text},
  135. {strObjet:text}, {dblMontantTotal}, {dblMontantTVA}, {strORIGINE_DONNEES:text})
  136. """, intExercice=facture.numExBudget,
  137. strLiquidation=facture.numLiqMandat,
  138. intLiquidationLigne=facture.numLigneMandat,
  139. strEngagement=facture.numMandat,
  140. strEnveloppe=facture.numEnv,
  141. strService='7710',
  142. strTiers=facture.numTiers,
  143. strTiersLibelle=facture.libRai,
  144. strMotsClefs=facture.refIntMandat,
  145. dtmDeb=facture.dateDepDelai,
  146. intOperation=facture.codePeriode or None,
  147. strNomenclature0=facture.typeNomencMarche,
  148. strAxe=facture.codeAxe,
  149. strCentreCout=facture.codeCout,
  150. strObjet=facture.dateMandat.strftime("%d/%m/%Y"),
  151. dblMontantTVA=facture.mntTvaMandat or 0,
  152. dblMontantTotal=facture.mntVent or 0,
  153. strORIGINE_DONNEES='ASTRE_{:%y%m%d}'.format(datetime.now())
  154. )
  155. logger.debug("> %s", sql)
  156. analytique_db.execute(sql)
  157. facture.factureId = analytique_db.first("SELECT TOP 1 dblFactureId FROM tbl_Factures ORDER BY dblFactureId DESC").dblFactureId
  158. if facture.codeAxe == "ENGIN":
  159. # La ligne concerne un engin: insère les données dans la table tbl_Facture_Engin
  160. logger.info("> mise à jour de tbl_Facture_Engin")
  161. materiel = analytique_db.first("SELECT intlMaterielID FROM tbl_Materiel WHERE [txtMateriel]='{}'".format(facture.codeCout))
  162. materielId = materiel.intlMaterielID if materiel else '859'
  163. logger.debug("retrieve intlMaterielID: %s", materielId)
  164. sql = Sql.format("""INSERT INTO tbl_Facture_Engin ( intlMaterielID, txtMateriel, dblFactureId, strLibelle, dblMontant, strType )
  165. VALUES ({}, {:text}, {}, {:text}, {}, {:text})
  166. """, materielId,
  167. facture.codeCout,
  168. facture.factureId,
  169. facture.libCout,
  170. facture.mntVent,
  171. facture.libRai
  172. )
  173. logger.debug("> %s", sql)
  174. analytique_db.execute(sql)
  175. elif facture.codeAxe == "AFFAI":
  176. # La ligne concerne une affaire: insère les données dans la table tbl_Facture_Affaire
  177. logger.info("> mise à jour de tbl_Facture_Affaire")
  178. sql = Sql.format("""INSERT INTO tbl_Facture_Affaire ( strAffaireId, dblFactureId, strLibelle, dblMontant, strType )
  179. VALUES ({:text}, {}, {:text}, {}, {:text})
  180. """, facture.codeCout,
  181. facture.factureId,
  182. facture.libRai ,
  183. facture.mntVent,
  184. facture.libCout,
  185. )
  186. logger.debug("> %s", sql)
  187. analytique_db.execute(sql)
  188. logger.info("> mise à jour de tbl_Mandatement")
  189. # Insère les données dans la table tbl_Mandatement
  190. sql = Sql.format("""INSERT INTO tbl_Mandatement ( dblFacture, strNumMandat, dtmMandat, strBordereau )
  191. VALUES ({}, {:text}, {:date}, {:text})
  192. """, facture.factureId, facture.numMandat, facture.dateMandat, facture.numBj)
  193. logger.debug("> %s", sql)
  194. analytique_db.execute(sql)
  195. # Commit les insertions dans la base
  196. analytique_db.commit()
  197. logger.info("Facture %s : ok", facture.factureId)
  198. if __name__ == "__main__":
  199. main()
  200. logger.info("-- Fin --")