From 5765df18a8047174bd086bca3d1f996026033ff0 Mon Sep 17 00:00:00 2001
From: benjamin <benjamin@le-filament.com>
Date: Mon, 13 Dec 2021 18:34:01 +0100
Subject: [PATCH] [add] review all module to add calculation process, exo
 lines, template, scop_bordereau inheritance

---
 __init__.py                                   |   5 +-
 __manifest__.py                               |  15 +-
 datas/ir_sequence_data.xml                    |   9 +-
 datas/mail_data.xml                           |  63 ----
 models/__init__.py                            |   4 +
 models/res_partner.py                         |  15 +
 models/scop_bordereau.py                      |  57 +++
 models/scop_cotisation_cg.py                  |  34 ++
 models/scop_cotisation_cg_exo.py              | 328 +++++++++++++++++-
 models/scop_cotisation_cg_exo_line.py         |  38 ++
 security/ir.model.access.csv                  |   7 +-
 security/security_rules.xml                   |  29 +-
 templates/report_scop_exo.xml                 |  55 ++-
 views/res_partner.xml                         |  55 +++
 views/scop_bordereau_cg.xml                   | 224 +-----------
 views/scop_cotisation_cg_exo.xml              |  81 ++++-
 wizard/__init__.py                            |   4 +
 wizard/scop_bordereau_update_liasse_wizard.py |  18 +
 18 files changed, 694 insertions(+), 347 deletions(-)
 delete mode 100644 datas/mail_data.xml
 create mode 100644 models/res_partner.py
 create mode 100644 models/scop_bordereau.py
 create mode 100644 models/scop_cotisation_cg.py
 create mode 100644 models/scop_cotisation_cg_exo_line.py
 create mode 100644 views/res_partner.xml
 create mode 100755 wizard/__init__.py
 create mode 100644 wizard/scop_bordereau_update_liasse_wizard.py

diff --git a/__init__.py b/__init__.py
index dc5e6b6..f8a7089 100755
--- a/__init__.py
+++ b/__init__.py
@@ -1,4 +1,5 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
+# © 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
 from . import models
+from . import wizard
diff --git a/__manifest__.py b/__manifest__.py
index f4f578d..fa07e41 100755
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -11,17 +11,18 @@
         "cgscop_instance",
     ],
     "data": [
-        # SECURITY
-        # "security/security_rules.xml",
+        # security
+        "security/security_rules.xml",
         "security/ir.model.access.csv",
-        # DATAS
+        # datas
         "datas/exo_ur_notice_data.xml",
-        # "datas/mail_data.xml",
-        # "datas/ir_sequence_data.xml",
-        # VIEWS
+        "datas/ir_sequence_data.xml",
+        # views
+        "views/res_partner.xml",
+        "views/scop_bordereau_cg.xml",
         "views/scop_cotisation_cg_exo.xml",
         "views/scop_cotisation_cg_exo_ur_notice.xml",
-        # TEMPLATES
+        # templates
         "templates/report_scop_exo.xml"
     ]
 }
diff --git a/datas/ir_sequence_data.xml b/datas/ir_sequence_data.xml
index b4fc35a..3a5abd7 100644
--- a/datas/ir_sequence_data.xml
+++ b/datas/ir_sequence_data.xml
@@ -1,12 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 Le Filament
+     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
 <odoo>
     <data noupdate="1">
 
-        <record id="seq_scop_bordereau" model="ir.sequence">
-            <field name="name">SCOP EXONERATION Séquence</field>
+        <!-- Sequences for scop.cotisation.cg.exo -->
+        <record id="seq_scop_cotisation_cg_exo" model="ir.sequence">
+            <field name="name">CG Scop - Séquence Exonérations</field>
             <field name="code">scop.cotisation.cg.exo</field>
+            <field name="prefix">EXO-%(year)s-</field>
             <field name="padding">4</field>
-            <field name="prefix">EXO%(year)s</field>
             <field name="company_id" ref="base.main_company"/>
         </record>
 
diff --git a/datas/mail_data.xml b/datas/mail_data.xml
deleted file mode 100644
index 74c5b65..0000000
--- a/datas/mail_data.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
-    <data noupdate="0">
-
-    <!-- Template and notification section -->
-
-        <record id="email_template_cotisation_cg" model="mail.template">
-            <field name="name">CG Scop - Mail cotisations CG</field>
-            <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/>
-            <field name="email_from">${object.env.user.email_formatted |safe}</field>
-            <field name="partner_to">${object.get_recipients()}</field>
-            <field name="subject">Appel de cotisations ${object.year}</field>
-            <field name="body_html" type="html">
-                <div style="margin: 0px; padding: 0px;">
-                    <p style="margin: 0px; padding: 0px; font-size: 13px;">
-                        <p>Chère Coopératrice, Cher Coopérateur,</p>
-
-                        <p>Cotisation sur votre espace extranet : lien</p>
-
-                        <p>Nous vous prions d'agréer, Chère Coopératrice, Cher Coopérateur, nos sentiments les meilleurs.</p>
-
-                        <p>Pour la CG Scop,</p>
- 
-                        <p>JACQUES LANDRIOT</p>
-                        <p>Président</p>
-                    </p>
-                </div>
-            </field>
-            <field name="lang">${object.partner_id.lang}</field>
-            <field name="user_signature" eval="False"/>
-            <field name="auto_delete" eval="False"/>
-        </record>
-
-
-<!--        <record id="email_template_send_again" model="mail.template">-->
-<!--            <field name="name">CG Scop - Liste Ministere: Renvoyer le dossier</field>-->
-<!--            <field name="model_id" ref="cgscop_liste_ministere.model_liste_ministere"/>-->
-<!--            <field name="email_from">${object.user_id.email_formatted |safe}</field>-->
-<!--            <field name="partner_to">${object.get_recipients()}</field>-->
-<!--            <field name="subject">Dossier incomplet</field>-->
-<!--            <field name="body_html" type="html">-->
-<!--                <div style="margin: 0px; padding: 0px;">-->
-<!--                    <p style="margin: 0px; padding: 0px; font-size: 13px;">-->
-<!--                        <p>Madame, Monsieur,</p>-->
-<!-- -->
-<!--                        <p>Nous accusons réception de votre dossier de demande de renouvellement d’inscription sur la Liste Ministère, parvenu à la Confédération Générale des Scop qui n’est pas la année de clôture d’exercice demandé<br/>-->
-<!--                        Nous vous indiquons que votre dossier doit être complété par les pièces suivantes :</p>-->
-
-<!--                        <p>Nous vous invitons à nous retourner ces documents dans les meilleurs délais.</p>-->
-<!-- -->
-<!--                        <p>Dans l’attente de votre retour, nous restons à votre disposition pour toute précision utile.</p>-->
-<!-- -->
-<!--                        <p>Très cordialement</p>-->
-<!--                    </p>-->
-<!--                </div>-->
-<!--            </field>-->
-<!--            <field name="lang">${object.partner_id.lang}</field>-->
-<!--            <field name="user_signature" eval="False"/>-->
-<!--            <field name="auto_delete" eval="False"/>-->
-<!--        </record>-->
-
-    </data>
-</odoo>
diff --git a/models/__init__.py b/models/__init__.py
index b91725d..9bd897d 100755
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,5 +1,9 @@
 # © 2021 Le Filament (<http://www.le-filament.com>)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
