diff --git a/__init__.py b/__init__.py
index 128fa3f8ff8d7de77d5d026b933d37c8d3b76db1..a453b4b3a3656a314b8ca36c4d0b0f54f491c85e 100755
--- a/__init__.py
+++ b/__init__.py
@@ -1,7 +1,8 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
+# © 2022 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 report
 from . import wizard
 
 from odoo import api, SUPERUSER_ID
diff --git a/__manifest__.py b/__manifest__.py
index 2f7efcd22c929accdb6d1d82b742201b82fc6f5a..323070940c3d915ecb1cbe547b2bafea7c600e29 100755
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -1,3 +1,5 @@
+# © 2022 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 {
     "name": "CG SCOP - Cotisations",
     "summary": "CG SCOP - Cotisations",
@@ -24,6 +26,7 @@
         "views/res_config_settings.xml",
         "views/res_partner.xml",
         "views/scop_cotisation_task.xml",
+        "report/scop_contribution_report.xml",
     ],
     "qweb": [
         "static/src/xml/*.xml",
diff --git a/models/__init__.py b/models/__init__.py
index 1a0bf4d8dc8f0d671c7079b0fa32e101d572f6f7..419d51378ff1cf86ca82d88e686bdf688a159866 100755
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -6,6 +6,7 @@ from . import account_payment_order
 from . import chart_template
 from . import res_company
 from . import res_config_settings
+from . import res_partner
 from . import scop_contribution
 from . import scop_cotisation
 from . import scop_cotisation_task
diff --git a/models/res_partner.py b/models/res_partner.py
new file mode 100644
index 0000000000000000000000000000000000000000..67c5ab6ec7046cba252932d4083ecd6b1bdffd43
--- /dev/null
+++ b/models/res_partner.py
@@ -0,0 +1,14 @@
+# © 2020 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'
+
+    contribution_report_ids = fields.One2many(
+        comodel_name='scop.contribution.report',
+        inverse_name='partner_id',
+        string='Cotisations',
+    )
diff --git a/report/__init__.py b/report/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..530b04980c303e369063f0beec2f53cace402497
--- /dev/null
+++ b/report/__init__.py
@@ -0,0 +1,4 @@
+# © 2020 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from . import scop_contribution_report
diff --git a/report/scop_contribution_report.py b/report/scop_contribution_report.py
new file mode 100644
index 0000000000000000000000000000000000000000..23dedb4d5f8fcd3980afcb2b526b9600b172309c
--- /dev/null
+++ b/report/scop_contribution_report.py
@@ -0,0 +1,199 @@
+# © 2022 Le Filament (<http://www.le-filament.com>)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from odoo import tools
+from odoo import models, fields, api
+
+
+class ScopContributionReport(models.Model):
+    _name = "scop.contribution.report"
+    _description = "Vue cotisations"
+    _auto = False
+    _order = 'year desc, partner_id'
+
+    name = fields.Char(compute='_compute_name')
+    year = fields.Char('Année de cotisation')
+    type_contribution_id = fields.Many2one(
+        comodel_name="scop.contribution.type",
+        string="Type de cotisation",
+        readonly=True)
+    partner_id = fields.Many2one('res.partner', string='Partner', readonly=True)
+    amount_called = fields.Float('Montant Appelé')
+    amount_paid = fields.Float('Montant Payé')
+    amount_due = fields.Float('Montant Restant')
+    is_loss = fields.Boolean('Exonération/Perte')
+    payments = fields.Html('Paiements', compute="_compute_payments")
+
+    _depends = {
+        'account.invoice': [
+            'year', 'type_contribution_id', 'partner_id',
+            'amount_total_signed', 'residual_company_signed',
+            'state', 'type', 'is_contribution', 'refund_invoice_id'
+        ],
+    }
+
+    # ------------------------------------------------------
+    # Sub Query
+    # ------------------------------------------------------
+    def _select_invoice(self):
+        select_str = """
+            SELECT
+                CAST(i.year AS VARCHAR),
+                i.type_contribution_id,
+                i.partner_id,
+                SUM(i.amount_total_signed) AS amount_called,
+                SUM(i.amount_total_signed - i.residual_company_signed) AS amount_paid,
+                SUM(i.residual_company_signed) AS amount_due,
+                (CASE WHEN max(refund.id) IS NOT NULL THEN true ELSE FALSE END) AS is_loss
+        """
+        return select_str
+
+    def _from_invoice(self):
+        from_str = """
+                    FROM
+                        account_invoice i
+                    LEFT JOIN
+                        account_invoice refund ON refund.refund_invoice_id = i.id
+                """
+        return from_str
+
+    def _where_invoice(self):
+        where_str = """
+            WHERE
+                i.type = 'out_invoice' AND
+                i.state in ('open', 'paid') AND
+                i.is_contribution = true
+        """
+        return where_str
+
+    def _groupby_invoice(self):
+        groupby_str = """
+            GROUP BY
+                i.year,
+                i.type_contribution_id,
+                i.partner_id
+        """
+        return groupby_str
+
+    def _query_invoice(self):
+        query = "(%s %s %s %s)" % (
+            self._select_invoice(), self._from_invoice(),
+            self._where_invoice(), self._groupby_invoice())
+        return query
+
+    def _subquery(self):
+        return self._query_invoice()
+
+    # ------------------------------------------------------
+    # Main Query
+    # ------------------------------------------------------
+    def _select(self):
+        select_str = """
+            SELECT
+                ROW_NUMBER() OVER(ORDER BY c.year, c.partner_id) AS id,
+                c.year,
+                c.type_contribution_id,
+                c.partner_id,
+                SUM(c.amount_called) AS amount_called,
+                SUM(c.amount_paid) AS amount_paid,
+                SUM(c.amount_due) AS amount_due,
+                BOOL_OR(c.is_loss) AS is_loss
+            FROM (
+        """
+        return select_str
+
+    def _query_groupby(self):
+        return """
+        GROUP BY
+            c.year,
+            c.type_contribution_id,
+            c.partner_id
+        """
+
+    def _query_order(self):
+        return "ORDER BY c.year DESC"
+
+    def _query(self):
+        query = (
+                self._select() + self._subquery() + ') c ' +
+                self._query_groupby() + self._query_order()
+        )
+        return query
+
+    @api.model_cr
+    def init(self):
+        tools.drop_view_if_exists(self.env.cr, self._table)
+        self.env.cr.execute("""CREATE or REPLACE VIEW %s as (
+            %s
+        )""" % (
+            self._table, self._query()))
+
+    # ------------------------------------------------------
+    # Computed fields
+    # ------------------------------------------------------
+    @api.multi
+    def _compute_name(self):
+        for contribution in self:
+            contribution.name = (contribution.year + ' - ' +
+                                 contribution.type_contribution_id.name +
+                                 ' - ' + contribution.partner_id.name)
+
+    @api.multi
+    def _compute_payments(self):
+        for contribution in self:
+            contribution.payments = contribution._get_payment()
+
+    # ------------------------------------------------------
+    # Business functions
+    # ------------------------------------------------------
+    def _get_payment(self):
+        self.ensure_one()
+        invoice_ids = self.env['account.invoice'].search([
+            ('year', '=', int(self.year)),
+            ('partner_id', '=', self.partner_id.id),
+            ('type_contribution_id', '=', self.type_contribution_id.id),
+            ('type', '=', 'out_invoice')
+        ])
+        payment_ids = invoice_ids.mapped('payment_move_line_ids')
+        if payment_ids:
+            payments = payment_ids.mapped(lambda p: {
+                'date': p.date,
+                'name': p.name,
+                'ref': p.ref,
+                'credit': p.credit,
+            })
+            payments_html = self._get_html_table(payments)
+        else:
+            payments_html = "<p>Il n'y a pas de paiements associés à cette cotisation</p>"
+        return payments_html
+
+    def _get_html_table(self, payments):
+        """
+        :param payments: list of dict {date, name, ref, amount}
+        @return: HTML table with payments
+        """
+        start_html = """
+            <table class='table table-sm table-striped table-hover table-bordered'>
+                <thead><tr>
+                    <th>Date</th>
+                    <th>Libellé</th>
+                    <th>Référence</th>
+                    <th>Montant</th>
+                </tr></thead>
+                <tbody>
+        """
+        content_html = ""
+        for payment in payments:
+            content_html += """
+                <tr>
+                    <td>%s</td>
+                    <td>%s</td>
+                    <td>%s</td>
+                    <td class='text-right'>%.2f €</td>
+                </tr>
+            """ % (payment.get('date', '').strftime('%d/%m/%Y'),
+                   payment.get('name', ''), payment.get('ref', ''),
+                   payment.get('credit', 0.0),)
+
+        end_html = "</tbody></table>"
+        return start_html + content_html + end_html
diff --git a/report/scop_contribution_report.xml b/report/scop_contribution_report.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6b7fbd72581220dcf5dd99fc7dbb5dce9d511d09
--- /dev/null
+++ b/report/scop_contribution_report.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <data>
+        <!-- SEARCH VIEW -->
+        <record model="ir.ui.view" id="scop_contribution_report_search">
+            <field name="name">scop.contribution.report.search</field>
+            <field name="model">scop.contribution.report</field>
+            <field name="arch" type="xml">
+                <search string="Cotisations">
+                    <field name='partner_id'/>
+                    <field name="year"/>
+                    <field name="type_contribution_id"/>
+                    <filter name="filter_paid" string="À régler" domain="[('amount_due', '!=', 0)]"/>
+                    <filter name="filter_paid" string="Réglé" domain="[('amount_due', '=', 0)]"/>
+                    <separator></separator>
+                    <filter name="filter_cg" string="Cotisations CG" domain="[('type_contribution_id', '=', 1)]"/>
+                    <filter name="filter_ur" string="Cotisations UR" domain="[('type_contribution_id', '=', 3)]"/>
+                    <filter name="filter_fede" string="Cotisations Fédé" domain="[('type_contribution_id', '=', 2)]"/>
+                    <group expand="0" string="Group By">
+                        <filter name="group_by_type_contribution_id" string="Type de cotisation" context="{'group_by':'type_contribution_id'}"/>
+                        <filter name="group_by_year" string="Année" context="{'group_by':'year'}"/>
+                    </group>
+                </search>
+            </field>
+        </record>
+
+        <!-- TREE VIEW -->
+        <record model="ir.ui.view" id="scop_contribution_report_tree">
+            <field name="name">scop.contribution.report.tree</field>
+            <field name="model">scop.contribution.report</field>
+            <field name="arch" type="xml">
+                <tree string="Cotisations">
+                    <field name="year"/>
+                    <field name="type_contribution_id"/>
+                    <field name='partner_id'/>
+                    <field name='amount_called'/>
+                    <field name='amount_paid'/>
+                    <field name='amount_due'/>
+                </tree>
+            </field>
+        </record>
+
+        <!-- FORM VIEW -->
+        <record model="ir.ui.view" id="scop_contribution_report_form">
+            <field name="name">scop.contribution.report.form</field>
+            <field name="model">scop.contribution.report</field>
+            <field name="arch" type="xml">
+                <form string="Cotisations">
+                    <sheet>
+                        <h2><field name="name"/></h2>
+                        <separator></separator>
+                        <table class="table table-striped table-hover">
+                            <thead>
+                                <tr>
+                                    <th>Montant Appelé</th>
+                                    <th>Montant payé</th>
+                                    <th>Montant restant</th>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td><field name='amount_called'/> €</td>
+                                    <td><field name='amount_paid'/> €</td>
+                                    <td><field name='amount_due'/> €</td>
+                                </tr>
+                            </tbody>
+                        </table>
+                        <separator></separator>
+                        <h4>Détail des Paiements</h4>
+                        <field name='payments' widget="html"/>
+                    </sheet>
+                </form>
+            </field>
+        </record>
+
+        <!-- PIVOT VIEW -->
+        <record model="ir.ui.view" id="scop_contribution_report_pivot">
+            <field name="name">scop.contribution.report.pivot</field>
+            <field name="model">scop.contribution.report</field>
+            <field name="arch" type="xml">
+                <pivot string="Cotisations">
+                    <field name="year" type="row"/>
+                    <field name="type_contribution_id" type="row"/>
+                    <field name='amount_called' type="measure"/>
+                    <field name='amount_paid' type="measure"/>
+                    <field name='amount_due' type="measure"/>
+                </pivot>
+            </field>
+        </record>
+
+        <!-- GRAPH VIEW -->
+        <record model="ir.ui.view" id="scop_contribution_report_graph">
+            <field name="name">scop.contribution.report.graph</field>
+            <field name="model">scop.contribution.report</field>
+            <field name="arch" type="xml">
+                <graph string="Cotisations">
+                    <field name="year"/>
+                    <field name='amount_called' type="measure"/>
+                    <field name='amount_paid' type="measure"/>
+                    <field name='amount_due' type="measure"/>
+                </graph>
+            </field>
+        </record>
+
+        <!-- ACTION -->
+        <record id="scop_contribution_report_action" model="ir.actions.act_window">
+            <field name="name">Vue cotisations</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">scop.contribution.report</field>
+            <field name="view_mode">tree,pivot,graph,form</field>
+        </record>
+
+        <!-- Menu -->
+        <menuitem
+                id="scop_contribution_report_menu"
+                name="Cotisations"
+                parent="account.account_reports_management_menu"
+                action="scop_contribution_report_action"
+                sequence="80"
+        />
+    </data>
+</odoo>
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index e20c8308a728efd5f82e307bdc0f250490c415a3..731427df84b1438d79b90c8dc5c23e0953a861b1 100755
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -1,3 +1,4 @@
 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
 access_scop_cotisation_task,access_scop_cotisation_task,model_scop_cotisation_task,account.group_account_manager,1,1,1,0
 admin_access_scop_cotisation_task,admin_access_scop_cotisation_task,model_scop_cotisation_task,cgscop_partner.group_cg_administrator,1,1,1,1
+access_scop_contribution_report_user,access_scop_contribution_report_user,model_scop_contribution_report,base.group_user,1,0,0,0
\ No newline at end of file
diff --git a/views/res_partner.xml b/views/res_partner.xml
index 3a9946327458e17d45390276491290a35f8cc686..b0a5c4532275eb22b82859dcb5002806ca057788 100644
--- a/views/res_partner.xml
+++ b/views/res_partner.xml
@@ -9,9 +9,24 @@
             <field name="model">res.partner</field>
             <field name="inherit_id" ref="cgscop_partner.scop_contact_view_form"/>
             <field name="arch" type="xml">
-                <xpath expr="//field[@name='contribution_ids']/tree/field[@name='spreading']" position="before">
-                    <field name="is_exempt" style="text-align: center;"/>
-                    <button name="view_refund" type="object" icon="fa-eye" attrs="{'invisible': [('is_exempt', '=', False)]}" style="pointer-events: visible;"/>
+                <xpath expr="//page[@name='scop_membership']" position="after">
+                    <page name='scop_contribution' string="Cotisations" attrs="{'invisible': ['|', ('is_cooperative', '!=', True), ('project_status', '!=', '6_suivi')]}">
+                        <notebook>
+                            <page name="contribution" string="Appels de Cotisations">
+                                <field name="contribution_report_ids" mode="tree,form">
+                                    <tree create="false" edit="false" delete="false" default_order="year desc">
+                                        <field name="year"/>
+                                        <field name="type_contribution_id"/>
+                                        <field name='partner_id'/>
+                                        <field name='amount_called'/>
+                                        <field name='amount_paid'/>
+                                        <field name='amount_due'/>
+                                        <field name='is_loss' style="text-align: center;"/>
+                                    </tree>
+                                </field>
+                            </page>
+                        </notebook>
+                    </page>
                 </xpath>
             </field>
         </record>