From 093fbf55f508b97b871c1ea937fba117b40e6e75 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Wed, 14 May 2025 12:22:00 +0200
Subject: [PATCH 01/10] [ADD] add some fields for import

---
 models/product_category.py                 |  1 +
 models/product_template.py                 |  3 +++
 models/sale_intervention_plant_sequence.py |  8 +++++++
 models/sale_project.py                     | 27 +++++++++++++++++++++-
 views/product_template_views.xml           | 13 +++++++++++
 views/sale_intervention_view.xml           |  1 +
 views/sale_project_view.xml                |  6 +++++
 7 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/models/product_category.py b/models/product_category.py
index c2330f9..169a514 100644
--- a/models/product_category.py
+++ b/models/product_category.py
@@ -12,6 +12,7 @@ class ProductCategory(models.Model):
     # ------------------------------------------------------
     symbol = fields.Char("Symbole")
     accessory_label = fields.Char("Nom de l'accessoire associé")
+    nurseryman_id =  fields.Char("Numéro de pépinieriste")
 
     # ------------------------------------------------------
     # SQL Constraints
diff --git a/models/product_template.py b/models/product_template.py
index 79224d0..ece02f6 100644
--- a/models/product_template.py
+++ b/models/product_template.py
@@ -13,6 +13,9 @@ class ProductTemplate(models.Model):
     nb_accesories = fields.Float("Nombre d'accessoires")
     accessory_label = fields.Char(related="categ_id.accessory_label", readonly=True)
     mulch_quantity_multiplier = fields.Float("Multiplicateur paillage", default=1.0)
+    latin_name = fields.Char(
+         string="Nom latin"
+    )
 
     # ------------------------------------------------------
     # SQL Constraints
diff --git a/models/sale_intervention_plant_sequence.py b/models/sale_intervention_plant_sequence.py
index 32984f1..6fea647 100644
--- a/models/sale_intervention_plant_sequence.py
+++ b/models/sale_intervention_plant_sequence.py
@@ -46,6 +46,14 @@ class SaleInterventionPlantSequence(models.Model):
     qty = fields.Integer("Qté par espèces")
     sequence = fields.Integer(string="Ordre dans la séquence", default=10)
     is_list = fields.Boolean("Est une construction liste")
+    scale = fields.Selection(
+        [
+            ("little", "Petit"),
+            ("middle", "Moyen"),
+            ("big", "grand"),
+        ],
+        string="Taille",
+    )
 
     # ------------------------------------------------------
     # SQL Constraints
diff --git a/models/sale_project.py b/models/sale_project.py
index 0883c19..19ca8dd 100644
--- a/models/sale_project.py
+++ b/models/sale_project.py
@@ -124,7 +124,14 @@ class SaleProject(models.Model):
         readonly=True,
         states={"draft": [("readonly", False)], "sent": [("readonly", False)]},
     )
-
+    plant_sector_id = fields.Many2one(
+        comodel_name="res.partner.geo.sector", string="Secteur de plantation"
+    )
+    delivery_sector_id = fields.Many2one(
+        comodel_name="res.partner.geo.sector", string="Secteur de livraison"
+    )
+    bark_supplier = fields.Char("Fournisseur de copeaux/écorce")
+    comment = fields.Text("Commentaires")
     saison_id = fields.Many2one(
         "sale.project.saison",
         "Saison",
@@ -132,6 +139,24 @@ class SaleProject(models.Model):
         ondelete="restrict",
     )
     date_visit = fields.Date("Date de visite")
+    delivery_month = fields.Selection(
+        [
+            ("jan", "Janvier"),
+            ("feb", "Février"),
+            ("mar", "Mars"),
+            ("apr", "Avril"),
+            ("may", "Mai"),
+            ("jun", "Juin"),
+            ("jul", "Juillet"),
+            ("aug", "Août"),
+            ("sep", "Septembre"),
+            ("oct", "Octobre"),
+            ("nov", "Novembre"),
+            ("dec", "Décembre"),
+        ],
+        string="Moi de livraison",
+    )
+    is_demo = fields.Boolean("Accueil démo")
 
     intervention_ids = fields.One2many(
         comodel_name="sale.intervention",
diff --git a/views/product_template_views.xml b/views/product_template_views.xml
index 31b5080..eb69be6 100644
--- a/views/product_template_views.xml
+++ b/views/product_template_views.xml
@@ -10,6 +10,19 @@
                     <field name="symbol" />
                     <field name="accessory_label" />
                     <field name="name" />
+                    <field name="nurseryman_id" />
+                </field>
+            </field>
+        </record>
+
+
+        <record id="product_template_form_view" model="ir.ui.view">
+            <field name="name">product.template.form.inherit</field>
+            <field name="model">product.template</field>
+            <field name="inherit_id" ref="product.product_template_form_view" />
+            <field name="arch" type="xml">
+                <field name="type" position="after">
+                    <field name="latin_name" />
                 </field>
             </field>
         </record>
diff --git a/views/sale_intervention_view.xml b/views/sale_intervention_view.xml
index d844bcc..2e2ba1b 100644
--- a/views/sale_intervention_view.xml
+++ b/views/sale_intervention_view.xml
@@ -240,6 +240,7 @@
                                     options="{'no_open': True, 'no_create': True}"
                                 />
                                     <field name="is_local" />
+                                    <field name="scale" />
                                 </tree>
                             </field>
                          <group
diff --git a/views/sale_project_view.xml b/views/sale_project_view.xml
index 1c4da6f..cbee72f 100644
--- a/views/sale_project_view.xml
+++ b/views/sale_project_view.xml
@@ -38,11 +38,17 @@
                             <group>
                                 <field name="project_subvention_id" readonly="True" />
                                 <field name="geo_sector_id" />
+                                <field name="plant_sector_id" />
+                                <field name="delivery_sector_id" />
                                 <field name="user_id" />
+                                <field name="bark_supplier" />
                             </group>
                             <group>
                                 <field name="saison_id" />
                                 <field name="date_visit" />
+                                <field name="comment" />
+                                <field name="delivery_month" />
+                                <field name="is_demo" />
                                 <field name="state" invisible="1" />
                             </group>
                         </group>
-- 
GitLab


From 74938b5ff7b2e8d57e0f3ef79d4ae0ff821b9304 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Wed, 14 May 2025 14:12:45 +0200
Subject: [PATCH 02/10] [UPD] review

---
 models/sale_intervention.py      |  8 +++++
 models/sale_project.py           | 51 +++++++++++++++++++++++---------
 views/product_template_views.xml |  1 -
 views/sale_intervention_view.xml |  1 +
 views/sale_project_view.xml      |  4 ++-
 5 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/models/sale_intervention.py b/models/sale_intervention.py
index e90e6bb..d23508b 100644
--- a/models/sale_intervention.py
+++ b/models/sale_intervention.py
@@ -439,6 +439,14 @@ class SaleIntervention(models.Model):
         inverse_name="sale_intervention_id",
         string="Articles à déstocker",
     )
+    location = fields.Selection(
+        [
+            ("in_field", "Plein champ"),
+            ("near_facility", "Bord de bâtiment"),
+            ("border", "Bordure"),
+        ],
+        string="Implantation",
+    )
 
     # ------------------------------------------------------
     # SQL Constraints