+from . import res_partner
+from . import scop_bordereau
+from . import scop_cotisation_cg
 from . import scop_cotisation_cg_exo_ur_notice
 from . import scop_cotisation_cg_exo
+from . import scop_cotisation_cg_exo_line
diff --git a/models/res_partner.py b/models/res_partner.py
new file mode 100644
index 0000000..3d0a346
--- /dev/null
+++ b/models/res_partner.py
@@ -0,0 +1,15 @@
+# © 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from odoo import fields, models
+
+
+class ResPartner(models.Model):
+    _inherit = 'res.partner'
+
+    exo_ids = fields.One2many(
+        comodel_name='scop.cotisation.cg.exo',
+        inverse_name='partner_id',
+        domain=[('state', '=', 'done')],
+        string='Exonérations'
+    )
diff --git a/models/scop_bordereau.py b/models/scop_bordereau.py
new file mode 100644
index 0000000..644c7bf
--- /dev/null
+++ b/models/scop_bordereau.py
@@ -0,0 +1,57 @@
+# © 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from odoo import fields, models, api
+
+
+class ScopBordereau(models.Model):
+    _inherit = 'scop.bordereau'
+
+    exo_ids = fields.Many2many(
+        comodel_name='scop.cotisation.cg.exo',
+        relation='scop_bordereau_exo_rel',
+        column1='bordereau_id',
+        column2='exo_id',
+        string="Exonérations"
+    )
+    exo_count = fields.Integer(
+        string='Nombre d\'exonérations',
+        compute='_compute_exo_count'
+    )
+
+    # ------------------------------------------------------
+    # Compute fields
+    # ------------------------------------------------------
+    @api.multi
+    def _compute_exo_count(self):
+        for bdx in self:
+            bdx.exo_count = len(bdx.exo_ids)
+
+    # ------------------------------------------------------
+    # Button Action
+    # ------------------------------------------------------
+    def action_show_exo(self):
+        """
+        Ouvre la vue sur les exonérations liées
+        """
+        return {
+            'type': 'ir.actions.act_window',
+            'name': 'Exonérations',
+            'view_mode': 'tree,form',
+            'res_model': 'scop.cotisation.cg.exo',
+            'target': 'current',
+            'domain': [('id', 'in', self.exo_ids.ids)],
+        }
+
+    # ------------------------------------------------------
+    # Override parent
+    # ------------------------------------------------------
+    def update_bordereau_with_liasse(self):
+        """
+        Recrée les exonérations si nécessaire
+        :return:
+        """
+        super(ScopBordereau).update_bordereau_with_liasse()
+        if self.exo_ids:
+            for exo in self.exo_ids:
+                exo.create_exo_refund(self)
diff --git a/models/scop_cotisation_cg.py b/models/scop_cotisation_cg.py
new file mode 100644
index 0000000..3f8e53b
--- /dev/null
+++ b/models/scop_cotisation_cg.py
@@ -0,0 +1,34 @@
+# © 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
+
+
+class ScopCotisation(models.Model):
+    _inherit = "scop.cotisation.cg"
+
+    # ------------------------------------------------------
+    # Override parent
+    # ------------------------------------------------------
+    def create_bordereau(
+            self, member, nb_quarter, liasse=None, date=False, is_regul=False):
+        """
+        Surcharge la fonction parente pour créer les avoirs si il y a des exonérations
+        """
+        bordereau = super(ScopCotisation, self).create_bordereau(
+            member, nb_quarter, liasse=liasse, date=date, is_regul=is_regul
+        )
+        bdx_id = self.bordereau_ids.browse(bordereau)
+
+        # Search if exo
+        exo_ids = self.env['scop.cotisation.cg.exo'].search([
+            ('partner_id', '=', bdx_id.partner_id.id),
+            ('year_exo_start', '<=', bdx_id.year),
+            ('year_exo_end', '>=', bdx_id.year),
+            ('state', '=', 'done')
+        ])
+        if exo_ids:
+            for exo in exo_ids:
+                exo.create_exo_refund(bdx_id)
+
+        return bordereau
diff --git a/models/scop_cotisation_cg_exo.py b/models/scop_cotisation_cg_exo.py
index 6fc2bbf..75c55c9 100644
--- a/models/scop_cotisation_cg_exo.py
+++ b/models/scop_cotisation_cg_exo.py
@@ -1,9 +1,17 @@
 # © 2021 Le Filament (<http://www.le-filament.com>)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
-from datetime import datetime
+from datetime import datetime, date
 
 from odoo import models, fields, api
+from odoo.exceptions import ValidationError, UserError
+
+QUARTER_SELECTION = [
+        (1, '1er trimestre'),
+        (2, '2ème trimestre'),
+        (3, '3ème trimestre'),
+        (4, '4ème trimestre')
+]
 
 
 class ScopCotisationExo(models.Model):
@@ -12,10 +20,16 @@ class ScopCotisationExo(models.Model):
     _inherit = ['mail.thread', 'mail.activity.mixin']
     _order = 'date_request desc'
 
+    @api.model
+    def get_year_selection(self):
+        return [
+            (num, str(num)) for num in range((datetime.now().year + 3), (datetime.now().year - 10))]
+
     name = fields.Char(
         string='Nom',
         compute='_compute_name',
         search='_search_name')
+    number = fields.Char('Référence', readonly=1, index=True, default='New')
     partner_id = fields.Many2one(
         comodel_name='res.partner',
         string="Coopérative",
@@ -24,12 +38,6 @@ class ScopCotisationExo(models.Model):
         track_visibility='onchange')
     date_request = fields.Date('Date de la demande', required=True, track_visibility='onchange')
     motivation = fields.Text('Motivation de la SCOP')
-    type = fields.Selection([
-        ('exo', 'Exonération'),
-        ('adjustment', 'Ajustement')],
-        string='Type',
-        default='exo',
-        required=True)
     source = fields.Selection([
         ('scop', 'SCOP'),
         ('ur', 'UR')],
@@ -46,8 +54,8 @@ class ScopCotisationExo(models.Model):
         default='todo',
         track_visibility='onchange')
     final_notice = fields.Selection([
-        ('favorable', 'Faborable'),
-        ('unfavorable', 'Défaborable')],
+        ('favorable', 'Favorable'),
+        ('unfavorable', 'Défavorable')],
         string="Avis Final",
         track_visibility='onchange')
     ur_notice = fields.Many2one(
@@ -55,8 +63,49 @@ class ScopCotisationExo(models.Model):
         string="Avis UR",
         required=True,
         track_visibility='onchange')
