ctrl2analytique.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  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 à {}".format(compteur, affaires_file))
  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,
  135. tblCompactageResultats.dtmDuree, tblCompactagePartChantiers.strTrcRegard, tblMateriaux.strMatériau,
  136. tblCompactageResultats.bytPartChantierId, tblCompactageIntervs.sngIntervIdMos
  137. FROM ((tblMateriaux RIGHT JOIN ((((tblCompactageIntervs LEFT JOIN tblCompactageEngins ON tblCompactageIntervs.strEquipeId = tblCompactageEngins.strEquipeId)
  138. INNER JOIN tblCompactageResultats ON (tblCompactageIntervs.bytIntervId = tblCompactageResultats.bytIntervId)
  139. AND (tblCompactageIntervs.lngChantierId = tblCompactageResultats.lngChantierId)) INNER JOIN tblCompactagePartChantiers
  140. ON (tblCompactageResultats.bytPartChantierId = tblCompactagePartChantiers.bytPartChantierId)
  141. AND (tblCompactageResultats.lngChantierId = tblCompactagePartChantiers.lngChantierId)) INNER JOIN tblCompactageBases
  142. ON tblCompactageIntervs.lngChantierId = tblCompactageBases.lngChantierId) ON tblMateriaux.strMateriauId = tblCompactagePartChantiers.strMateriauRemblaiId)
  143. LEFT JOIN tblMateriaux AS tblMateriaux_1 ON tblCompactagePartChantiers.strMateriauEnrobageId = tblMateriaux_1.strMateriauId)
  144. LEFT JOIN tblMateriaux AS tblMateriaux_2 ON tblCompactagePartChantiers.strMateriauLitId = tblMateriaux_2.strMateriauId
  145. WHERE (((tblCompactageIntervs.sngIntervIdMos)=0 Or (tblCompactageIntervs.sngIntervIdMos) Is Null))
  146. """
  147. def get_type_compactage_interv(observation):
  148. """ retourne le sous-type d'intervention à partir du commentaire associé """
  149. if "ASSAINISEMENT" or "ASSAINISEMENT" in observation:
  150. return "CC3"
  151. elif "CABLE" in observation:
  152. return "CC1"
  153. elif "A.E.P" in observation:
  154. return "CC2"
  155. elif "GAZ" in observation:
  156. return "CC4"
  157. else:
  158. return "CC3"
  159. for data in controles_db.read(sql):
  160. interv = Interv()
  161. interv.strEquipeId = "C{}".format(data.strEquipeId)
  162. interv.strEnginId = data.strEnginId
  163. interv.strRapportId = data.lngRapportId
  164. interv.strTypeInterventionId = get_type_compactage_interv(data.memTravaux)
  165. interv.strCatégorieInterventionId = "CC"
  166. interv.dblquantite = 1
  167. interv.strunite = "u"
  168. interv.dtmIntervention = data.dtmEssai
  169. interv.dtmDureeIntervention = data.dtmDuree
  170. interv.dtmDureeInstallation = 0 # Les temps d'installation seront calculés en fin de traitement
  171. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  172. interv.strArticleId = data.strEnginId
  173. interv.intPeriode = get_periode_validite(data.dtmEssai)
  174. interv.remarques = data.strTrcRegard
  175. interv.strgrandeur1 = data.strMatériau
  176. interv.strgrandeur2 = data.strMatériau
  177. interv.strgrandeur3 = data.strMatériau
  178. interv.strcaracteristique1 = "Matériau remblai"
  179. interv.strcaracteristique2 = "Matériau lit de pose"
  180. interv.strcaracteristique3 = "Matériau enrobage"
  181. interv.strunite1 = ""
  182. interv.strunite2 = ""
  183. interv.strunite3 = ""
  184. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  185. interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
  186. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  187. # pour garder le lien avec la donnée d'origine:
  188. interv.lngChantierId = data.lngChantierId
  189. interv.bytCommandeId = data.bytCommandeId
  190. interv.bytIntervId = data.bytIntervId
  191. # Créé la ligne dans le fichier intervs.csv
  192. interv.dump(intervs_file)
  193. compteur += 1
  194. logger.info("> {} interventions Compactage ajoutées au fichier".format(compteur))
  195. # ########## IMPORT DES INTERVENTIONS D'ETANCHEITE ##########
  196. # Importe les interventions de contrôle d'étanchéité dans le fichier intervs.csv
  197. compteur = 0
  198. sql = """SELECT tblEtancheiteIntervs.lngChantierId, tblEtancheiteIntervs.bytCommandeId, tblEtancheiteIntervs.bytIntervId, tblEtancheiteIntervs.strEquipeId,
  199. tblEtancheiteIntervs.lngRapportId, tblEtancheitePartChantiers.bytTypeEssai, tblMateriaux.strMateriauId, tblMateriaux.strMatériau,
  200. tblEtancheitePartChantiers.intDiametre, tblEtancheitePartChantiers.sngLgHt, tblEtancheitePartChantiers.intNbJoint, tblEtancheiteResultats.dtmDuree,
  201. tblEtancheiteResultats.dtmEssai, tblEtancheitePartChantiers.strTrcRegard, tblEtancheiteResultats.bytPartChantierId
  202. FROM ((tblEtancheiteIntervs INNER JOIN tblEtancheiteResultats ON (tblEtancheiteIntervs.lngChantierId = tblEtancheiteResultats.lngChantierId)
  203. AND (tblEtancheiteIntervs.bytIntervId = tblEtancheiteResultats.bytIntervId)) INNER JOIN tblEtancheitePartChantiers
  204. ON (tblEtancheiteResultats.lngChantierId = tblEtancheitePartChantiers.lngChantierId)
  205. AND (tblEtancheiteResultats.bytPartChantierId = tblEtancheitePartChantiers.bytPartChantierId)) INNER JOIN tblMateriaux
  206. ON tblEtancheitePartChantiers.strMateriauId = tblMateriaux.strMateriauId
  207. WHERE (((tblEtancheiteIntervs.sngIntervIdMos)=0 Or (tblEtancheiteIntervs.sngIntervIdMos) Is Null));
  208. """
  209. def get_engin_etancheite(equipe, diametre, materiau, type_essai):
  210. """ retourne l'engin correspondant à l'essai en fonction eds caractéristiques de l'essai """
  211. sql = """SELECT strEnginId FROM tblEtancheiteEngins
  212. WHERE ([strEquipeId] = '{}') AND ([intDiametre] = {}) AND ([strMateriauId] = '{}') AND ([bytTypeEssaiId] ={})
  213. """.format(equipe, diametre, materiau, type_essai)
  214. row = controles_db.first(sql)
  215. return row.strEnginId if row else ""
  216. for data in controles_db.read(sql):
  217. interv = Interv()
  218. interv.strEquipeId = "C{}".format(data.strEquipeId)
  219. interv.strEnginId = get_engin_etancheite(data.strEquipeId, data.intDiametre, data.strMateriauId, data.bytTypeEssai)
  220. interv.strRapportId = data.lngRapportId
  221. interv.strTypeInterventionId = "CE{}".format(data.bytTypeEssai)
  222. interv.strCatégorieInterventionId = "CE"
  223. interv.dblquantite = int(data.intNbJoint)
  224. interv.strunite = "u"
  225. interv.dtmIntervention = data.dtmEssai
  226. interv.dtmDureeIntervention = data.dtmDuree
  227. interv.dtmDureeInstallation = 0 # Les temps d'installation seront recalculés en fin de traitement
  228. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  229. interv.strArticleId = interv.strEnginId
  230. interv.intPeriode = get_periode_validite(data.dtmEssai)
  231. interv.remarques = data.strTrcRegard
  232. interv.strgrandeur1 = data.strMatériau
  233. interv.strgrandeur2 = data.intDiametre
  234. interv.strgrandeur3 = data.sngLgHt
  235. interv.strcaracteristique1 = "Matériau"
  236. interv.strcaracteristique2 = "Diamètre"
  237. interv.strcaracteristique3 = "Longueur"
  238. interv.strunite1 = ""
  239. interv.strunite2 = "mm"
  240. interv.strunite3 = "m"
  241. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  242. interv.strTest = "{}/{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId, data.bytPartChantierId)
  243. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  244. # pour garder le lien avec la donnée d'origine:
  245. interv.lngChantierId = data.lngChantierId
  246. interv.bytCommandeId = data.bytCommandeId
  247. interv.bytIntervId = data.bytIntervId
  248. # Créé la ligne dans le fichier intervs.csv
  249. interv.dump(intervs_file)
  250. compteur += 1
  251. logger.info("> {} interventions Etanchéité ajoutées au fichier".format(compteur))
  252. # ########## IMPORT DES INTERVENTIONS D'INSPECTION VIDEO ##########
  253. # Importe les interventions d'inspection vidéo dans le fichier intervs.csv
  254. compteur = 0
  255. sql = """SELECT tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
  256. tblVideoEngins.strEnginId, tblVideoIntervs.lngRapportId, First(tblso_rate_Analyse.MateriauCourt) AS strmateriau, tblVideoIntervs.lngTroncon,
  257. tblVideoIntervs.sngNbJourFact, First(tblso_rate_Analyse.MaxDeDiametre) AS diam, tblVideoIntervs.dtmDuree, tblVideoIntervs.dtmIntervDu,
  258. First(tblVideoIntervs.memObservation) AS memObservation, tblChantiers.strEntrepriseId
  259. FROM ((tblVideoEngins RIGHT JOIN tblVideoIntervs ON tblVideoEngins.strEquipeId = tblVideoIntervs.strEquipeId) INNER JOIN tblso_rate_Analyse ON
  260. (tblVideoIntervs.lngChantierId = tblso_rate_Analyse.lngChantierId) AND (tblVideoIntervs.bytIntervId = tblso_rate_Analyse.bytIntervId)) INNER JOIN
  261. tblChantiers ON tblVideoIntervs.lngChantierId = tblChantiers.lngChantierId
  262. WHERE (((tblVideoIntervs.sngIntervIdMos) Is Null Or (tblVideoIntervs.sngIntervIdMos)=0))
  263. GROUP BY tblVideoIntervs.lngChantierId, tblVideoIntervs.bytCommandeId, tblVideoIntervs.bytIntervId, tblVideoIntervs.strEquipeId,
  264. tblVideoIntervs.lngRapportId, tblVideoIntervs.lngTroncon, tblVideoIntervs.sngNbJourFact, tblVideoIntervs.dtmDuree,
  265. tblVideoIntervs.dtmIntervDu, tblVideoEngins.strEnginId, tblChantiers.strEntrepriseId
  266. """
  267. for data in controles_db.read(sql):
  268. interv = Interv()
  269. interv.strEquipeId = "C{}".format(data.strEquipeId)
  270. interv.strEnginId = data.strEnginId
  271. interv.strRapportId = data.lngRapportId
  272. interv.strTypeInterventionId = "CI1" if data.strEntrepriseId != 195 else "CI2"
  273. interv.strCatégorieInterventionId = "CI"
  274. interv.dblquantite = int(data.sngNbJourFact)
  275. interv.strunite = "j"
  276. interv.dtmIntervention = data.dtmIntervDu
  277. interv.dtmDureeIntervention = data.dtmDuree
  278. interv.dtmDureeInstallation = 0 # Les temps d'installation seront recalculés en fin de traitement
  279. interv.strLiaisonControle = "{}/{}/{}".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  280. interv.strArticleId = data.strEnginId
  281. interv.intPeriode = get_periode_validite(data.dtmIntervDu)
  282. interv.remarques = data.memObservation
  283. interv.strgrandeur1 = data.strmateriau
  284. interv.strgrandeur2 = data.diam
  285. interv.strgrandeur3 = data.lngTroncon
  286. interv.strcaracteristique1 = "Matériau"
  287. interv.strcaracteristique2 = "Diamètre"
  288. interv.strcaracteristique3 = "Longueur inspectée"
  289. interv.strunite1 = ""
  290. interv.strunite2 = "mm"
  291. interv.strunite3 = "m"
  292. interv.dtmImportation = "{}".format(datetime.now().strftime("%Y-%m-%d"))
  293. interv.strTest = "{}/{}/{}/1".format(data.lngChantierId, data.bytCommandeId, data.bytIntervId)
  294. interv.LienAff = "{}/{}".format(data.lngChantierId, data.bytCommandeId)
  295. # pour garder le lien avec la donnée d'origine:
  296. interv.lngChantierId = data.lngChantierId
  297. interv.bytCommandeId = data.bytCommandeId
  298. interv.bytIntervId = data.bytIntervId
  299. # Créé la ligne dans le fichier intervs.csv
  300. interv.dump(intervs_file)
  301. compteur += 1
  302. logger.info("> {} interventions ITV ajoutées au fichier".format(compteur))
  303. logging.info("Les données à importer ont été ajoutées aux fichiers '{}' et '{}'".format(affaires_file, intervs_file))
  304. logging.info("Ces fichiers sont au format CSV (séparateur: tabulation)")
  305. # ########## CONTROLE ET CORRECTION DES DONNEES ##########
  306. errors = -1
  307. while errors:
  308. errors = []
  309. with open(affaires_file) as f:
  310. next(f) # saute la première ligne
  311. for line in f:
  312. affaire = Affaire.from_csv(line)
  313. prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
  314. if not affaire.strMOId:
  315. errors.append(prefix + "MO manquant")
  316. else:
  317. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOId)):
  318. errors.append(prefix + "Le MO {} n'existe pas dans tblTiers".format(affaire.strMOId))
  319. if not affaire.strMOeId:
  320. errors.append(prefix + "MOe manquant")
  321. else:
  322. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOeId)):
  323. errors.append(prefix + "Le MOe {} n'existe pas dans tblTiers".format(affaire.strMOeId))
  324. if not affaire.strEntrepriseId:
  325. errors.append(prefix + "Entreprise manquante")
  326. else:
  327. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strEntrepriseId)):
  328. errors.append(prefix + "L'entreprise {} n'existe pas dans tblTiers".format(affaire.strEntrepriseId))
  329. if not affaire.strCommneId:
  330. errors.append(prefix + "Commune manquante")
  331. else:
  332. if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strCommneId)):
  333. errors.append(prefix + "La commune {} n'existe pas dans tblTiers".format(affaire.strCommneId))
  334. if not affaire.strTypeId:
  335. errors.append(prefix + "Type d'affaire manquant")
  336. if not affaire.dtmCommande:
  337. errors.append(prefix + "Date de commande manquante")
  338. if affaire.blnMarche == True and not affaire.intDevisId:
  339. errors.append(prefix + "Numéro de devis manquant")
  340. if analytique_db.exists("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)):
  341. errors.append(prefix + "Une affaire portant ce code existe déjà: {}".format(affaire.strLiaisonControle))
  342. with open(intervs_file) as f:
  343. next(f) # saute la première ligne
  344. for line in f:
  345. interv = Interv.from_csv(line)
  346. prefix = "Intervention {}: ".format(interv.strLiaisonControle)
  347. if not interv.strEquipeId:
  348. errors.append(prefix + "Equipe manquante")
  349. if not interv.strEnginId:
  350. errors.append(prefix + "Engin manquant")
  351. if not interv.strRapportId:
  352. errors.append(prefix + "Rapport manquant")
  353. if not interv.strCatégorieInterventionId:
  354. errors.append(prefix + "Catégorie de l'intervention manquante")
  355. if not interv.strTypeInterventionId:
  356. errors.append(prefix + "Type d'intervention manquant")
  357. if not interv.dblquantite:
  358. errors.append(prefix + "Quantité nulle")
  359. if not interv.strunite:
  360. errors.append(prefix + "Unité non renseignée")
  361. if not interv.dtmIntervention:
  362. errors.append(prefix + "Erreur : date d'intervention")
  363. if not interv.dtmDureeIntervention:
  364. errors.append(prefix + "Durée d'intervention nulle")
  365. if not interv.strunite:
  366. errors.append(prefix + "Unité non renseignée")
  367. if not engin_existe(interv.strEnginId):
  368. errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
  369. # *** 6- Interruption pour corection manuelle des données (si nécessaire)
  370. if errors:
  371. logging.error("<!> Des erreurs ont été détectées dans les données à importer. <!>")
  372. for msg in errors:
  373. logging.error(msg)
  374. else:
  375. logging.info("Aucune erreur n'a été détéctée dans les données.")
  376. if no_prompt:
  377. break
  378. # prompt = ""
  379. # while prompt != "v":
  380. # prompt = input(">> Veuillez contrôler les données, puis taper 'v' pour continuer, ou 'q' pour quitter...")
  381. # if prompt == "q":
  382. # sys.exit(1)
  383. # ########## MISE A JOUR DE LA BASE DE DONNEES ANALYTIQUE ##########
  384. # On charge en mémoire les affaires et les interventions
  385. logger.info("# Mise à jour de la base Analytique")
  386. logger.info("> NB: Les modifications ne seront appliquées à la base que si toutes les opérations se déroulent normalement.")
  387. affaires = []
  388. with open(affaires_file) as f:
  389. next(f) # saute la première ligne
  390. for line in f:
  391. affaire = Affaire.from_csv(line)
  392. affaires.append(affaire)
  393. intervs = []
  394. with open(intervs_file) as f:
  395. next(f) # saute la première ligne
  396. for line in f:
  397. interv = Interv.from_csv(line)
  398. intervs.append(interv)
  399. class Tarification(Model):
  400. """ Modèle de donnée d'une ligne de tarification """
  401. _FIELDS = ["DblAffaireId", "strRapportId", "strArticleId", "dblQuantite", "strUnite",
  402. "dtmDebut", "dtmFin", "bytPeriode", "dblPrixUnitaire", "dblPrixTotal", "strStatut"]
  403. # On insère les affaires, interventions dans Analytique, et on génère la ou les lignes de tarification associées
  404. for affaire in affaires:
  405. # insertion dans tbl_Affaires
  406. sql = """ INSERT INTO tbl_Affaires ( strMOId, strMOeId, strEntrepriseId, strCommneId, strLieux, strTypeId, dtmCommande, Ref,
  407. blnMarche, dblMarche, intTypeContrat, strCT, strLiaisonControle, blnTarification,
  408. blnAnalyse, strSituation, intCoefFG )
  409. VALUES ('{affaire.strMOId}', '{affaire.strMOeId}', '{affaire.strEntrepriseId}', '{affaire.strCommneId}', '{affaire.strLieux}', '{affaire.strTypeId}',
  410. #{affaire.dtmCommande}#, '{affaire.Ref}', {affaire.blnMarche}, {affaire.dblMarche}, {affaire.intTypeContrat}, '{affaire.strCT}',
  411. '{affaire.strLiaisonControle}', True, False, '{affaire.strSituation}', {affaire.intCoefFG})
  412. """.format(affaire=affaire)
  413. analytique_db.execute(sql)
  414. logger.info("> Ajout de l'affaire: {}".format(affaire.strLiaisonControle))
  415. # On insère les interventions dans tbl_Intervention
  416. for interv in intervs:
  417. affaire = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(interv.LienAff))
  418. if not affaire:
  419. logger.error("Intervention {} : Impossible de trouver l'affaire {}".format(interv.strTest, interv.LienAff))
  420. continue
  421. interv.dblAffaireId = affaire.DblAffaireId
  422. sql = """INSERT INTO tbl_Intervention ( DblAffaireId, strEquipeId, strEnginId, strRapportId, strCatégorieInterventionId, strTypeInterventionId,
  423. dblquantite, strunite, dtmIntervention, dtmDureeIntervention, strcaracteristique1, strgrandeur1, strunite1,
  424. strcaracteristique2, strgrandeur2, strunite2, strcaracteristique3, strgrandeur3, strunite3, strLiaisonControle, strarticleId,
  425. intPeriode, blnTarification, blnAnalyse, blnFacturer, remarques, blnPeriode, dtmImportation, strTest )
  426. VALUES ({interv.dblAffaireId}, '{interv.strEquipeId}', '{interv.strEnginId}', '{interv.strRapportId}', '{interv.strCatégorieInterventionId}',
  427. '{interv.strTypeInterventionId}', {interv.dblquantite}, '{interv.strunite}', #{interv.dtmIntervention}#, #{interv.dtmDureeIntervention}#,
  428. '{interv.strcaracteristique1}', '{interv.strgrandeur1}', '{interv.strunite1}', '{interv.strcaracteristique2}',
  429. '{interv.strgrandeur2}', '{interv.strunite2}', '{interv.strcaracteristique3}', '{interv.strgrandeur3}', '{interv.strunite3}', '{interv.strLiaisonControle}',
  430. '{interv.strArticleId}', {interv.intPeriode}, True, False, False, '{interv.remarques}',
  431. False, #{interv.dtmImportation}#, '{interv.strTest}')
  432. """.format(interv=interv)
  433. analytique_db.execute(sql)
  434. logger.info("> Ajout de l'intervention: {}".format(interv.strTest))
  435. # Calcul de la tarification et ajout à tbl_Tarification
  436. # > On va créer une ligne de tarification pour chaque groupe d'interventions
  437. # > partageant le même lngRapportid et strArticleId (cad le même engin)
  438. for strRapportId, strArticleId in set([(interv.strRapportId, interv.strArticleId) for interv in intervs]):
  439. tarif = Tarification()
  440. tarif.intervs = [interv for interv in intervs if interv.strRapportId == strRapportId and interv.strArticleId == strArticleId]
  441. # recupere le prix unitaire de l'engin
  442. prix_unitaire = commun_db.first("""SELECT dblPU FROM tblTarif WHERE [strArticleId]='{}' AND [intPeriodeValiditeId]={}
  443. """.format(strArticleId, tarif.intervs[0].intPeriode)).dblPU
  444. # recupere le taux de tva applicable à l'engin
  445. taux_tva = commun_db.first("""SELECT tblTVATaux.dblTVATaux FROM tblArticle INNER JOIN tblTVATaux ON tblArticle.bytTVAArticleId = tblTVATaux.bytTVAId
  446. WHERE (((tblArticle.strArticleId)='{}'));""".format(strArticleId)).dblTVATaux
  447. tarif.DblAffaireId = tarif.intervs[0].dblAffaireId
  448. tarif.strRapportId = strRapportId
  449. tarif.strArticleId = strArticleId
  450. tarif.dblQuantite = sum([int(interv.dblquantite) for interv in tarif.intervs])
  451. tarif.strUnite = tarif.intervs[0].strunite
  452. tarif.dtmDebut = min([interv.dtmIntervention for interv in tarif.intervs])
  453. tarif.dtmFin = max([interv.dtmIntervention for interv in tarif.intervs])
  454. tarif.bytPeriode = tarif.intervs[0].intPeriode
  455. tarif.dblPrixUnitaire = prix_unitaire
  456. tarif.dblPrixTotal = tarif.dblQuantite * tarif.dblPrixUnitaire
  457. tarif.dblTauxTVA = taux_tva
  458. tarif.dblPrixTVA = tarif.dblPrixTotal * (0.01 * tarif.dblTauxTVA)
  459. tarif.strStatut = 'A facturer'
  460. sql = """ INSERT INTO tbl_Tarification ( DblAffaireId, strRapportId, strArticleId, dblQuantite, strUnite, dtmDebut, dtmFin, bytPeriode,
  461. dblPrixUnitaire, dblPrixTotal, dblTauxTVA, dblPrixTVA, strStatut )
  462. VALUES ({tarif.DblAffaireId}, '{tarif.strRapportId}', '{tarif.strArticleId}', {tarif.dblQuantite}, '{tarif.strUnite}', #{tarif.dtmDebut}#,
  463. #{tarif.dtmFin}#, {tarif.bytPeriode}, {tarif.dblPrixUnitaire}, {tarif.dblPrixTotal},
  464. {tarif.dblTauxTVA}, {tarif.dblPrixTVA}, '{tarif.strStatut}')
  465. """.format(tarif=tarif)
  466. analytique_db.execute(sql)
  467. logger.info("> Génération d'une ligne de tarification pour l'affaire {} (rapport {}, article: {})".format(tarif.intervs[0].LienAff, strRapportId, strArticleId))
  468. # Maj champs MOS
  469. # Ces champs sont utilisés dans les tables Controles pour savoir si une ligne a déjà été importée
  470. for affaire in affaires:
  471. dblAffaireId = analytique_db.first("SELECT TOP 1 DblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)).DblAffaireId
  472. sql = """UPDATE tblCommandes SET tblCommandes.sngAffaireIdMos = {DblAffaireId}
  473. WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId}
  474. """.format(DblAffaireId=dblAffaireId,
  475. lngChantierId=affaire.lngChantierId,
  476. bytCommandeId=affaire.bytCommandeId)
  477. controles_db.execute(sql)
  478. for interv in intervs:
  479. if interv.strCatégorieInterventionId == "CC":
  480. tbl = "tblCompactageIntervs"
  481. elif interv.strCatégorieInterventionId == "CE":
  482. tbl = "tblEtancheiteIntervs"
  483. elif interv.strCatégorieInterventionId == "CI":
  484. tbl = "tblVideoIntervs"
  485. else:
  486. continue
  487. sql = """UPDATE {tbl} SET {tbl}.sngIntervIdMos = {DblAffaireId}
  488. WHERE [lngChantierId]={lngChantierId} AND [bytCommandeId]={bytCommandeId} AND [bytIntervId]={bytIntervId}
  489. """.format(tbl=tbl,
  490. DblAffaireId=interv.dblAffaireId,
  491. lngChantierId=interv.lngChantierId,
  492. bytCommandeId=interv.bytCommandeId,
  493. bytIntervId=interv.bytIntervId)
  494. controles_db.execute(sql)
  495. logger.info("> Mise à jour des champs MOS")
  496. # On commit les modifications
  497. logger.info("Commit des modifications...")
  498. analytique_db.commit()
  499. # ########## MISE A JOUR DES TEMPS D'INSTALLATION ##########
  500. # > Le temps d'installation est le temps passé par chaque agent en transport, préparation, reporting...etc.
  501. # > C'est donc le temps de travail théorique, moins le temps d'intervention.
  502. logger.info("Mise à jour des temps d'installation...")
  503. sql = """ SELECT First(tbl_Intervention.dblInterventionId) AS dblInterventionId,
  504. tbl_Intervention.strEquipeId, tbl_Intervention.dtmIntervention,
  505. Year([dtmIntervention]) AS annee, Sum(tbl_Intervention.dtmDureeIntervention) AS SD
  506. FROM tbl_Intervention
  507. WHERE (((tbl_Intervention.strLiaisonControle) Like '*/*'))
  508. GROUP BY tbl_Intervention.strEquipeId, Year([dtmIntervention]), tbl_Intervention.dtmIntervention
  509. HAVING (((tbl_Intervention.strEquipeId) Is Not Null)
  510. AND ((Year([dtmIntervention]))>=Year(Date())-1)
  511. AND ((tbl_Intervention.dtmIntervention) Is Not Null)
  512. AND ((Sum(tbl_Intervention.dtmDureeIntervention))>0
  513. And (Sum(tbl_Intervention.dtmDureeIntervention)) Is Not Null)
  514. AND ((Count(tbl_Intervention.dtmDureeIntervention))>0
  515. And (Count(tbl_Intervention.dtmDureeIntervention)) Is Not Null))"""
  516. for interv in analytique_db.read(sql):
  517. if interv.SD < (8 / 24):
  518. tps_install = ((8 / 24) - interv.SD)
  519. sql = """UPDATE tbl_Intervention SET tbl_Intervention.dtmDureeInstallation = #{}#
  520. WHERE (((tbl_Intervention.dblInterventionId)={}));""".format(tps_install, interv.dblInterventionId)
  521. logger.debug("* Mise à jour du temps d'installation de l'intervention {}".format(interv.dblInterventionId))
  522. logger.info("# Import terminé")