diff --git a/models/sale_project.py b/models/sale_project.py
index 19ca8dd..3f3ac28 100644
--- a/models/sale_project.py
+++ b/models/sale_project.py
@@ -141,22 +141,45 @@ class SaleProject(models.Model):
     date_visit = fields.Date("Date de visite")
     delivery_month = fields.Selection(
         [
-            ("jan", "Janvier"),
-            ("feb", "Février"),
-            ("mar", "Mars"),
-            ("apr", "Avril"),
-            ("may", "Mai"),
-            ("jun", "Juin"),
-            ("jul", "Juillet"),
-            ("aug", "Août"),
-            ("sep", "Septembre"),
-            ("oct", "Octobre"),
-            ("nov", "Novembre"),
-            ("dec", "Décembre"),
+            ("1", "Janvier"),
+            ("2", "Février"),
+            ("3", "Mars"),
+            ("4", "Avril"),
+            ("5", "Mai"),
+            ("6", "Juin"),
+            ("7", "Juillet"),
+            ("8", "Août"),
+            ("9", "Septembre"),
+            ("10", "Octobre"),
+            ("11", "Novembre"),
+            ("12", "Décembre"),
         ],
-        string="Moi de livraison",
+        string="Mois de livraison",
+    )
+    demo = fields.Selection(
+        [
+            ("yes", "Oui"),
+            ("no", "Non"),
+        ],
+        string="Accueil démo",
+    )
+
+    online = fields.Selection(
+        [
+            ("yes", "Oui"),
+            ("no", "Non"),
+        ],
+        string="Diffusion sur site internet",
+    )
+
+    plant_goal = fields.Selection(
+        [
+            ("landscape", "Enjeu paysage"),
+            ("fence", "Clôture"),
+            ("biodiversity", "Préservation biodiversité"),
+        ],
+        string="Objectif plantation",
     )
-    is_demo = fields.Boolean("Accueil démo")
 
     intervention_ids = fields.One2many(
         comodel_name="sale.intervention",
diff --git a/views/product_template_views.xml b/views/product_template_views.xml
index eb69be6..35e7c26 100644
--- a/views/product_template_views.xml
+++ b/views/product_template_views.xml
@@ -9,7 +9,6 @@
                 <field name="parent_id" position="after">
                     <field name="symbol" />
                     <field name="accessory_label" />
-                    <field name="name" />
                     <field name="nurseryman_id" />
                 </field>
             </field>
diff --git a/views/sale_intervention_view.xml b/views/sale_intervention_view.xml
index 2e2ba1b..d1f79f8 100644
--- a/views/sale_intervention_view.xml
+++ b/views/sale_intervention_view.xml
@@ -25,6 +25,7 @@
                                 name="intervention_type_id"
                                 options="{'no_open': True, 'no_create': True}"
                             />
+                            <field name="location" />
                             <field name="intervention_uom_name" invisible="True" />
                         </group>
                         <group>
diff --git a/views/sale_project_view.xml b/views/sale_project_view.xml
index cbee72f..7c55e82 100644
--- a/views/sale_project_view.xml
+++ b/views/sale_project_view.xml
@@ -48,7 +48,9 @@
                                 <field name="date_visit" />
                                 <field name="comment" />
                                 <field name="delivery_month" />
-                                <field name="is_demo" />
+                                <field name="demo" />
+                                <field name="plant_goal" />
+                                <field name="online" />
                                 <field name="state" invisible="1" />
                             </group>
                         </group>
-- 
GitLab


From 898ab92763f658ef9d52c8d674708dc5c14811b9 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Wed, 14 May 2025 15:52:03 +0200
Subject: [PATCH 03/10] [UPD] review add some models

---
 __manifest__.py                           |  2 ++
 models/sale_intervention.py               | 19 +++++++----
 models/sale_project.py                    | 19 +++++++----
 security/ir.model.access.csv              |  2 ++
 views/sale_intervention_location_view.xml | 41 +++++++++++++++++++++++
 views/sale_intervention_view.xml          |  2 +-
 views/sale_project_plant_goal_view.xml    | 41 +++++++++++++++++++++++
 views/sale_project_view.xml               |  2 +-
 8 files changed, 112 insertions(+), 16 deletions(-)
 create mode 100644 views/sale_intervention_location_view.xml
 create mode 100644 views/sale_project_plant_goal_view.xml

diff --git a/__manifest__.py b/__manifest__.py
index 2bf177e..c68871e 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -23,6 +23,8 @@
         "views/sale_project_admin_state_views.xml",
         "views/product_template_views.xml",
         "views/res_partner_views.xml",
+        "views/sale_intervention_location_view.xml",
+        "views/sale_project_plant_goal_view.xml",
         # views menu
         # wizard
     ],
diff --git a/models/sale_intervention.py b/models/sale_intervention.py
index d23508b..e49b24c 100644
--- a/models/sale_intervention.py
+++ b/models/sale_intervention.py
@@ -439,13 +439,10 @@ class SaleIntervention(models.Model):
         inverse_name="sale_intervention_id",
         string="Articles à déstocker",
     )
-    location = fields.Selection(
-        [
-            ("in_field", "Plein champ"),
-            ("near_facility", "Bord de bâtiment"),
-            ("border", "Bordure"),
-        ],
-        string="Implantation",
+    location_id = fields.Many2one(
+        "sale.intervention.location",
+        "Implantation",
+        ondelete="restrict",
     )
 
     # ------------------------------------------------------
@@ -1049,3 +1046,11 @@ class SaleIntervention(models.Model):
         res = super().unlink()
         project_id.update_order_lines()
         return res
+
+
+class SaleInterventionLocation(models.Model):
+    _name = "sale.intervention.location"
+    _description = "Implantation"
+    _order = "name"
+
+    name = fields.Char(string="Implantation", required=True)
diff --git a/models/sale_project.py b/models/sale_project.py
index 3f3ac28..26981a6 100644
--- a/models/sale_project.py
+++ b/models/sale_project.py
@@ -172,13 +172,10 @@ class SaleProject(models.Model):
         string="Diffusion sur site internet",
     )
 