-    date_exo_start = fields.Date('Début Exo', required=True)
-    date_exo_end = fields.Date('Fin Exo', required=True)
+    quarter_exo_start = fields.Selection(
+        selection=QUARTER_SELECTION,
+        string='Trimestre initial'
+    )
+    year_exo_start = fields.Selection(
+        selection=[(year, str(year)) for year in
+            range(2021, 2035)],
+        string='Année initiale'
+    )
+    quarter_exo_end = fields.Selection(
+        selection=QUARTER_SELECTION,
+        string='Trimestre final'
+    )
+    year_exo_end = fields.Selection(
+        selection=[(year, str(year)) for year in
+            range(2021, 2035)],
+        string='Année finale'
+    )
+    date_exo_start = fields.Date(
+        string='Date Début Exo',
+        compute='_compute_exo_dates',
+        track_visibility='onchange',
+        store=True
+    )
+    date_exo_end = fields.Date(
+        string='Date Fin Exo',
+        compute='_compute_exo_dates',
+        track_visibility='onchange',
+        store=True
+    )
+    nb_quarter = fields.Integer(
+        string='Nb trimestres exonérés',
+        compute='_compute_exo_dates',
+        store=True
+    )
+    type_exo = fields.Selection([
+        ('amount', 'Montant'),
+        ('percent', 'Pourcentage')],
+        string="Type d'exonération",
+        track_visibility='onchange'
+    )
+    amount_quarter = fields.Float('Montant souhaité pour les échéances')
+    percent_quarter = fields.Integer('Réduction appliquée aux échéances')
     date_exo_ok = fields.Date('Date Acceptation Exo')
     head_office_comment = fields.Text('Commentaire du bureau')
     note = fields.Text('Notes internes')
@@ -64,10 +113,64 @@ class ScopCotisationExo(models.Model):
         comodel_name='scop.instance',
         string="Instance"
     )
+    bordereau_ids = fields.Many2many(
+        comodel_name='scop.bordereau',
+        relation='scop_bordereau_exo_rel',
+        column2='bordereau_id',
+        column1='exo_id',
+        string="Bordereaux"
+    )
+    company_id = fields.Many2one(
+        comodel_name='res.company',
+        string='Company', change_default=True,
+        required=True, readonly=True,
+        default=lambda self: self.env['res.company']._company_default_get('scop.cotisation.cg.followup'))
+    exo_line_ids = fields.One2many(
+        comodel_name='scop.cotisation.cg.exo.line',
+        inverse_name='exo_id',
+        string='Ligne Exonération'
+    )
 
     # ------------------------------------------------------
     # Contraintes SQL
     # ------------------------------------------------------
+    @api.constrains('date_exo_start', 'date_exo_end')
+    def _check_dates(self):
+        for exo in self:
+            if exo.date_exo_start and exo.date_exo_end:
+                if exo.date_exo_start > exo.date_exo_end:
+                    raise ValidationError(
+                        "La date de fin doit êre supérieure à la date de début d'exonération.")
+            intersect_exo_ids = self.search([
+                ('partner_id', '=', exo.partner_id.id),
+                ('date_exo_end', '>=', exo.date_exo_start),
+                ('date_exo_start', '<=', exo.date_exo_end),
+                ('state', '=', 'done')
+            ])
+            if intersect_exo_ids:
+                raise ValidationError(
+                    "Les périodes d'exonérations ne peuvent pas se chevaucher.")
+
+    @api.constrains('percent_quarter')
+    def _check_description(self):
+        for exo in self:
+            if exo.percent_quarter < 0 or exo.percent_quarter > 100:
+                raise ValidationError(
+                    "La valeur de la réduction doit être comprise entre 1 et 100")
+
+    # ------------------------------------------------------
+    # Onchange fields
+    # ------------------------------------------------------
+    @api.onchange('instance_id')
+    def _onchange_instance_id(self):
+        if self.state in ('todo', 'waiting'):
+            if self.instance_id:
+                self.state = 'waiting'
+                if not self.number:
+                    prefix = 5 - len(str(self.id))
+                    self.number = 'EXO-' + str(datetime.now().year) + '-' + (prefix * '0') + str(self.id)
+            else:
+                self.state = 'todo'
 
     # ------------------------------------------------------
     # Compute fields
@@ -86,12 +189,215 @@ class ScopCotisationExo(models.Model):
             ('date_request', operator, value),
         ]
 
+    @api.depends(
+        'quarter_exo_start', 'year_exo_start',
+        'quarter_exo_end', 'year_exo_end',
+    )
+    @api.multi
+    def _compute_exo_dates(self):
+        for exo in self:
+            if exo.year_exo_start and exo.quarter_exo_start:
+                exo.date_exo_start = date(int(exo.year_exo_start), exo.quarter_exo_start * 3 - 2, 1)
+            if exo.year_exo_end and exo.quarter_exo_end:
+                end_day = 31 if exo.quarter_exo_end in (1, 4) else 30
+                exo.date_exo_end = date(int(exo.year_exo_end), exo.quarter_exo_end * 3, end_day)
+            if exo.date_exo_start and exo.date_exo_end:
+                exo.nb_quarter = int(
+                    ((exo.date_exo_end.year - exo.date_exo_start.year) * 12 +
+                      exo.date_exo_end.month - exo.date_exo_start.month + 1) / 3)
+
     # ------------------------------------------------------
     # Button functions
     # ------------------------------------------------------
+    def action_view_bordereau(self):
+        years = list(range(self.year_exo_start, self.year_exo_start + 1))
+        return {
+            'name': 'Bordereaux de cotisation - ' + self.partner_id.name,
+            'type': 'ir.actions.act_window',
+            'view_mode': 'tree,form',
+            'res_model': 'scop.bordereau',
+            'domain': [
+                ('partner_id', '=', self.partner_id.id),
+                ('year', 'in', years),
+            ],
+        }
+
+    def action_view_exo(self):
+        return {
+            'name': 'Bordereaux avec exonération - ' + self.partner_id.name,
+            'type': 'ir.actions.act_window',
+            'view_mode': 'tree,form',
+            'res_model': 'scop.bordereau',
+            'domain': [('id', 'in', self.bordereau_ids.ids)],
+        }
+
+    def create_lines(self):
+        self.exo_line_ids.unlink()
+        contrib_type = self.partner_id.get_partner_contribution_type()
+
+        # if exo is percent => affects percent to each line
+        if self.type_exo == 'percent':
+            for contrib in contrib_type:
+                self.exo_line_ids.create({
+                    'exo_id': self.id,
+                    'contribution_id': contrib.id,
+                    'percent': self.percent_quarter
+                })
+
+        # if exo is amount
+        if self.type_exo == 'amount':
+            # if various contribution type => affects prorate amount to each line
+            if len(contrib_type) > 1:
+                # Get last bdx for prorate calculation
+                last_bdx = self.env['scop.bordereau'].search([
+                    ('state', 'in', ['validated', 'paid']),
+                    ('partner_id', '=', self.partner_id.id)
+                ], order='date_cotisation desc', limit=1)
+                if last_bdx:
+                    last_amount = last_bdx.amount_total_cotiz
+                    amount_by_type = last_bdx.get_contribution_type()
+
+                    for contrib in contrib_type:
+                        for amount in amount_by_type:
+                            if contrib.id == amount.get('type_contribution_id')[0]:
+                                self.exo_line_ids.create({
+                                    'exo_id': self.id,
+                                    'contribution_id': contrib.id,
+                                    'amount': self.amount_quarter * amount.get('amount_total_signed') / last_amount
+                                })
+                else:
+                    for contrib in contrib_type:
+                        self.exo_line_ids.create({
+                            'exo_id': self.id,
+                            'contribution_id': contrib.id,
+                            'amount': round(self.amount_quarter / len(contrib_type), 2)
+                        })
+            else:
+                self.exo_line_ids.create({
+                    'exo_id': self.id,
+                    'contribution_id': contrib_type[0].id,
+                    'amount': self.amount_quarter
+                })
+
     def cancel_exo(self):
         self.state = 'cancel'
 
