From 5d350e4179db7e1acdf523f7cf901a9d5260b64b Mon Sep 17 00:00:00 2001
From: Julien - Le Filament <julien@le-filament.com>
Date: Mon, 19 Feb 2024 16:36:35 +0100
Subject: [PATCH] 16.0 keys

---
 README.rst                                   |   4 +-
 __init__.py                                  |   1 +
 __manifest__.py                              |   4 +-
 models/__init__.py                           |   2 +-
 models/acc_operation.py                      |   4 +-
 models/acc_repartition_keys.py               |   2 +-
 security/ir.model.access.csv                 |   3 +-
 static/description/icon.png                  | Bin 0 -> 9161 bytes
 tools/__init__.py                            |   0
 tools/key_file.py                            | 149 +++++++++++++++++++
 views/acc_operation_views.xml                |  21 +--
 views/acc_repartition_keys_views.xml         |  21 +++
 wizard/__init__.py                           |   1 +
 wizard/acc_repartition_keys_wizard.py        |  82 ++++++++++
 wizard/acc_repartition_keys_wizard_views.xml |  34 +++++
 15 files changed, 307 insertions(+), 21 deletions(-)
 create mode 100755 static/description/icon.png
 create mode 100644 tools/__init__.py
 create mode 100644 tools/key_file.py
 create mode 100644 views/acc_repartition_keys_views.xml
 create mode 100644 wizard/__init__.py
 create mode 100644 wizard/acc_repartition_keys_wizard.py
 create mode 100644 wizard/acc_repartition_keys_wizard_views.xml

diff --git a/README.rst b/README.rst
index 786b31a..ae6b9ae 100644
--- a/README.rst
+++ b/README.rst
@@ -3,9 +3,9 @@
    :alt: License: AGPL-3
 
 
-===========
+============================
 OACC - Clefs de répartition
-===========
+============================
 
 Ce module permet d'uploader de tester et d envoyer a enedis les clefs de répartion pour une Opération d'AutoConsommation Collective:
 
diff --git a/__init__.py b/__init__.py
index 0650744..9b42961 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1,2 @@
 from . import models
+from . import wizard
diff --git a/__manifest__.py b/__manifest__.py
index 7fe4121..6acd514 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -11,9 +11,11 @@
         # datas
         # views
         "views/acc_operation_views.xml",
+        "views/acc_repartition_keys_views.xml",
         # views menu
         # wizard
+        "wizard/acc_repartition_keys_wizard_views.xml",
     ],
     "installable": True,
     "auto_install": False,
-}
\ No newline at end of file
+}
diff --git a/models/__init__.py b/models/__init__.py
index 95d38c2..1831689 100644
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,2 +1,2 @@
 from . import acc_operation
-from . import acc_repartition_keys
\ No newline at end of file
+from . import acc_repartition_keys
diff --git a/models/acc_operation.py b/models/acc_operation.py
index fd774d8..1d29e8b 100644
--- a/models/acc_operation.py
+++ b/models/acc_operation.py
@@ -1,8 +1,8 @@
 # Copyright 2021- Le Filament (https://le-filament.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 import logging
-from odoo import _, fields, models
 
+from odoo import fields, models
 
 _logger = logging.getLogger(__name__)
 
@@ -41,4 +41,4 @@ class AccOperation(models.Model):
 
     # ------------------------------------------------------
     # Actions
-    # ------------------------------------------------------
\ No newline at end of file
+    # ------------------------------------------------------
diff --git a/models/acc_repartition_keys.py b/models/acc_repartition_keys.py
index b71474b..c71adcb 100644
--- a/models/acc_repartition_keys.py
+++ b/models/acc_repartition_keys.py
@@ -38,4 +38,4 @@ class AccRepartitionKeys(models.Model):
 
     # ------------------------------------------------------
     # Business methods
-    # ------------------------------------------------------
\ No newline at end of file
+    # ------------------------------------------------------
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index a07c238..8ce08fb 100644
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -1,3 +1,4 @@
 id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
 "access_acc_repartition_keys_group_partner_manager","acc_repartition_keys group_partner_manager","model_acc_repartition_keys","base.group_partner_manager",1,1,1,1