-    plant_goal = fields.Selection(
-        [
-            ("landscape", "Enjeu paysage"),
-            ("fence", "Clôture"),
-            ("biodiversity", "Préservation biodiversité"),
-        ],
-        string="Objectif plantation",
+    plant_goal_id = fields.Many2one(
+        "sale.project.plant.goal",
+        "Objectif plantation",
+        ondelete="restrict",
     )
 
     intervention_ids = fields.One2many(
@@ -433,6 +430,14 @@ class SaleProjectSaison(models.Model):
     end_date = fields.Date("Date de fin", required=True)
 
 
+class SaleProjectPlantGoal(models.Model):
+    _name = "sale.project.plant.goal"
+    _description = "Objectif plantation"
+    _order = "name"
+
+    name = fields.Char(string="Objectif plantation", required=True)
+
+
 class SaleProjectAdminState(models.Model):
     _name = "sale.project.admin.state"
     _description = "Étapes du projet"
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index 997ea40..26c8623 100644
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -8,4 +8,6 @@ sale_financial_help_salesteam,sale_financial_help_salesteam,model_sale_financial
 sale_intervention_salesteam,sale_intervention_salesteam,model_sale_intervention,sales_team.group_sale_salesman,1,1,1,1
 sale_intervention_stock_salesteam,sale_intervention_stock_salesteam,model_sale_intervention_stock,sales_team.group_sale_salesman,1,1,1,1
 sale_intervention_plant_sequence_salesteam,sale_intervention_plant_sequence_salesteam,model_sale_intervention_plant_sequence,sales_team.group_sale_salesman,1,1,1,1
+sale_intervention_location_salesteam,sale_intervention_location_salesteam,model_sale_intervention_location,sales_team.group_sale_salesman,1,1,1,1
 sale_project_subvention_salesteam,sale_project_subvention_salesteam,model_sale_project_subvention,sales_team.group_sale_salesman,1,1,1,1
+sale_project_saison_salesteam,sale_project_plant_goal_salesteam,model_sale_project_plant_goal,sales_team.group_sale_salesman,1,1,1,1
diff --git a/views/sale_intervention_location_view.xml b/views/sale_intervention_location_view.xml
new file mode 100644
index 0000000..5344251
--- /dev/null
+++ b/views/sale_intervention_location_view.xml
@@ -0,0 +1,41 @@
+<odoo>
+
+    <!-- Form View-->
+    <record id="sale_intervention_admin_location_view_form" model="ir.ui.view">
+        <field name="name">Implantation</field>
+        <field name="model">sale.intervention.location</field>
+        <field name="arch" type="xml">
+            <form>
+              <field name="name" />
+            </form>
+        </field>
+    </record>
+
+    <!-- List View-->
+    <record id="sale_intervention_admin_location_view_list" model="ir.ui.view">
+        <field name="name">Implantation</field>
+        <field name="model">sale.intervention.location</field>
+        <field name="arch" type="xml">
+            <tree>
+                <field name="name" />
+            </tree>
+        </field>
+    </record>
+
+    <!-- Actions opening views on models -->
+    <record model="ir.actions.act_window" id="sale_intervention_admin_location_act_window">
+        <field name="name">Implantation</field>
+        <field name="res_model">sale.intervention.location</field>
+        <field name="view_mode">tree,form</field>
+    </record>
+
+    <!-- Menu Items -->
+    <menuitem
+        id="menu_sale_intervention_admin_goal"
+        name="Implantation"
+        parent="ap_sale_project.menu_sale_project_config"
+        action="sale_intervention_admin_location_act_window"
+        sequence="22"
+    />
+
+</odoo>
diff --git a/views/sale_intervention_view.xml b/views/sale_intervention_view.xml
index d1f79f8..162d249 100644
--- a/views/sale_intervention_view.xml
+++ b/views/sale_intervention_view.xml
@@ -25,7 +25,7 @@
                                 name="intervention_type_id"
                                 options="{'no_open': True, 'no_create': True}"
                             />
-                            <field name="location" />
+                            <field name="location_id" />
                             <field name="intervention_uom_name" invisible="True" />
                         </group>
                         <group>
diff --git a/views/sale_project_plant_goal_view.xml b/views/sale_project_plant_goal_view.xml
new file mode 100644
index 0000000..c41b2ee
--- /dev/null
+++ b/views/sale_project_plant_goal_view.xml
@@ -0,0 +1,41 @@
+<odoo>
+
+    <!-- Form View-->
+    <record id="sale_project_admin_goal_view_form" model="ir.ui.view">
+        <field name="name">Objectif plantation</field>
+        <field name="model">sale.project.plant.goal</field>
+        <field name="arch" type="xml">
+            <form>
+              <field name="name" />
+            </form>
+        </field>
+    </record>
+
+    <!-- List View-->
+    <record id="sale_project_admin_goal_view_list" model="ir.ui.view">
+        <field name="name">Objectif plantation</field>
+        <field name="model">sale.project.plant.goal</field>
+        <field name="arch" type="xml">
+            <tree>
+                <field name="name" />
+            </tree>
+        </field>
+    </record>
+
+    <!-- Actions opening views on models -->
+    <record model="ir.actions.act_window" id="sale_project_admin_location_act_window">
+        <field name="name">Objectif plantation</field>
+        <field name="res_model">sale.project.plant.goal</field>
+        <field name="view_mode">tree,form</field>
+    </record>
+
+    <!-- Menu Items -->
+    <menuitem
+        id="menu_sale_project_admin_goal"
+        name="Objectif plantation"
+        parent="ap_sale_project.menu_sale_project_config"
+        action="sale_project_admin_location_act_window"
+        sequence="21"
+    />
+
+</odoo>
diff --git a/views/sale_project_view.xml b/views/sale_project_view.xml
index 7c55e82..0ec7675 100644
--- a/views/sale_project_view.xml
+++ b/views/sale_project_view.xml
@@ -49,7 +49,7 @@
                                 <field name="comment" />
                                 <field name="delivery_month" />
                                 <field name="demo" />
-                                <field name="plant_goal" />
+                                <field name="plant_goal_id" />
                                 <field name="online" />
                                 <field name="state" invisible="1" />
                             </group>
-- 
GitLab


From 195258d56ea44d92ab538ee36494d06d0db52dcb Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Wed, 14 May 2025 16:14:38 +0200
Subject: [PATCH 04/10] [UPD] review add some models

---
 views/sale_intervention_location_view.xml | 16 ++--------------
 views/sale_project_plant_goal_view.xml    | 15 ++-------------
 2 files changed, 4 insertions(+), 27 deletions(-)

diff --git a/views/sale_intervention_location_view.xml b/views/sale_intervention_location_view.xml
index 5344251..04b672c 100644
--- a/views/sale_intervention_location_view.xml
+++ b/views/sale_intervention_location_view.xml
@@ -1,22 +1,11 @@
 <odoo>
 
-    <!-- Form View-->
-    <record id="sale_intervention_admin_location_view_form" model="ir.ui.view">
-        <field name="name">Implantation</field>
-        <field name="model">sale.intervention.location</field>
-        <field name="arch" type="xml">
-            <form>
-              <field name="name" />
-            </form>
-        </field>
-    </record>
-
     <!-- List View-->
     <record id="sale_intervention_admin_location_view_list" model="ir.ui.view">
         <field name="name">Implantation</field>
         <field name="model">sale.intervention.location</field>
         <field name="arch" type="xml">
-            <tree>
+            <tree editable="bottom">
                 <field name="name" />
             </tree>
         </field>
@@ -26,7 +15,7 @@
     <record model="ir.actions.act_window" id="sale_intervention_admin_location_act_window">
         <field name="name">Implantation</field>
         <field name="res_model">sale.intervention.location</field>
-        <field name="view_mode">tree,form</field>
+        <field name="view_mode">tree</field>
     </record>
 
     <!-- Menu Items -->
@@ -37,5 +26,4 @@
         action="sale_intervention_admin_location_act_window"
         sequence="22"
     />
-
 </odoo>
diff --git a/views/sale_project_plant_goal_view.xml b/views/sale_project_plant_goal_view.xml
index c41b2ee..aeaceee 100644
--- a/views/sale_project_plant_goal_view.xml
+++ b/views/sale_project_plant_goal_view.xml
@@ -1,22 +1,11 @@
 <odoo>
 
-    <!-- Form View-->
-    <record id="sale_project_admin_goal_view_form" model="ir.ui.view">
-        <field name="name">Objectif plantation</field>
-        <field name="model">sale.project.plant.goal</field>
-        <field name="arch" type="xml">
-            <form>
-              <field name="name" />
-            </form>
-        </field>
-    </record>
-
     <!-- List View-->
     <record id="sale_project_admin_goal_view_list" model="ir.ui.view">
         <field name="name">Objectif plantation</field>
         <field name="model">sale.project.plant.goal</field>
         <field name="arch" type="xml">
-            <tree>
+            <tree editable="bottom">
                 <field name="name" />
             </tree>
         </field>
@@ -26,7 +15,7 @@
     <record model="ir.actions.act_window" id="sale_project_admin_location_act_window">
         <field name="name">Objectif plantation</field>
         <field name="res_model">sale.project.plant.goal</field>
-        <field name="view_mode">tree,form</field>
+        <field name="view_mode">tree</field>
     </record>
 
     <!-- Menu Items -->
-- 
GitLab


From 77880c6dfa3bc424b7d63276028b99a523a0f017 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Thu, 15 May 2025 11:29:33 +0200
Subject: [PATCH 05/10] [FIX] typo un security csv

---
 security/ir.model.access.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index 26c8623..861182d 100644
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -10,4 +10,4 @@ sale_intervention_stock_salesteam,sale_intervention_stock_salesteam,model_sale_i
 sale_intervention_plant_sequence_salesteam,sale_intervention_plant_sequence_salesteam,model_sale_intervention_plant_sequence,sales_team.group_sale_salesman,1,1,1,1
 sale_intervention_location_salesteam,sale_intervention_location_salesteam,model_sale_intervention_location,sales_team.group_sale_salesman,1,1,1,1
 sale_project_subvention_salesteam,sale_project_subvention_salesteam,model_sale_project_subvention,sales_team.group_sale_salesman,1,1,1,1
-sale_project_saison_salesteam,sale_project_plant_goal_salesteam,model_sale_project_plant_goal,sales_team.group_sale_salesman,1,1,1,1
+sale_project_plant_goal_salesteam,sale_project_plant_goal_salesteam,model_sale_project_plant_goal,sales_team.group_sale_salesman,1,1,1,1
-- 
GitLab


From bcb5b82ec0aac18a476890ebc5ab0be0fd6ed17c Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Thu, 15 May 2025 17:34:18 +0200
Subject: [PATCH 06/10] [ADD] add export data

---
 __init__.py                                |   2 +
 __manifest__.py                            |   1 +
 controllers/__init__.py                    |   1 +
 controllers/export.py                      | 216 +++++++++++++++++++++
 models/sale_intervention.py                |   2 +
 models/sale_intervention_plant_sequence.py |   2 +-
 security/ir.model.access.csv               |   2 +
 views/sale_intervention_view.xml           |   1 +
 wizard/__init__.py                         |   1 +
 wizard/ap_export_wizard.py                 |  27 +++
 wizard/ap_export_wizard_views.xml          |  50 +++++
 11 files changed, 304 insertions(+), 1 deletion(-)
 create mode 100644 controllers/__init__.py
 create mode 100644 controllers/export.py
 create mode 100644 wizard/__init__.py
 create mode 100644 wizard/ap_export_wizard.py
 create mode 100644 wizard/ap_export_wizard_views.xml

diff --git a/__init__.py b/__init__.py
index e0e3732..39bfba2 100644
--- a/__init__.py
+++ b/__init__.py
@@ -2,3 +2,5 @@
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 
 from . import models
+from . import wizard
+from . import controllers
diff --git a/__manifest__.py b/__manifest__.py
index c68871e..a4a1454 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -27,6 +27,7 @@
         "views/sale_project_plant_goal_view.xml",
         # views menu
         # wizard
+        "wizard/ap_export_wizard_views.xml",
     ],
     "qweb": [
         # "static/src/xml/*.xml",
diff --git a/controllers/__init__.py b/controllers/__init__.py
new file mode 100644
index 0000000..4435ea7
--- /dev/null
+++ b/controllers/__init__.py
@@ -0,0 +1 @@
+from . import export
diff --git a/controllers/export.py b/controllers/export.py
new file mode 100644
index 0000000..b9558b1
--- /dev/null
+++ b/controllers/export.py
@@ -0,0 +1,216 @@
+from odoo import http
+from odoo.http import request
+from odoo.addons.web.controllers.main import ExcelExport
+import io
+import xlsxwriter
+
+
+def export_xlsx(data_by_sheet):
+    """
+    Génère un fichier XLSX avec plusieurs feuilles.
+
+    :param data_by_sheet: dict où chaque clé est un nom de feuille,
+                          et chaque valeur est une liste de dictionnaires.
+                          Exemple :
+                          {
+                              'Feuille1': [{'Nom': 'Alice', 'Age': 30}],
+                              'Feuille2': [{'Produit': 'Widget', 'Prix': 20.5}]
+                          }
+    :return: binary XLSX content
+    """
+    output = io.BytesIO()
+    workbook = xlsxwriter.Workbook(output, {"in_memory": True})
+
+    for sheet_name, rows in data_by_sheet.items():
+        sheet = workbook.add_worksheet(sheet_name[:31])  # Excel limite à 31 caractères
+
+        if not rows:
+            continue
+
+        headers = list(rows[0].keys())
+        for col_idx, header in enumerate(headers):
+            sheet.write(0, col_idx, header)
+
+        for row_idx, row in enumerate(rows, start=1):
+            for col_idx, header in enumerate(headers):
+                sheet.write(row_idx, col_idx, row.get(header, ""))
+
+    workbook.close()
+    output.seek(0)
+    return output.read()
+
+
+class ApExportController(http.Controller):
+
+    def get_filename(self, type, saison):
+        prefix = {
+            "order": "suivi_commande",
+            "fin": "suivi_financeurs",
+            "tech": "suivi_technique",
+        }
+
+        return f"{prefix.get(type, 'fichier')}_{saison.name}.xlsx"
+
+    def get_nb_staples(self, inter_id):
+        staples = 0
+        if inter_id.mulch_has_staples:
+            staples += inter_id.mulch_staples_qty * inter_id.mulch_qty
+        if inter_id.mulch2_has_staples:
+            staples += inter_id.mulch2_staples_qty * inter_id.mulch2_qty
+
+    def get_linked_contact(self, partner_id):
+        if partner_id.child_ids:
+            return ",".join([child.name for child in partner_id.child_ids])
+        return ""
+
+    def get_financial_help(self, inter):
+        if inter.financial_help_ids:
+            return ",".join([h.name for h in inter.financial_help_ids])
+        return ""
+
+    def get_nb_tree(self, inter_id):
+
+        if inter_id.plant_sequence_ids:
+            to_count = inter_id.plant_sequence_ids
+        else:
+            to_count = inter_id.plant_list_ids
+
+        trees = to_count.search([("scale", "=", "big")])
+        return len(trees)
+
+    def get_order_data(self, saison):
+        """
+        export commande
+        """
+        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
+        file = {}
+
+        for project in projects:
+            file[project.name] = [{"test": "test"}]
+
+        return file
+
+    def get_fin_data(self, saison):
+        """
+        export suivi financeurs
+        """
+        sheet_name = "suivi financeurs"
+        file = {sheet_name: []}
+
+        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
+
+        for project in projects:
+            if project.intervention_ids:
+                for inter in project.intervention_ids:
+                    project_data = {
+                        "Saison": saison.name or "",
+                        "Programme": project.project_subvention_id.name or "",
+                        "Numéro client": project.partner_id.ref or "",
+                        "Secteur de plantation": project.plant_sector_id.name or "",
+                        "Secteur de livraison": project.delivery_sector_id.name or "",
+                        "Réferent": project.user_id.name or "",
+                        "Planteur": project.partner_id.name or "",
+                        "Contacts": self.get_linked_contact(project.partner_id),
+                        "Adresse (rue)": project.partner_id.street or "",
+                        "Adresse (rue 2)": project.partner_id.street2 or "",
+                        "Code postal": project.partner_id.zip or "",
+                        "Ville": project.partner_id.city or "",
+                        "Télephone": project.partner_id.phone or "",
+                        "Email": project.partner_id.email or "",
+                        "Commune intervention": inter.city or "",
+                        "Geo latitude": str(inter.latitude).replace(',', '.'),
+                        "Geo longitude": str(inter.longitude).replace(',', '.'),
+                        "Bord de départementale": inter.near_road or "",
+                        "Forme juridique": project.partner_id.partner_company_type_id.name or "",
+                        "Type de culture": project.partner_id.culture_type_id.name or "",
+                        #todo format date
+                        "Date de première adhésion": project.partner_id.year_1st_membership or "",
+                        "Diffusion sur site internet": project.online or "",
+                        "Longueur de haie": inter.intervention_length or "",
+                        "Nombre de plants": inter.plant_qty or "",
+                        "Surface (en m²)": inter.surface or "",
+                        "Dont nombre total d'arbres": self.get_nb_tree(inter),
+                        "Type d'intervention": inter.intervention_type_id.name or "",
+                        "Implantation": inter.location_id.name or "",
+                        "Objectif plantation": project.plant_goal_id.name or "",
+                        "Avancement": project.admin_state_id.name,
+                        "Aides financières": self.get_financial_help(inter)
+                    }
+
+                file.get(sheet_name).append(project_data)
+
+        return file
+
+    def get_tech_data(self, saison):
+        """
+        export suivi technique
+        """
+        sheet_name = "suivi technique"
+        file = {sheet_name: []}
+        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
+
+        for project in projects:
+            if project.intervention_ids:
+                for inter in project.intervention_ids:
+                    project_data = {
+                        "Saison": saison.name or "",
+                        "Programme": project.project_subvention_id.name or "",
+                        "Client": project.partner_id.ref or "",
+                        "Secteur de plantation": project.plant_sector_id.name or "",
+                        "Secteur de livraison": project.delivery_sector_id.name or "",
+                        "Réferent": project.user_id.name or "",
+                        "Planteur": project.partner_id.name or "",
+                        "Contacts": self.get_linked_contact(project.partner_id),
+                        "Adresse (rue)": project.partner_id.street or "",
+                        "Adresse (rue 2)": project.partner_id.street2 or "",
+                        "Code postal": project.partner_id.zip or "",
+                        "Ville": project.partner_id.city or "",
+                        "Télephone": project.partner_id.phone or "",
+                        "Email": project.partner_id.email or "",
+                        "Commune intervention": inter.city or "",
+                        "Longueur de haie": inter.intervention_length or "",
+                        "Nombre de plants": inter.plant_qty or "",
+                        "Surface (en m²)": inter.surface or "",
+                        "Dont nombre total d'arbres": self.get_nb_tree(inter),
+                        "Type d'intervention": inter.intervention_type_id.name or "",
+                        "Paillage 1": inter.mulch_id.name or "",
+                        "Paillage 2": inter.mulch2_id.name or "",
+                        "Qté paillage 1": inter.mulch_qty or "",
+                        "Qté paillage 2": inter.mulch2_qty or "",
+                        "Nom fournisseur copeaux/écorce": project.bark_supplier or "",
+                        "Qté protegée haut": inter.high_protection_qty or "",
+                        "Qté piquets": inter.stake_qty or "",
+                        "Qté protegée bas": inter.low_protection_qty or "",
+                        "Qté bambous": inter.bamboo_qty or "",
+                        "Nombre d'agrafes": self.get_nb_staples(inter),
+                        "Nombre de collerettes": inter.collarette_qty or "",
+                        "Mois livraison": project.delivery_month or "",
+                        "Accueil démo": project.demo or "",
+                        "Commentaires": project.comment or "",
+                    }
+
+                file.get(sheet_name).append(project_data)
+
+        return file
+
+    @http.route("/ap_export/xlsx_download", type="http", auth="user")
+    def download_xlsx(self, wizard_id, **kwargs):
+        wizard = request.env["ap.export.wizard"].sudo().browse(int(wizard_id))
+        filename = self.get_filename(wizard.export_type, wizard.saison_id)
+
+        data_methods = {
+            "order": self.get_order_data,
+            "fin": self.get_fin_data,
+            "tech": self.get_tech_data,
+        }
+
+        xlsx_data = export_xlsx(data_methods.get(wizard.export_type)(wizard.saison_id))
+
+        headers = [
+            (
+                "Content-Type",
+                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+            ),
+            ("Content-Disposition", 'attachment; filename="%s"' % filename),
+        ]
+        return request.make_response(xlsx_data, headers)
diff --git a/models/sale_intervention.py b/models/sale_intervention.py
index e49b24c..098f22f 100644
--- a/models/sale_intervention.py
+++ b/models/sale_intervention.py
@@ -445,6 +445,8 @@ class SaleIntervention(models.Model):
         ondelete="restrict",
     )
 
+    near_road = fields.Char("Bord de départementale")
+
     # ------------------------------------------------------
     # SQL Constraints
     # ------------------------------------------------------
diff --git a/models/sale_intervention_plant_sequence.py b/models/sale_intervention_plant_sequence.py
index 6fea647..d9703ff 100644
--- a/models/sale_intervention_plant_sequence.py
+++ b/models/sale_intervention_plant_sequence.py
@@ -50,7 +50,7 @@ class SaleInterventionPlantSequence(models.Model):
         [
             ("little", "Petit"),
             ("middle", "Moyen"),
-            ("big", "grand"),
+            ("big", "Grand"),
         ],
         string="Taille",
     )
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index 861182d..bb3e919 100644
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -11,3 +11,5 @@ sale_intervention_plant_sequence_salesteam,sale_intervention_plant_sequence_sale
 sale_intervention_location_salesteam,sale_intervention_location_salesteam,model_sale_intervention_location,sales_team.group_sale_salesman,1,1,1,1
 sale_project_subvention_salesteam,sale_project_subvention_salesteam,model_sale_project_subvention,sales_team.group_sale_salesman,1,1,1,1
 sale_project_plant_goal_salesteam,sale_project_plant_goal_salesteam,model_sale_project_plant_goal,sales_team.group_sale_salesman,1,1,1,1
