ctrl2analytique.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. '''
  2. Génère les affaires dans la base Analytique à partir des données de la base Contrôles.
  3. **IMPORTANT**: pour lancer le script sans interaction avec l'utilisateur
  4. (par ex, dans le cas d'une tâche planifiée), appeller le script avec l'option '-n'.
  5. @author: olivier.massot, févr. 2018
  6. '''
  7. from datetime import datetime, timedelta
  8. import logging
  9. import sys
  10. from path import Path # @UnusedImport
  11. from core import logconf
  12. from core.model import Model
  13. from core.pde import ControlesDb, AnalytiqueDb, mk_workdir, CommunDb
  14. logger = logging.getLogger("ctrl2analytique")
  15. logconf.start("ctrl2analytique", logging.DEBUG)
  16. # # POUR TESTER, décommenter les lignes suivantes
  17. # > Lancer le script /resources/test_ctrl2analytique.py pour reinitialiser les données de la base de test
  18. ##-----------------------------------------------
  19. # logger.warning("<<<<<<<<<<<<<< Mode TEST >>>>>>>>>>>>>>>>>")
  20. # ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
  21. # AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
  22. # CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
  23. ##-----------------------------------------------
  24. # ######### INITIALISATION ##########
  25. logger.info("Initialisation...")
  26. no_prompt = ("-n" in sys.argv)
  27. if no_prompt:
  28. logger.info("> Lancé en mode automatique (sans interruption)")
  29. # Connexion à Analytique
  30. analytique_db = AnalytiqueDb(autocommit=False)
  31. # Connexion à Controles
  32. controles_db = ControlesDb(autocommit=False)
  33. # Connexion à CommunDb
  34. commun_db = CommunDb(autocommit=False)
  35. # Créé le répertoire de travail
  36. workdir = mk_workdir("ctrl2analytique")
  37. affaires_file = workdir / "affaires.csv"
  38. intervs_file = workdir / "intervs.csv"
  39. # > Supprime les fichiers d'import s'il existent
  40. for file in (affaires_file, intervs_file):
  41. if file.exists():
  42. logger.debug("Supprime le fichier %s", file)
  43. file.remove()
  44. class Affaire(Model):
  45. """ Modèle de données d'une affaire Analytique """
  46. _FIELDS = ["strLiaisonControle", "strMOeId", "strCommneId", "strLieux",
  47. "strEntrepriseId", "strMOId", "dtmCommande", "Ref", "blnMarche",
  48. "dblMarche", "intTypeContrat", "strCT", "strTypeId",
  49. "intCoefFG", "strSituation",
  50. "lngChantierId", "bytCommandeId"]
  51. def get_type_id(lngChantierId, bytCommandeId):
  52. """ Recupère le type de chantier.
  53. 'ZP': Chantier de contrôle d'étanchéité
  54. 'ZC': Chantier de contrôle du compactage
  55. 'ZI': Chantier d'inspection vidéo
  56. 'ZZ': Chantier mixte.
  57. '': Inconnu
  58. """
  59. sql = """SELECT lngChantierId, 'ZP' as type FROM tblEtancheiteBases WHERE [lngChantierId] = {chantier} AND [bytCommandeId] = {commande}
  60. UNION
  61. SELECT lngChantierId, 'ZC' as type FROM tblCompactageBases WHERE [lngChantierId] = {chantier}
  62. UNION
  63. SELECT lngChantierId, 'ZI' as type FROM tblVideoBases WHERE [lngChantierId] = {chantier};
  64. """.format(chantier=lngChantierId,
  65. commande=bytCommandeId)
  66. res = controles_db.read_all(sql)
  67. if len(res) == 0:
  68. return ""
  69. elif len(res) == 1:
  70. return res[0].type
  71. else:
  72. return "ZZ"
  73. def get_coeff_k(lngChantierId):
  74. """ Récupère le coefficient de calcul des frais généraux (batiments, frais administratifs...Etc.) """
  75. # On déduit l'année du chantier à partir du code chantier
  76. annee = "20" + str(lngChantierId)[:2] if len(str(lngChantierId)) == 6 else "200" + str(lngChantierId)[:1]
  77. return analytique_db.first("SELECT [COEFFG] FROM tbl_COEFFG WHERE [ANNEE] = {}".format(annee)).COEFFG / 100
  78. # ########## IMPORT DES AFFAIRES ##########
  79. # Parcourt les chantiers de contrôle pour lesquels aucune affaire n'a été créée, et les ajoute au fichier affaire.csv
  80. compteur = 0
  81. sql = """ SELECT tblCommandes.lngChantierId, tblCommandes.bytCommandeId, tblChantiers.strSubdivisionId, tblChantiers.strCollectiviteId as ChantierCollectiviteId, tblChantiers.strLocChantier,
  82. tblChantiers.strEntrepriseId, tblCommandes.strCollectiviteId as CommandeCollectiviteId, tblCommandes.dtmCommande, tblCommandes.strRefCommande, tblCommandes.blnMarche, tblCommandes.dblMtMarche, tblCommandes.strdevis
  83. FROM tblChantiers INNER JOIN tblCommandes ON tblChantiers.lngChantierId = tblCommandes.lngChantierId
  84. WHERE (((tblCommandes.sngAffaireIdMos) Is Null Or (tblCommandes.sngAffaireIdMos)=0))
  85. """
  86. for data in controles_db.read(sql):
  87. # Création de l'affaire
  88. affaire = Affaire()
  89. affaire.strLiaisonControle = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  90. affaire.strMOeId = data.strSubdivisionId
  91. affaire.strCommneId = data.ChantierCollectiviteId
  92. affaire.strLieux = data.strLocChantier
  93. affaire.strEntrepriseId = data.strEntrepriseId
  94. affaire.strMOId = data.CommandeCollectiviteId
  95. affaire.dtmCommande = data.dtmCommande
  96. affaire.Ref = data.strRefCommande
  97. affaire.blnMarche = data.blnMarche
  98. affaire.dblMarche = data.dblMtMarche
  99. affaire.intTypeContrat = 1
  100. affaire.strCT = '1'
  101. affaire.strTypeId = get_type_id(data.lngChantierId, data.bytCommandeId)
  102. affaire.intCoefFG = get_coeff_k(data.lngChantierId)
  103. affaire.strSituation = "En cours"
  104. # pour garder le lien avec la donnée d'origine:
  105. affaire.lngChantierId = data.lngChantierId
  106. affaire.bytCommandeId = data.bytCommandeId
  107. # Créé la ligne dans le fichier affaires.csv
  108. affaire.dump(affaires_file)
  109. compteur += 1
  110. logger.info("> {} affaires ajoutées au fichier".format(compteur))
  111. # ########## IMPORT DES INTERVENTIONS DE COMPACTAGE ##########
  112. # Importe les interventions de contrôle du compactage dans le fichier intervs.csv
  113. class Interv(Model):
  114. """ Modèle de données d'une intervention de contrôle réseaux """
  115. _FIELDS = ["strEquipeId", "strEnginId", "strRapportId", "strTypeInterventionId",
  116. "strCatégorieInterventionId", "dblquantite", "strunite", "dtmIntervention",
  117. "dtmDureeIntervention", "strLiaisonControle", "strArticleId", "intPeriode",
  118. "remarques", "strgrandeur1", "strgrandeur2", "strgrandeur3",
  119. "strcaracteristique1", "strcaracteristique2", "strcaracteristique3",
  120. "strunite1", "strunite2", "strunite3",
  121. "dtmImportation", "strTest", "LienAff",
  122. "lngChantierId", "bytCommandeId", "bytIntervId"
  123. ]
  124. def engin_existe(strEnginId):
  125. """ retourne True si le code de l'engin existe dans la table tbl_Engin """
  126. return analytique_db.exists("SELECT strEnginId FROM tbl_Engin WHERE strEnginId='{}'".format(strEnginId))
  127. def get_periode_validite(date_interv):
  128. """ retourne la préiode comptable correspondant à la date de l'intervention """
  129. sql = """SELECT intPeriodeValiditeId FROM tblTarifValidite
  130. WHERE [dtmValiditeDebut] <= #{date_interv}# AND [dtmValiditeFin] > #{date_interv}# AND [bytClasseTarifId]=1
  131. """.format(date_interv=date_interv)
  132. return commun_db.first(sql).intPeriodeValiditeId
  133. compteur = 0
  134. sql = """SELECT tblCompactageIntervs.lngChantierId, tblCompactageIntervs.bytCommandeId, tblCompactageIntervs.bytIntervId, tblCompactageIntervs.strEquipeId,
  135. tblCompactageEngins.strEnginId, tblCompactageIntervs.lngRapportId, tblCompactageBases.memTravaux, tblCompactageResultats.dtmEssai, tblCompactageResultats.dtmDuree,
  136. tblCompactagePartChantiers.strTrcRegard, tblMateriaux.strMatériau AS str_materiau_remblai, tblMateriaux_1.strMatériau AS str_materiau_enrobage,
  137. tblMateriaux_2.strMatériau AS str_materiau_lit, tblCompactageResultats.bytPartChantierId, tblCompactageIntervs.sngIntervIdMos
  138. FROM ((tblMateriaux RIGHT JOIN ((((tblCompactageIntervs LEFT JOIN tblCompactageEngins ON tblCompactageIntervs.strEquipeId = tblCompactageEngins.strEquipeId)
  139. INNER JOIN tblCompactageResultats ON (tblCompactageIntervs.lngChantierId = tblCompactageResultats.lngChantierId) AND
  140. (tblCompactageIntervs.bytIntervId = tblCompactageResultats.bytIntervId)) INNER JOIN tblCompactagePartChantiers ON
  141. (tblCompactageResultats.lngChantierId = tblCompactagePartChantiers.lngChantierId) AND
  142. (tblCompactageResultats.bytPartChantierId = tblCompactagePartChantiers.bytPartChantierId))
  143. INNER JOIN tblCompactageBases ON tblCompactageIntervs.lngChantierId = tblCompactageBases.lngChantierId)
  144. ON tblMateriaux.strMateriauId = tblCompactagePartChantiers.strMateriauRemblaiId) LEFT JOIN tblMateriaux AS tblMateriaux_1
  145. ON tblCompactagePartChantiers.strMateriauEnrobageId = tblMateriaux_1.strMateriauId) LEFT JOIN tblMateriaux AS tblMateriaux_2
  146. ON tblCompactagePartChantiers.strMateriauLitId = tblMateriaux_2.strMateriauId
  147. WHERE (((tblCompactageIntervs.sngIntervIdMos)=0 Or (tblCompactageIntervs.sngIntervIdMos) Is Null))
  148. """
  149. def get_type_compactage_interv(observation):
  150. """ retourne le sous-type d'intervention à partir du commentaire associé """
  151. if "ASSAINISEMENT" or "ASSAINISEMENT" in observation:
  152. return "CC3"
  153. elif "CABLE" in observation:
  154. return "CC1"
  155. elif "A.E.P" in observation:
  156. return "CC2"
  157. elif "GAZ" in observation:
  158. return "CC4"
  159. else:
  160. return "CC3"
  161. for data in controles_db.read(sql):
  162. interv = Interv()
  163. interv.strEquipeId = "C{}".format(data.strEquipeId)
  164. interv.strEnginId = data.strEnginId
  165. interv.strRapportId = data.lngRapportId
  166. interv.strTypeInterventionId = get_type_compactage_interv(data.memTravaux)
  167. interv.strCatégorieInterventionId = "CC"
  168. interv.dblquantite = 1.0
  169. interv.strunite = "u"
  170. interv.dtmIntervention = data.dtmEssai
  171. interv.dtmDureeIntervention = data.dtmDuree
  172. interv.dtmDureeInstallation = 0 # Les temps d'installation seront calculés en fin de traitement
  173. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  174. interv.strArticleId = data.strEnginId
  175. interv.intPeriode = get_periode_validite(data.dtmEssai)
  176. interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
  177. interv.strgrandeur1 = data.str_materiau_remblai
  178. interv.strgrandeur2 = data.str_materiau_lit
  179. interv.strgrandeur3 = data.str_materiau_enrobage
  180. interv.strcaracteristique1 = "Matériau remblai"
  181. interv.strcaracteristique2 = "Matériau lit de pose"
  182. interv.strcaracteristique3 = "Matériau enrobage"
  183. interv.strunite1 = ""
  184. interv.strunite2 = ""
  185. interv.strunite3 = ""
  186. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  187. interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
  188. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  189. # pour garder le lien avec la donnée d'origine:
  190. interv.lngChantierId = data.lngChantierId
  191. interv.bytCommandeId = data.bytCommandeId
  192. interv.bytIntervId = data.bytIntervId
  193. # Créé la ligne dans le fichier intervs.csv
  194. interv.dump(intervs_file)
  195. compteur += 1
  196. logger.info("> {} interventions Compactage ajoutées au fichier".format(compteur))
  197. # ########## IMPORT DES INTERVENTIONS D'ETANCHEITE ##########
  198. # Importe les interventions de contrôle d'étanchéité dans le fichier intervs.csv
  199. compteur = 0
  200. sql = """SELECT tblEtancheiteIntervs.lngChantierId, tblEtancheiteIntervs.bytCommandeId, tblEtancheiteIntervs.bytIntervId, tblEtancheiteIntervs.strEquipeId,
  201. tblEtancheiteIntervs.lngRapportId, tblEtancheitePartChantiers.bytTypeEssai, tblMateriaux.strMateriauId, tblMateriaux.strMatériau,
  202. tblEtancheitePartChantiers.intDiametre, tblEtancheitePartChantiers.sngLgHt, tblEtancheitePartChantiers.intNbJoint, tblEtancheiteResultats.dtmDuree,
  203. tblEtancheiteResultats.dtmEssai, tblEtancheitePartChantiers.strTrcRegard, tblEtancheiteResultats.bytPartChantierId
  204. FROM ((tblEtancheiteIntervs INNER JOIN tblEtancheiteResultats ON (tblEtancheiteIntervs.lngChantierId = tblEtancheiteResultats.lngChantierId)
  205. AND (tblEtancheiteIntervs.bytIntervId = tblEtancheiteResultats.bytIntervId)) INNER JOIN tblEtancheitePartChantiers
  206. ON (tblEtancheiteResultats.lngChantierId = tblEtancheitePartChantiers.lngChantierId)
  207. AND (tblEtancheiteResultats.bytPartChantierId = tblEtancheitePartChantiers.bytPartChantierId)) INNER JOIN tblMateriaux
  208. ON tblEtancheitePartChantiers.strMateriauId = tblMateriaux.strMateriauId
  209. WHERE (((tblEtancheiteIntervs.sngIntervIdMos)=0 Or (tblEtancheiteIntervs.sngIntervIdMos) Is Null));
  210. """
  211. def get_engin_etancheite(equipe, diametre, materiau, type_essai):
  212. """ retourne l'engin correspondant à l'essai en fonction eds caractéristiques de l'essai """
  213. sql = """SELECT strEnginId FROM tblEtancheiteEngins
  214. WHERE ([strEquipeId] = '{}') AND ([intDiametre] = {}) AND ([strMateriauId] = '{}') AND ([bytTypeEssaiId] ={})
  215. """.format(equipe, diametre, materiau, type_essai)
  216. row = controles_db.first(sql)
  217. return row.strEnginId if row else ""
  218. for data in controles_db.read(sql):
  219. interv = Interv()
  220. interv.strEquipeId = "C{}".format(data.strEquipeId)
  221. interv.strEnginId = get_engin_etancheite(data.strEquipeId, data.intDiametre, data.strMateriauId, data.bytTypeEssai)
  222. interv.strRapportId = data.lngRapportId
  223. interv.strTypeInterventionId = "CE{}".format(data.bytTypeEssai)
  224. interv.strCatégorieInterventionId = "CE"
  225. interv.dblquantite = float(data.intNbJoint)
  226. interv.strunite = "u"
  227. interv.dtmIntervention = data.dtmEssai
  228. interv.dtmDureeIntervention = data.dtmDuree
  229. interv.dtmDureeInstallation = 0 # Les temps d'installation seront recalculés en fin de traitement
  230. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  231. interv.strArticleId = interv.strEnginId
  232. interv.intPeriode = get_periode_validite(data.dtmEssai)
  233. interv.remarques = data.strTrcRegard if data.strTrcRegard else "-"
  234. interv.strgrandeur1 = data.strMatériau
  235. interv.strgrandeur2 = data.intDiametre
  236. interv.strgrandeur3 = data.sngLgHt
  237. interv.strcaracteristique1 = "Matériau"
  238. interv.strcaracteristique2 = "Diamètre"
  239. interv.strcaracteristique3 = "Longueur"
  240. interv.strunite1 = ""
  241. interv.strunite2 = "mm"
  242. interv.strunite3 = "m"
  243. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  244. interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
  245. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  246. # pour garder le lien avec la donnée d'origine:
  247. interv.lngChantierId = data.lngChantierId
  248. interv.bytCommandeId = data.bytCommandeId
  249. interv.bytIntervId = data.bytIntervId
  250. # Créé la ligne dans le fichier intervs.csv
  251. interv.dump(intervs_file)
  252. compteur += 1
  253. logger.info("> {} interventions Etanchéité ajoutées au fichier".format(compteur))
  254. # ########## IMPORT DES INTERVENTIONS D'INSPECTION VIDEO ##########
  255. # Importe les interventions d'inspection vidéo dans le fichier intervs.csv
  256. compteur = 0
  257. sql = """SELECT tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
  258. tblVideoEngins.strEnginId, tblVideoIntervs.lngRapportId, First(tblso_rate_Analyse.MateriauCourt) AS strmateriau, tblVideoIntervs.lngTroncon,
  259. tblVideoIntervs.sngNbJourFact, First(tblso_rate_Analyse.MaxDeDiametre) AS diam, tblVideoIntervs.dtmDuree, tblVideoIntervs.dtmIntervDu,
  260. First(tblVideoIntervs.memObservation) AS memObservation, tblChantiers.strEntrepriseId
  261. FROM ((tblVideoEngins RIGHT JOIN tblVideoIntervs ON tblVideoEngins.strEquipeId = tblVideoIntervs.strEquipeId) INNER JOIN tblso_rate_Analyse ON
  262. (tblVideoIntervs.lngChantierId = tblso_rate_Analyse.lngChantierId) AND (tblVideoIntervs.bytIntervId = tblso_rate_Analyse.bytIntervId)) INNER JOIN
  263. tblChantiers ON tblVideoIntervs.lngChantierId = tblChantiers.lngChantierId
  264. WHERE (((tblVideoIntervs.sngIntervIdMos) Is Null Or (tblVideoIntervs.sngIntervIdMos)=0))
  265. GROUP BY tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
  266. tblVideoIntervs.lngRapportId, tblVideoIntervs.lngTroncon, tblVideoIntervs.sngNbJourFact, tblVideoIntervs.dtmDuree,
  267. tblVideoIntervs.dtmIntervDu, tblVideoEngins.strEnginId, tblChantiers.strEntrepriseId
  268. """
  269. for data in controles_db.read(sql):
  270. interv = Interv()
  271. interv.strEquipeId = "C{}".format(data.strEquipeId)
  272. interv.strEnginId = data.strEnginId
  273. interv.strRapportId = data.lngRapportId
  274. interv.strTypeInterventionId = "CI1" if data.strEntrepriseId != 195 else "CI2"
  275. interv.strCatégorieInterventionId = "CI"
  276. interv.dblquantite = float(data.sngNbJourFact)
  277. interv.strunite = "j"
  278. interv.dtmIntervention = data.dtmIntervDu
  279. interv.dtmDureeIntervention = data.dtmDuree
  280. interv.dtmDureeInstallation = 0 # Les temps d'installation seront recalculés en fin de traitement
  281. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  282. interv.strArticleId = data.strEnginId
  283. interv.intPeriode = get_periode_validite(data.dtmIntervDu)
  284. interv.remarques = data.memObservation if data.memObservation else "-"
  285. interv.strgrandeur1 = data.strmateriau
  286. interv.strgrandeur2 = data.diam
  287. interv.strgrandeur3 = data.lngTroncon
  288. interv.strcaracteristique1 = "Matériau"
  289. interv.strcaracteristique2 = "Diamètre"
  290. interv.strcaracteristique3 = "Longueur inspectée"
  291. interv.strunite1 = ""
  292. interv.strunite2 = "mm"
  293. interv.strunite3 = "m"
  294. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  295. interv.strTest = "{}/{}/{}/1".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  296. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  297. # pour garder le lien avec la donnée d'origine:
  298. interv.lngChantierId = data.lngChantierId
  299. interv.bytCommandeId = data.bytCommandeId
  300. interv.bytIntervId = data.bytIntervId
  301. # Créé la ligne dans le fichier intervs.csv
  302. interv.dump(intervs_file)
  303. compteur += 1
  304. logger.info("> {} interventions ITV ajoutées au fichier".format(compteur))
  305. logging.info("Les données à importer ont été ajoutées aux fichiers '{}' et '{}'".format(affaires_file, intervs_file))
  306. logging.info("Ces fichiers sont au format CSV (séparateur: tabulation)")
  307. # ########## CONTROLE ET CORRECTION DES DONNEES ##########
  308. errors = -1
  309. while errors:
  310. errors = []
  311. with open(affaires_file) as f:
  312. next(f) # saute la première ligne
  313. for line in f:
  314. affaire = Affaire.from_csv(line)
  315. prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
  316. if not affaire.strMOId:
  317. errors.append(prefix + "MO manquant")
  318. else:
  319. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOId)):
  320. errors.append(prefix + "Le MO {} n'existe pas dans tblTiers".format(affaire.strMOId))
  321. if not affaire.strMOeId:
  322. errors.append(prefix + "MOe manquant")
  323. else:
  324. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOeId)):
  325. errors.append(prefix + "Le MOe {} n'existe pas dans tblTiers".format(affaire.strMOeId))
  326. if not affaire.strEntrepriseId:
  327. errors.append(prefix + "Entreprise manquante")
  328. else:
  329. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strEntrepriseId)):
  330. errors.append(prefix + "L'entreprise {} n'existe pas dans tblTiers".format(affaire.strEntrepriseId))
  331. if not affaire.strCommneId:
  332. errors.append(prefix + "Commune manquante")
  333. else:
  334. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strCommneId)):
  335. errors.append(prefix + "La commune {} n'existe pas dans tblTiers".format(affaire.strCommneId))
  336. if not affaire.strTypeId:
  337. errors.append(prefix + "Type d'affaire manquant")
  338. if not affaire.dtmCommande:
  339. errors.append(prefix + "Date de commande manquante")
  340. if affaire.blnMarche == True and not affaire.intDevisId:
  341. errors.append(prefix + "Numéro de devis manquant")
  342. if analytique_db.exists("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)):
  343. errors.append(prefix + "Une affaire portant ce code existe déjà: {}".format(affaire.strLiaisonControle))
  344. with open(intervs_file) as f:
  345. next(f) # saute la première ligne
  346. for line in f:
  347. interv = Interv.from_csv(line)
  348. prefix = "Intervention {}: ".format(interv.strLiaisonControle)
  349. if not interv.strEquipeId:
  350. errors.append(prefix + "Equipe manquante")
  351. if not interv.strEnginId:
  352. errors.append(prefix + "Engin manquant")
  353. if not interv.strRapportId:
  354. errors.append(prefix + "Rapport manquant")
  355. if not interv.strCatégorieInterventionId:
  356. errors.append(prefix + "Catégorie de l'intervention manquante")
  357. if not interv.strTypeInterventionId:
  358. errors.append(prefix + "Type d'intervention manquant")
  359. if not interv.dblquantite:
  360. errors.append(prefix + "Quantité nulle")
  361. if not interv.strunite:
  362. errors.append(prefix + "Unité non renseignée")
  363. if not interv.dtmIntervention:
  364. errors.append(prefix + "Erreur : date d'intervention")
  365. if not interv.dtmDureeIntervention:
  366. errors.append(prefix + "Durée d'intervention nulle")
  367. if not interv.strunite:
  368. errors.append(prefix + "Unité non renseignée")
  369. if not engin_existe(interv.strEnginId):
  370. errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
  371. # *** 6- Interruption pour corection manuelle des données (si nécessaire)
  372. if errors:
  373. logging.error("<!> Des erreurs ont été détectées dans les données à importer. <!>")
  374. for msg in errors:
  375. logging.error(msg)
  376. else:
  377. logging.info("Aucune erreur n'a été détectée dans les données.")
  378. if no_prompt:
  379. break
  380. prompt = ""
  381. while prompt != "v":
  382. prompt = input(">> Veuillez contrôler les données, puis taper 'v' pour continuer, ou 'q' pour quitter...")
  383. if prompt == "q":
  384. logger.info("# Annulation de l'import")
  385. sys.exit(1)
  386. # ########## MISE A JOUR DE LA BASE DE DONNEES ANALYTIQUE ##########
  387. # On charge en mémoire les affaires et les interventions
  388. logger.info("# Mise à jour de la base Analytique")
  389. logger.info("> NB: Les modifications ne seront appliquées à la base que si toutes les opérations se déroulent normalement.")
  390. affaires = []
  391. with open(affaires_file) as f:
  392. next(f) # saute la première ligne
  393. for line in f:
  394. affaire = Affaire.from_csv(line)
  395. affaires.append(affaire)
  396. intervs = []
  397. with open(intervs_file) as f:
  398. next(f) # saute la première ligne
  399. for line in f:
  400. interv = Interv.from_csv(line)
  401. intervs.append(interv)
  402. class Tarification(Model):
  403. """ Modèle de donnée d'une ligne de tarification """
  404. _FIELDS = ["DblAffaireId", "strRapportId", "strArticleId", "dblQuantite", "strUnite",
  405. "dtmDebut", "dtmFin", "bytPeriode", "dblPrixUnitaire", "dblPrixTotal", "strStatut"]
  406. # On insère les affaires, interventions dans Analytique, et on génère la ou les lignes de tarification associées
  407. for affaire in affaires:
  408. # insertion dans tbl_Affaires
  409. sql = """ INSERT INTO tbl_Affaires ( strMOId, strMOeId, strEntrepriseId, strCommneId, strLieux, strTypeId, dtmCommande, Ref,
  410. blnMarche, dblMarche, intTypeContrat, strCT, strLiaisonControle, blnTarification,
  411. blnAnalyse, strSituation, intCoefFG )
  412. VALUES ('{affaire.strMOId}', '{affaire.strMOeId}', '{affaire.strEntrepriseId}', '{affaire.strCommneId}', '{affaire.strLieux}', '{affaire.strTypeId}',
  413. #{affaire.dtmCommande}#, '{affaire.Ref}', {affaire.blnMarche}, {affaire.dblMarche}, {affaire.intTypeContrat}, '{affaire.strCT}',
  414. '{affaire.strLiaisonControle}', True, False, '{affaire.strSituation}', {affaire.intCoefFG})
  415. """.format(affaire=affaire)
  416. analytique_db.execute(sql)
  417. logger.info("> Ajout de l'affaire: {}".format(affaire.strLiaisonControle))
  418. # On insère les interventions dans tbl_Intervention
  419. for interv in intervs:
  420. affaire = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(interv.LienAff))
  421. if not affaire:
  422. logger.error("Intervention {} : Impossible de trouver l'affaire {}".format(interv.strTest, interv.LienAff))
  423. continue
  424. interv.dblAffaireId = affaire.DblAffaireId
  425. sql = """INSERT INTO tbl_Intervention ( DblAffaireId, strEquipeId, strEnginId, strRapportId, strCatégorieInterventionId, strTypeInterventionId,
  426. dblquantite, strunite, dtmIntervention, dtmDureeIntervention, strcaracteristique1, strgrandeur1, strunite1,
  427. strcaracteristique2, strgrandeur2, strunite2, strcaracteristique3, strgrandeur3, strunite3, strLiaisonControle, strarticleId,
  428. intPeriode, blnTarification, blnAnalyse, blnFacturer, remarques, blnPeriode, dtmImportation, strTest )
  429. VALUES ({interv.dblAffaireId}, '{interv.strEquipeId}', '{interv.strEnginId}', '{interv.strRapportId}', '{interv.strCatégorieInterventionId}',
  430. '{interv.strTypeInterventionId}', {interv.dblquantite}, '{interv.strunite}', #{interv.dtmIntervention}#, #{interv.dtmDureeIntervention}#,
  431. '{interv.strcaracteristique1}', '{interv.strgrandeur1}', '{interv.strunite1}', '{interv.strcaracteristique2}',
  432. '{interv.strgrandeur2}', '{interv.strunite2}', '{interv.strcaracteristique3}', '{interv.strgrandeur3}', '{interv.strunite3}', '{interv.strLiaisonControle}',
  433. '{interv.strArticleId}', {interv.intPeriode}, True, False, False, '{interv.remarques}',
  434. False, #{interv.dtmImportation}#, '{interv.strTest}')
  435. """.format(interv=interv)
  436. analytique_db.execute(sql)
  437. logger.info("> Ajout de l'intervention: {}".format(interv.strTest))
  438. # Calcul de la tarification et ajout à tbl_Tarification
  439. # > On va créer une ligne de tarification pour chaque groupe d'interventions
  440. # > partageant le même lngRapportid et strArticleId (cad le même engin)
  441. for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId) for interv in intervs]):
  442. tarif = Tarification()
  443. tarif.intervs = [interv for interv in intervs if interv.strRapportId == strRapportId and interv.strArticleId == strArticleId]
  444. # recupere le prix unitaire de l'engin
  445. prix_unitaire = commun_db.first("""SELECT dblPU FROM tblTarif WHERE [strArticleId]='{}' AND [intPeriodeValiditeId]={}
  446. """.format(strArticleId, tarif.intervs[0].intPeriode)).dblPU
  447. # recupere le taux de tva applicable à l'engin
  448. taux_tva = commun_db.first("""SELECT tblTVATaux.dblTVATaux FROM tblArticle INNER JOIN tblTVATaux ON tblArticle.bytTVAArticleId = tblTVATaux.bytTVAId
  449. WHERE (((tblArticle.strArticleId)='{}'));""".format(strArticleId)).dblTVATaux
  450. tarif.DblAffaireId = tarif.intervs[0].dblAffaireId
  451. tarif.strRapportId = strRapportId
  452. tarif.strArticleId = strArticleId
  453. tarif.dblQuantite = sum([float(interv.dblquantite) for interv in tarif.intervs])
  454. tarif.strUnite = tarif.intervs[0].strunite
  455. tarif.dtmDebut = min([interv.dtmIntervention for interv in tarif.intervs])
  456. tarif.dtmFin = max([interv.dtmIntervention for interv in tarif.intervs])
  457. tarif.bytPeriode = tarif.intervs[0].intPeriode
  458. tarif.dblPrixUnitaire = prix_unitaire
  459. tarif.dblPrixTotal = tarif.dblQuantite * tarif.dblPrixUnitaire
  460. tarif.dblTauxTVA = taux_tva
  461. tarif.dblPrixTVA = tarif.dblPrixTotal * (0.01 * tarif.dblTauxTVA)
  462. tarif.strStatut = 'A facturer'
  463. sql = """ INSERT INTO tbl_Tarification ( DblAffaireId, strRapportId, strArticleId, dblQuantite, strUnite, dtmDebut, dtmFin, bytPeriode,
  464. dblPrixUnitaire, dblPrixTotal, dblTauxTVA, dblPrixTVA, strStatut )
  465. VALUES ({tarif.DblAffaireId}, '{tarif.strRapportId}', '{tarif.strArticleId}', {tarif.dblQuantite}, '{tarif.strUnite}', #{tarif.dtmDebut}#,
  466. #{tarif.dtmFin}#, {tarif.bytPeriode}, {tarif.dblPrixUnitaire}, {tarif.dblPrixTotal},
  467. {tarif.dblTauxTVA}, {tarif.dblPrixTVA}, '{tarif.strStatut}')
  468. """.format(tarif=tarif)
  469. analytique_db.execute(sql)
  470. logger.info("> Génération d'une ligne de tarification pour l'affaire {} (rapport {}, article: {})".format(tarif.intervs[0].LienAff, strRapportId, strArticleId))
  471. # Maj champs MOS
  472. # Ces champs sont utilisés dans les tables Controles pour savoir si une ligne a déjà été importée
  473. for affaire in affaires:
  474. dblAffaireId = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)).DblAffaireId
  475. sql = """UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
  476. WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId}
  477. """.format(DblAffaireId=dblAffaireId,
  478. lngChantierId=affaire.lngChantierId,
  479. bytCommandeId=affaire.bytCommandeId)
  480. controles_db.execute(sql)
  481. for interv in intervs:
  482. if interv.strCatégorieInterventionId == "CC":
  483. tbl = "tblCompactageIntervs"
  484. elif interv.strCatégorieInterventionId == "CE":
  485. tbl = "tblEtancheiteIntervs"
  486. elif interv.strCatégorieInterventionId == "CI":
  487. tbl = "tblVideoIntervs"
  488. else:
  489. continue
  490. sql = """UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
  491. WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId} AND [bytIntervId]={bytIntervId}
  492. """.format(tbl=tbl,
  493. DblAffaireId=interv.dblAffaireId,
  494. lngChantierId=interv.lngChantierId,
  495. bytCommandeId=interv.bytCommandeId,
  496. bytIntervId=interv.bytIntervId)
  497. controles_db.execute(sql)
  498. logger.info("> Mise à jour des champs MOS")
  499. # On commit les modifications
  500. logger.info("Commit des modifications...")
  501. analytique_db.commit()
  502. # ########## MISE A JOUR DES TEMPS D'INSTALLATION ##########
  503. # > Le temps d'installation est le temps passé par chaque agent en transport, préparation, reporting...etc.
  504. # > C'est donc le temps de travail théorique, moins le temps d'intervention.
  505. # > pour des raisons de performances, on ne commence le traitement qu'à partir de l'année N-1
  506. logger.info("Mise à jour des temps d'installation...")
  507. # date zéro pour Access
  508. date0 = datetime(1899, 12, 30, 0, 0, 0)
  509. # On parcourt les interventions.
  510. # Lorsque le temps d'intervention total d'une même équipe un même jour est inférieur à 8h,
  511. # On affecte la différence de temps à la première intervention en tant que 'temps d'installation'
  512. sql = """SELECT First(tbl_Intervention.dblInterventionId) AS dblInterventionId, tbl_Intervention.strEquipeId,
  513. tbl_Intervention.dtmIntervention, CDate(Sum(tbl_Intervention.dtmDureeIntervention)) AS SD
  514. FROM tbl_Intervention
  515. WHERE tbl_Intervention.strLiaisonControle Like '%/%'
  516. AND Year([dtmIntervention])>={}
  517. AND tbl_Intervention.dtmDureeIntervention > 0
  518. AND tbl_Intervention.strEquipeId Is Not Null
  519. GROUP BY tbl_Intervention.strEquipeId, tbl_Intervention.dtmIntervention
  520. HAVING (((CDate(Sum(tbl_Intervention.dtmDureeIntervention)))<#1899/12/30 8:0:0#))
  521. """.format(datetime.now().year - 1)
  522. for interv in analytique_db.read_all(sql):
  523. tps_install = (date0 + timedelta(hours=8) - interv.SD)
  524. sql = """UPDATE tbl_Intervention SET tbl_Intervention.dtmDureeInstallation = #{}#
  525. WHERE (((tbl_Intervention.dblInterventionId)={}));""".format(date0 + tps_install, interv.dblInterventionId)
  526. analytique_db.execute(sql)
  527. logger.debug("* Mise à jour du temps d'installation de l'intervention {}".format(interv.dblInterventionId))
  528. analytique_db.commit()
  529. logger.info("# Import terminé")