diff --git a/__init__.py b/__init__.py
index 09f29e14370809193737242f352f70c82640b943..db3c96a20d0d825c802d0635aabb49823593e8b8 100644
--- a/__init__.py
+++ b/__init__.py
@@ -2,4 +2,3 @@
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
 from . import models
-from . import controllers
diff --git a/__manifest__.py b/__manifest__.py
index d212f929fbb69051930f9467a743515b48be2a60..fea4a3ce315df71933d95c388be815516d382654 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -3,7 +3,7 @@
     'summary': """Filament - Lien entre commandes et projets""",
     'author': "Le Filament",
     'website': "https://www.le-filament.com",
-    'version': '12.0.1.0.1',
+    'version': '13.0.1.0.1',
     'license': "AGPL-3",
     'category': 'Sale Management',
     'depends': ['sale_timesheet', 'project'],
diff --git a/models/__init__.py b/models/__init__.py
index a3fa0c885925e7c0806469a9bd665d66775ee386..eaa1c10c07b7acadcdd8d69f4618975156fc1f54 100644
--- a/models/__init__.py
+++ b/models/__init__.py
@@ -1,7 +1,8 @@
 # Copyright 2019 Le Filament (<http://www.le-filament.com>)
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
 
-from . import sale_order
 from . import res_company
 from . import res_config_settings
 from . import product_template