+access_ap_export_wizard,access_ap_export_wizard,model_ap_export_wizard,sales_team.group_sale_salesman,1,1,1,0
+
diff --git a/views/sale_intervention_view.xml b/views/sale_intervention_view.xml
index 162d249..01f55dd 100644
--- a/views/sale_intervention_view.xml
+++ b/views/sale_intervention_view.xml
@@ -26,6 +26,7 @@
                                 options="{'no_open': True, 'no_create': True}"
                             />
                             <field name="location_id" />
+                            <field name="near_road" />
                             <field name="intervention_uom_name" invisible="True" />
                         </group>
                         <group>
diff --git a/wizard/__init__.py b/wizard/__init__.py
new file mode 100644
index 0000000..3ae425c
--- /dev/null
+++ b/wizard/__init__.py
@@ -0,0 +1 @@
+from . import ap_export_wizard
diff --git a/wizard/ap_export_wizard.py b/wizard/ap_export_wizard.py
new file mode 100644
index 0000000..c9945fc
--- /dev/null
+++ b/wizard/ap_export_wizard.py
@@ -0,0 +1,27 @@
+from odoo import models, fields
+
+
+class ApExportWizard(models.TransientModel):
+    _name = "ap.export.wizard"
+    _description = "Export de données"
+
+    export_type = fields.Selection(
+        [
+            ("order", "Commandes"),
+            ("fin", "Suivi financeurs"),
+            ("tech", "Suivi technique"),
+        ],
+        required=True,
+        default="order",
+    )
+
+    saison_id = fields.Many2one(
+        comodel_name="sale.project.saison", string="Saison", required=True
+    )
+
+    def action_exporter(self):
+        return {
+            "type": "ir.actions.act_url",
+            "url": f"/ap_export/xlsx_download?wizard_id={self.id}",
+            "target": "self",
+        }
diff --git a/wizard/ap_export_wizard_views.xml b/wizard/ap_export_wizard_views.xml
new file mode 100644
index 0000000..eca1364
--- /dev/null
+++ b/wizard/ap_export_wizard_views.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" ?>
+<!-- Copyright 2025 Le Filament
+     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+<odoo>
+    <data>
+        <record id="view_ap_export_wizard_form" model="ir.ui.view">
+            <field name="name">ap.export.wizard.form</field>
+            <field name="model">ap.export.wizard</field>
+            <field name="arch" type="xml">
+                <form string="Export de données">
+                    <group>
+                        <field name="export_type"/>
+                    </group>
+                    <group>
+                        <field name="saison_id"/>
+                    </group>
+                    <footer>
+                        <button string="Valider" type="object" name="action_exporter" class="btn-primary"/>
+                        <button string="Annuler" class="btn-secondary" special="cancel"/>
+                    </footer>
+                </form>
+            </field>
+        </record>
+
+        <record id="action_ap_export_wizard" model="ir.actions.act_window">
+            <field name="name">Export de données</field>
+            <field name="res_model">ap.export.wizard</field>
+            <field name="view_mode">form</field>
+            <field name="target">new</field>
+        </record>
+
+        <menuitem
+            id="menu_export_root"
+            name="Export de données"
+            parent="sale.sale_menu_root"
+            sequence="7"
+            groups="base.group_system"
+        />
+
+        <menuitem
+            id="menu_export_wizard"
+            parent="menu_export_root"
+            name="Exporter"
+            action="action_ap_export_wizard"
+            sequence="10"
+            groups="base.group_system"
+        />
+
+    </data>
+</odoo>
-- 
GitLab


