ctrl2analytique.py 32 KB

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