+from . import project_overview
+from . import sale_order
diff --git a/models/__pycache__/__init__.cpython-36.pyc b/models/__pycache__/__init__.cpython-36.pyc
deleted file mode 100644
index f3dd9c2928ea769333c10a98dcae3dab63e3dfc6..0000000000000000000000000000000000000000
Binary files a/models/__pycache__/__init__.cpython-36.pyc and /dev/null differ
diff --git a/models/__pycache__/product_template.cpython-36.pyc b/models/__pycache__/product_template.cpython-36.pyc
deleted file mode 100644
index b540924c439385ccaf2b7d8c3adedcaa94b79c0f..0000000000000000000000000000000000000000
Binary files a/models/__pycache__/product_template.cpython-36.pyc and /dev/null differ
diff --git a/models/__pycache__/res_company.cpython-36.pyc b/models/__pycache__/res_company.cpython-36.pyc
deleted file mode 100644
index 11f9a18f35dc764f26ec0cb4c2b3d96049554cc9..0000000000000000000000000000000000000000
Binary files a/models/__pycache__/res_company.cpython-36.pyc and /dev/null differ
diff --git a/models/__pycache__/res_config_settings.cpython-36.pyc b/models/__pycache__/res_config_settings.cpython-36.pyc
deleted file mode 100644
index 4c248db163efc306fe2c0f5125066e7b4d418b4b..0000000000000000000000000000000000000000
Binary files a/models/__pycache__/res_config_settings.cpython-36.pyc and /dev/null differ
diff --git a/models/__pycache__/sale_order.cpython-36.pyc b/models/__pycache__/sale_order.cpython-36.pyc
deleted file mode 100644
index d030919e351b2b5206f75ccee555db1128fc88cf..0000000000000000000000000000000000000000
Binary files a/models/__pycache__/sale_order.cpython-36.pyc and /dev/null differ
diff --git a/models/project_overview.py b/models/project_overview.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe1dcde48a72c36021b7e8ee61afa809cb42fbaa
--- /dev/null
+++ b/models/project_overview.py
@@ -0,0 +1,161 @@
+# 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 fields, models, api, _
+
+
+class Project(models.Model):
+    _inherit = 'project.project'
+
+    # ------------------------------------------------------
+    # Fields declaration
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # SQL Constraints
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Default methods
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Computed fields / Search Fields
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Onchange / Constraints
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # CRUD methods (ORM overrides)
+    # ------------------------------------------------------
+    def _table_get_line_values(self):
+        """ return the header and the rows informations of the table """
+        if not self:
+            return False
+
+        uom_hour = self.env.ref('uom.product_uom_hour')
+
+        # build SQL query and fetch raw data
+        query, query_params = self._table_rows_sql_query()
+        self.env.cr.execute(query, query_params)
+        raw_data = self.env.cr.dictfetchall()
+        rows_employee = self._table_rows_get_employee_lines(raw_data)
+        default_row_vals = self._table_row_default()
+
+        empty_line_ids, empty_order_ids = self._table_get_empty_so_lines()
+
+        # extract row labels
+        sale_line_ids = set()
+        sale_order_ids = set()
+        for key_tuple, row in rows_employee.items():
+            if row[0]['sale_line_id']:
+                sale_line_ids.add(row[0]['sale_line_id'])
+            if row[0]['sale_order_id']:
+                sale_order_ids.add(row[0]['sale_order_id'])
+
+        sale_orders = self.env['sale.order'].sudo().browse(sale_order_ids | empty_order_ids)
+        sale_order_lines = self.env['sale.order.line'].sudo().browse(sale_line_ids | empty_line_ids)
+        map_so_names = {so.id: so.name for so in sale_orders}
+        map_so_cancel = {so.id: so.state == 'cancel' for so in sale_orders}
+        map_sol = {sol.id: sol for sol in sale_order_lines}
+        map_sol_names = {sol.id: sol.name.split('\n')[0] if sol.name else _('No Sales Order Line') for sol in
+                         sale_order_lines}
+        map_sol_so = {sol.id: sol.order_id.id for sol in sale_order_lines}
+
+        rows_sale_line = {}  # (so, sol) -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted]
+        for sale_line_id in empty_line_ids:  # add service SO line having no timesheet
+            sale_line_row_key = (map_sol_so.get(sale_line_id), sale_line_id)
+            sale_line = map_sol.get(sale_line_id)
+            is_milestone = sale_line.product_id.invoice_policy == 'delivery' and sale_line.product_id.service_type == 'manual' if sale_line else False
+            rows_sale_line[sale_line_row_key] = [{'label': map_sol_names.get(sale_line_id, _('No Sales Order Line')),
+                                                  'res_id': sale_line_id, 'res_model': 'sale.order.line',
+                                                  'type': 'sale_order_line',
+                                                  'is_milestone': is_milestone}] + default_row_vals[:]
+            if not is_milestone:
+                # ***** Modif Filament *****
+                rows_sale_line[sale_line_row_key][
+                    -2] = sale_line.product_uom_qty * sale_line.price_unit / sale_line.order_id.taux_horaire if sale_line else 0.0
+                # rows_sale_line[sale_line_row_key][-2] = sale_line.product_uom._compute_quantity(
+                #     sale_line.product_uom_qty, uom_hour, raise_if_failure=False) if sale_line else 0.0
+
+        for row_key, row_employee in rows_employee.items():
+            sale_line_id = row_key[1]
+            sale_order_id = row_key[0]
+            # sale line row
+            sale_line_row_key = (sale_order_id, sale_line_id)
+            if sale_line_row_key not in rows_sale_line:
+                sale_line = map_sol.get(sale_line_id, self.env['sale.order.line'])
+                is_milestone = sale_line.product_id.invoice_policy == 'delivery' and sale_line.product_id.service_type == 'manual' if sale_line else False
+                rows_sale_line[sale_line_row_key] = [{'label': map_sol_names.get(sale_line.id) if sale_line else _(
+                    'No Sales Order Line'), 'res_id': sale_line_id, 'res_model': 'sale.order.line',
+                                                      'type': 'sale_order_line',
+                                                      'is_milestone': is_milestone}] + default_row_vals[
+                                                                                       :]  # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted
+                if not is_milestone:
+                    # ***** Modif Filament *****
+                    rows_sale_line[sale_line_row_key][
+                        -2] = sale_line.product_uom_qty * sale_line.price_unit / sale_line.order_id.taux_horaire if sale_line else 0.0
+                    # rows_sale_line[sale_line_row_key][-2] = sale_line.product_uom._compute_quantity(
+                    #     sale_line.product_uom_qty, uom_hour, raise_if_failure=False) if sale_line else 0.0
+
+            for index in range(len(rows_employee[row_key])):
+                if index != 0:
+                    rows_sale_line[sale_line_row_key][index] += rows_employee[row_key][index]
+                    if not rows_sale_line[sale_line_row_key][0].get('is_milestone'):
+                        rows_sale_line[sale_line_row_key][-1] = rows_sale_line[sale_line_row_key][-2] - \
+                                                                rows_sale_line[sale_line_row_key][5]
+                    else:
+                        rows_sale_line[sale_line_row_key][-1] = 0
+
+        rows_sale_order = {}  # so -> [INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted]
+        rows_sale_order_done_sold = {key: dict(sold=0.0, done=0.0) for key in
+                                     set(map_sol_so.values()) | set([None])}  # SO id -> {'sold':0.0, 'done': 0.0}
+        for row_key, row_sale_line in rows_sale_line.items():
+            sale_order_id = row_key[0]
+            # sale order row
+            if sale_order_id not in rows_sale_order:
+                rows_sale_order[sale_order_id] = [{'label': map_so_names.get(sale_order_id, _('No Sales Order')),
+                                                   'canceled': map_so_cancel.get(sale_order_id, False),
+                                                   'res_id': sale_order_id, 'res_model': 'sale.order',
+                                                   'type': 'sale_order'}] + default_row_vals[
+                                                                            :]  # INFO, before, M1, M2, M3, Done, M3, M4, M5, After, Forecasted
+
+            for index in range(len(rows_sale_line[row_key])):
+                if index != 0:
+                    rows_sale_order[sale_order_id][index] += rows_sale_line[row_key][index]
+
+            # do not sum the milestone SO line for sold and done (for remaining computation)
+            if not rows_sale_line[row_key][0].get('is_milestone'):
+                rows_sale_order_done_sold[sale_order_id]['sold'] += rows_sale_line[row_key][-2]
+                rows_sale_order_done_sold[sale_order_id]['done'] += rows_sale_line[row_key][5]
+
+        # remaining computation of SO row, as Sold - Done (timesheet total)
+        for sale_order_id, done_sold_vals in rows_sale_order_done_sold.items():
+            if sale_order_id in rows_sale_order:
+                rows_sale_order[sale_order_id][-1] = done_sold_vals['sold'] - done_sold_vals['done']
+
+        # group rows SO, SOL and their related employee rows.
+        timesheet_forecast_table_rows = []
+        for sale_order_id, sale_order_row in rows_sale_order.items():
+            timesheet_forecast_table_rows.append(sale_order_row)
+            for sale_line_row_key, sale_line_row in rows_sale_line.items():
+                if sale_order_id == sale_line_row_key[0]:
+                    timesheet_forecast_table_rows.append(sale_line_row)
+                    for employee_row_key, employee_row in rows_employee.items():
+                        if sale_order_id == employee_row_key[0] and sale_line_row_key[1] == employee_row_key[1]:
+                            timesheet_forecast_table_rows.append(employee_row)
+
+        # complete table data
+        return {
+            'header': self._table_header(),
+            'rows': timesheet_forecast_table_rows
+        }
+    # ------------------------------------------------------
+    # Actions
+    # ------------------------------------------------------
+
+    # ------------------------------------------------------
+    # Business methods
+    # ------------------------------------------------------
diff --git a/models/sale_order.py b/models/sale_order.py
index 24323b35404a1ea5a542d4be44e80ca7460b7240..8c78f1bcff65e8c98cbea8c521659fbfb7e9c8b9 100644
--- a/models/sale_order.py
+++ b/models/sale_order.py
@@ -21,7 +21,7 @@ class SaleOrder(models.Model):
 
     taux_horaire = fields.Integer(
         'Taux horaire',
-        default=lambda self: self.env.user.company_id.taux_horaire)
+        default=lambda self: self.env.company.taux_horaire)
 
     @api.onchange("partner_id", "order_line")
     def _project_name_to_create(self):