From 91bd2cfb433b66bd3fd986d5886ca577518d5e6f Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Mon, 19 May 2025 17:19:52 +0200
Subject: [PATCH 07/10] [ADD] add order export data

---
 controllers/export.py | 95 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 83 insertions(+), 12 deletions(-)

diff --git a/controllers/export.py b/controllers/export.py
index b9558b1..a4bbf75 100644
--- a/controllers/export.py
+++ b/controllers/export.py
@@ -29,7 +29,12 @@ def export_xlsx(data_by_sheet):
 
         headers = list(rows[0].keys())
         for col_idx, header in enumerate(headers):
-            sheet.write(0, col_idx, header)
+            if header[:3] == "ROT":
+                cell_format = workbook.add_format()
+                cell_format.set_rotation(90)
+                sheet.write(0, col_idx, header[3:], cell_format)
+            else:
+                sheet.write(0, col_idx, header)
 
         for row_idx, row in enumerate(rows, start=1):
             for col_idx, header in enumerate(headers):
@@ -82,12 +87,73 @@ class ApExportController(http.Controller):
         """
         export commande
         """
-        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
-        file = {}
+        # un onglet par programme (subvention)
+        programmes = request.env["sale.project.subvention"].search([])
 
-        for project in projects:
-            file[project.name] = [{"test": "test"}]
+        file = {}
 