+    def validate_exo(self):
+        if not self.date_exo_ok:
+            raise UserError("Il est nécessaire de renseigner une date d'acceptation pour valider")
+        else:
+            # Unfavorable => nothing to do
+            if self.final_notice == 'unfavorable':
+                self.state = 'done'
+
+            # Favorable
+            elif self.final_notice == 'favorable':
+                bordereau_ids = self.env['scop.bordereau'].search([
+                    ('partner_id', '=', self.partner_id.id),
+                    ('year', 'in', list(range(self.year_exo_start, self.year_exo_end + 1))),
+                    ('state', '!=', 'cancel')
+                ], order='year')
+
+                # If no bdx on period => nothing to do
+                if not bordereau_ids:
+                    self.state = 'done'
+
+                else:
+                    for bdx in bordereau_ids:
+                        self.create_exo_refund(bdx)
+                    self.state = 'done'
+
+            else:
+                raise UserError("Il est nécessaire de renseigner un avis final pour valider")
+
+    # ------------------------------------------------------
+    # Override ORM
+    # ------------------------------------------------------
+    @api.model
+    def create(self, vals):
+        if vals.get('number', 'New' == 'New'):
+            vals['number'] = self.env['ir.sequence'].next_by_code('scop.cotisation.cg.exo') or 'New'
+        result = super(ScopCotisationExo, self).create(vals)
+        return result
+
     # ------------------------------------------------------
     # Business functions
     # ------------------------------------------------------
+    def get_quarters(self):
+        """
+        Get quarters by year
+        @return dict(year, [quarters])
+        """
+        quarters = {}
+        if self.year_exo_start == self.year_exo_end:
+            quarters[self.year_exo_start] = range(self.quarter_exo_start, self.quarter_exo_end + 1)
+        else:
+            for year in range(self.year_exo_start, self.year_exo_end + 1):
+                if year == self.year_exo_start:
+                    quarters[str(year)] = list(range(self.quarter_exo_start, 5))
+                elif year == self.year_exo_end:
+                    quarters[str(year)] = list(range(1, self.quarter_exo_end + 1))
+                else:
+                    quarters[str(year)] = list(range(1, 5))
+        return quarters
+
+    def create_exo_refund(self, bordereau_id):
+        bordereau_id.ensure_one()
+        exo_quarters = self.get_quarters()
+        # check exo period
+        exo_period = exo_quarters[bordereau_id.year]
+
+        # Quarter loop
+        for quarter in exo_period:
+            # Get all invoices on specific quarter
+            amount_period = bordereau_id.invoice_ids.read_group(
+                [('bordereau_id', '=', bordereau_id.id), ('cotiz_quarter', '=', quarter)],
+                ['type_contribution_id', 'amount_total_signed'],
+                ['type_contribution_id'])
+
+            # Get bdx schedule
+            base_cotiz = bordereau_id.base_cotisation_cg
+            bdx_quarters = [base_cotiz.trimester_1, base_cotiz.trimester_2,
+                            base_cotiz.trimester_3, base_cotiz.trimester_4]
+
+            # Contribution type loop
+            for amount_type in amount_period:
+                inv_type = amount_type.get('type_contribution_id')[0]
+                inv_amount = amount_type.get('amount_total_signed')
+                # Get invoice on this type (for refund creation)
+                inv_id = bordereau_id.invoice_ids.filtered(lambda i: i.type_contribution_id.id == inv_type)[0]
+
+                exo_line_id = self.exo_line_ids.filtered(lambda l: l.contribution_id.id == inv_type)
+
+                if self.type_exo == 'percent':
+                    exo_amount = round(inv_amount * (1 - exo_line_id.percent / 100), 2)
+                elif self.type_exo == 'amount':
+                    exo_amount = round(inv_amount - exo_line_id.amount, 2)
+                else:
+                    raise UserError("Erreur, le type d'exonération n'est pas configuré")
+
+                # Create refund
+                member_refund = bordereau_id.invoice_ids.create({
+                    'partner_id': bordereau_id.partner_id.id,
+                    'liasse_fiscale_id': bordereau_id.liasse_fiscale_id.id,
+                    'type': 'out_refund',
+                    'year': bordereau_id.year,
+                    'is_contribution': True,
+                    'type_contribution_id': inv_type,
+                    'journal_id': inv_id.journal_id.id,
+                    'state': 'draft',
+                    'account_id': inv_id.account_id.id,
+                    'payment_mode_id': bordereau_id.payment_mode_id.id,
+                    'date_invoice': self.date_exo_ok,
+                    'date_due': bdx_quarters[quarter - 1],
+                    'bordereau_id': bordereau_id.id,
+                    'cotiz_quarter': quarter,
+                })
+                member_refund.invoice_line_ids.create({
+                    'invoice_id': member_refund.id,
+                    'product_id': inv_id.invoice_line_ids[0].product_id.id,
+                    'account_id': inv_id.invoice_line_ids[0].account_id.id,
+                    'name': inv_id.invoice_line_ids[0].product_id.name + " T" + str(quarter),
+                    'price_unit': exo_amount
+                })
+                self.update({'bordereau_ids': [(4, bordereau_id.id)]})
diff --git a/models/scop_cotisation_cg_exo_line.py b/models/scop_cotisation_cg_exo_line.py
new file mode 100644
index 0000000..4ba250c
--- /dev/null
+++ b/models/scop_cotisation_cg_exo_line.py
@@ -0,0 +1,38 @@
+# © 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from datetime import datetime, date
+
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError, UserError
+
+
+class ScopCotisationExoLine(models.Model):
+    _name = "scop.cotisation.cg.exo.line"
+    _description = "Ligne Exoneration CG"
+    _order = "create_date desc, contribution_id"
+
+    exo_id = fields.Many2one(
+        comodel_name='scop.cotisation.cg.exo',
+        string="Exonération",
+        required=True)
+    contribution_id = fields.Many2one(
+        comodel_name='scop.contribution.type',
+        required=True
+    )
+    amount = fields.Float('Montant souhaité')
+    percent = fields.Integer('% réduction')
+    type_exo = fields.Selection([
+        ('amount', 'Montant'),
+        ('percent', 'Pourcentage')],
+        string="Type d'exonération",
+        related='exo_id.type_exo'
+    )
+
+    # ------------------------------------------------------
+    # Onchange fields
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Compute fields
+    # ------------------------------------------------------
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index f49651d..1ebc59b 100755
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -1,5 +1,8 @@
 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_scop_cotisation_cg_exo,access_scop_cotisation_cg_exo,model_scop_cotisation_cg_exo,account.group_account_manager,1,1,1,1
+access_scop_cotisation_cg_exo_user,access_scop_cotisation_cg_exo_user,model_scop_cotisation_cg_exo,base.group_user,1,0,0,0
+access_scop_cotisation_cg_exo_manager,access_scop_cotisation_cg_exo_manager,model_scop_cotisation_cg_exo,account.group_account_manager,1,1,1,0
 admin_access_scop_cotisation_cg_exo,admin_access_scop_cotisation_cg_exo,model_scop_cotisation_cg_exo,cgscop_partner.group_cg_administrator,1,1,1,1
