diff --git a/models/acc_operation.py b/models/acc_operation.py
index bd228c5012a25675a6d785ea40f243a1f38b2c89..ce31e0a27a58e79b2c754468e1d17519de97c113 100644
--- a/models/acc_operation.py
+++ b/models/acc_operation.py
@@ -1,10 +1,12 @@
 # Copyright 2021- Le Filament (https://le-filament.com)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
+from io import StringIO
+from ..tools import export_cdc
 from datetime import datetime
 
 from dateutil.relativedelta import relativedelta
 
-from odoo import _, fields, models
+from odoo import _, fields, models, http
 from odoo.exceptions import ValidationError
 from odoo.osv import expression
 from odoo.tools import date_utils
@@ -69,6 +71,7 @@ class AccOperation(models.Model):
         @returns: last acc.enedis.cdc record in operations
         """
         domain = [("acc_operation_id", "in", self.ids)]
+
         if partner_id:
             domain = expression.AND([domain, [("partner_id", "=", partner_id)]])
         # Get last date slot recorded
@@ -162,12 +165,12 @@ class AccOperation(models.Model):
         """
         Function to build SELECT section of query for retrieving curves
         @param
-            char date_slot : granularity (one of "hour", "day", "month", "year")
+            char date_slot : granularity (one of "minute", "hour", "day", "month", "year")
             [char] curve_types : list of type of curves
                 (allowed values : 'cons', 'autocons', 'allocons',
                     'prod', 'surplus', 'autoprod')
         """
-        if date_slot not in ("hour", "day", "month", "year"):
+        if date_slot not in ("minute", "hour", "day", "month", "year"):
             raise ValidationError(_("Incorrect date_slot in SELECT section"))
         result = f"""
             SELECT date_trunc('{date_slot}',
@@ -227,7 +230,7 @@ class AccOperation(models.Model):
             date start_date : first date to be retrieved
             date end_date : last date to be retrieved
             int prm_id : id of PRM to be retrieved (optional)
-            char extra_curve_type : extra curve to be retrieved (optional)
+            [char] extra_curve_type : extra curve to be retrieved (optional)
                 (allowed values : 'cons', 'autocons', 'allocons',
                     'prod', 'surplus', 'autoprod')
             int partner_id : id of partner to be retrieved (optional)
@@ -246,7 +249,13 @@ class AccOperation(models.Model):
             AND cdc.date_slot >= '{start_datetime}'
             AND cdc.date_slot < '{end_datetime}'
             """
-        if prm_id and isinstance(prm_id, int):
+
+        if partner_id and isinstance(partner_id, int):
+            result += f" AND ((cdc.partner_id = {partner_id} "
+            if prm_id and isinstance(prm_id, int):
+                result += f" AND cdc.acc_counter_id = {prm_id}) "
+            else:
+                result = f"{result})"
             if extra_curve_type and extra_curve_type in (
                 "cons",
                 "autocons",
@@ -255,25 +264,19 @@ class AccOperation(models.Model):
                 "surplus",
                 "autoprod",
             ):
-                result += f"""
-                     AND (cdc.comp_data_type = '{extra_curve_type}'
-                        OR cdc.acc_counter_id = {prm_id})
-                    """
+                result = f"{result} OR cdc.comp_data_type = '{extra_curve_type}' )"
             else:
-                result += f"""
-                    AND cdc.acc_counter_id = {prm_id}
-                    """
-        if partner_id and isinstance(partner_id, int):
-            result += f" AND cdc.partner_id = {partner_id} "
+                result = f"{result})"
+
         return result
 
     def _group_clause(self, date_slot):
         """
         Function to build GROUP BY section of query for retrieving curves
         @param
-            char date_slot : granularity (one of "hour", "day", "month", "year")
+            char date_slot : granularity (one of "minute", "hour", "day", "month", "year")
         """
-        if date_slot not in ("hour", "day", "month", "year"):
+        if date_slot not in ("minute", "hour", "day", "month", "year"):
             raise ValidationError(_("Incorrect date_slot in GROUP BY section"))
         return f"""
             GROUP BY date_trunc('{date_slot}',
@@ -283,6 +286,43 @@ class AccOperation(models.Model):
     def _order_clause(self):
         return "ORDER BY date_slot ASC;"
 
+    def _cdc_by_query_cons(
+        self,
+        slot_type,
+        start_date,
+        end_date,
+        prm_id=None,
+        partner_id=None,
+        curve_types=["cons", "autocons", "prod"],
+    ):
+        """
+        Fonction permettant de récupérer les données pour les consommateurs
+
+        :param: char slot_type: type de slot pour la query ("minute", "hour", "month" ou
+                    "year")
+                datetime start_date: date début
+                datetime end_date: date de fin
+                int prm_id : PRM de soutirage à récupérer
+                int partner_id: contact associé à la courbe
+                [char] curve_types: type de données
+        @returns: resultat de la requête
+                (labels et data pour les charts à afficher)
+
+        """
+        in_search = ", ".join(f"'{curve}'" for curve in curve_types)
+        query = (
+            self._select_clause(date_slot=slot_type, curve_types=curve_types)
+            + self._from_clause()
+            + self._where_clause(
+                self.id, start_date, end_date, prm_id, "prod", partner_id
+            )
+            + f"AND cdc.comp_data_type IN ({in_search})"
+            + self._group_clause(date_slot=slot_type)
+            + self._order_clause()
+        )
+        self.env.cr.execute(query)
+        return self.env.cr.fetchall()
+
     def get_cdc_by_query_cons(
         self, slot_type, start_date, end_date, prm_id=None, partner_id=None
     ):
@@ -290,13 +330,13 @@ class AccOperation(models.Model):
         Fonction permettant de récupérer les données pour la
         construction des chart pour une ou des opérations données
         pour les consommateurs
-        :param: char slot_type: type de slot pour la query ("hour", "month" ou
+        :param: char slot_type: type de slot pour la query ("minute", "hour", "month" ou
                     "year")
                 datetime start_date: date début
                 datetime end_date: date de fin
                 int prm_id : PRM de soutirage à récupérer
                 int partner_id: contact associé à la courbe
-        @returns: un dictionnaire de données
+        @returns: resultat de la requête
                 (labels et data pour les charts à afficher)
         """
         self.ensure_one()
@@ -307,20 +347,10 @@ class AccOperation(models.Model):
         data_cons = []
         data_prod = []
 
-        query = (
-            self._select_clause(
-                date_slot=slot_type, curve_types=["cons", "autocons", "prod"]
-            )
-            + self._from_clause()
-            + self._where_clause(
-                self.id, start_date, end_date, prm_id, "prod", partner_id
-            )
-            + "AND cdc.comp_data_type IN ('autocons', 'cons', 'prod')"
-            + self._group_clause(date_slot=slot_type)
-            + self._order_clause()
+        raw_data = self._cdc_by_query_cons(
+            slot_type, start_date, end_date, prm_id, partner_id
         )
-        self.env.cr.execute(query)
-        raw_data = self.env.cr.fetchall()
+
         for row in raw_data:
             label.append(row[0])
             data_cons.append({"x": row[0], "y": round(row[1], 2)})
@@ -338,7 +368,11 @@ class AccOperation(models.Model):
         return cdc_cons
 
     def get_cdc_by_query_daily_histo_cons(
-        self, start_date, end_date, prm_id=None, partner_id=None
+        self,
+        start_date,
+        end_date,
+        prm_id=None,
+        partner_id=None,
     ):
         """
         Fonction permettant de récupérer les données pour la construction
@@ -382,29 +416,31 @@ class AccOperation(models.Model):
         }
         return cdc_cons
 
-    def get_cdc_by_query_prod(
-        self, slot_type, start_date, end_date, prm_id=None, partner_id=None
+    def _cdc_by_query_prod(
+        self,
+        slot_type,
+        start_date,
+        end_date,
+        prm_id=None,
+        partner_id=None,
+        curve_types=["autoprod", "surplus"],
     ):
         """
         Fonction permettant de récupérer les données pour la construction des
           chart pour une ou des opérations données pour les consommateurs
-        :param: char slot_type: type de slot pour la query ("hour", "month" ou
+        :param: char slot_type: type de slot pour la query ("minute", "hour", "month" ou
                   "year")
                 datetime start_date: date début
                 datetime end_date: date de fin
                 int prm_id : PRM d'injection à récupérer
                 int partner_id: contact associé à la courbe
+                [char] curve_types: type de données
         :return: un dictionnaire de données
                 (labels et data pour les charts à afficher)
         """
-        label = []
-        data_autocons = []
-        data_surplus = []
 
         query = (
-            self._select_clause(
-                date_slot=slot_type, curve_types=["autoprod", "surplus"]
-            )
+            self._select_clause(date_slot=slot_type, curve_types=curve_types)
             + self._from_clause()
             + self._where_clause(
                 operation_id=self.id,
@@ -418,7 +454,31 @@ class AccOperation(models.Model):
         )
 
         self.env.cr.execute(query)
-        raw_data = self.env.cr.fetchall()
+        return self.env.cr.fetchall()
+
+    def get_cdc_by_query_prod(
+        self, slot_type, start_date, end_date, prm_id=None, partner_id=None
+    ):
+        """
+        Fonction permettant de récupérer les données pour la construction des
+          chart pour une ou des opérations données pour les consommateurs
+        :param: char slot_type: type de slot pour la query ("minute", "hour", "month" ou
+                  "year")
+                datetime start_date: date début
+                datetime end_date: date de fin
+                int prm_id : PRM d'injection à récupérer
+                int partner_id: contact associé à la courbe
+        :return: un dictionnaire de données
+                (labels et data pour les charts à afficher)
+        """
+        label = []
+        data_autocons = []
+        data_surplus = []
+
+        raw_data = self._cdc_by_query_prod(
+            slot_type, start_date, end_date, prm_id, partner_id
+        )
+
         for row in raw_data:
             label.append(row[0])
             data_autocons.append({"x": row[0], "y": round(row[1], 2)})
@@ -558,3 +618,60 @@ class AccOperation(models.Model):
             }
         )
         return result_graph
+
+    def export_cdc(self, start_date, end_date, partner_id, prm_id, data_type):
+        """
+
+        :param start_date:
+        :param end_date:
+        :param partner_id:
+        :param prm_id:
+        :param data_type:
+        :return:
+        """
+        start = datetime.strptime(start_date, "%d/%m/%Y")
+        end = datetime.strptime(end_date, "%d/%m/%Y") + relativedelta(days=1)
+
+        filename = export_cdc.make_filename(
+            self, start_date, end_date, partner_id, prm_id, data_type
+        )
+        header = export_cdc.make_header_lines(self, partner_id, prm_id, data_type)
+        if data_type in ["cons"]:
+            data_file = export_cdc.make_cons_data(
+                self._cdc_by_query_cons(
+                    "minute",
+                    start,
+                    end,
+                    prm_id,
+                    partner_id,
+                    curve_types=["cons", "autocons", "allocons"],
+                )
+            )
+        if data_type in ["prod"]:
+            data_file = export_cdc.make_prod_data(
+                self._cdc_by_query_prod(
+                    "minute",
+                    start,
+                    end,
+                    prm_id,
+                    partner_id,
+                    curve_types=["autoprod", "surplus", "prod"],
+                )
+            )
+
+        fout = StringIO()
+        for h_line in header:
+            fout.write(f"{h_line}\n")
+        for d_line in data_file:
+            fout.write(f"{d_line}\n")
+
+        fout.seek(0)
+        data = fout.read()
+        fout.close()
+
+        csv_http_headers = [
+            ("Content-Type", "text/csv;charset=utf8"),
+            ("Content-Disposition", http.content_disposition(filename)),
+        ]
+
+        return {"data": data, "name": filename, "headers": csv_http_headers}
diff --git a/tools/__init__.py b/tools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tools/export_cdc.py b/tools/export_cdc.py
new file mode 100644
index 0000000000000000000000000000000000000000..425484ef52571a57b86c5436f85ceef5bae4df51
--- /dev/null
+++ b/tools/export_cdc.py
@@ -0,0 +1,186 @@
+import string
+import unicodedata
+
+
+PROD_HEADER = [
+    "Horodatage",
+    "Production (W)",
+    "Production (kWh)",
+    "Surplus (kWh)",
+    "Production autoconsommee (kWh)",
+]
+CONS_HEADER = [
+    "Horodatage",
+    "Consommation (W)",
+    "Consommation (kWh)",
+    "Alloconsommation(kWh)",
+    "Autoconsommation (kWh)",
+]
+
+
+def make_header_lines(operation, partner_id, prm_id, data_type):
+    """
+    Données Elocoop de production de l'opération [nom opé] - [contact ou PRM]
+
+    :param operation: id de l operation
+    :param start_date: date de debut
+    :param end_date: date de fin
+    :param partner_id: id du partner
+    :param prm_id: id du prm
+    :param data_type: type de données (consomation ou production)
+    :return:
+    """
+    header = []
+    if prm_id:
+        desc = f" - {get_prm_name_from_id(operation, prm_id)}"
+    elif partner_id:
+        desc = f" - {get_partner_name_from_id(operation, partner_id)}"
+    else:
+        desc = ""
+
+    if data_type in ["cons"]:
+        header.append(
+            f"Données Elocoop de consommation de l'opération {operation.name}{desc}"
+        )
+        header.append(";".join(CONS_HEADER))
+    elif data_type in ["prod"]:
+        header.append(
+            f"Données Elocoop de production de l'opération {operation.name}{desc}"
+        )
+        header.append(";".join(PROD_HEADER))
+
+    return header
+
+
+def make_filename(
+    operation, start_date, end_date, partner_id, prm_id, data_type, file_type="csv"
+):
+    """
+    Genere le nom du fichier exporte sous la forme
+    Elocoop_[nom opé]_[date_debut]_[date_fin]_[production ou consommation]_[contact ou PRM].csv
+    :param operation: id de l operation
+    :param start_date: date de debut
+    :param end_date: date de fin
+    :param partner_id: id du partner
+    :param prm_id: id du prm
+    :param data_type: type de données (consomation ou production)
+    :param file_type: type de fichier par defaut csv
+    :return:
+    """
+    if prm_id:
+        desc = f"_{clean_for_title(get_prm_name_from_id(operation, prm_id))}"
+    elif partner_id:
+        desc = f"_{clean_for_title(get_partner_name_from_id(operation, partner_id))}"
+    else:
+        desc = ""
+
+    filename = (
+        f"Elocoop_{clean_for_title(operation.name)}_"
+        f"{start_date.replace('/', '')}_{end_date.replace('/', '')}_"
+        f"{'consommation' if data_type == 'cons' else 'production'}{desc}.{file_type}"
+    )
+    return filename
+
+
+def make_cons_data(raw_data):
+    """
+    make data file with cons hearder
+    horodatage cons w cons kwh allocons autocans
+    :param raw_data:
+    :return:
+    """
+    data_file_lines = []
+    rounding = 3
+    for row in raw_data:
+        data_file_lines.append(
+            ";".join(
+                [
+                    # horodatage
+                    row[0].strftime("%d/%m/%Y %H:%M"),
+                    # consommation en watt
+                    str(round(row[1] * 2000, rounding)),
+                    # consommation en kwh
+                    str(round(row[1], rounding)),
+                    # allocons
+                    str(round(row[3], rounding)),
+                    # autocons
+                    str(round(row[2], rounding)),
+                ]
+            )
+        )
+
+    return data_file_lines
+
+
+def make_prod_data(raw_data):
+    """
+    make data file with prod hearder
+    horodatage prod w prod kwh surplus autocons
+    :param raw_data:
+    :return:
+    """
+    data_file_lines = []
+    rounding = 3
+    for row in raw_data:
+        data_file_lines.append(
+            ";".join(
+                [
+                    # horodatage
+                    row[0].strftime("%d/%m/%Y %H:%M"),
+                    # production en watt
+                    str(round(row[3] * 2000, rounding)),
+                    # production en kwh
+                    str(round(row[3], rounding)),
+                    # surplus
+                    str(round(row[2], rounding)),
+                    # autocons
+                    str(round(row[1], rounding)),
+                ]
+            )
+        )
+
+    return data_file_lines
+
+
+def clean_for_title(word):
+    """
+    remplace les espace et caracteres speciaux par '_'
+    retire les accents
+
+    :param word: chaine de caractere d entrée
+    :return: chaine de caractere "nettoyée"
+    """
+    if isinstance(word, str):
+        char = list(string.punctuation)
+        char.append(" ")
+
+        for c in char:
+            word = word.replace(c, "_")
+
+    return "".join(
+        (
+            c
+            for c in unicodedata.normalize("NFD", word)
+            if unicodedata.category(c) != "Mn"
+        )
+    )
+
+
+def get_prm_name_from_id(operation, prm_id):
+    """
+    :param operation: operation
+    :param prm_id: id du prm
+    :return: chaine avec le nom du prm
+    """
+
+    return operation.env["acc.counter"].browse(int(prm_id)).name
+
+
+def get_partner_name_from_id(operation, partner_id):
+    """
+    :param operation: operation
+    :param partner_id: id du partner
+    :return: chaine avec le nom du partner
+    """
+
+    return operation.env["res.partner"].browse(int(partner_id)).name