+        for programme in programmes:
+            # on créer l onglet du programme
+            file[programme.name] = []
+            page = file.get(programme.name)
+
+            # tout les projets sous ce programme pour la saison dont le devis est signé
+            projects = request.env["sale.project"].search(
+                [
+                    ("saison_id", "=", saison.id),
+                    ("project_subvention_id", "=", programme.id),
+                    ("sale_order_id.state", "=", "sale"),
+                ]
+            )
+            interventions = request.env["sale.intervention"].search(
+                [("project_id", "in", projects.ids)]
+            )
+            plant_data = {}
+            seeders_list = []
+            for intervention in interventions:
+                seeders_list.append(intervention.partner_id)
+                for sequence in intervention.plant_sequence_ids:
+                    plant = plant_data.setdefault(sequence.product_id, {})
+                    if plant.get(f"ROT{intervention.partner_id.name}"):
+                        plant[f"ROT{intervention.partner_id.name}"] += sequence.qty
+                    else:
+                        plant[f"ROT{intervention.partner_id.name}"] = sequence.qty
+
+            # format data for xls
+
+            # premiere ligne on va chercher les secteurs de plantation
+
+            first_line = {
+                "Article": "Secteur plantation",
+                "Nom latin": "",
+                "Nom de la catégorie": "",
+                "Numéro de pépiniesriste": "",
+            }
+
+            for seeder in set(seeders_list):
+                secteur_plat = (
+                    request.env["sale.project"]
+                    .search([("partner_id", "=", seeder.id)])
+                    .mapped("plant_sector_id")
+                    .mapped("name")
+                )
+                first_line[f"ROT{seeder.name}"] = ",".join(secteur_plat)
+            page.append(first_line)
+
+            for plant in plant_data:
+                line = {}
+                line.update(
+                    {
+                        "Article": plant.name,
+                        "Nom latin": plant.latin_name or "",
+                        "Nom de la catégorie": plant.categ_id.name or "",
+                        "Numéro de pépiniériste": plant.categ_id.nurseryman_id or "",
+                    }
+                )
+                qty = plant_data.get(plant)
+                for seeder in qty:
+                    line[seeder] = qty[seeder]
+                page.append(line)
         return file
 
     def get_fin_data(self, saison):