-access_scop_cotisation_cg_exo_ur_notice,access_scop_cotisation_cg_exo_ur_notice,model_scop_cotisation_cg_exo_ur_notice,account.group_account_manager,1,1,1,1
+access_scop_cotisation_cg_exo_line_user,access_scop_cotisation_cg_exo_line_user,model_scop_cotisation_cg_exo_line,base.group_user,1,0,0,0
+access_scop_cotisation_cg_exo_line_manager,access_scop_cotisation_cg_exo_line_manager,model_scop_cotisation_cg_exo_line,account.group_account_manager,1,1,1,1
+access_scop_cotisation_cg_exo_ur_notice,access_scop_cotisation_cg_exo_ur_notice,model_scop_cotisation_cg_exo_ur_notice,account.group_account_manager,1,1,1,0
 admin_access_scop_cotisation_cg_exo_ur_notice,admin_access_scop_cotisation_cg_exo_ur_notice,model_scop_cotisation_cg_exo_ur_notice,cgscop_partner.group_cg_administrator,1,1,1,1
diff --git a/security/security_rules.xml b/security/security_rules.xml
index ebb8d7e..bb99099 100644
--- a/security/security_rules.xml
+++ b/security/security_rules.xml
@@ -1,29 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2020 Le Filament
-     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
-
 <odoo>
-    <data noupdate="0">
 
-        <record id="cg_cotisation_cg_rule" model="ir.rule">
-            <field name="name">Cotisations consultables que pour sa société</field>
-            <field name="model_id" ref="cgscop_cotisation_cg.model_scop_cotisation_cg"/>
+    <data noupdate="1">
+        <record id="invoice_comp_rule" model="ir.rule">
+            <field name="name">CG Scop - Cotisation - Exonérations multi-company</field>
+            <field name="model_id" ref="model_scop_cotisation_cg_exo"/>
+            <field name="global" eval="True"/>
             <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
-            <field name="perm_read" eval="True"/>
-            <field name="perm_write" eval="True"/>
-            <field name="perm_create" eval="True"/>
-            <field name="perm_unlink" eval="True"/>
         </record>
-
-        <record id="cg_cotisation_cg_rule" model="ir.rule">
-            <field name="name">Bordereaux consultables que pour sa société</field>
-            <field name="model_id" ref="cgscop_cotisation_cg.model_scop_bordereau"/>
-            <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
-            <field name="perm_read" eval="True"/>
-            <field name="perm_write" eval="True"/>
-            <field name="perm_create" eval="True"/>
-            <field name="perm_unlink" eval="True"/>
-        </record>
-
     </data>
-</odoo>
\ No newline at end of file
+</odoo>
diff --git a/templates/report_scop_exo.xml b/templates/report_scop_exo.xml
index a452d1b..e64c7d9 100644
--- a/templates/report_scop_exo.xml
+++ b/templates/report_scop_exo.xml
@@ -18,29 +18,66 @@
                         </div>
                     </div>
                     <div class="row">
-                        <div class="col-12 text-center">
+                        <div class="col-12 text-center mb32">
                             <h2 style="color: #E5074D;">
-                                Appel de cotisation <span t-esc="str(o.year)"/>
+                                Demande d'exonération de cotisations
                             </h2>
                         </div>
                     </div>
                     <div class="row">
-                        <div class="col-12 mb16">
+                        <div class="col-12 mb32">
                             <p>
-                                Paris, le <span t-esc="o.date_cotisation" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/>
+                                Paris, le <span t-esc="o.date_exo_ok" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/>
                             </p>
                             <p>
                                 N° adhérent : <t t-esc="str(o.partner_id.member_number_int)"/><br/>
-                                Objet : <span t-field="o.type"/><br/>
+                                N° exonération : <span t-esc="o.number"/>
                             </p>
                         </div>
                     </div>
                     <div class="row mb16">
                         <div class="col-12" style="test-align: justify;">
-                            <p>Chère Coopératrice,</p>
-                            <p>Pour faire suite à votre dernier courrier, Le Bureau de la Direction Nationale qui s’est réuni le «Demande_Exonération_Date_Acceptation» a décidé de vous accorder une exonération exceptionnelle de vos cotisations confédérales.</p>
-                            <p>Le Bureau prend donc acte des modalités suivantes : Exonération des cotisations du 4ème trimestre 2019.</p>
-                            <p>Je vous prie de croire, chère Coopératrice, en l’assurance de toute ma considération coopérative.</p>
+                            <p class="mb32">Cher Coopérateur,</p>
+                            <!-- Avis Favorable-->
+                            <t t-if="o.final_notice == 'favorable'">
+                                <p>Pour faire suite à votre dernier courrier, Le Bureau de la Direction Nationale qui s’est réuni le<span t-esc="o.instance_id.date" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/> a décidé de vous accorder une exonération exceptionnelle de vos cotisations confédérales.</p>
+                                <p>
+                                    Le Bureau prend donc acte des modalités suivantes :
+                                </p>
+                                <p style="padding: 15px;">
+                                    <!-- Exonération totale-->
+                                    <t t-if="(o.type_exo == 'percent' and o.percent_quarter == 100) or (o.type_exo == 'amount' and o.amount_quarter == 0)">
+                                        Exonération totale des cotisations pour la période du
+                                    </t>
+                                    <t t-else="">
+                                        <!-- Réduction des échéances-->
+                                        <t t-if="o.type_exo == 'percent'">
+                                            Exonération de <span t-field="o.percent_quarter"/>% du montant des échéances trimestrielles sur la période du
+                                        </t>
+                                        <!-- Montant fixe des échéances-->
+                                        <t t-if="o.type_exo == 'amount'">
+                                            Un montant des prochaines échéances fixé à <span t-field="o.amount_quarter"/>€ pour la période du
+                                        </t>
+                                    </t>
+
+                                    <strong>
+                                        <t t-if="o.nb_quarter == 1"><span t-field="o.quarter_exo_start"/> <span t-field="o.year_exo_start"/>.</t>
+                                        <t t-else="">
+                                            <span t-field="o.quarter_exo_start"/> <span t-field="o.year_exo_start"/> au <span t-field="o.quarter_exo_end"/> <span t-field="o.year_exo_end"/>.
+                                        </t>
+                                    </strong>
+                                </p>
+                            </t>
+
+                            <!-- Avis Défavorable-->
+                            <t t-if="o.final_notice == 'unfavorable'">
+                                <p>Pour faire suite à votre dernier courrier, Le Bureau de la Direction Nationale qui s’est réuni le<span t-esc="o.instance_id.date" t-options="{'widget': 'date', 'format': 'd MMMM YYYY'}"/> a décidé de na pas vous accorder une exonération exceptionnelle de vos cotisations confédérales pour la raison suivante : </p>
+                                <p>
+                                    <t t-esc="o.head_office_comment"/>
+                                </p>
+                            </t>
+
+                            <p>Je vous prie de croire, cher Coopérateur, en l’assurance de toute ma considération coopérative.</p>
                             <p>Le Président</p>
                             <p>Jacques LANDRIOT</p>
                             <p>Copie : <t t-esc="o.partner_id.ur_id.name"/></p>
