From b48c9b79bd522644e9e643f3c814250df9b486dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Laporte?= <stephane.laporte@enercoop.org> Date: Thu, 12 Jun 2025 17:44:25 +0200 Subject: [PATCH 1/2] =?UTF-8?q?[FIX]=20erreur=20lors=20de=20la=20tentative?= =?UTF-8?q?=20de=20cr=C3=A9ation=20du=20PRM=20et/ou=20de=20la=20nouvelle?= =?UTF-8?q?=20p=C3=A9riode,=20=C3=A0=20v=C3=A9rifier=20manuellement=20:=20?= =?UTF-8?q?Impossible=20de=20cr=C3=A9er=20une=20nouvelle=20p=C3=A9riode=20?= =?UTF-8?q?pour=20le=20mod=C3=A8le=20Gestion=20des=20prix,=20il=20y=20a=20?= =?UTF-8?q?d=C3=A9j=C3=A0=20une=20p=C3=A9riode=20qui=20d=C3=A9marre=20le?= =?UTF-8?q?=202025-06-02=20et=20qui=20chevauche=20la=20p=C3=A9riode=20exis?= =?UTF-8?q?tante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/acc_operation.py | 506 ++++++++++++++++++++++++++-------------- 1 file changed, 325 insertions(+), 181 deletions(-) diff --git a/models/acc_operation.py b/models/acc_operation.py index b68a2e7..57f09a8 100644 --- a/models/acc_operation.py +++ b/models/acc_operation.py @@ -1,10 +1,22 @@ # Copyright 2021- Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from datetime import date +import json +from datetime import date, datetime from odoo import _, api, fields, models from odoo.exceptions import AccessError, ValidationError from odoo.tools import html_sanitize +from odoo.osv import expression + +from enum import Enum + +class GetPerimeterEvent(Enum): + NOT_SET = 1 + NO_CHANGE = 2 + PRM_IN = 3 + PRM_OUT = 4 + DATE_CHANGE = 5 + class AccOperation(models.Model): @@ -133,40 +145,154 @@ class AccOperation(models.Model): # ------------------------------------------------------ # API functions # ------------------------------------------------------ + + def _gen_log_table(self, tLogDatas, log_type, header): + message = "" + + n = sum(1 for item in tLogDatas if item.get('log_type') == log_type) + if n == 0: return message + + message += f"<h5>{header}</h5>" + message += "<table class='table table-striped'>" + message += "<thead>" + message += "<th>PRM</th><th>type</th><th>début</th><th>fin</th><th>action à prendre</th>" + message += "</thead>" + for tLogData in tLogDatas: + if (tLogData['log_type'] != log_type): continue + end_date = '' if tLogData['end'] is False else tLogData['end'].strftime('%d/%m/%Y') + message += (f"<tr>") + message += (f"<td class='text-nowrap'>{tLogData['id']}</td>") + message += (f"<td class='text-nowrap'>{'injection' if tLogData['prm_type'] == 'PROD' else 'soutirage'}</td>") + message += (f"<td class='text-nowrap'>{tLogData['start']:%d/%m/%Y}</td>") + message += f"<td class='text-nowrap' style='width: 10em;'>{end_date}</td>" + message += ("<td>") + if 'action' in tLogData: + message += ("<ul>") + for v in tLogData['action']: + message += (f"<li>{v}</li>") + message += ("</ul>") + message += (f"</td>") + message += (f"</tr>") + message += "</table>" + return message + + def _gen_log_tables(self, tLogDatas): + message = "" + # MULTI + message += self._gen_log_table(tLogDatas, GetPerimeterEvent.PRM_IN, 'Entrées de PRM: des actions sont requises') + message += self._gen_log_table(tLogDatas, GetPerimeterEvent.DATE_CHANGE, 'Dates modifiées: des actions sont requises') + message += self._gen_log_table(tLogDatas, GetPerimeterEvent.PRM_OUT, 'Sorties de PRM: pour information, ne requiert pas d\'action de votre part') + message += self._gen_log_table(tLogDatas, GetPerimeterEvent.NO_CHANGE, 'Pas de changement sur ces PRM: pour information, ne requiert pas d\'action de votre part') + return message + + def gen_log_prm_list(self, tLogDatas, prm_type): + t_ids = [ + item["id"] for item in tLogDatas + if item.get("log_type") in ( + GetPerimeterEvent.NO_CHANGE, + GetPerimeterEvent.PRM_IN, + GetPerimeterEvent.PRM_OUT + ) + and item.get("prm_type") == prm_type + ] + header = ("Liste complète des " + str(len(t_ids))+ " PRM d'injection" + if prm_type == "PROD" else "Liste complète des " + str(len(t_ids))+ " PRM de soutirage") + message = f"<h5>{header}</h5>" + message += "<table class='table table-striped'>" + for id in t_ids: + message += (f"<tr>") + message += (f"<td>{id}</td>") + message += (f"</tr>") + message += "</table>" + return message + + def gen_log_prm_lists(self, tLogDatas): + message = "" + message += self.gen_log_prm_list(tLogDatas, "PROD") + message += self.gen_log_prm_list(tLogDatas, "CONS") + return message + + def clean_database(self): + o_counters = self.env["acc.counter"].search([("name", ">=", "90000000000001"),("name", "<=", "90000000000010")]) + #print(o_counters) + o_counter_periods = o_counters.period_ids + #print(o_counter_periods) + o_prices = self.env["acc.price.conf"].search([("acc_injection_counter_id", "in", o_counters.ids)]) + #print(o_prices) + o_prices.unlink() + o_prices = self.env["acc.price.conf"].search([("acc_delivery_counter_id", "in", o_counters.ids)]) + #print(o_prices) + o_prices.unlink() + o_counter_periods.unlink() + o_counters.unlink() + + def debug_database(self): + o_counters = self.env["acc.counter"].search([("name", ">=", "90000000000001"),("name", "<=", "90000000000010")]) + for o_counter in o_counters: + print('added counter ' + str(o_counter.id) + ' ' + o_counter.name) + o_counter_periods = o_counters.period_ids + for o_counter_period in o_counter_periods: + print('added counter period ' + str(o_counter_period.id) + ' ' + o_counter_period.acc_counter_id.name + ' ' + str(o_counter_period.start_date) + ' ' + str(o_counter_period.end_date)) + o_prices = self.env["acc.price.conf"].search([("acc_injection_counter_id", "in", o_counters.ids)]) + for o_price in o_prices: + print('added price ' + str(o_price.id) + ' ' + o_price.acc_injection_counter_id.name + ' ' + o_price.acc_delivery_counter_id.name + ' ' + str(o_price.start_date) + ' ' + str(o_price.end_date) + ' ' + str(o_price.price)) + o_prices = self.env["acc.price.conf"].search([("acc_delivery_counter_id", "in", o_counters.ids)]) + for o_price in o_prices: + print('added price ' + str(o_price.id) + ' ' + o_price.acc_injection_counter_id.name + ' ' + o_price.acc_delivery_counter_id.name + ' ' + str(o_price.start_date) + ' ' + str(o_price.end_date) + ' ' + str(o_price.price)) + + def _perimeter(self, from_cron=False): """ Récupère les données de l'opération concernant le périmètre: - liste des PRM - date de début opération - @returns: log_id created with details + @returns: log_id created with detailsnv + + Format du flux reçu + { + 'agreement_id': 'ACC00000119', + 'usage_points': [ + { + 'usage_point_id': '30002521267277', + 'type': 'PROD / CONS', + 'start': '2022-02-01', + 'end': '2025-06-16 / 9999-12-31' + }, """ - # TODO : refactor, too complex - self._check_access_api() message = "" + message += ( - "<h1>Appel Enedis Périmètre " + "<p>Début appel API Enedis pour mise à jour du périmètre " + self.name - + " du " + + " : " + str(fields.Datetime.now()) - + "</h1>" + + "</p>" ) - message += "<p><strong>Appel API ...<br/>" - try: - perimeter_data = self._get_perimeter() - except ValidationError as e: - if from_cron: - log_id = self.create_log(message=str(e), type_log="cron") - log_id.send_api_error_mail() - raise e from e + if False: + self._check_access_api() + message += "<p>Appel API ...</p>" + try: + perimeter_data = self._get_perimeter() + except ValidationError as e: + if from_cron: + log_id = self.create_log(message=str(e), type_log="cron") + log_id.send_api_error_mail() + raise e from e + + message += "<p>Appel API terminé</p>" + else: + self.clean_database() + with open('/home/stephane.laporte/Téléchargements/enedis_perimeter_ecoe_castelet.txt', 'r') as file: + sJsonData = file.read() + sJsonData = sJsonData.replace("'", '"') + perimeter_data = json.loads(sJsonData) - message += "<p><strong>Appel API terminé<br/>" "Traitement des données ...<br/>" usage_points = perimeter_data.get("usage_points") counter_used = [] - list_injection = [] - list_soutirage = [] + tLogDatas = [] for usage_point in sorted( usage_points, key=lambda p: date.fromisoformat(p["start"]) @@ -182,163 +308,157 @@ class AccOperation(models.Model): usage_point_prm_type = "delivery" elif usage_point["type"] == "PROD": usage_point_prm_type = "injection" - message += ( - "<br/>PRM " - + usage_point["type"] - + " : " - + usage_point["usage_point_id"] - + " - Dates Enedis : " - + usage_point["start"] - + " - " - + usage_point["end"] - + "<br/>" - ) - counter_id = self.env["acc.counter"].search( - [ - ("name", "=", usage_point["usage_point_id"]), - ] - ) - if counter_id and len(counter_id) == 1: - message += "Ce PRM existe déjà dans Elocoop<br/>" - counter_period_ids = counter_id.period_ids.filtered( - lambda p: p.prm_type == usage_point_prm_type - ) - if counter_period_ids.filtered( - lambda p: p.start_date == usage_point_start - and p.end_date == usage_point_end - ): - message += ( - "Les dates de début et de fin sont identiques à celles " - "déjà enregistrées dans Elocoop.<br/>" - ) - elif counter_period_ids.filtered( - lambda p: p.start_date == usage_point_start - ): - counter_period_ids.filtered( - lambda p: p.start_date == usage_point_start - ).end_date = usage_point_end - message += ( - "période existante avec la même date de début, mais date de " - "fin différente, mise à jour date de fin<br/>" - ) - else: - try: - self.env["acc.counter.period"].create( - { - "acc_counter_id": counter_id.id, - "prm_type": usage_point_prm_type, - "acc_operation_id": self.id, - "start_date": usage_point_start, - "end_date": usage_point_end, - } - ) - - counter_used.append(counter_id.name) - message += ( - "De nouvelles dates sont renvoyées par Enedis : veuillez" - " renseigner le participant lié dans l’onglet " - "'Points de soutirage / Point d’injection' " - "(en fonction du cas)<br/>" - ) - except ValidationError as e: - message += ( - "<strong>erreur lors de la tentative de création d'une " - "nouvelle période, à vérifier manuellement :</strong><br/>" - + str(e) - + "<br/>" - ) - - elif len(counter_id) > 1: - message += "Plusieurs PRMs trouvés avec ce numéro - pas de modif<br/>" - else: - message += "PRM n'existe pas : Création ...<br/>" - if usage_point_prm_type == "injection": - # Si la date de l'opération n'est pas renseignée ou - # après la date de démarrage du point d'injection - # alors on force la date à celle du point d'injection - if ( - not self.date_start_contract - or self.date_start_contract > usage_point_start - ): - self.date_start_contract = usage_point_start - try: - counter_id = self.env["acc.counter"].create( - { - "name": usage_point["usage_point_id"], - } - ) - counter_used.append(counter_id.name) - self.env["acc.counter.period"].create( - { - "acc_counter_id": counter_id.id, - "prm_type": usage_point_prm_type, - "acc_operation_id": self.id, - "start_date": usage_point_start, - "end_date": usage_point_end, - "sale_price": self.sale_price_by_default, - } - ) - - self.check_sale_price_conf( - counter_id=counter_id, periode_start_date=usage_point_start - ) - - # If delivery counter add to first priority group if exist - if usage_point_prm_type == "delivery": - if self.check_priority_groups(counter=counter_id): - message += ( - "Ajout du nouveau PRM au premier groupe " - "de priorité<br/>" - ) - - message += "Fin de la création du PRM<br/>" - - except ValidationError as e: - message += ( - "<strong>erreur lors de la tentative de création du PRM et/ou " - "de la nouvelle période, à vérifier manuellement :<strong><br/>" - + str(e) - + "<br/>" - ) - - if usage_point_prm_type == "injection": - if usage_point["usage_point_id"] not in list_injection: - list_injection.append(usage_point["usage_point_id"]) - if usage_point_prm_type == "delivery": - if usage_point["usage_point_id"] not in list_soutirage: - list_soutirage.append(usage_point["usage_point_id"]) - - message += "<p>Liste complète des PRMs : </br>PRM Injection</br>" + # else ? on génère une exception ? + + tLogData = { + 'log_type': GetPerimeterEvent.NOT_SET, + 'id': usage_point["usage_point_id"], + 'prm_type': usage_point["type"], + 'start': usage_point_start, + 'end': usage_point_end, # date() or False + } - i = 1 - for inj in list_injection: - message += str(i) + " - " + inj + "<br/>" - i += 1 - message += "Total: " + str(len(list_injection)) + "</br>" + counter_id = self.env["acc.counter"].search([ + ("name", "=", usage_point["usage_point_id"]), + ]) + if counter_id: + (counter_used, message) = self.update_existing_counter(counter_id, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData) + #elif len(counter_id) > 1: + # il y a une contrainte d'unicité sur le champ name + # par contre si le champ acc_operation_id ne correspond pas on aurait un problème + else: + (counter_used, message) = self.add_new_counter(usage_point, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData) - message += "<br/>PRM Soutirage<br/>" - i = 1 - for inj in list_soutirage: - message += str(i) + " - " + inj + "<br/>" - i += 1 - message += "Total: " + str(len(list_soutirage)) + "</br>" + tLogDatas.append(tLogData) message += ( - "<h1>Fin appel API Périmètre: " + str(fields.Datetime.now()) + "</h1>" + "<p>Fin appel API Enedis pour mise à jour du périmètre: " + str(fields.Datetime.now()) + "</p>" ) - updated_objects = "<br/>".join(counter_used) + message += self._gen_log_tables(tLogDatas) + message += self.gen_log_prm_lists(tLogDatas) + + t_counters = [item['id'] for item in tLogDatas if item.get('log_type') in (GetPerimeterEvent.PRM_IN, GetPerimeterEvent.PRM_OUT, GetPerimeterEvent.DATE_CHANGE)] + updated_objects = "<br/>".join(t_counters) log_id = self.create_log( message=message, type_log="cron" if from_cron else "api", updated_objects=html_sanitize(updated_objects), ) - if from_cron and counter_used: + if from_cron and (len(t_counters) > 0): log_id.send_new_prm_email() + self.debug_database() + return log_id + def update_existing_counter(self, counter_id, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData): + counter_period_ids = counter_id.period_ids.filtered( + lambda p: p.prm_type == usage_point_prm_type + ) + if counter_period_ids.filtered( + lambda p: p.start_date == usage_point_start + and p.end_date == usage_point_end + ): + tLogData['message'] = 'Pas de changement' + tLogData['log_type'] = GetPerimeterEvent.NO_CHANGE + elif counter_period_ids.filtered( + lambda p: p.start_date == usage_point_start + ): + counter_period_ids.filtered( + lambda p: p.start_date == usage_point_start + ).end_date = usage_point_end + counter_used.append(counter_id.name) + tLogData['message'] = 'Changement de la date de sortie du PRM' + tLogData['log_type'] = GetPerimeterEvent.PRM_OUT + else: + try: + self.env["acc.counter.period"].create({ + "acc_counter_id": counter_id.id, + "prm_type": usage_point_prm_type, + "acc_operation_id": self.id, + "start_date": usage_point_start, + "end_date": usage_point_end, + }) + + counter_used.append(counter_id.name) + tLogData['message'] = 'Changement de dates' + tLogData['log_type'] = GetPerimeterEvent.DATE_CHANGE + tLogData['action'] = ['Renseigner le participant lié dans l’onglet ' + ( + 'Points d\'injection' if usage_point_prm_type == 'injection' else 'Points de soutirage')] + except ValidationError as e: + message += ( + "<strong>erreur lors de la tentative de création d'une " + "nouvelle période, à vérifier manuellement :</strong><br/>" + + str(e) + + "<br/>" + ) + return counter_used, message + + def add_new_counter(self, usage_point, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData): + tLogData['message'] = 'Entrée du PRM' + tLogData['log_type'] = GetPerimeterEvent.PRM_IN + if usage_point_prm_type == "injection": + # Si la date de l'opération n'est pas renseignée ou + # postérieure à la date de démarrage du point d'injection + # alors on force celle-ci à la date de démarrage du point d'injection + self.update_date_start_contract(usage_point_start) + try: + # ajout compteur + # le type est initialisé à posteriori lorsque les périodes sont mises à jour + counter_id = self.env["acc.counter"].create({ + "name": usage_point["usage_point_id"] + }) + counter_used.append(counter_id.name) + + # ajout période compteur + counter_period_id = self.env["acc.counter.period"].create({ + "acc_counter_id": counter_id.id, + "prm_type": usage_point_prm_type, + "acc_operation_id": self.id, + "start_date": usage_point_start, + "end_date": usage_point_end, + "sale_price": self.sale_price_by_default, + }) + + # ajout acc.price.conf se fait désormais lors de la création de la période + + self.check_sale_price_conf( + counter_id=counter_id, periode_start_date=usage_point_start + ) + + # If delivery counter add to first priority group if exist + b_added = False + if usage_point_prm_type == "delivery": + if self.check_priority_groups(counter=counter_id): + b_added = True + + # ajout au premier groupe de priorité par défaut en fonction du type de clé de répartition de l'opération se fait désormais lors de la création de la période + + tLogData['action'] = ['Renseigner le participant lié dans l’onglet ' + ( + 'Points d\'injection' if usage_point_prm_type == 'injection' else 'Points de soutirage')] + # TODO afficher uniquement si on a des groupes de priorité pour cette opération + if b_added: + tLogData[ + 'action'].append('Vérifier le paramétrage de la clé de répartition si nécessaire, le nouveau PRM a été ajouté par défaut au premier groupe de priorité') + + except ValidationError as e: + message += ( + "<strong>erreur lors de la tentative de création du PRM et/ou " + "de la nouvelle période, à vérifier manuellement :</strong><br/>" + + str(e) + + "<br/>" + ) + return counter_used, message + + def update_date_start_contract(self, usage_point_start): + if ( + not self.date_start_contract + or self.date_start_contract > usage_point_start + ): + self.date_start_contract = usage_point_start + # ------------------------------------------------------ # Business methods # ------------------------------------------------------ @@ -355,22 +475,29 @@ class AccOperation(models.Model): return True return False - def check_sale_price_conf(self, counter_id, periode_start_date): - """ - create sale price conf on new counter - """ - if counter_id.type in ["del", "del_inj"]: - inj_periods = self.env["acc.counter.period"].search( - [("acc_operation_id", "=", self.id), ("prm_type", "=", "injection")] + def check_sale_price_conf_for_delivery_counter(self, counter_id, periode_start_date): + if self.use_default_sale_price: + + domain = [("acc_operation_id", "=", self.id), ("prm_type", "=", "injection")] + date_end_domain = expression.OR( + [ + [("end_date", ">=", periode_start_date)], + [("end_date", "=", False)], + ] ) + domain = expression.AND([domain, date_end_domain]) + inj_periods = self.env["acc.counter.period"].search(domain) for inj_period in inj_periods: if counter_id.type == "del_inj": price = self.sale_price_by_default else: price = inj_period.sale_price + if price == 0.0: + # ex: compteur d'injection ajouté récemment + price = self.sale_price_by_default - if self.use_default_sale_price and price > 0.0: + if price > 0.0: self.env["acc.price.conf"].create( { "start_date": inj_period.start_date, @@ -382,20 +509,37 @@ class AccOperation(models.Model): } ) - elif counter_id.type in ["inj"]: - del_periods = self.env["acc.counter.period"].search( - [("acc_operation_id", "=", self.id), ("prm_type", "=", "delivery")] + def check_sale_price_conf_for_injection_counter(self, counter_id, periode_start_date): + + if self.use_default_sale_price and self.sale_price_by_default > 0.0: + domain = [("acc_operation_id", "=", self.id), ("prm_type", "=", "delivery")] + date_end_domain = expression.OR( + [ + [("end_date", ">=", periode_start_date)], + [("end_date", "=", False)], + ] ) + domain = expression.AND([domain, date_end_domain]) + del_periods = self.env["acc.counter.period"].search(domain) for del_period in del_periods: - if self.use_default_sale_price and self.sale_price_by_default > 0.0: - self.env["acc.price.conf"].create( - { - "start_date": periode_start_date, - "acc_operation_id": self.id, - "acc_injection_counter_id": counter_id.id, - "acc_delivery_counter_id": del_period.acc_counter_id.id, - "price": self.sale_price_by_default, - "type": "sale", - } - ) + self.env["acc.price.conf"].create( + { + "start_date": periode_start_date, + "acc_operation_id": self.id, + "acc_injection_counter_id": counter_id.id, + "acc_delivery_counter_id": del_period.acc_counter_id.id, + "price": self.sale_price_by_default, + "type": "sale", + } + ) + + def check_sale_price_conf(self, counter_id, periode_start_date): + """ + create sale price conf on new counter + """ + if counter_id.type in ["del", "del_inj"]: + self.check_sale_price_conf_for_delivery_counter(counter_id, periode_start_date) + + elif counter_id.type in ["inj"]: + self.check_sale_price_conf_for_injection_counter(counter_id, periode_start_date) -- GitLab From 60cdbfd48e560f41a4ab5f21769a890c1932a298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Laporte?= <stephane.laporte@enercoop.org> Date: Thu, 26 Jun 2025 15:20:09 +0200 Subject: [PATCH 2/2] [UPD] get perimeter exception --- data/mail_template_data.xml | 93 +----- models/acc_logs.py | 14 +- models/acc_operation.py | 584 ++++++++++++++++++++++-------------- 3 files changed, 382 insertions(+), 309 deletions(-) diff --git a/data/mail_template_data.xml b/data/mail_template_data.xml index 92798cf..dbfae80 100644 --- a/data/mail_template_data.xml +++ b/data/mail_template_data.xml @@ -3,8 +3,8 @@ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> <!--Email template --> - <record id="email_template_new_prm" model="mail.template"> - <field name="name">Logs périmètres: Nouveau PRM ou période</field> + <record id="email_template_enedis_perimeter" model="mail.template"> + <field name="name">Compte-rendu suite à la mise à jour du périmètre</field> <field name="model_id" ref="oacc.model_acc_logs" /> <field name="email_from">"Elocoop" <bonjour@elo.coop></field> <field @@ -14,90 +14,23 @@ <field name="reply_to">"Elocoop" <support@elo.coop></field> <field name="subject" - >Action requise - entrées/sorties de PRM de l’opération {{object.acc_operation_id.name}}</field> + >Opération {{object.acc_operation_id.name}} : Compte-rendu suite à la mise à jour du périmètre</field> <field name="description" - >Envoi notification nouveau PRM ou nouvelle période détecté.</field> + >Compte-rendu suite à la mise à jour du périmètre</field> <field name="body_html" type="html"> <div style="margin: 0px; padding: 0px;"> - Bonjour,<br /><br /> - Vous recevez ce mail car vous êtes administrateur de l’opération d’autoconsommation collective : <t - t-out="object.acc_operation_id.name" - >operation</t> (<t - t-out="object.acc_operation_id.description" - >oacc</t>)<br /> - D’après les données Enedis, des nouveaux PRM ont été ajoutés et/ou retirés de l’opération ou ont changé de propriétaire.<br - /><br /> - Numéros des PRM concernées :<br /> - <t - t-out="object.updated_objects" - t-options="{'widget':'html'}" - /> - - <br /> - <br /> - - Si des PRM sont ajoutés, afin qu’ils soient bien pris en compte dans Elocoop, nous vous remercions de renseigner les informations des participants liés à ces PRM dans votre espace d’administration Elocoop, de cette manière :<br - /> - <ul> - <li - >Si ce n’est pas encore fait, créer le participant dans l’onglet Participants</li> - <li - >Lier le nouveau PRM à ce participant dans l’onglet Points de soutirage ou Points d’injection</li> - <li>Indiquer les prix de vente pour ce PRM si nécessaire</li> - <li - >Donner accès au portail Elocoop au participant si nécessaire</li> - </ul> - - Nous restons disponibles pour toute question à l’adresse bonjour@elo.coop.<br - /><br /> - Bonne journée,<br /><br /> - - Le service client Elocoop<br /> - </div> - </field> - </record> - - <record id="email_template_api_error" model="mail.template"> - <field name="name">Logs Périmètres: Erreur API Enedis</field> - <field name="model_id" ref="oacc.model_acc_logs" /> - <field name="email_from">"Elocoop" <bonjour@elo.coop></field> - <field name="partner_to" /> - <field name="email_to">"Elocoop" <support@elo.coop></field> - <field name="reply_to">"Elocoop" <support@elo.coop></field> - <field - name="subject" - >Action requise - erreur d’API Enedis pour l’opération {{object.acc_operation_id.name}}</field> - <field - name="description" - >Envoi notification suite à une erreur api enedis.</field> - <field name="body_html" type="html"> - <div style="margin: 0px; padding: 0px;"> - Bonjour,<br /><br /> - Vous recevez ce mail car vous êtes administrateur de l’opération d’autoconsommation collective: <t - t-out="object.acc_operation_id.name" - >operation</t> (<t - t-out="object.acc_operation_id.description" - >oacc</t>).<br /> - Lors de la mise à jour automatique du périmètre et des données Enedis, l’API a renvoyé l’erreur suivante :<br - /><br /> - <t t-out="object.message">operation</t><br /><br /> - Nous vous remercions de vérifier que :<br /> - <ul> - <li - >Vos identifiants Enedis sont corrects dans l’onglet Autres Informations de votre espace d’administration Elocoop</li> - <li>L’identifiant <t - t-out="object.acc_operation_id.name" - >operation</t> est correct sur la page de votre opération.</li> - <li - >Nous informer en retour de ce mail lorsque cela est fait afin que nous vérifions la bonne configuration de l’API</li> - </ul> - + Bonjour,<br /><br /> + Vous recevez ce mail car vous êtes administrateur de + l’opération d’autoconsommation collective : + <t t-out="object.acc_operation_id.name">name</t> + (<t t-out="object.acc_operation_id.description">description</t>)<br /> + Veuillez prendre connaissance des informations qui suivent. Nous restons disponibles pour toute question à l’adresse bonjour@elo.coop.<br - /><br /> - Bonne journée,<br /><br /> - + /> Le service client Elocoop<br /> + <br /> + <t t-out="object.message" t-options="{'widget':'html'}" /><br /> </div> </field> </record> diff --git a/models/acc_logs.py b/models/acc_logs.py index 4fa0d5a..d9f4de6 100644 --- a/models/acc_logs.py +++ b/models/acc_logs.py @@ -33,20 +33,12 @@ class AccLogs(models.Model): # ------------------------------------------------------ # Actions # ------------------------------------------------------ - def send_new_prm_email(self): + def send_enedis_perimeter_email(self): """ - send email for new prm + send email for new prm or update or exception raising """ self.ensure_one() - template_id = self.env.ref("oacc_perimeter_api.email_template_new_prm") - template_id.send_mail(self.id) - - def send_api_error_mail(self): - """ - send api error email - """ - self.ensure_one() - template_id = self.env.ref("oacc_perimeter_api.email_template_api_error") + template_id = self.env.ref("oacc_perimeter_api.email_template_enedis_perimeter") template_id.send_mail(self.id) # ------------------------------------------------------ diff --git a/models/acc_operation.py b/models/acc_operation.py index 57f09a8..3125a30 100644 --- a/models/acc_operation.py +++ b/models/acc_operation.py @@ -1,14 +1,16 @@ # Copyright 2021- Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -import json -from datetime import date, datetime +import traceback +from datetime import date +from enum import Enum from odoo import _, api, fields, models -from odoo.exceptions import AccessError, ValidationError -from odoo.tools import html_sanitize +from odoo.exceptions import AccessError, UserError, ValidationError from odoo.osv import expression +from odoo.tools import html_sanitize + +from odoo.addons.api_enedis_acc.tools.elo_enums import EloCode -from enum import Enum class GetPerimeterEvent(Enum): NOT_SET = 1 @@ -16,7 +18,7 @@ class GetPerimeterEvent(Enum): PRM_IN = 3 PRM_OUT = 4 DATE_CHANGE = 5 - + EXCEPTION = 6 class AccOperation(models.Model): @@ -50,7 +52,7 @@ class AccOperation(models.Model): # ------------------------------------------------------ # Actions # ------------------------------------------------------ - def create_log(self, message, type_log="api", updated_objects="", call="perimetre"): + def create_log(self, message, type_log="api", call="perimetre"): if call == "perimetre": title = "Appel API Enedis Périmètre " else: @@ -61,7 +63,6 @@ class AccOperation(models.Model): "name": title + self.name + " du " + str(fields.Date.today()), "date_launched": fields.Datetime.now(), "type_log": type_log, - "updated_objects": updated_objects, "message": message, "acc_operation_id": self.id, } @@ -69,7 +70,7 @@ class AccOperation(models.Model): def get_perimeter(self): """ - call from user, for manual perimeter + called by user to update perimeter """ self.ensure_one() @@ -98,6 +99,7 @@ class AccOperation(models.Model): "flags": {"initial_mode": "view"}, } + # TODO Pourquoi on dit que tout va bien si admin return { "type": "ir.actions.client", "tag": "display_notification", @@ -146,63 +148,137 @@ class AccOperation(models.Model): # API functions # ------------------------------------------------------ + def _gen_error_table(selfself, tLogDatas): + message = "" + n = sum( + 1 + for item in tLogDatas + if item.get("log_type") == GetPerimeterEvent.EXCEPTION + ) + if n == 0: + return message + + message += ( + "<h5>Erreurs survenues pendant le " + "traitement des données provenant de Enedis</h5>" + ) + message += "<table class='table table-bordered table-striped'>" + message += "<thead>" + message += "<th>description</th><th>action à prendre</th>" + message += "</thead>" + for tLogData in tLogDatas: + if tLogData["log_type"] != GetPerimeterEvent.EXCEPTION: + continue + message += "<tr>" + data = (tLogData["data"] + "<br/>") if "data" in tLogData else "" + message += ( + f"<td>{tLogData['message']}<br/>{data}{tLogData['stack_trace']}</td>" + ) + message += "<td>" + if "action" in tLogData: + message += "<ul>" + for v in tLogData["action"]: + message += f"<li>{v}</li>" + message += "</ul>" + message += "</td>" + message += "</tr>" + message += "</table>" + return message + def _gen_log_table(self, tLogDatas, log_type, header): message = "" - n = sum(1 for item in tLogDatas if item.get('log_type') == log_type) - if n == 0: return message + n = sum(1 for item in tLogDatas if item.get("log_type") == log_type) + if n == 0: + return message message += f"<h5>{header}</h5>" message += "<table class='table table-striped'>" message += "<thead>" - message += "<th>PRM</th><th>type</th><th>début</th><th>fin</th><th>action à prendre</th>" + message += ( + "<th>PRM</th><th>type</th><th>début</th><th>fin</th>" + "<th>action à prendre</th>" + ) message += "</thead>" for tLogData in tLogDatas: - if (tLogData['log_type'] != log_type): continue - end_date = '' if tLogData['end'] is False else tLogData['end'].strftime('%d/%m/%Y') - message += (f"<tr>") - message += (f"<td class='text-nowrap'>{tLogData['id']}</td>") - message += (f"<td class='text-nowrap'>{'injection' if tLogData['prm_type'] == 'PROD' else 'soutirage'}</td>") - message += (f"<td class='text-nowrap'>{tLogData['start']:%d/%m/%Y}</td>") + if tLogData["log_type"] != log_type: + continue + end_date = ( + "" if tLogData["end"] is False else tLogData["end"].strftime("%d/%m/%Y") + ) + message += "<tr>" + message += f"<td class='text-nowrap'>{tLogData['id']}</td>" + message += ( + f"<td class='text-nowrap'>" + f"{'injection' if tLogData['prm_type'] == 'PROD' else 'soutirage'}" + f"</td>" + ) + message += f"<td class='text-nowrap'>{tLogData['start']:%d/%m/%Y}</td>" message += f"<td class='text-nowrap' style='width: 10em;'>{end_date}</td>" - message += ("<td>") - if 'action' in tLogData: - message += ("<ul>") - for v in tLogData['action']: - message += (f"<li>{v}</li>") - message += ("</ul>") - message += (f"</td>") - message += (f"</tr>") + message += "<td>" + if "action" in tLogData: + message += "<ul>" + for v in tLogData["action"]: + message += f"<li>{v}</li>" + message += "</ul>" + message += "</td>" + message += "</tr>" message += "</table>" return message def _gen_log_tables(self, tLogDatas): message = "" - # MULTI - message += self._gen_log_table(tLogDatas, GetPerimeterEvent.PRM_IN, 'Entrées de PRM: des actions sont requises') - message += self._gen_log_table(tLogDatas, GetPerimeterEvent.DATE_CHANGE, 'Dates modifiées: des actions sont requises') - message += self._gen_log_table(tLogDatas, GetPerimeterEvent.PRM_OUT, 'Sorties de PRM: pour information, ne requiert pas d\'action de votre part') - message += self._gen_log_table(tLogDatas, GetPerimeterEvent.NO_CHANGE, 'Pas de changement sur ces PRM: pour information, ne requiert pas d\'action de votre part') + message += self._gen_log_table( + tLogDatas, + GetPerimeterEvent.PRM_IN, + "Entrées de PRM: des actions sont requises", + ) + message += self._gen_log_table( + tLogDatas, + GetPerimeterEvent.DATE_CHANGE, + "Dates modifiées: des actions sont requises", + ) + message += self._gen_log_table( + tLogDatas, + GetPerimeterEvent.PRM_OUT, + "Sorties de PRM: " + "pour information, ne requiert pas d'action de votre part", + ) + message += self._gen_log_table( + tLogDatas, + GetPerimeterEvent.NO_CHANGE, + "Pas de changement sur ces PRM: " + "pour information, ne requiert pas d'action de votre part", + ) return message def gen_log_prm_list(self, tLogDatas, prm_type): + message = "" t_ids = [ - item["id"] for item in tLogDatas - if item.get("log_type") in ( + item["id"] + for item in tLogDatas + if item.get("log_type") + in ( GetPerimeterEvent.NO_CHANGE, GetPerimeterEvent.PRM_IN, - GetPerimeterEvent.PRM_OUT + GetPerimeterEvent.PRM_OUT, ) and item.get("prm_type") == prm_type ] - header = ("Liste complète des " + str(len(t_ids))+ " PRM d'injection" - if prm_type == "PROD" else "Liste complète des " + str(len(t_ids))+ " PRM de soutirage") - message = f"<h5>{header}</h5>" + n = len(t_ids) + if n == 0: + return message + header = ( + "Pour information, liste des " + str(n) + " PRM d'injection" + if prm_type == "PROD" + else "Pour information, liste des " + str(n) + " PRM de soutirage" + ) + message += f"<h5>{header}</h5>" message += "<table class='table table-striped'>" - for id in t_ids: - message += (f"<tr>") - message += (f"<td>{id}</td>") - message += (f"</tr>") + for cid in t_ids: + message += "<tr>" + message += f"<td>{cid}</td>" + message += "</tr>" message += "</table>" return message @@ -212,35 +288,6 @@ class AccOperation(models.Model): message += self.gen_log_prm_list(tLogDatas, "CONS") return message - def clean_database(self): - o_counters = self.env["acc.counter"].search([("name", ">=", "90000000000001"),("name", "<=", "90000000000010")]) - #print(o_counters) - o_counter_periods = o_counters.period_ids - #print(o_counter_periods) - o_prices = self.env["acc.price.conf"].search([("acc_injection_counter_id", "in", o_counters.ids)]) - #print(o_prices) - o_prices.unlink() - o_prices = self.env["acc.price.conf"].search([("acc_delivery_counter_id", "in", o_counters.ids)]) - #print(o_prices) - o_prices.unlink() - o_counter_periods.unlink() - o_counters.unlink() - - def debug_database(self): - o_counters = self.env["acc.counter"].search([("name", ">=", "90000000000001"),("name", "<=", "90000000000010")]) - for o_counter in o_counters: - print('added counter ' + str(o_counter.id) + ' ' + o_counter.name) - o_counter_periods = o_counters.period_ids - for o_counter_period in o_counter_periods: - print('added counter period ' + str(o_counter_period.id) + ' ' + o_counter_period.acc_counter_id.name + ' ' + str(o_counter_period.start_date) + ' ' + str(o_counter_period.end_date)) - o_prices = self.env["acc.price.conf"].search([("acc_injection_counter_id", "in", o_counters.ids)]) - for o_price in o_prices: - print('added price ' + str(o_price.id) + ' ' + o_price.acc_injection_counter_id.name + ' ' + o_price.acc_delivery_counter_id.name + ' ' + str(o_price.start_date) + ' ' + str(o_price.end_date) + ' ' + str(o_price.price)) - o_prices = self.env["acc.price.conf"].search([("acc_delivery_counter_id", "in", o_counters.ids)]) - for o_price in o_prices: - print('added price ' + str(o_price.id) + ' ' + o_price.acc_injection_counter_id.name + ' ' + o_price.acc_delivery_counter_id.name + ' ' + str(o_price.start_date) + ' ' + str(o_price.end_date) + ' ' + str(o_price.price)) - - def _perimeter(self, from_cron=False): """ Récupère les données de l'opération concernant le périmètre: @@ -260,204 +307,296 @@ class AccOperation(models.Model): 'end': '2025-06-16 / 9999-12-31' }, """ + tLogDatas = [] message = "" + try: + message += ( + "<p>Début mise à jour du périmètre de l'opération " + + self.name + + " : " + + str(fields.Datetime.now()) + + "</p>" + ) - message += ( - "<p>Début appel API Enedis pour mise à jour du périmètre " - + self.name - + " : " - + str(fields.Datetime.now()) - + "</p>" - ) - if False: self._check_access_api() - message += "<p>Appel API ...</p>" - try: - perimeter_data = self._get_perimeter() - except ValidationError as e: - if from_cron: - log_id = self.create_log(message=str(e), type_log="cron") - log_id.send_api_error_mail() - raise e from e - - message += "<p>Appel API terminé</p>" - else: - self.clean_database() - with open('/home/stephane.laporte/Téléchargements/enedis_perimeter_ecoe_castelet.txt', 'r') as file: - sJsonData = file.read() - sJsonData = sJsonData.replace("'", '"') - perimeter_data = json.loads(sJsonData) - usage_points = perimeter_data.get("usage_points") + message += ( + "<p>Début appel API Enedis :" + str(fields.Datetime.now()) + "</p>" + ) + + perimeter_data = self._get_perimeter() - counter_used = [] + message += "<p>Fin appel API Enedis :" + str(fields.Datetime.now()) + "</p>" - tLogDatas = [] + usage_points = perimeter_data.get("usage_points") - for usage_point in sorted( - usage_points, key=lambda p: date.fromisoformat(p["start"]) - ): - usage_point_start = date.fromisoformat(usage_point["start"]) - usage_point_end = ( - date.fromisoformat(usage_point["end"]) - if usage_point["end"] != "9999-12-31" - else False + sorted_usage_points = sorted( + usage_points, key=lambda p: date.fromisoformat(p["start"]) ) - usage_point_prm_type = False - if usage_point["type"] == "CONS": - usage_point_prm_type = "delivery" - elif usage_point["type"] == "PROD": - usage_point_prm_type = "injection" - # else ? on génère une exception ? + for usage_point in sorted_usage_points: + try: + usage_point_start = date.fromisoformat(usage_point["start"]) + usage_point_end = ( + date.fromisoformat(usage_point["end"]) + if usage_point["end"] != "9999-12-31" + else False + ) + if usage_point["type"] == "CONS": + usage_point_prm_type = "delivery" + elif usage_point["type"] == "PROD": + usage_point_prm_type = "injection" + else: + raise UserError(_("type incorrect.")) + except Exception: + tLogData = { + "log_type": GetPerimeterEvent.EXCEPTION, + "message": "Ces données provenant de Enedis sont mal formées.", + "data": str(usage_point), + "stack_trace": traceback.format_exc(), + "action": ["Contacter le service client Elocoop"], + } + tLogDatas.append(tLogData) + continue + + tLogData = { + "id": usage_point["usage_point_id"], + "prm_type": usage_point["type"], + "start": usage_point_start, + "end": usage_point_end, # date() or False + } + + counter_id = self.env["acc.counter"].search( + [ + ("name", "=", usage_point["usage_point_id"]), + ] + ) + if counter_id: + try: + self.update_existing_counter( + counter_id, + usage_point_prm_type, + usage_point_start, + usage_point_end, + tLogData, + ) + except Exception: + tLogData = { + "log_type": GetPerimeterEvent.EXCEPTION, + "message": "Une erreur est survenue lors de " + "la création de la période.", + "data": str(usage_point), + "stack_trace": traceback.format_exc(), + "action": ["Contacter le service client Elocoop"], + } + tLogDatas.append(tLogData) + continue + # elif len(counter_id) > 1: + # il y a une contrainte d'unicité sur le champ name + # par contre si le champ acc_operation_id + # ne correspond pas on aurait un problème + else: + try: + self.add_new_counter( + usage_point, + usage_point_prm_type, + usage_point_start, + usage_point_end, + tLogData, + ) + except Exception: + tLogData = { + "log_type": GetPerimeterEvent.EXCEPTION, + "message": "Une erreur est survenue lors de " + "la création du PRM ou de la période associée.", + "data": str(usage_point), + "stack_trace": traceback.format_exc(), + "action": ["Contacter le service client Elocoop"], + } + tLogDatas.append(tLogData) + continue + + tLogDatas.append(tLogData) + + message += ( + "<p>Fin mise à jour du périmètre: " + + str(fields.Datetime.now()) + + "</p>" + ) + + except Exception as e: tLogData = { - 'log_type': GetPerimeterEvent.NOT_SET, - 'id': usage_point["usage_point_id"], - 'prm_type': usage_point["type"], - 'start': usage_point_start, - 'end': usage_point_end, # date() or False + "log_type": GetPerimeterEvent.EXCEPTION, + "message": "Une erreur est survenue lors du " + "traitement des données provenant de Enedis.", + "stack_trace": traceback.format_exc(), } - - counter_id = self.env["acc.counter"].search([ - ("name", "=", usage_point["usage_point_id"]), - ]) - if counter_id: - (counter_used, message) = self.update_existing_counter(counter_id, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData) - #elif len(counter_id) > 1: - # il y a une contrainte d'unicité sur le champ name - # par contre si le champ acc_operation_id ne correspond pas on aurait un problème + code = getattr(e, "code", 0) + if code == EloCode.MISSING_ENEDIS_ID: + tLogData["action"] = [ + "Vérifier que les identifiants Enedis " + "sont corrects dans l’onglet Autres Informations " + "de votre espace d’administration Elocoop", + f"Vérifier que l’identifiant de " + f"l'opération {self.name} est correct", + "Dès que vous avez tout vérifié, " + "nous en informer en répondant à cet mail", + ] else: - (counter_used, message) = self.add_new_counter(usage_point, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData) - + tLogData["action"] = ["Contacter le service client Elocoop"] tLogDatas.append(tLogData) - message += ( - "<p>Fin appel API Enedis pour mise à jour du périmètre: " + str(fields.Datetime.now()) + "</p>" + t_counters = [ + item["id"] + for item in tLogDatas + if item.get("log_type") + in ( + GetPerimeterEvent.PRM_IN, + GetPerimeterEvent.PRM_OUT, + GetPerimeterEvent.DATE_CHANGE, + ) + ] + n_exceptions = sum( + 1 for d in tLogDatas if d.get("log_type") == GetPerimeterEvent.EXCEPTION ) + message += self._gen_error_table(tLogDatas) message += self._gen_log_tables(tLogDatas) message += self.gen_log_prm_lists(tLogDatas) - t_counters = [item['id'] for item in tLogDatas if item.get('log_type') in (GetPerimeterEvent.PRM_IN, GetPerimeterEvent.PRM_OUT, GetPerimeterEvent.DATE_CHANGE)] - updated_objects = "<br/>".join(t_counters) - log_id = self.create_log( - message=message, + message=html_sanitize(message), type_log="cron" if from_cron else "api", - updated_objects=html_sanitize(updated_objects), ) - if from_cron and (len(t_counters) > 0): - log_id.send_new_prm_email() - - self.debug_database() - + # envoyer le mail si une exception se produit et pas uniquement + # si une modification est détectée au niveau des PRM + if from_cron and ((len(t_counters) > 0) or (n_exceptions > 0)): + log_id.send_enedis_perimeter_email() return log_id - def update_existing_counter(self, counter_id, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData): + def update_existing_counter( + self, + counter_id, + usage_point_prm_type, + usage_point_start, + usage_point_end, + tLogData, + ): counter_period_ids = counter_id.period_ids.filtered( lambda p: p.prm_type == usage_point_prm_type ) if counter_period_ids.filtered( lambda p: p.start_date == usage_point_start - and p.end_date == usage_point_end - ): - tLogData['message'] = 'Pas de changement' - tLogData['log_type'] = GetPerimeterEvent.NO_CHANGE - elif counter_period_ids.filtered( - lambda p: p.start_date == usage_point_start + and p.end_date == usage_point_end ): + tLogData["message"] = "Pas de changement" + tLogData["log_type"] = GetPerimeterEvent.NO_CHANGE + elif counter_period_ids.filtered(lambda p: p.start_date == usage_point_start): counter_period_ids.filtered( lambda p: p.start_date == usage_point_start ).end_date = usage_point_end - counter_used.append(counter_id.name) - tLogData['message'] = 'Changement de la date de sortie du PRM' - tLogData['log_type'] = GetPerimeterEvent.PRM_OUT + tLogData["message"] = "Changement de la date de sortie du PRM" + tLogData["log_type"] = GetPerimeterEvent.PRM_OUT else: - try: - self.env["acc.counter.period"].create({ + self.env["acc.counter.period"].create( + { "acc_counter_id": counter_id.id, "prm_type": usage_point_prm_type, "acc_operation_id": self.id, "start_date": usage_point_start, "end_date": usage_point_end, - }) - - counter_used.append(counter_id.name) - tLogData['message'] = 'Changement de dates' - tLogData['log_type'] = GetPerimeterEvent.DATE_CHANGE - tLogData['action'] = ['Renseigner le participant lié dans l’onglet ' + ( - 'Points d\'injection' if usage_point_prm_type == 'injection' else 'Points de soutirage')] - except ValidationError as e: - message += ( - "<strong>erreur lors de la tentative de création d'une " - "nouvelle période, à vérifier manuellement :</strong><br/>" - + str(e) - + "<br/>" - ) - return counter_used, message - - def add_new_counter(self, usage_point, usage_point_prm_type, usage_point_start, usage_point_end, counter_used, message, tLogData): - tLogData['message'] = 'Entrée du PRM' - tLogData['log_type'] = GetPerimeterEvent.PRM_IN + } + ) + tLogData["message"] = "Changement des dates" + tLogData["log_type"] = GetPerimeterEvent.DATE_CHANGE + tLogData["action"] = [ + "Ajouter, si ce n'est pas déjà le cas, le participant " + "dans l'onglet Participants de l'opération", + "Lier le PRM et le participant dans l’onglet " + + ( + "Points d'injection" + if usage_point_prm_type == "injection" + else "Points de soutirage" + ), + "Indiquer si nécessaire le prix de vente pour ce PRM", + "Vérifier le paramétrage de la clé de répartition le cas échéant", + "Donner éventuellement au participant l'accès au portail Elocoop", + ] + + def add_new_counter( + self, + usage_point, + usage_point_prm_type, + usage_point_start, + usage_point_end, + tLogData, + ): + tLogData["message"] = "Entrée du PRM" + tLogData["log_type"] = GetPerimeterEvent.PRM_IN if usage_point_prm_type == "injection": # Si la date de l'opération n'est pas renseignée ou # postérieure à la date de démarrage du point d'injection # alors on force celle-ci à la date de démarrage du point d'injection self.update_date_start_contract(usage_point_start) - try: - # ajout compteur - # le type est initialisé à posteriori lorsque les périodes sont mises à jour - counter_id = self.env["acc.counter"].create({ - "name": usage_point["usage_point_id"] - }) - counter_used.append(counter_id.name) - - # ajout période compteur - counter_period_id = self.env["acc.counter.period"].create({ + + # ajout compteur + # le type est initialisé à posteriori lorsque les périodes sont mises à jour + counter_id = self.env["acc.counter"].create( + {"name": usage_point["usage_point_id"]} + ) + + # ajout période compteur + self.env["acc.counter.period"].create( + { "acc_counter_id": counter_id.id, "prm_type": usage_point_prm_type, "acc_operation_id": self.id, "start_date": usage_point_start, "end_date": usage_point_end, "sale_price": self.sale_price_by_default, - }) - - # ajout acc.price.conf se fait désormais lors de la création de la période - - self.check_sale_price_conf( - counter_id=counter_id, periode_start_date=usage_point_start - ) - - # If delivery counter add to first priority group if exist - b_added = False - if usage_point_prm_type == "delivery": - if self.check_priority_groups(counter=counter_id): - b_added = True + } + ) - # ajout au premier groupe de priorité par défaut en fonction du type de clé de répartition de l'opération se fait désormais lors de la création de la période + # ajout acc.price.conf se fait désormais lors de la création de la période - tLogData['action'] = ['Renseigner le participant lié dans l’onglet ' + ( - 'Points d\'injection' if usage_point_prm_type == 'injection' else 'Points de soutirage')] - # TODO afficher uniquement si on a des groupes de priorité pour cette opération - if b_added: - tLogData[ - 'action'].append('Vérifier le paramétrage de la clé de répartition si nécessaire, le nouveau PRM a été ajouté par défaut au premier groupe de priorité') + self.check_sale_price_conf( + counter_id=counter_id, periode_start_date=usage_point_start + ) - except ValidationError as e: - message += ( - "<strong>erreur lors de la tentative de création du PRM et/ou " - "de la nouvelle période, à vérifier manuellement :</strong><br/>" - + str(e) - + "<br/>" + # If delivery counter add to first priority group if exist + b_added = False + if usage_point_prm_type == "delivery": + if self.check_priority_groups(counter=counter_id): + b_added = True + + # ajout au premier groupe de priorité par défaut + # en fonction du type de clé de répartition de l'opération + # se fera désormais lors de la création de la période + + tLogData["action"] = [ + "Ajouter, si ce n'est pas déjà le cas, " + "le participant dans l'onglet Participants de l'opération", + "Lier le nouveau PRM et le participant dans l’onglet " + + ( + "Points d'injection" + if usage_point_prm_type == "injection" + else "Points de soutirage" + ), + "Indiquer si nécessaire le prix de vente pour ce PRM", + ] + if b_added: + tLogData["action"].append( + "Vérifier le paramétrage de la clé de répartition si nécessaire, " + "le nouveau PRM a été ajouté par défaut au premier groupe de priorité", ) - return counter_used, message + tLogData["action"].append( + "Donner éventuellement au participant l'accès au portail Elocoop", + ) def update_date_start_contract(self, usage_point_start): - if ( - not self.date_start_contract - or self.date_start_contract > usage_point_start - ): + if not self.date_start_contract or self.date_start_contract > usage_point_start: self.date_start_contract = usage_point_start + # ? à quel moment on enregistre en base ? # ------------------------------------------------------ # Business methods @@ -475,10 +614,14 @@ class AccOperation(models.Model): return True return False - def check_sale_price_conf_for_delivery_counter(self, counter_id, periode_start_date): + def check_sale_price_conf_for_delivery_counter( + self, counter_id, periode_start_date + ): if self.use_default_sale_price: - - domain = [("acc_operation_id", "=", self.id), ("prm_type", "=", "injection")] + domain = [ + ("acc_operation_id", "=", self.id), + ("prm_type", "=", "injection"), + ] date_end_domain = expression.OR( [ [("end_date", ">=", periode_start_date)], @@ -509,8 +652,9 @@ class AccOperation(models.Model): } ) - def check_sale_price_conf_for_injection_counter(self, counter_id, periode_start_date): - + def check_sale_price_conf_for_injection_counter( + self, counter_id, periode_start_date + ): if self.use_default_sale_price and self.sale_price_by_default > 0.0: domain = [("acc_operation_id", "=", self.id), ("prm_type", "=", "delivery")] date_end_domain = expression.OR( @@ -539,7 +683,11 @@ class AccOperation(models.Model): create sale price conf on new counter """ if counter_id.type in ["del", "del_inj"]: - self.check_sale_price_conf_for_delivery_counter(counter_id, periode_start_date) + self.check_sale_price_conf_for_delivery_counter( + counter_id, periode_start_date + ) elif counter_id.type in ["inj"]: - self.check_sale_price_conf_for_injection_counter(counter_id, periode_start_date) + self.check_sale_price_conf_for_injection_counter( + counter_id, periode_start_date + ) -- GitLab