@@ -49,7 +49,6 @@ class SaleOrder(models.Model):
                 if so_line_new_project_with_tasks and self.partner_id:
                     self.project_name_to_create = self.partner_id.name + str(' - ')
 
-    @api.multi
     def action_confirm(self):
         # on différencie so_line_new_project (dans _timesheet_service_generation) de so_line_new_project_with_tasks
         # car on laisse le fonctionnement natif pour les articles où on crée le projet sans les tâches
@@ -74,7 +73,6 @@ class SaleOrder(models.Model):
 class SaleOrderLine(models.Model):
     _inherit = "sale.order.line"
 
-    @api.multi
     def _convert_qty_company_hours(self):
         """ Reprise de la fonction native pour changer le mode de calcul des heures planifiées dans timesheet
         """
@@ -87,7 +85,6 @@ class SaleOrderLine(models.Model):
             planned_hours = (self.product_uom_qty * self.price_unit) / taux_horaire
         return planned_hours
 
-    @api.multi
     def _timesheet_create_task(self, project):
         """ Pour gérer le stage_id et le nom des tâches pour les projets maintenance et support
         """
@@ -103,7 +100,6 @@ class SaleOrderLine(models.Model):
                 task.write({'name': client_name})
         return task
 
-    @api.multi
     def _timesheet_create_project(self, name_project):
         """ Genère le projet de la même manière mais lui donne le nom choisi
         """
@@ -111,7 +107,6 @@ class SaleOrderLine(models.Model):
         project.name = name_project  # on réécrit le nom du projet
         return project
 
-    @api.multi
     def _timesheet_service_generation(self):
         """ Réécriture de la fonction native de manière quasi-identique
             mais qui permet d'associer chaque ligne du devis à un projet