diff --git a/views/res_partner.xml b/views/res_partner.xml
new file mode 100644
index 0000000..d857daa
--- /dev/null
+++ b/views/res_partner.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 Le Filament
+     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+<odoo>
+    <data>
+
+        <record id="scop_contact_view_form_inherit" model="ir.ui.view">
+            <field name="name">scop.contact.view.form.inherit</field>
+            <field name="model">res.partner</field>
+            <field name="inherit_id" ref="cgscop_partner.scop_contact_view_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//page[@name='scop_contribution']" position="after">
+                    <page name='scop_exo' string="Exonérations" attrs="{'invisible': ['|', ('is_cooperative', '!=', True), ('project_status', '!=', '6_suivi')]}">
+                        <field name="exo_ids" mode="tree,form" readonly="1">
+                            <tree create="false" edit="false" delete="false" default_order="date desc">
+                                <field name="date_request"/>
+                                <field name="source"/>
+                                <field name="date_exo_start"/>
+                                <field name="date_exo_end"/>
+                                <field name="ur_notice"/>
+                                <field name="final_notice"/>
+                                <field name="date_exo_ok"/>
+                            </tree>
+                            <form>
+                                <group string="Demande initiale">
+                                    <group name="request">
+                                        <field name="date_request"/>
+                                        <field name="source"/>
+                                        <field name="ur_notice" options="{'no_create': True, 'no_open': True}"/>
+                                    </group>
+                                    <group>
+                                        <field name="date_exo_start"/>
+                                        <field name="date_exo_end"/>
+                                    </group>
+                                </group>
+                                <strong><label for="motivation"/></strong>
+                                <field name="motivation" placeholder=""/>
+                                <group string="Bureau">
+                                    <group>
+                                        <field name="date_exo_ok"/>
+                                    </group>
+                                    <group>
+                                        <field name="final_notice"/>
+                                    </group>
+                                </group>
+                                <strong><label for="head_office_comment"/></strong>
+                                <field name="head_office_comment" placeholder="Commentaire du bureau"/>
+                            </form>
+                        </field>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+    </data>
+</odoo>
\ No newline at end of file
diff --git a/views/scop_bordereau_cg.xml b/views/scop_bordereau_cg.xml
index 3b5fe37..8ffb246 100644
--- a/views/scop_bordereau_cg.xml
+++ b/views/scop_bordereau_cg.xml
@@ -4,225 +4,19 @@
 <odoo>
     <data>
 
-        <!-- Form -->
-        <record id="scop_bordereau_form_view" model="ir.ui.view">
-            <field name="name">scop.bordereau.form</field>
+        <!-- Inherit Form View to Modify it -->
+        <record id="scop_bordereau_exo_form_view" model="ir.ui.view">
+            <field name="name">scop.bordereau.exo.form</field>
             <field name="model">scop.bordereau</field>
+            <field name="inherit_id" ref="cgscop_cotisation_cg.scop_bordereau_form_view"/>
             <field name="arch" type="xml">
-                <form create="false" string="Bordereaux">
-                    <header>
-                        <field name="state" widget="statusbar" clickable="False" readonly="True"/>
-                        <button name="validate_bordereau" class="oe_highlight" string="Valider le bordereau" type="object" states="new" confirm="Confirmer la validation du bordereau ?"/>
-                        <button name="print_bordereau" string="Imprimer" type="object" states="validated,paid"/>
-                        <button name="action_send_email" string="Envoyer par mail" type="object" states="validated"/>
-                        <button name="update_cotiz_and_lines" class="btn-info" string="Mettre à jour les cotisations" type="object" states="new"/>
-                        <button name="%(cgscop_cotisation_cg.scop_cotisation_regul_wizard_act_window)d"
-                                class="btn-warning"
-                                string="Effectuer une Régularisation"
-                                type="action"
-                                states="validated,paid"/>
-                        <button name="%(cgscop_bordereau_report_refund)d"
-                                class="btn-info"
-                                string="Imprimer l'avoir"
-                                type="action"
-                                states="cancel"/>
-                    </header>
-                    <sheet>
-                        <div class="oe_button_box" name="button_box">
-                            <button class="oe_stat_button" name="open_payment"
-                                    string="Paiements en cours" type="object"
-                                    attrs="{'invisible':['|', ('move_reconciled','=',True), ('state', '!=', 'validated')]}" icon="fa-university"/>
-                            <field name="move_reconciled" invisible="1"/>
-                        </div>
-                        <div class="oe_title">
-                            <h1>
-                                <field name="name"/>
-                            </h1>
-                        </div>
-                        <h4>
-                            Adhérent : <field name="partner_id"/> <br/>
-                            UR : <field name="partner_ur_id" readonly="True" options="{'no_open': True}"/>
-                        </h4>
-                        <field name="is_regul" invisible="1"/>
-                        <div class="alert alert-warning" role="alert" attrs="{'invisible': [('is_regul', '!=', True)]}">
-                            <strong>Bordereau annulé le : </strong><field name="date_regul" readonly="True"/><br/>
-                            <strong>Motif : </strong><field name="comment_regul" readonly="True"/><br/>
-                            <strong>Nouveau bordereau : </strong><field name="refund_id" readonly="True"/>
-                        </div>
-                        <group>
-                            <group>
-                                <field name="base_cotisation_cg"/>
-                                <field name="date_cotisation" attrs="{'readonly':[('state','!=','new')]}"/>
-                                <field name="payment_term_id" attrs="{'readonly':[('state','!=','new')]}"/>
-                                <field name="payment_mode_id" attrs="{'readonly':[('state','!=','new')]}"/>
-                                <field name="liasse_fiscale_id"/>
-                                <field name="type_liasse_fiscale" options="{'no_open': True}"/>
-                            </group>
-                            <group>
-                                <field name="year_liasse_retenue" attrs="{'readonly':[('state','!=','new')]}"/>
-                                <field name="type_assiette_retenu"/>
-                                <field name="montant_assiette"/>
-                                <field name="amount_total_cotiz"/>
-                                <field name="dureeExercice"/>
-                                <field name="nb_quarter" attrs="{'readonly':[('state','!=','new')]}"/>
-                            </group>
-                            <hr/>
-                            <field name="invoice_ids" widget="one2many">
-                                <tree edit="false" create="false" delete="false">
-                                    <field name="type_contribution_id"/>
-                                    <field name="date_invoice"/>
-                                    <field name="name"/>
-                                    <field name="amount_total_signed" string="Montant total" sum="Total"/>
-                                    <field name="residual_signed" string="Montant dû" sum="Total"/>
-                                    <field name="state"/>
-                                    <button name="view_cotiz" string="Afficher" type="object" icon="fa-pencil-square-o"/>
-                                </tree>
-                                <form>
-                                    <group>
-                                        <field name="type_contribution_id"/>
-                                        <field name="date_invoice"/>
-                                        <field name="name"/>
-                                        <field name="amount_cg_calculated"/>
-                                        <field name="amount_total_signed"/>
-                                        <field name="residual_signed"/>
-                                        <field name="nb_quarter" />
-                                        <field name="state"/>
-                                    </group>
-                                </form>
-                            </field>
-                        </group>
-                        <notebook>
-                            <page string="Cotisation CG Scop">
-                                <group col="2">
-                                    <group string="Données liasse fiscale">
-                                        <field name="type_assiette_connu"/>
-                                        <field name="ca_connu"/>
-                                        <field name="va_connu"/>
-                                    </group>
-                                    <group string="Données pour le calcul">
-                                        <field name="type_assiette_retenu"/>
-                                        <field name="ca_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="va_cg_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                    </group>
-                                </group>
-                            </page>
-                            <page string="Cotisation Fédé de la com">
-                                <group col="2">
-                                    <group string="Données liasse fiscale">
-                                        <field name="va_connu"/>
-                                        <field name="staff_average_connu"/>
-                                        <field name="staff_count_connu"/>
-                                    </group>
-                                    <group string="Données pour le calcul">
-                                        <field name="va_fede_com_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="staff_average_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="staff_fede_com_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                    </group>
-                                </group>
-                            </page>
-                            <page string="Cotisation UR">
-                                <group col="2">
-                                    <group string="Données liasse fiscale">
-                                        <field name="net_results_connu"/>
-                                        <field name="staff_shareholder_count_connu"/>
-                                        <field name="staff_count_connu"/>
-                                        <field name="wage_cg_connu"/>
-                                    </group>
-                                    <group string="Données pour le calcul">
-                                        <field name="net_results_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="staff_shareholder_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="staff_ur_med_count_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                        <field name="wage_cg_retenu" attrs="{'readonly':[('state','!=','new')]}"/>
-                                    </group>
-                                </group>
-                            </page>
-                        </notebook>
-                    </sheet>
-                    <div class="oe_chatter">
-                        <field name="message_follower_ids" widget="mail_followers"/>
-                        <field name="message_ids" widget="mail_thread"/>
-                    </div>
-                </form>
-            </field>
-        </record>
+                <xpath expr="//button[@name='action_show_cotiz']" position="after">
+                    <button name="action_show_exo" type="object" class="oe_stat_button" icon="fa-list" attrs="{'invisible':[('exo_count','=',0)]}">
+                        <field string="Exonération(s)" name="exo_count" widget="statinfo"/>
+                    </button>
+                </xpath>
 
