diff --git a/__manifest__.py b/__manifest__.py index f40cf05d17dbb6994252a55c1808bb29ff29411b..75ba6c1024cc7cfd1518b9a3f8f8ef51f0241626 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -26,6 +26,7 @@ "web.assets_backend": [ "/oacc_repartition_keys/static/src/js/kanban_button.js", "/oacc_repartition_keys/static/src/xml/kanban_button.xml", + "/oacc_repartition_keys/static/src/css/custom.css", ], }, "installable": True, diff --git a/models/acc_operation.py b/models/acc_operation.py index b10c25b0321eadcc3cb65c41738ffd3612b06e38..a8ba3607ea79cf1dcae41f3e95510b7340df9646 100644 --- a/models/acc_operation.py +++ b/models/acc_operation.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import base64 import csv +import math from collections import OrderedDict from datetime import date, datetime from io import StringIO @@ -98,6 +99,14 @@ class AccOperation(models.Model): for repartition in repartitions: repartition.unlink() + def get_affect_sum(self, data_slot): + affect_sum = 0.0 + for counter in data_slot.get("affect"): + affect_counter = data_slot.get("affect").get(counter) + if affect_counter: + affect_sum += affect_counter + return affect_sum + def generate(self): """ generate repartition keys @@ -112,7 +121,13 @@ class AccOperation(models.Model): repartition = self.env["acc.repartition.keys"].create({"operation_id": self.id}) - data = None + data = self.env["acc.enedis.raw.cdc"].get_repartition_data(operation_id=self) + + # on sauvegarde prod_totale pour permettre en fin de traitement + # le calcul du pourcentage attribué à chaque compteur + for slot in data: + data[slot]["prod_initiale"] = data.get(slot).get("prod_totale") + for priority in self.acc_priority_group_ids: data = priority.compute(data) @@ -121,41 +136,48 @@ class AccOperation(models.Model): _("Pas de données brute pour le mois précedent présente") ) + # contrôle, l'ensemble des affectations ne doit pas dépasser la production à affecter for slot in data: - affect = data.get(slot).get("affect") + item = data.get(slot) + affect = item.get("affect") if affect: - total_affecte = sum(affect.values()) + total_production = item.get("prod_initiale") + remaining_prod = item.get("prod_totale") + affect_sum = self.get_affect_sum(item) + if abs(affect_sum + remaining_prod - total_production) > 1e-3: + raise ValidationError( + _( + "Une erreur s'est produite lors de l'affectation de l'auto-consommation" + ) + ) - slot_result = {} + # enregistrement en base + for slot in data: + item = data.get(slot) + affect = item.get("affect") + if affect: + total_production = item.get("prod_initiale") + + # calcul du pourcentage attribué à chaque compteur par rapport à la production totale + weights = {} for counter_id in affect: - if total_affecte == 0: + affecte_counter = affect.get(counter_id) + if total_production == 0: weight = 0.0 else: - weight = round( - (affect.get(counter_id) * 100) / total_affecte, 8 - ) - - slot_result[counter_id] = weight - - total_weight = sum(slot_result.values()) - max_value = None - if total_weight > 100.0: - max_value = max(slot_result.values()) + weight = math.floor((affecte_counter * 100 / total_production) * 1e+6) / 1e+6 + weights[counter_id] = weight slot_line = [] - for counter in slot_result: - if max_value is not None: - if slot_result[counter] == max_value: - slot_result[counter] -= 0.0000001 + for counter in weights: slot_line.append( { "acc_repartition_id": repartition.id, "time_slot": slot, - "weight": slot_result[counter], + "weight": weights[counter], "acc_counter_id": counter.id, } ) - self.env["acc.repartition.counter"].create(slot_line) # ------------------------------------------------------ diff --git a/models/acc_priority_group.py b/models/acc_priority_group.py index 2da6651f2199a6ca8decb688c252d0ce9a6698b2..f5ca34d5c963191c7d850293f47a1bd1343b4c5a 100644 --- a/models/acc_priority_group.py +++ b/models/acc_priority_group.py @@ -16,6 +16,7 @@ class AccPriorityGroup(models.Model): type_algo = fields.Selection( [ ("prorata", "Répartition au prorata"), + ("quotepart", "Répartition par quote-part"), ], string="Répartition au sein du groupe", default="prorata", @@ -32,6 +33,7 @@ class AccPriorityGroup(models.Model): ) counter_datas = fields.Json(compute="_compute_counter_datas") + total_share = fields.Float(compute="_compute_total_share", store=False) # ------------------------------------------------------ # SQL Constraints # ------------------------------------------------------ @@ -43,6 +45,12 @@ class AccPriorityGroup(models.Model): # ------------------------------------------------------ # Computed fields / Search Fields # ------------------------------------------------------ + @api.depends("acc_priority_group_counter_ids") + def _compute_total_share(self): + for record in self: + record.total_share = sum( + record.acc_priority_group_counter_ids.mapped("acc_counter_share") + ) @api.depends("type_algo", "sequence") def _compute_display_name(self): @@ -137,14 +145,9 @@ class AccPriorityGroup(models.Model): def compute(self, data=None): compute_algo = { "prorata": self._prorata, + "quotepart": self._quotepart, } - if data is None: - data = self.env["acc.enedis.raw.cdc"].get_repartition_data( - operation_id=self.acc_operation_id - ) - d = compute_algo[self.type_algo](data) - return d def get_conso_sum(self, data_slot): @@ -159,7 +162,7 @@ class AccPriorityGroup(models.Model): def _prorata(self, data): for slot in data: - prod = data.get(slot).get("prod_totale") + remaining_prod = data.get(slot).get("prod_totale") priority_counters_conso_sum = self.get_conso_sum(data.get(slot)) total_affecte = 0.0 @@ -171,16 +174,47 @@ class AccPriorityGroup(models.Model): # si le compteur rentre en cours de periode il figure dans le # groupe de repartition mais pas dans les données brutes conso_k = None # dans ce cas on affecte 0 - if prod == 0 or priority_counters_conso_sum == 0 or conso_k is None: + if ( + remaining_prod < 0 + or priority_counters_conso_sum == 0 + or conso_k is None + ): part_a_affecter = 0.0 else: - part_a_affecter = min( - conso_k, prod * (conso_k / priority_counters_conso_sum) - ) - + part_a_affecter = ( + remaining_prod * conso_k + ) / priority_counters_conso_sum + part_a_affecter = min(conso_k, part_a_affecter) data[slot]["affect"][counter] = part_a_affecter total_affecte += part_a_affecter - data[slot]["prod_totale"] = prod - total_affecte + data[slot]["prod_totale"] = remaining_prod - total_affecte + # possiblement on peut avoir affecté plus que la production total_affecte=496.00000000000006 + + return data + def _quotepart(self, data): + total_share = self.total_share + for slot in data: + remaining_prod = data.get(slot).get("prod_totale") + total_affecte = 0.0 + if not data.get(slot).get("affect"): + data[slot]["affect"] = {} + for priority_group_counter in self.acc_priority_group_counter_ids: + counter = priority_group_counter.acc_counter_id + counter_share = priority_group_counter.acc_counter_share + conso_k = data.get(slot).get("conso").get(counter.name) + if ( + remaining_prod < 0 + or total_share == 0 + or conso_k is None + or counter_share == 0 + ): + part_a_affecter = 0.0 + else: + part_a_affecter = (remaining_prod * counter_share) / total_share + part_a_affecter = min(conso_k, part_a_affecter) + data[slot]["affect"][counter] = part_a_affecter + total_affecte += part_a_affecter + data[slot]["prod_totale"] = remaining_prod - total_affecte return data diff --git a/models/acc_priority_group_counter.py b/models/acc_priority_group_counter.py index 3df09d7b25ade1130c4b721f7a80db25196765cc..426efd3b5e7687633443d0d5cfea0b630109979b 100644 --- a/models/acc_priority_group_counter.py +++ b/models/acc_priority_group_counter.py @@ -39,6 +39,10 @@ class AccPriorityGroupCounter(models.Model): string="Counter domain", compute="_compute_acc_counter_id_domain" ) + acc_counter_share = fields.Float(string='Quote-part', help="Part de la production affectée à ce PRM, au prorata de l'ensemble des parts affectées aux autres PRMs") + group_type_algo = fields.Selection(related="acc_priority_group_id.type_algo", readonly=True) + acc_counter_percentage = fields.Float(compute="_compute_acc_counter_percentage", store=False) + # ------------------------------------------------------ # SQL Constraints # ------------------------------------------------------ @@ -50,6 +54,13 @@ class AccPriorityGroupCounter(models.Model): # ------------------------------------------------------ # Computed fields / Search Fields # ------------------------------------------------------ + + @api.depends('acc_priority_group_id.acc_priority_group_counter_ids', "acc_counter_share") + def _compute_acc_counter_percentage(self): + for record in self: + total_share = record.acc_priority_group_id.total_share + record.acc_counter_percentage = record.acc_counter_share / total_share if total_share else 0 + @api.depends("acc_counter_id", "acc_priority_group_id") def _compute_acc_counter_id_domain(self): for counter in self: @@ -108,3 +119,16 @@ class AccPriorityGroupCounter(models.Model): domain=[("acc_operation_id", "=", operation_id)], order="sequence, id" ) return group_ids + + def action_open_counter_share_form(self): + print('action_open_counter_share_form') + return { + 'type': 'ir.actions.act_window', + 'name': 'Modifier la quote-part du point de soutirage', + 'res_model': 'acc.priority.group.counter', + 'view_mode': 'form', + 'target': 'new', + 'res_id': self.id, + 'view_id': self.env.ref('oacc_repartition_keys.acc_operation_priority_group_counter_share_form').id, + #'context': {'default_field_name': 'Auto-filled Text'} + } diff --git a/static/src/css/custom.css b/static/src/css/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..358531928d37c53294b68a2e80fa675263ee5510 --- /dev/null +++ b/static/src/css/custom.css @@ -0,0 +1,3 @@ +.oacc_quick_create.o_form_editable .o_cell .o_form_label { + margin-bottom: 0 !important; +} diff --git a/views/acc_priority_group_counter_views.xml b/views/acc_priority_group_counter_views.xml index 5bf60f294bc1d5bc358a6bf4668d1e2f251a4e74..3f6eb4e93e9ddfd19a5d119fe02703badfc04fa3 100644 --- a/views/acc_priority_group_counter_views.xml +++ b/views/acc_priority_group_counter_views.xml @@ -1,5 +1,21 @@ <?xml version="1.0" encoding="utf-8" ?> <odoo> + + <record id="acc_operation_priority_group_counter_share_form" model="ir.ui.view"> + <field name="name">acc_operation_priority_group_counter_share_form</field> + <field name="model">acc.priority.group.counter</field> + <field name="arch" type="xml"> + <form string="My Model"> + <div> + <group> + <field name="acc_counter_share" style="width: 15em;"/> + </group> + </div> + </form> + </field> + </record> + + <record id="acc_priority_group_counter_act_window" model="ir.actions.act_window"> <field name="name">Clé de répartition par groupes de priorité</field> <field name="res_model">acc.priority.group.counter</field> @@ -12,27 +28,32 @@ <field name="name">acc.operation.priority.group.counter.form</field> <field name="model">acc.priority.group.counter</field> <field name="arch" type="xml"> - <form> - <field name="acc_priority_group_id" invisible="1" /> - <field name="acc_operation_id" invisible="1" /> - <field name="acc_counter_id_domain" invisible="1" /> + <form class="oacc_quick_create"> + <field name="acc_priority_group_id" invisible="1" /> + <field name="acc_operation_id" invisible="1" /> + <field name="acc_counter_id_domain" invisible="1" /> + <group> <field - name="acc_counter_id" - options="{'no_create_edit': True, 'no_create': True}" - domain="acc_counter_id_domain" - /> - </form> + name="acc_counter_id" + options="{'no_create_edit': True, 'no_create': True}" + domain="acc_counter_id_domain" + /> + </group> + <field name="group_type_algo" invisible="1"/> + <group attrs="{'invisible': [('group_type_algo', '!=', 'quotepart')]}"> + <field name="acc_counter_share" style="width: 15em;"/> + </group> + </form> </field> </record> - <record id="acc_operation_priority_group_counter_kanban" model="ir.ui.view"> <field name="name">acc.operation.priority.group.counter.kanban</field> <field name="model">acc.priority.group.counter</field> <field name="arch" type="xml"> <kanban default_group_by="acc_priority_group_id" - class="o_kanban_small_column" + class="o_kanban_medium_column" on_create="quick_create" quick_create_view="oacc_repartition_keys.acc_operation_priority_group_counter_form" archivable="false" @@ -47,7 +68,7 @@ <templates> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_global_click oe_kanban_card"> + <div t-attf-class="oe_kanban_card"> <div class="oe_kanban_content"> <div class="o_kanban_record_top"> <div class="o_kanban_record_headings"> @@ -64,9 +85,20 @@ </div> </div> <div class="o_kanban_record_body"> - <field name="counter_owner" /> - <br /> - <field name="counter_street" /> + <div><field name="counter_owner" /></div> + <div><field name="counter_street" /></div> + <field name="group_type_algo" invisible="1"/> + <group attrs="{'invisible': [('group_type_algo', '!=', 'quotepart')]}"> + <div class="d-flex justify-content-between"> + <div> + <div> + Quote-part: <field name="acc_counter_share"/> + (<field name="acc_counter_percentage" widget="percentage"/>) + </div> + </div> + <div class="align-self-end"><button name="action_open_counter_share_form" type="object" class="btn btn-sm btn-primary text-end">Modifier</button></div> + </div> + </group> </div> </div> <div class="clearfix" />