@@ -118,13 +184,18 @@ class ApExportController(http.Controller):
                         "Télephone": project.partner_id.phone or "",
                         "Email": project.partner_id.email or "",
                         "Commune intervention": inter.city or "",
-                        "Geo latitude": str(inter.latitude).replace(',', '.'),
-                        "Geo longitude": str(inter.longitude).replace(',', '.'),
+                        "Geo latitude": str(inter.latitude).replace(",", "."),
+                        "Geo longitude": str(inter.longitude).replace(",", "."),
                         "Bord de départementale": inter.near_road or "",
-                        "Forme juridique": project.partner_id.partner_company_type_id.name or "",
-                        "Type de culture": project.partner_id.culture_type_id.name or "",
-                        #todo format date
-                        "Date de première adhésion": project.partner_id.year_1st_membership or "",
+                        "Forme juridique": project.partner_id.partner_company_type_id.name
+                        or "",
+                        "Type de culture": project.partner_id.culture_type_id.name
+                        or "",
+                        "Date de première adhésion": (
+                            project.partner_id.year_1st_membership.strftime("%d-%m-%Y")
+                            if project.partner_id.year_1st_membership
+                            else ""
+                        ),
                         "Diffusion sur site internet": project.online or "",
                         "Longueur de haie": inter.intervention_length or "",
                         "Nombre de plants": inter.plant_qty or "",
@@ -134,7 +205,7 @@ class ApExportController(http.Controller):
                         "Implantation": inter.location_id.name or "",
                         "Objectif plantation": project.plant_goal_id.name or "",
                         "Avancement": project.admin_state_id.name,
-                        "Aides financières": self.get_financial_help(inter)
+                        "Aides financières": self.get_financial_help(inter),
                     }
 
                 file.get(sheet_name).append(project_data)
-- 
GitLab


From ed548b39e611563ea661cf359d011bea84d2f7df Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Tue, 20 May 2025 11:07:19 +0200
Subject: [PATCH 08/10] [ADD] finalisation

---
 controllers/export.py             | 63 +++++++++++++++++--------------
 wizard/ap_export_wizard.py        |  3 ++
 wizard/ap_export_wizard_views.xml |  4 +-
 3 files changed, 40 insertions(+), 30 deletions(-)

diff --git a/controllers/export.py b/controllers/export.py
index a4bbf75..4a3206b 100644
--- a/controllers/export.py
+++ b/controllers/export.py
@@ -1,8 +1,11 @@
-from odoo import http
-from odoo.http import request
-from odoo.addons.web.controllers.main import ExcelExport
+# Copyright 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
 import io
 import xlsxwriter
+from odoo import http
+from odoo.http import request
+
 
 
 def export_xlsx(data_by_sheet):
@@ -74,7 +77,6 @@ class ApExportController(http.Controller):
         return ""
 
     def get_nb_tree(self, inter_id):
-
         if inter_id.plant_sequence_ids:
             to_count = inter_id.plant_sequence_ids
         else:
@@ -90,12 +92,12 @@ class ApExportController(http.Controller):
         # un onglet par programme (subvention)
         programmes = request.env["sale.project.subvention"].search([])
 
+        # fichier final
         file = {}
 
         for programme in programmes:
-            # on créer l onglet du programme
-            file[programme.name] = []
-            page = file.get(programme.name)
+
+            page = []
 
             # tout les projets sous ce programme pour la saison dont le devis est signé
             projects = request.env["sale.project"].search(
@@ -109,6 +111,7 @@ class ApExportController(http.Controller):
                 [("project_id", "in", projects.ids)]
             )
             plant_data = {}
+            # sauvegarde de la liste des planteurs pour la premiere ligne
             seeders_list = []
             for intervention in interventions:
                 seeders_list.append(intervention.partner_id)
@@ -120,26 +123,6 @@ class ApExportController(http.Controller):
                         plant[f"ROT{intervention.partner_id.name}"] = sequence.qty
 
             # format data for xls