-        <!-- Tree -->
-        <record id="scop_bordereau_tree_view" model="ir.ui.view">
-            <field name="name">scop.bordereau.tree</field>
-            <field name="model">scop.bordereau</field>
-            <field name="arch" type="xml">
-                <tree decoration-info="state == 'new'" create="false" string="Bordereaux">
-                    <field name="member_number"/>
-                    <field name="partner_id"/>
-                    <field name="partner_ur_id"/>
-                    <field name="year"/>
-                    <field name="state"/>
-                    <field name="type_assiette_retenu"/>
-                    <field name="montant_assiette"/>
-                    <field name="amount_total_cotiz" sum="Total"/>
-                    <field name="amount_residual" sum="Total"/>
-                </tree>
             </field>
         </record>
-
-        <!-- Search -->
-        <record id="scop_bordereau_search_view" model="ir.ui.view">
-            <field name="name">scop.bordereau.search</field>
-            <field name="model">scop.bordereau</field>
-            <field name="arch" type="xml">
-                <search string="Bordereaux">
-                    <field name="partner_id" string="Adhérent"/>
-                    <field name="member_number" string="N° Adhérent"/>
-                    <field name="name" string="Référence du bordereau"/>
-                    <field name="year" string="Année de cotisation"/>
-                    <filter name="state_new" string="Brouillon"
-                            domain="[('state', '=', 'new')]"/>
-                    <filter name="state_validated" string="Validé"
-                            domain="[('state', '=', 'validated')]"/>
-                    <filter name="state_paid" string="Payé"
-                            domain="[('state', '=', 'paid')]"/>
-                    <separator/>
-                    <filter name="current_year" string="Campagne année en cours"
-                            domain="[('year', '=', (context_today()).strftime('%Y'))]"/>
-                    <filter name="previous_year" string="Campagne année précédente"
-                            domain="[('year', '=', (context_today()-datetime.timedelta(weeks=52)).strftime('%Y'))]"/>
-                    <filter name="next_year" string="Campagne année suivante"
-                            domain="[('year', '=', (context_today()+datetime.timedelta(weeks=52)).strftime('%Y'))]"/>
-                    <separator/>
-                    <filter name="bordereau_equal_zero" string="Cotisation(s) égale(s) à zéro"
-                            domain="[('amount_total_cotiz', '=', 0)]"/>
-                    <filter name="assiette_equal_zero" string="Assiette(s) égale(s) à zéro"
-                            domain="[('montant_assiette', '=', 0)]"/>
-                    <separator/>
-                    <filter name="4_quarter" string="Sur 4 trimestres" domain="[('nb_quarter', '=', '4')]"/>
-                    <filter name="3_quarter" string="Sur 3 trimestres" domain="[('nb_quarter', '=', '3')]"/>
-                    <filter name="2_quarter" string="Sur 2 trimestres" domain="[('nb_quarter', '=', '2')]"/>
-                    <filter name="1_quarter" string="Sur 1 trimestre" domain="[('nb_quarter', '=', '1')]"/>
-                    <separator/>
-                    <filter name="is_ca" string="Assiette CA" domain="[('type_assiette_retenu', '=', 'ca')]"/>
-                    <filter name="is_va" string="Assiette VA" domain="[('type_assiette_retenu', '=', 'va')]"/>
-                    <group string="Group By">
-                        <filter name="by_year" string="Année de cotisation" context="{'group_by':'year'}"/>
-                    </group>
-                </search>
-            </field>
-        </record>
-
-        <!-- Action -->
-        <record id="scop_bordereau_act_window" model="ir.actions.act_window">
-            <field name="name">Bodereaux</field>
-            <field name="type">ir.actions.act_window</field>
-            <field name="res_model">scop.bordereau</field>
-            <field name="view_mode">tree,form</field>
-            <field name="context">{'search_default_current_year': 1}</field>
-        </record>
-
-        <!-- Menu -->
-        <menuitem name="Bordereaux"
-                  id="scop_bordereau_menu"
-                  parent="cgscop_cotisation.menu_scop_cotisation"
-                  action="scop_bordereau_act_window"
-                  sequence="30"/>
-
     </data>
 </odoo>
\ No newline at end of file
diff --git a/views/scop_cotisation_cg_exo.xml b/views/scop_cotisation_cg_exo.xml
index b663383..d7c82d0 100644
--- a/views/scop_cotisation_cg_exo.xml
+++ b/views/scop_cotisation_cg_exo.xml
@@ -14,15 +14,16 @@
                     <field name="partner_id"/>
                     <field name="name"/>
                     <field name="date_request"/>
-                    <filter name="filter_exo" domain="[('type', '=', 'exo')]" string="Exonérations"/>
-                    <filter name="filter_adjustment" domain="[('type', '=', 'adjustment')]" string="Ajustements"/>
+                    <separator/>
+                    <filter name="on_going" domain="[('state', 'in', ('todo', 'waiting'))]" string="En cours de traitement"/>
+                    <filter name="is_done" domain="[('state', '=', 'done')]" string="Traité"/>
+                    <filter name="is_cancel" domain="[('state', '=', 'cancel')]" string="Annulé"/>
                     <separator/>
                     <filter name="is_favorable" domain="[('final_notice', '=', 'favorable')]" string="Favorable"/>
                     <filter name="is_unfavorable" domain="[('final_notice', '=', 'unfavorable')]" string="Défavorable"/>
                     <group expand="0" name="group_by" string="Group By">
                         <filter name="group_state" string="Statut" context="{'group_by' : 'state'}"/>
                         <filter name="group_instance_id" string="Instance" context="{'group_by': 'instance_id'}"/>