-"access_acc_repartition_keys_group_user","acc_repartition_keys group_user","model_acc_repartition_keys","base.group_user",1,0,0,0
\ No newline at end of file
+"access_acc_repartition_keys_group_user","acc_repartition_keys group_user","model_acc_repartition_keys","base.group_user",1,0,0,0
+"access_acc_repartition_keys_wizard_group_partner_manager","acc_repartition_keys_wizard group_partner_manager","model_acc_repartition_keys_wizard","base.group_partner_manager",1,1,1,1
diff --git a/static/description/icon.png b/static/description/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..82ef47760a441cf229b5009f0a18ccf3842fbfa5
GIT binary patch
literal 9161
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

literal 0
HcmV?d00001

diff --git a/tools/__init__.py b/tools/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tools/key_file.py b/tools/key_file.py
new file mode 100644
index 0000000..7e8a0a7
--- /dev/null
+++ b/tools/key_file.py
@@ -0,0 +1,149 @@
+"""
+Repartion key entry file handler
+"""
+import csv
+from datetime import datetime
+
+
+class RepartitionKeyEntryFile:
+    def __init__(self, data, operation_counter_list):
+        self.data = data
+        self.operation_counter_list = operation_counter_list
+        self.json = self._to_json()
+
+    def check(self):
+        """
+        Lorsque le fichier est validé, des vérifications sont effectuées, et une erreur est affichée si le test ne passe pas :
+            • Première ligne, colonnes 2 à la fin : les numéros de PRM sont exactement ceux qui sont dans l’onglet Point de soutirage.
+             Aucun numéro de PRM n'apparaît plusieurs fois.
+        ◦ Erreur envoyée : “Les numéros de PRM ne correspondent pas à ceux de l’opération.”
+
+            • Lignes 2 à la fin, Horodate : Les dates sont celles d’un seul mois complet.
+        ◦ Erreur envoyée “Les dates doivent être celles d’un seul mois complet.”
+
+            • La somme sur chaque ligne doit être inférieure à 100.
+        ◦ Erreur envoyée “Ligne X : la somme dépasse 100%”
+
+        """
+        check_methods = [self._check_counter, self._check_same_month, self._check_max_value]
+        result = {"check": True, "message": ""}
+        for check in check_methods:
+            check_result = check()
+            if not check_result.get("check"):
+                result["check"] = False
+                result["message"] += f"{check_result['message']}\n"
+
+        return result
+
+    def _to_json(self):
+        """
+        make data ready to send { "horodatage": [{"id": "value"}, ....]}
+        """
+        json = {}
+        counter_list_from_file = self.data[0].split(";")
+        csv_file = csv.reader(self.data, delimiter=";")
+        line_count = 0
+        for line in csv_file:
+            if line:
+                if line_count == 0:
+                    line_count += 1
+                    continue
+                json[line[0]] = []
+                line_count += 1
+                counter_count = 1
+                for counter in counter_list_from_file[1:]:
+                    json[line[0]].append(
+                        {"id": counter, "key": line[counter_count].replace(",", ".")}
+                    )
+                    counter_count += 1
+        return json
+
+    def data_to_send(self, agreement_id, send_empty_key=False):
+        """
+        return dict {"route", "body} to enedis
+        """
+        call_list = []
+        for horo in self.json:
+            date = datetime.strptime(horo, "%d-%m-%Y %H:%M")
+            route = f"/agreements/{agreement_id}/repartition_keys/{date.strftime('%Y%m%dT%H%MZ')}"
+            body = []
+            for keys in self.json.get(horo):
+                if send_empty_key:
+                    body.append(keys)
+                else:
+                    if float(keys.get("key")) > 0:
+                        body.append(keys)
+            if body:
+                call_list.append({"route": route, "body": body})
+        return call_list
+
+    def _check_counter(self):
+        """
+        Check if all counter in file belong to operation
+
+        """
+
+        if self.data:
+            counter_list_from_file = self.data[0].split(";")[1:]
+
+            if sorted(self.operation_counter_list) == sorted(counter_list_from_file):
+                return {"check": True, "message": ""}
+            else:
+                missing_in_file = [
+                    counter
+                    for counter in self.operation_counter_list
+                    if counter not in counter_list_from_file
+                ]
+                missing_in_operation = [
+                    counter
+                    for counter in counter_list_from_file
+                    if counter not in self.operation_counter_list
+                ]
+                if missing_in_file or missing_in_operation:
+                    return {
+                        "check": False,
+                        "message": "Les numéros de PRM ne correspondent pas à ceux de l’opération",
+                    }
+                return {"check": True, "message": ""}
+
+    def _check_max_value(self):
+        """
+        check if all value are in the same month
+        """
+        for hour in self.json:
+            max = 0
+            for key in self.json[hour]:
+                if key["key"] == "0.00000000":
+                    key["key"] = 0.0
+                try:
+                    max = max + float(key["key"])
+                except ValueError:
+                    pass
+
+            if max > 100:
+                return {
+                    "check": False,
+                    "message": f"Ligne {hour} la somme dépasse 100.",
+                }
+        return {"check": True, "message": ""}
+
+    def _check_same_month(self):
+        """
+        check if all value are in the same month
+        """
+        if (
+            len(
+                set(
+                    [
+                        datetime.strptime(date, "%d-%m-%Y %H:%M").month
+                        for date in self.json
+                    ]
+                )
+            )
+            != 1
+        ):
+            return {
+                "check": False,
+                "message": "Les dates doivent être celles d’un seul mois complet.",
+            }
+        return {"check": True, "message": ""}
diff --git a/views/acc_operation_views.xml b/views/acc_operation_views.xml
index f5b2aa5..a70536f 100644
--- a/views/acc_operation_views.xml
+++ b/views/acc_operation_views.xml
@@ -2,7 +2,6 @@
 <!-- Copyright 2021- Le Filament (https://le-filament.com)
      License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
 <odoo>
-
     <record id="acc_operation_form_view" model="ir.ui.view">
         <field name="name">acc_operation_form_view.keys.form</field>
         <field name="model">acc.operation</field>
@@ -11,22 +10,18 @@
             <xpath expr="//notebook" position="inside">
                 <page string="Clefs de répartition" name="keys">
                     <header>
-<!--                        <button-->
-<!--                            string="Test"-->
-<!--                            type="object"-->
-<!--                            class="btn-primary"-->
-<!--                            name="test_file"-->
-<!--                        />-->
+                        <button
+                            string="Importer un fichier"
+                            type="action"
+                            name="%(oacc_repartition_keys.acc_repartition_keys_wizard_action)d"
+                            class="btn-primary"
+                        />
                     </header>
-                    <group>
+                    <tree>
                         <field name="keys_repartition_ids" />
-<!--                            <tree>-->
-<!--                                <field name="date_send"/>-->
-<!--                            </tree>-->
-                    </group>
+                    </tree>
                 </page>
             </xpath>
         </field>
     </record>
-
 </odoo>
\ No newline at end of file
diff --git a/views/acc_repartition_keys_views.xml b/views/acc_repartition_keys_views.xml
new file mode 100644
index 0000000..e247914
--- /dev/null
+++ b/views/acc_repartition_keys_views.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+<!--    <data>-->
+        <record id="acc_keys_repatition_action" model="ir.actions.act_window">
+            <field name="name">Keys</field>
+            <field name="res_model">acc.repartition.keys</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <record id="acc_keys_repartition_tree" model="ir.ui.view">
+            <field name="name">acc.repartition.keys.tree</field>
+            <field name="model">acc.repartition.keys</field>
+            <field name="arch" type="xml">
+                <tree string="Keys" editable="bottom">
+                    <field name="date_send"/>
+                    <field name="csv_file"/>
+                    <field name="operation_id"/>
+                </tree>
+            </field>
+        </record>
+<!--    </data>-->
+</odoo>
\ No newline at end of file
diff --git a/wizard/__init__.py b/wizard/__init__.py
new file mode 100644
index 0000000..cbd8c61
--- /dev/null
+++ b/wizard/__init__.py
@@ -0,0 +1 @@
+from . import acc_repartition_keys_wizard
diff --git a/wizard/acc_repartition_keys_wizard.py b/wizard/acc_repartition_keys_wizard.py
new file mode 100644
index 0000000..1d414cc
--- /dev/null
+++ b/wizard/acc_repartition_keys_wizard.py
@@ -0,0 +1,82 @@
+# Copyright 2021- Le Filament (https://le-filament.com)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+import base64
+from odoo import fields, models
+from odoo.exceptions import UserError
+
+from ..tools.key_file import RepartitionKeyEntryFile
+
+
+class AccRepartitionKeysWizard(models.TransientModel):
+    _name = "acc.repartition.keys.wizard"
+    _description = "Clefs de répartition"
+
+    # ------------------------------------------------------
+    # Default methods
+    # ------------------------------------------------------
+    def _default_operation_id(self):
+        return self.env.context.get("active_id")
+
+    # ------------------------------------------------------
+    # Fields declaration
+    # ------------------------------------------------------
+    csv_file = fields.Binary("Contenu du fichier CSV")
+    date_send = fields.Date("Date de l'envoi des clefs", default=None)
+    operation_id = fields.Many2one(
+        "acc.operation", "Opération", default=_default_operation_id
+    )
+
+    # ------------------------------------------------------
+    # SQL Constraints
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Computed fields / Search Fields
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Onchange / Constraints
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # CRUD methods (ORM overrides)
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Actions
+    # ------------------------------------------------------
+    def send_imported_file(self):
+        """
+        testing a file, check taht all prm id exist, are in operation and all operation prm are in file
+        :return:
+        """
+        if self.csv_file:
+            file = (
+                base64.b64decode(self.csv_file)
+                .decode("utf-8")
+                .replace("\r", "")
+                .split("\n")
+            )
+
+            counter_list_from_operation = [
+                counter.name
+                for counter in self.env["acc.counter"].search(
+                    [("acc_operation_id.id", "=", self.operation_id.id)]
+                )
+            ]
+
+            entry_file_handler = RepartitionKeyEntryFile(
+                data=file, operation_counter_list=counter_list_from_operation
+            )
+
+            file_check_result = entry_file_handler.check()
+
+            if not file_check_result.get("check"):
+                raise UserError(file_check_result.get("message"))
+
+            data_to_send = entry_file_handler.data_to_send(agreement_id=self.operation_id.name)
+
+
+    # ------------------------------------------------------
+    # Business methods
+    # ------------------------------------------------------
diff --git a/wizard/acc_repartition_keys_wizard_views.xml b/wizard/acc_repartition_keys_wizard_views.xml
new file mode 100644
index 0000000..77b2181
--- /dev/null
+++ b/wizard/acc_repartition_keys_wizard_views.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <record id="acc_repartition_keys_wizard_form" model="ir.ui.view">
+        <field name="name">acc.repartition.keys.wizard.form</field>
+        <field name="model">acc.repartition.keys.wizard</field>
+        <field name="arch" type="xml">
+            <form string="Création clefs de repartition">
+                <header>
+                     <button
+                        class="btn btn-primary"
+                        name="send_imported_file"
+                        type="object"
+                        string="Envoyer a enedis"
+                    />
+                </header>
+                <group name="keys" string="Clefs de repartition" col="2">
+                    <field name="csv_file"/>
+                </group>
+            </form>
+        </field>
+    </record>
+
+    <record id="acc_repartition_keys_wizard_action" model="ir.actions.act_window">
+        <field name="name">Création clefs de repartition</field>
+        <field name="type">ir.actions.act_window</field>
+        <field name="res_model">acc.repartition.keys.wizard</field>
+        <field name="view_mode">form</field>
+        <field
+            name="view_id"
+            ref="acc_repartition_keys_wizard_form"
+        />
+        <field name="target">new</field>
+    </record>
+</odoo>
\ No newline at end of file
-- 
GitLab