-
-            # premiere ligne on va chercher les secteurs de plantation
-
-            first_line = {
-                "Article": "Secteur plantation",
-                "Nom latin": "",
-                "Nom de la catégorie": "",
-                "Numéro de pépiniesriste": "",
-            }
-
-            for seeder in set(seeders_list):
-                secteur_plat = (
-                    request.env["sale.project"]
-                    .search([("partner_id", "=", seeder.id)])
-                    .mapped("plant_sector_id")
-                    .mapped("name")
-                )
-                first_line[f"ROT{seeder.name}"] = ",".join(secteur_plat)
-            page.append(first_line)
-
             for plant in plant_data:
                 line = {}
                 line.update(
@@ -154,6 +137,30 @@ class ApExportController(http.Controller):
                 for seeder in qty:
                     line[seeder] = qty[seeder]
                 page.append(line)
+
+            # si on des commandes pour ce programe on l ajoute au fichier
+            if page:
+                # on construit la premier ligne avec les secteurs
+                # de plantation par planteur
+
+                first_line = {
+                    "Article": "Secteur plantation",
+                    "Nom latin": "",
+                    "Nom de la catégorie": "",
+                    "Numéro de pépiniesriste": "",
+                }
+
+                for seeder in set(seeders_list):
+                    secteur_plat = (
+                        request.env["sale.project"]
+                        .search([("partner_id", "=", seeder.id)])
+                        .mapped("plant_sector_id")
+                        .mapped("name")
+                    )
+                    first_line[f"ROT{seeder.name}"] = ",".join(secteur_plat)
+                page.insert(0, first_line)
+                file[programme.name] = page
+
         return file
 
     def get_fin_data(self, saison):
@@ -266,7 +273,7 @@ class ApExportController(http.Controller):
 
     @http.route("/ap_export/xlsx_download", type="http", auth="user")
     def download_xlsx(self, wizard_id, **kwargs):
-        wizard = request.env["ap.export.wizard"].sudo().browse(int(wizard_id))
+        wizard = request.env["ap.export.wizard"].browse(int(wizard_id))
         filename = self.get_filename(wizard.export_type, wizard.saison_id)
 
         data_methods = {
diff --git a/wizard/ap_export_wizard.py b/wizard/ap_export_wizard.py
index c9945fc..59f3f6f 100644
--- a/wizard/ap_export_wizard.py
+++ b/wizard/ap_export_wizard.py
@@ -1,3 +1,6 @@
+# Copyright 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
 from odoo import models, fields
 
 
diff --git a/wizard/ap_export_wizard_views.xml b/wizard/ap_export_wizard_views.xml
index eca1364..abe37f5 100644
--- a/wizard/ap_export_wizard_views.xml
+++ b/wizard/ap_export_wizard_views.xml
@@ -34,7 +34,7 @@
             name="Export de données"
             parent="sale.sale_menu_root"
             sequence="7"
-            groups="base.group_system"
+            groups="sales_team.group_sale_salesman"
         />
 
         <menuitem
@@ -43,7 +43,7 @@
             name="Exporter"
             action="action_ap_export_wizard"
             sequence="10"
-            groups="base.group_system"
+            groups="sales_team.group_sale_salesman"
         />
 
     </data>
-- 
GitLab


From 2188bbaa636d7c5d23a736b571a3f858fc7b4188 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Wed, 21 May 2025 09:47:06 +0200
Subject: [PATCH 09/10] [FIX] typo

---
 controllers/export.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/controllers/export.py b/controllers/export.py
index 4a3206b..87c8926 100644
--- a/controllers/export.py
+++ b/controllers/export.py
@@ -147,7 +147,7 @@ class ApExportController(http.Controller):
                     "Article": "Secteur plantation",
                     "Nom latin": "",
                     "Nom de la catégorie": "",
-                    "Numéro de pépiniesriste": "",
+                    "Numéro de pépiniériste": "",
                 }
 
                 for seeder in set(seeders_list):
-- 
GitLab


From e7e269bc8eb19a06a65d55d28d518f57f0aa8616 Mon Sep 17 00:00:00 2001
From: Julien Ortet <julien@le-filament.com>
Date: Mon, 2 Jun 2025 11:56:27 +0200
Subject: [PATCH 10/10] [FIX] review

---
 controllers/export.py            | 54 ++++++++++++++++++++++++--------
 views/sale_intervention_view.xml |  1 +
 wizard/ap_export_wizard.py       |  1 +
 3 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/controllers/export.py b/controllers/export.py
index 87c8926..d056e26 100644
--- a/controllers/export.py
+++ b/controllers/export.py
@@ -7,7 +7,6 @@ from odoo import http
 from odoo.http import request
 
 
-
 def export_xlsx(data_by_sheet):
     """
     Génère un fichier XLSX avec plusieurs feuilles.
@@ -62,9 +61,24 @@ class ApExportController(http.Controller):
     def get_nb_staples(self, inter_id):
         staples = 0
         if inter_id.mulch_has_staples:
-            staples += inter_id.mulch_staples_qty * inter_id.mulch_qty
+
+            if inter_id.mulch_id:
+                if inter_id.mulch_id.nb_accesories and inter_id.mulch_id.nb_accesories > 0.0:
+                    mulch_qty = inter_id.mulch_id.nb_accesories
+                else:
+                    mulch_qty = 2
+                staples += inter_id.mulch_qty * mulch_qty
+
         if inter_id.mulch2_has_staples:
-            staples += inter_id.mulch2_staples_qty * inter_id.mulch2_qty
+
+            if inter_id.mulch2_id:
+                if inter_id.mulch2_id.nb_accesories and inter_id.mulch2_id.nb_accesories > 0.0:
+                    mulch2_qty = inter_id.mulch2_id.nb_accesories
+                else:
+                    mulch2_qty = 2
+                staples += inter_id.mulch2_qty * mulch2_qty
+
+        return staples if staples > 0 else ""
 
     def get_linked_contact(self, partner_id):
         if partner_id.child_ids:
@@ -76,14 +90,14 @@ class ApExportController(http.Controller):
             return ",".join([h.name for h in inter.financial_help_ids])
         return ""
 
-    def get_nb_tree(self, inter_id):
+    def get_nb_big_trees(self, inter_id):
         if inter_id.plant_sequence_ids:
             to_count = inter_id.plant_sequence_ids
         else:
             to_count = inter_id.plant_list_ids
 
         trees = to_count.search([("scale", "=", "big")])
-        return len(trees)
+        return len(trees) if len(trees) > 0 else ""
 
     def get_order_data(self, saison):
         """
@@ -170,7 +184,9 @@ class ApExportController(http.Controller):
         sheet_name = "suivi financeurs"
         file = {sheet_name: []}
 
-        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
+        projects = request.env["sale.project"].search(
+            [("saison_id", "=", saison.id), ("state", "=", "sale")]
+        )
 
         for project in projects:
             if project.intervention_ids:
@@ -203,11 +219,16 @@ class ApExportController(http.Controller):
                             if project.partner_id.year_1st_membership
                             else ""
                         ),
-                        "Diffusion sur site internet": project.online or "",
+                        "Diffusion sur site internet": dict(
+                            project._fields["online"].selection
+                        ).get(project.online)
+                        or "",
                         "Longueur de haie": inter.intervention_length or "",
                         "Nombre de plants": inter.plant_qty or "",
                         "Surface (en m²)": inter.surface or "",
-                        "Dont nombre total d'arbres": self.get_nb_tree(inter),
+                        "Dont nombre total d'arbres (grands)": self.get_nb_big_trees(
+                            inter
+                        ),
                         "Type d'intervention": inter.intervention_type_id.name or "",
                         "Implantation": inter.location_id.name or "",
                         "Objectif plantation": project.plant_goal_id.name or "",
@@ -215,7 +236,7 @@ class ApExportController(http.Controller):
                         "Aides financières": self.get_financial_help(inter),
                     }
 
-                file.get(sheet_name).append(project_data)
+                    file.get(sheet_name).append(project_data)
 
         return file
 
@@ -225,7 +246,9 @@ class ApExportController(http.Controller):
         """
         sheet_name = "suivi technique"
         file = {sheet_name: []}
-        projects = request.env["sale.project"].search([("saison_id", "=", saison.id)])
+        projects = request.env["sale.project"].search(
+            [("saison_id", "=", saison.id), ("state", "=", "sale")]
+        )
 
         for project in projects:
             if project.intervention_ids:
@@ -249,7 +272,9 @@ class ApExportController(http.Controller):
                         "Longueur de haie": inter.intervention_length or "",
                         "Nombre de plants": inter.plant_qty or "",
                         "Surface (en m²)": inter.surface or "",
-                        "Dont nombre total d'arbres": self.get_nb_tree(inter),
+                        "Dont nombre total d'arbres (grands)": self.get_nb_big_trees(
+                            inter
+                        ),
                         "Type d'intervention": inter.intervention_type_id.name or "",
                         "Paillage 1": inter.mulch_id.name or "",
                         "Paillage 2": inter.mulch2_id.name or "",
@@ -263,11 +288,14 @@ class ApExportController(http.Controller):
                         "Nombre d'agrafes": self.get_nb_staples(inter),
                         "Nombre de collerettes": inter.collarette_qty or "",
                         "Mois livraison": project.delivery_month or "",
-                        "Accueil démo": project.demo or "",
+                        "Accueil démo": dict(project._fields["demo"].selection).get(
+                            project.demo
+                        )
+                        or "",
                         "Commentaires": project.comment or "",
                     }
 
-                file.get(sheet_name).append(project_data)
+                    file.get(sheet_name).append(project_data)
 
         return file
 
diff --git a/views/sale_intervention_view.xml b/views/sale_intervention_view.xml
index 01f55dd..4b08390 100644
--- a/views/sale_intervention_view.xml
+++ b/views/sale_intervention_view.xml
@@ -217,6 +217,7 @@
                                 />
                                     <field name="qty" />
                                     <field name="is_local" />
+                                    <field name="scale" />
                                 </tree>
                             </field>
                             <field
diff --git a/wizard/ap_export_wizard.py b/wizard/ap_export_wizard.py
index 59f3f6f..0b36c9d 100644
--- a/wizard/ap_export_wizard.py
+++ b/wizard/ap_export_wizard.py
@@ -16,6 +16,7 @@ class ApExportWizard(models.TransientModel):
         ],
         required=True,
         default="order",
+        string="Type d'export"
     )
 
     saison_id = fields.Many2one(
-- 
GitLab