-                        <filter name="group_by_type" string="Type" context="{'group_by': 'type'}"/>
                     </group>
                 </search>
             </field>
@@ -36,7 +37,6 @@
                 <tree string="Exonerations">
                     <field name="date_request"/>
                     <field name="partner_id"/>
-                    <field name="type"/>
                     <field name="state"/>
                     <field name="date_exo_start"/>
                     <field name="date_exo_end"/>
@@ -53,32 +53,89 @@
             <field name="arch" type="xml">
                 <form string="Exonerations">
                     <header>
+                        <button name="validate_exo" type="object" string="Valider l'exonération" class="oe_highlight" confirm="Êtes-vous certain(e) de vouloir valider cette exonération ?" attrs="{'invisible': [('state', '!=', 'waiting')]}"/>
                         <button name="cancel_exo" type="object" string="Annuler" confirm="Êtes-vous certain(e) de vouloir annuler cette exonération ?" attrs="{'invisible': [('state', '=', 'cancel')]}"/>
-                        <field name="state" widget="statusbar" clickable="False"/>
+                        <field name="state" widget="statusbar" readonly="1" force_save="1" />
                     </header>
                     <sheet>
-                        <h1><field name="partner_id" options="{'no_create': True, 'no_open': True}"/></h1>
+                        <div class="oe_button_box" name="button_box">
+                            <button name="action_view_bordereau" type="object" icon="fa-files-o" attrs="{'invisible': [('state', '=', 'done')]}" string="Borderaux en cours"></button>
+                            <button name="action_view_exo" type="object" class="oe_stat_button" icon="fa-list" attrs="{'invisible': [('state', '!=', 'done')]}" string="Bordereaux Exonérés"></button>
+                        </div>
+                        <label for="partner_id" string="Coopérative"/>
+                        <h1><field name="partner_id" options="{'no_create': True}"/></h1>
+                        <label for="number" string="Référence de l'exonération"/>
+                        <h3><field name="number"/></h3>
                         <group string="Demande initiale">
                             <group name="request">
                                 <field name="date_request"/>
-                                <field name="type" />
                                 <field name="source"/>
+                            </group>
+                            <group>
                                 <field name="ur_notice" options="{'no_create': True, 'no_open': True}"/>
                             </group>
+                        </group>
+                        <strong><label for="motivation"/></strong>
+                        <field name="motivation" placeholder=""/>
+                        <group string="Données de calcul">
                             <group>
+                                <label for="quarter_exo_start" string="Début de l'exonération"/>
+                                <div>
+                                    <field name="quarter_exo_start" class="oe_inline" style="margin-right: 20px;" required="1"/>
+                                    <field name="year_exo_start" class="oe_inline"  required="1"/>
+                                </div>
+                                <label for="quarter_exo_end" string="Fin de l'exonération"/>
+                                <div>
+                                    <field name="quarter_exo_end" class="oe_inline" style="margin-right: 20px;" required="1"/>
+                                    <field name="year_exo_end" class="oe_inline"  required="1"/>
+                                </div>
+                                <separator></separator>
                                 <field name="date_exo_start"/>
                                 <field name="date_exo_end"/>
+                                <field name="nb_quarter"/>
+                            </group>
+                            <group>
+                                <field name="type_exo"/>
+                                <div class="alert alert-light oe_edit_only" role="alert" colspan="2" attrs="{'invisible': [('type_exo', '!=', 'amount')]}">
+                                    Le montant saisi dans le champ ci-dessous correspond au <strong>montant de l'échéanche souhaité</strong> pour les échéances exonérées.
+                                </div>
+                                <field name="amount_quarter" attrs="{'invisible': [('type_exo', '!=', 'amount')]}"/>
+
+                                <div class="alert alert-light oe_edit_only" role="alert" colspan="2" attrs="{'invisible': [('type_exo', '!=', 'percent')]}">
+                                    Le nombre saisi dans le champ ci-dessous correspond au <strong>pourcentage de réduction</strong> appliqué aux échéances.
+                                </div>
+                                <field name="percent_quarter" attrs="{'invisible': [('type_exo', '!=', 'percent')]}"/>
+                                <button name="create_lines" type="object" class="btn btn-info" string="Créer/Mettre à jour les lignes" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}"></button>
+
+                                <div colspan="2">
+                                    <field name="exo_line_ids" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}">
+                                        <tree create="0" delete="0" editable="top">
+                                            <field name="type_exo" invisible="1"/>
+                                            <field name="contribution_id" readonly="1" string="Type de cotisation" options="{'no_open': True}"/>
+                                            <field name="amount" attrs="{'readonly': [('type_exo', '!=', 'amount')], 'invisible': [('type_exo', '!=', 'amount')]}" sum="Total"/>
+                                            <field name="percent" attrs="{'readonly': [('type_exo', '!=', 'percent')], 'invisible': [('type_exo', '!=', 'percent')]}"/>
+                                        </tree>
+                                        <form>
+                                            <group>
+                                                <field name="type_exo" invisible="1"/>
+                                                <field name="contribution_id" readonly="1" string="Type de cotisation" options="{'no_open': True}"/>
+                                                <field name="amount" attrs="{'readonly': [('type_exo', '!=', 'amount')], 'invisible': [('type_exo', '!=', 'amount')]}"/>
+                                                <field name="percent" attrs="{'readonly': [('type_exo', '!=', 'percent')], 'invisible': [('type_exo', '!=', 'percent')]}"/>
+                                            </group>
+                                        </form>
+                                    </field>
+                                </div>
+
                             </group>
                         </group>
-                        <strong><label for="motivation"/></strong>
-                        <field name="motivation" placeholder=""/>
+
                         <group string="Bureau">
                             <group>
-                                <field name="date_exo_ok"/>
-                                <field name="final_notice"/>
+                                <field name="instance_id" options="{'no_create': True}" string="Instance de présentation"/>
                             </group>
                             <group>
-                                <field name="instance_id" options="{'no_create': True, 'no_open': True}"/>
+                                <field name="date_exo_ok"/>
+                                <field name="final_notice"/>
                             </group>
                         </group>
                         <strong><label for="head_office_comment"/></strong>
diff --git a/wizard/__init__.py b/wizard/__init__.py
new file mode 100755
index 0000000..39df84d
--- /dev/null
+++ b/wizard/__init__.py
@@ -0,0 +1,4 @@
+# © 2021 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from . import scop_bordereau_update_liasse_wizard
diff --git a/wizard/scop_bordereau_update_liasse_wizard.py b/wizard/scop_bordereau_update_liasse_wizard.py
new file mode 100644
index 0000000..792f58e
--- /dev/null
+++ b/wizard/scop_bordereau_update_liasse_wizard.py
@@ -0,0 +1,18 @@
+# Copyright 2021 Le Filament
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import models
+
+
+class ScopBordereauChangeLiasse(models.TransientModel):
+    _inherit = 'scop.bordereau.change.liasse.wizard'
+
+    # ------------------------------------------------------
+    # Override Parent
+    # ------------------------------------------------------
+    def update_liasse_fiscale(self):
+        res = super(ScopBordereauChangeLiasse, self).update_liasse_fiscale()
+        if self.bordereau_id.exo_ids:
+            for exo in self.bordereau_id.exo_ids:
+                exo.create_exo_refund(self.bordereau_id)
+        return res
-- 
GitLab