Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • cff5b1bb17e944d832bcd2b4a20a3dbf87eca2bc
  • 12.0 par défaut protégée
  • 18.0
  • 17.0
  • 16.0
  • 15.0
  • 14.0
  • 13.0
8 résultats

.editorconfig

Blame
  • calendar.py 19,40 Kio
    # © 2019 Le Filament (<http://www.le-filament.com>)
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
    
    from datetime import timedelta
    import pytz
    
    from odoo import models, fields, api
    from odoo.fields import Date
    from odoo.tools import pycompat
    from odoo.exceptions import UserError, ValidationError
    
    from odoo.addons.calendar.models.calendar import calendar_id2real_id
    
    
    class CGScopAttendee(models.Model):
        _inherit = 'calendar.attendee'
    
        timesheet_ids = fields.One2many(
            comodel_name='account.analytic.line',
            inverse_name='attendee_id',
            string='Timesheet linked',
            copy=False)
    
    
    class CGScopCalendar(models.Model):
        _inherit = 'calendar.event'
    
        @api.model
        def _default_coop_id(self):
            if self.env.context.get('default_res_model') == 'res.partner':
                if self.env.context.get('default_res_id'):
                    return self.env['res.partner'].browse(
                        self.env.context.get('default_res_id'))
            return False
    
        def _default_ur(self):
            return self.env['res.company']._ur_default_get()
    
        partner_ids = fields.Many2many(domain=[
            ('user_ids', '!=', False)])
        type = fields.Selection(
            [('outside', 'Extérieur'),
             ('ur', 'UR'),
             ('training', 'Formation'),
             ('absent', 'Absence, Congés, Divers')],
            string="Type de Réunion")
        location = fields.Text()
        coop_id = fields.Many2one(
            comodel_name='res.partner',
            string='Contact',
            domain=[('is_company', '=', 'True')],
            ondelete='set null',
            default=_default_coop_id)
        project_id = fields.Many2one(
            comodel_name="project.project",
            string="Code Activité UR",
            domain=[('allow_timesheets', '=', True)])
        cgscop_timesheet_code_id = fields.Many2one(
            related='project_id.cgscop_timesheet_code_id',
            string='Code Activité National',
            store=True)
        ur_financial_system_id = fields.Many2one(
            comodel_name='ur.financial.system',
            string='Dispositif Financier')
        ur_regional_convention_id = fields.Many2one(
            comodel_name='ur.regional.convention',
            string='Convention Régionale')
        ur_id = fields.Many2one(
            'union.regionale',
            string='Union Régionale',
            index=True,
            ondelete='restrict',
            default=_default_ur)
        ur_financial_system_nb = fields.Integer(
            string="Nb Dispositifs Financiers",
            compute="_compute_ur_system_nb")
        ur_regional_convention_nb = fields.Integer(
            string="Nb conventions régionales",
            compute="_compute_ur_system_nb")
        attendees_initial = fields.Char(
            string='Initiales Participants',
            compute='_compute_attendees_initial')
        state = fields.Selection(
            [('needsAction', 'Non répondu'),
             ('tentative', 'Incertain'),
             ('declined', 'Refusé'),
             ('accepted', 'Accepté')],
            string='Statut',
            compute='_compute_attendee_state',
            help="Statut du participant",
            default='needsAction')
        is_attendee = fields.Boolean(
            string='Est participant',
            compute='_compute_is_attendee',
            default=False)
        is_transfered = fields.Boolean(
            string='Transféré',
            compute='_compute_is_transfered',
            default=False)
    
        # ------------------------------------------------------
        # Compute
        # ------------------------------------------------------
        @api.depends('ur_id')
        def _compute_ur_system_nb(self):
            for event in self:
                # Calcul nombre de dispositifs financiers
                financial_system = event.env['ur.financial.system'].search([
                    ('ur_id', '=', event.ur_id.id)])
                event.ur_financial_system_nb = len(
                    financial_system)
                # Calcul nombre de conventions
                regional_convention = event.env['ur.regional.convention'].search([
                    ('ur_id', '=', event.ur_id.id)])
                event.ur_regional_convention_nb = len(
                    regional_convention)
    
        @api.depends('partner_ids')
        def _compute_attendees_initial(self):
            for event in self:
                initials = ''
                for partner in event.partner_ids:
                    initials += (partner.lastname[0] + '.' + partner.firstname[0]
                                 + ', ')
                event.attendees_initial = initials
    
        def _compute_attendee_state(self):
            for event in self:
                attendee = self.env['calendar.attendee'].search([
                    ('event_id', '=', event.id),
                    ('partner_id', '=', self.env.user.partner_id.id)])
                event.state = attendee.state
    
        def _compute_is_attendee(self):
            for event in self:
                if self.env.user.partner_id in event.partner_ids:
                    event.is_attendee = True
                else:
                    event.is_attendee = False
    
        def _compute_is_transfered(self):
            for event in self:
                event_id = event.get_metadata()[0].get('id')
                attendee = self.env['calendar.attendee'].search([
                    ('event_id', '=', event_id),
                    ('partner_id', '=', self.env.user.partner_id.id)])
                # l'attendee a des feuilles de temps liées à la même date
                if (attendee.timesheet_ids
                        and attendee.timesheet_ids.filtered(
                            lambda t: t.date == event.start.date())):
                    event.is_transfered = True
                else:
                    event.is_transfered = False
    
        # ------------------------------------------------------
        # Onchange
        # ------------------------------------------------------
        @api.onchange('project_id')
        def _onchange_project_id(self):
            if self.project_id.partner_id:
                self.coop_id = self.project_id.partner_id
    
        @api.onchange('coop_id')
        def _onchange_coop_id(self):
            # affiche l'adresse de la coop sur le RDV
            address = ''
            if self.coop_id.street:
                address += self.coop_id.street + '\n'
            if self.coop_id.street2:
                address += self.coop_id.street2 + '\n'
            if self.coop_id.street3:
                address += self.coop_id.street3 + '\n'
            if self.coop_id.zip:
                address += self.coop_id.zip + ' '
            if self.coop_id.city:
                address += self.coop_id.city
            self.location = address
            # affiche le Dispositif Financier par défaut sur le RDV
            # si il n'y a pas de date limite du dispositif
            # ou si la date du RDV est inférieure à la date limite du dispositif
            if not self.coop_id.ur_financial_system_date or \
               fields.Date.to_date(self.start) <= self.coop_id.ur_financial_system_date:
                self.ur_financial_system_id = self.coop_id.ur_financial_system_id
            # affiche la Convention par défaut sur le RDV
            # si il n'y a pas de date limite de la convention
            # ou si la date du RDV est inférieure à la date limite de la convention
            if not self.coop_id.ur_regional_convention_date or \
               fields.Date.to_date(self.start) <= self.coop_id.ur_regional_convention_date:
                self.ur_regional_convention_id = self.coop_id.\
                    ur_regional_convention_id
    
        # ------------------------------------------------------
        # Contrains
        # ------------------------------------------------------
        @api.constrains('project_id', 'start', 'stop')
        def _check_activity_code(self):
            for event in self:
                if event.project_id:
                    # Récupère les entrées en intersection
                    # avec la plage horaire
                    entries = self.search([
                        ('start', '<', event.stop),
                        ('stop', '>', event.start),
                        ('user_id', '=', self.env.uid),
                        ('project_id', '=', event.project_id.id),
                        ('id', '!=', event.id)
                    ])
                    if entries:
                        raise ValidationError(
                            "Vous ne pourvez programmer 2 évènements avec le "
                            "même code activité sur la même plage horaire\n"
                            "Evènement : %s" % str(entries.mapped('name'))[1:-1])
    
        # ------------------------------------------------------
        # Fonction boutons
        # ------------------------------------------------------
        @api.multi
        def do_accept(self):
            """ Accepte l'invitation
                Modifie le statut de la table Attendees
            """
            for event in self:
                attendee = self.env['calendar.attendee'].search([
                    ('event_id', '=', event.id),
                    ('partner_id', '=', self.env.user.partner_id.id)])
                attendee.state = 'accepted'
    
        @api.multi
        def do_decline(self):
            """ Refuse l'invitation
                Modifie le statut de la table Attendees
            """
            for event in self:
                attendee = self.env['calendar.attendee'].search([
                    ('event_id', '=', event.id),
                    ('partner_id', '=', self.env.user.partner_id.id)])
                attendee.state = 'declined'
    
        @api.multi
        def do_tentative(self):
            """ Incertain pour l'invitation
                Modifie le statut de la table Attendees
            """
            for event in self:
                attendee = self.env['calendar.attendee'].search([
                    ('event_id', '=', event.id),
                    ('partner_id', '=', self.env.user.partner_id.id)])
                attendee.state = 'tentative'
    
        @api.multi
        def create_timesheet(self):
            """ Crée une ligne de temps à partir de l'entrée d'agenda
            """
            partner = self.env.user.partner_id
            for event in self:
                event_id = event.get_metadata()[0].get('id')
                if partner not in event.partner_ids:
                    raise UserError("Vous ne faites pas partie des participants, \
                        vous ne pouvez donc pas transformer cette entrée d'agenda \
                        en ligne de temps.")
                if not event.project_id.analytic_account_id:
                    raise UserError("Le code activité UR doit être \
                                    renseigné sur chaque entrée d'agenda")
                else:
                    attendee = self.env['calendar.attendee'].search([
                        ('event_id', '=', event_id),
                        ('partner_id', '=', partner.id)])
                    # l'attendee a des feuilles de temps liées à la même date
                    if (attendee.timesheet_ids
                            and attendee.timesheet_ids.filtered(
                                lambda t: t.date == event.start.date())):
                        raise UserError("Vous avez déjà transféré cette entrée \
                                    d'agenda : %s" % event.name)
                    else:
                        values = {
                            'user_id': self.env.user.id,
                            'project_id': event.project_id.id,
                            'account_id': event.project_id.analytic_account_id.id,
                            'ur_financial_system_id': event.ur_financial_system_id.id,
                            'ur_regional_convention_id': event.ur_regional_convention_id.id,
                            'name': event.name,
                            'company_id': self.env.user.company_id.id,
                            'partner_id': event.coop_id.id,
                            'event_id': event_id,
                            'attendee_id': attendee.id,
                        }
                        # Gestion des évènements sur toute la journée
                        if event.allday:
                            # Création d'une ligne de 8h pour chaque jour
                            for i in range((event.stop - event.start).days + 1):
                                values['date'] = event.start + timedelta(days=i)
                                values['unit_amount'] = 8.0
                                ts = self.env['account.analytic.line'].create(
                                    values)
                                attendee.write({'timesheet_id': ts.id})
                        # Gestion des évènements sur plusieurs jours non flagués
                        # allday
                        elif (event.stop - event.start).days > 0:
                            user_tz = self.env.user.tz
                            local = pytz.timezone(user_tz)
                            # Pour chaque jour
                            for i in range((event.stop - event.start).days + 1):
                                day = event.start + timedelta(days=i)
                                # si premier jour calcul heures
                                if i == 0:
                                    end_day_tz = local.localize(
                                        day.replace(hour=18))
                                    start_tz = fields.Datetime.context_timestamp(
                                        record=self.env.user,
                                        timestamp=event.start)
                                    hours_cal = ((end_day_tz - start_tz).seconds
                                                 // 3600)
                                    hours = hours_cal if hours_cal <= 8 else 8.0
                                # si dernier jour
                                elif i == (event.stop - event.start).days:
                                    start_day_tz = local.localize(
                                        day.replace(hour=8))
                                    stop_tz = fields.Datetime.context_timestamp(
                                        record=self.env.user,
                                        timestamp=event.stop)
                                    hours_cal = ((stop_tz - start_day_tz).seconds
                                                 // 3600)
                                    hours = hours_cal if hours_cal <= 8 else 8.0
                                else:
                                    hours = 8.0
                                values['date'] = day
                                values['unit_amount'] = hours
                                ts = self.env['account.analytic.line'].create(
                                    values)
                                attendee.write({'timesheet_id': ts.id})
                        # Gestion des évènements classiques
                        else:
                            values['date'] = event.start
                            values['unit_amount'] = event.duration
                            ts = self.env['account.analytic.line'].create(values)
                            attendee.write({'timesheet_id': ts.id})
    
        @api.multi
        def duplicate_entry(self):
            """
                Duplique la ligne de temps
            """
            for event in self:
                return {
                    'type': 'ir.actions.act_window',
                    'res_model': 'calendar.event',
                    'view_type': 'form',
                    'view_mode': 'form',
                    'target': 'new',
                    'context': {
                        'default_project_id': event.project_id.id,
                        'default_name': event.name,
                        'default_coop_id': event.coop_id.id,
                        'default_partner_ids': event.partner_ids.ids,
                        'default_start': event.start,
                        'default_duration': event.duration,
                        'default_stop': event.stop,
                        'default_allday': event.allday,
                        'default_type': event.type,
                        'default_ur_financial_system_id': event.ur_financial_system_id.id,
                    },
                }
    
        # ------------------------------------------------------
        # Override ORM
        # ------------------------------------------------------
        @api.multi
        def read(self, fields=None, load='_classic_read'):
            """ Surcharge la fonction read de calendar pour gérer le transfert des
                lignes de temps sur les virtual events.
    
                Ajoute le calcul de la valeur du champs 'is_transfered' dans la
                boucle 'for calendar_id, real_id in select'
            """
    
            if not fields:
                fields = list(self._fields)
            fields2 = fields and fields[:]
            EXTRAFIELDS = ('privacy', 'user_id', 'duration', 'allday', 'start',
                           'rrule')
            for f in EXTRAFIELDS:
                if fields and (f not in fields):
                    fields2.append(f)
    
            select = [(x, calendar_id2real_id(x)) for x in self.ids]
            real_events = self.browse([real_id for calendar_id, real_id in select])
            real_data = super(CGScopCalendar, real_events).read(fields=fields2,
                                                                load=load)
            real_data = dict((d['id'], d) for d in real_data)
            result = []
            for calendar_id, real_id in select:
                if not real_data.get(real_id):
                    continue
                res = real_data[real_id].copy()
                ls = calendar_id2real_id(calendar_id, with_date=res
                                         and res.get('duration', 0) > 0
                                         and res.get('duration') or 1)
                if not isinstance(ls, (pycompat.string_types,
                                       pycompat.integer_types)) and len(ls) >= 2:
                    res['start'] = ls[1]
                    res['stop'] = ls[2]
    
                    if res['allday']:
                        res['start_date'] = ls[1]
                        res['stop_date'] = ls[2]
                    else:
                        res['start_datetime'] = ls[1]
                        res['stop_datetime'] = ls[2]
    
                    if 'display_time' in fields:
                        res['display_time'] = self._get_display_time(
                            ls[1], ls[2], res['duration'], res['allday'])
    
                    attendee = self.env['calendar.attendee'].search([
                        ('event_id', '=', ls[0]),
                        ('partner_id', '=', self.env.user.partner_id.id)])
                    # l'attendee a des feuilles de temps liées à la même date
                    if (attendee.timesheet_ids
                            and attendee.timesheet_ids.filtered(
                                lambda t: t.date == Date.to_date(ls[1]))):
                        res['is_transfered'] = True
                    else:
                        res['is_transfered'] = False
    
                res['id'] = calendar_id
                result.append(res)
    
            for r in result:
                if r['user_id']:
                    user_id = (type(r['user_id']) in (tuple, list)
                               and r['user_id'][0] or r['user_id'])
                    partner_id = self.env.user.partner_id.id
                    if user_id == (self.env.user.id
                                   or partner_id in r.get("partner_ids", [])):
                        continue
                if r['privacy'] == 'private':
                    for f in r:
                        recurrent_fields = self._get_recurrent_fields()
                        public_fields = list(set(recurrent_fields
                                                 + ['id', 'allday', 'start',
                                                    'stop', 'display_start',
                                                    'display_stop', 'duration',
                                                    'user_id', 'state', 'interval',
                                                    'count', 'recurrent_id_date',
                                                    'rrule']))
                        if f not in public_fields:
                            if isinstance(r[f], list):
                                r[f] = []
                            else:
                                r[f] = False
                        if f == 'name':
                            r[f] = ('Busy')
    
            for r in result:
                for k in EXTRAFIELDS:
                    if (k in r) and (fields and (k not in fields)):
                        del r[k]
    
            return result