Skip to content
Extraits de code Groupes Projets
sale_order.py 10,6 ko
Newer Older
  • Learn to ignore specific revisions
  • # Copyright 2019 Le Filament (<http://www.le-filament.com>)
    
    Juliana's avatar
    Juliana a validé
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
    
    
    from odoo import models, fields, api
    from odoo.exceptions import ValidationError
    
    Juliana's avatar
    Juliana a validé
    
    
    class SaleOrder(models.Model):
    
        _inherit = "sale.order"
    
        project_id_linked = fields.Many2one('project.project', string='Projet associé',
                                            domain=[('allow_timesheets', '=', True), ('active', '=', True)])
        project_name_to_create = fields.Char("Nom du projet")
    
        project_tracking = fields.Selection([
            ('new', 'Créer un nouveau projet'),
            ('link', 'Associer un projet existant'),
        ])
    
        no_create_task = fields.Boolean(string="Ne pas créer les tâches")
    
    
        taux_horaire = fields.Integer(
            'Taux horaire',
            default=lambda self: self.env.user.company_id.taux_horaire)
    
    
        @api.onchange("partner_id", "order_line")
        def _project_name_to_create(self):
            so_line_new_project_with_tasks = self.mapped('order_line').filtered(
                lambda sol: sol.is_service and sol.product_id.service_tracking == 'task_new_project')
            if not so_line_new_project_with_tasks:
                self.project_tracking = False
            elif so_line_new_project_with_tasks and self.partner_id:
                if not self.project_id_linked:
                    self.project_tracking = "new"
                    if not self.project_name_to_create:
                        self.project_name_to_create = self.partner_id.name + str(' - ')
                else:
                    self.project_tracking = "link"
    
        @api.onchange("project_tracking")
        def _erase_project_tracking(self):
            if self.project_tracking == "link":
                self.project_name_to_create = None
            elif self.project_tracking == "new":
                self.project_id_linked = None
                if not self.project_name_to_create:
                    so_line_new_project_with_tasks = self.mapped('order_line').filtered(
                        lambda sol: sol.is_service and sol.product_id.service_tracking == 'task_new_project')
                    if so_line_new_project_with_tasks and self.partner_id:
                        self.project_name_to_create = self.partner_id.name + str(' - ')
    
    Juliana's avatar
    Juliana a validé
    
        @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
            so_line_new_project_with_tasks = self.mapped('order_line').filtered(
                lambda sol: sol.is_service and sol.product_id.service_tracking == 'task_new_project')
            if (so_line_new_project_with_tasks and not self.project_id_linked) and (
                    so_line_new_project_with_tasks and not self.project_name_to_create):
                raise ValidationError(
                    "Pas de projet associé à ce devis : merci de choisir un projet ou d'en créer un nouveau")
            elif so_line_new_project_with_tasks and self.project_id_linked and self.project_name_to_create:
                raise ValidationError(
                    "Vous ne pouvez pas créer un nouveau projet et associer à un projet existant en même temps : merci de "
                    "vérifier le projet que vous voulez associer à ce devis")  # cas normalement géré par le xml
            elif not so_line_new_project_with_tasks and (self.project_id_linked or self.project_name_to_create):
                raise ValidationError(
                    "Vous ne pouvez pas associer de projet à ce devis : tous les articles sont déjà liés à des projets "
                    "existants")  # cas normalement géré par le xml
            else:
                return super(SaleOrder, self).action_confirm()
    
    
    class SaleOrderLine(models.Model):
    
    Juliana's avatar
    Juliana a validé
        _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
            """
            company_time_uom_id = self.env.user.company_id.project_time_mode_id
    
            taux_horaire = self.order_id.taux_horaire
    
            if self.product_uom.id != company_time_uom_id.id \
                    and self.product_uom.category_id.id == company_time_uom_id.category_id.id:
                planned_hours = super(SaleOrderLine, self)._convert_qty_company_hours
            else:
                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
            """
            task = super(SaleOrderLine, self)._timesheet_create_task(project)
            if self.product_id.service_tracking == 'task_global_project':
                stage = self.product_id.project_linked_stage_id
                client_name = self.order_id.partner_id.name
                if stage:
                    task.stage_id = stage
                    task_name = str(client_name + " - " + stage.name)
                    task.write({'name': task_name})
                else:
                    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
            """
            project = super(SaleOrderLine, self)._timesheet_create_project()
            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
                précédemment créé ou choisi
            """
            so_line_task_global_project = self.filtered(
                lambda sol: sol.is_service and sol.product_id.service_tracking == 'task_global_project')
            so_line_new_project = self.filtered(
                lambda sol: sol.is_service and sol.product_id.service_tracking in ['project_only', 'task_new_project'])
    
            # search so lines from SO of current so lines having their project generated, in order to check if the
            # current one can create its own project, or reuse the one of its order.
            map_so_project = {}
            if so_line_new_project:
                order_ids = self.mapped('order_id').ids
                so_lines_with_project = self.search([('order_id', 'in', order_ids), ('project_id', '!=', False), (
                    'product_id.service_tracking', 'in', ['project_only', 'task_new_project']),
                                                     ('product_id.project_template_id', '=', False)])
                map_so_project = {sol.order_id.id: sol.project_id for sol in so_lines_with_project}
                so_lines_with_project_templates = self.search([('order_id', 'in', order_ids), ('project_id', '!=', False), (
                    'product_id.service_tracking', 'in', ['project_only', 'task_new_project']),
                                                               ('product_id.project_template_id', '!=', False)])
                map_so_project_templates = {(sol.order_id.id, sol.product_id.project_template_id.id): sol.project_id for sol
                                            in so_lines_with_project_templates}
    
            # search the global project of current SO lines, in which create their task
            map_sol_project = {}
            if so_line_task_global_project:
                map_sol_project = {sol.id: sol.product_id.with_context(force_company=sol.company_id.id).project_id for sol
                                   in so_line_task_global_project}
    
            def _can_create_project(sol):
                if not sol.project_id:
                    if sol.product_id.project_template_id:
                        return (sol.order_id.id, sol.product_id.project_template_id.id) not in map_so_project_templates
                    elif sol.order_id.id not in map_so_project:
                        return True
                return False
    
            # task_global_project: create task in global project
            for so_line in so_line_task_global_project:
                no_create_task = so_line.order_id.no_create_task  # on récupère la variable de classe créée
                if not so_line.task_id and not no_create_task:  # rajoute une conditon pour vérifier si on crée les tâches
                    if map_sol_project.get(so_line.id):
                        so_line._timesheet_create_task(project=map_sol_project[so_line.id])
    
            # project_only, task_new_project: use the project linked to the sale_order. May be create a task too.
            for so_line in so_line_new_project:
                project = so_line.project_id
                # on récupère les variables de classes créées
                no_create_task = so_line.order_id.no_create_task
                project_linked = so_line.order_id.project_id_linked
                new_project_name = so_line.order_id.project_name_to_create
                if not project and _can_create_project(so_line):
                    if project_linked:  # si on a choisi un projet ..
                        project = project_linked  # .. on associe la so_line au projet associé au devis
                        so_line.write({'project_id': project_linked.id})
                    elif new_project_name:  # si on a choisi un nom de nouveau projet, on crée ce projet
                        project = so_line._timesheet_create_project(new_project_name)
                    elif so_line.product_id.service_tracking == 'project_only':  # on est dans le cas "project_only"
                        project = super(SaleOrderLine, self)._timesheet_create_project()
                    if so_line.product_id.project_template_id:
                        map_so_project_templates[(so_line.order_id.id, so_line.product_id.project_template_id.id)] = project
                    else:
                        map_so_project[so_line.order_id.id] = project
                elif not project:
                    # Attach subsequent SO lines to the created project
                    so_line.project_id = (
                            map_so_project_templates.get((so_line.order_id.id, so_line.product_id.project_template_id.id))
                            or map_so_project.get(so_line.order_id.id)
    
                if so_line.product_id.service_tracking == 'task_new_project':
                    if not project:
                        if so_line.product_id.project_template_id:
                            project = map_so_project_templates[
                                (so_line.order_id.id, so_line.product_id.project_template_id.id)]
                        else:
                            project = map_so_project[so_line.order_id.id]
                    if not so_line.task_id and not no_create_task:  # rajoute conditon pour vérifier si on crée les tâches
                        so_line._timesheet_create_task(project=project)