diff --git a/golem_activity_queue/__init__.py b/golem_activity_queue/__init__.py new file mode 100644 index 0000000..8963423 --- /dev/null +++ b/golem_activity_queue/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from . import models, wizard diff --git a/golem_activity_queue/__manifest__.py b/golem_activity_queue/__manifest__.py new file mode 100644 index 0000000..03e8f7d --- /dev/null +++ b/golem_activity_queue/__manifest__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +{ + 'name': 'GOLEM activities queue', + 'summary': 'GOLEM activities queue', + 'description': ''' GOLEM resources management ''', + 'version': '10.0.2.1.0', + 'category': 'GOLEM', + 'author': 'Youssef El Ouahby, Fabien Bourgeois', + 'license': 'AGPL-3', + 'application': True, + 'installable': True, + 'depends': ['golem_activity', 'golem_activity_registration'], + 'data': [ + 'security/ir.model.access.csv', + 'views/golem_activity_queue_views.xml', + 'views/golem_activity_views.xml', + 'views/golem_member_views.xml', + 'wizard/golem_activity_queue_choose_views.xml', + 'wizard/golem_activity_automated_queue_activate_views.xml' + ] +} diff --git a/golem_activity_queue/models/__init__.py b/golem_activity_queue/models/__init__.py new file mode 100644 index 0000000..0ed9ff9 --- /dev/null +++ b/golem_activity_queue/models/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from . import golem_activity_queue +from . import golem_activity +from . import golem_member diff --git a/golem_activity_queue/models/golem_activity.py b/golem_activity_queue/models/golem_activity.py new file mode 100644 index 0000000..5036c1e --- /dev/null +++ b/golem_activity_queue/models/golem_activity.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + +class GolemActivity(models.Model): + """ GOLEM Activity adaptations """ + _inherit = 'golem.activity' + + #ajout d'un champs O2M vers activity_id de golem.activity.queue + activity_queue_ids = fields.One2many('golem.activity.queue', + 'activity_id', 'Pending registration') + # un booleen pour determiner si une fille d'attente est autorisé + queue_allowed = fields.Boolean(default=True, readonly=True) + # un booleen pour automatisé l'inscription sur une activité depuis la file d'attente + auto_registration_from_queue = fields.Boolean(default=True, readonly=True) + #ajout d'un champs pour calculer le nombre d'inscription en file d'attente + queue_activity_number = fields.Integer(compute="_compute_queue_activity_number", + store=True, string='Pending registration number') + #changer le mode de basculement en cas de desinctiption + @api.multi + def auto_registration_toggle(self): + """ switch registration from queueu mode """ + for activity in self: + activity.auto_registration_from_queue = not activity.auto_registration_from_queue + + #supprimer les personnes en attente si une inscription directement est faite + @api.multi + def write(self, vals): + """ Override method write to delete record from queue if they register in activity""" + super(GolemActivity, self).write(vals) + #recupérer les modification au niveau des registrations + registrations = vals.get('activity_registration_ids') + if registrations: + #parcourir les registrations + for registration in registrations: + #s'il une nouvelle registration est trouvé + if registration[0] == 0: + #recupérer les données de la registration + act_id = registration[2].get('activity_id') + mem_id = registration[2].get('member_id') + domain = [('activity_id', '=', act_id), + ('member_id', '=', mem_id)] + #chercher si le meme nomre est inscrit sur lattente du meme activité + queue = self.env['golem.activity.queue'].search(domain) + if queue: + #supprimer l'inscription sur la queue + self.activity_queue_ids = [(2, queue.id, 0)] + return True + + + #mettre à jour le status d'activité remplis sur chaque attente + @api.constrains('places_remain') + def update_activity_fullness(self): + """updates queue.is_activity_full based on places_remain""" + for activity in self: + for queue in self.activity_queue_ids: + if activity.places_remain == 0: + queue.is_activity_full = "Full activity" + else: + queue.is_activity_full = "Not full activity" + #Ajouter/supprimer une file à l'activité et afficher popup pour traitement automatisé + @api.multi + def queue_allowed_toggle(self): + """ Toggle queue_alowed boolean """ + for activity in self: + #si l'activité contient une file + if activity.queue_allowed: + # si la queue déja contient des elements à supprimer + if len(activity.activity_queue_ids) > 0: + #parcourir et supprimer les element de la queue + for queue in activity.activity_queue_ids: + activity.activity_queue_ids = [(2, queue.id, 0)] + #changer l'état de queue allowed et automated_registration en false + activity.queue_allowed = False + activity.auto_registration_from_queue = False + + else: + #lancer popup pour choisir activité à s'inscrire + self.ensure_one() + activity_id = self[0] + + return { + 'name' : _('Choose the activity to register in'), + 'type' : 'ir.actions.act_window', + 'res_model' : 'golem.activity.automated.queue.activate.wizard', + 'view_mode': 'form', + 'context' : {'default_activity_id' : activity_id.id}, + 'target': 'new', + } + + + + #contraint sur nombre d'inscription : une desincription declanche une inscription + #depuis attente mode automatique + @api.multi + @api.constrains('activity_registration_ids') + def _auto_registration_from_queue(self): + """automated registration from queue""" + for record in self: + # 1 verifier si une place est disponible sur activité + #2 verifier si la file contient des element + #3 verifier si la file est activé + #4 verifier si linscription automatique depuis la file est activé + if (len(record.activity_registration_ids) < record.places and + record.queue_activity_number > 0 and + record.queue_allowed and + record.auto_registration_from_queue): + #recupérer la liste en file d'attente + queues = record.activity_queue_ids + #trier la liste selon l'id : récupérer l'ancien element + queues_sorted = sorted(queues, key=lambda k: k['id']) + #suppose que le membre est enrigistré + membre_registred = True + #parcourir les element sur l'attente + for queue in queues_sorted: + #inverse l'etat du memebre + membre_registred = False + #recuperer la liste des registration + registrations = record.activity_registration_ids + #parcourir les registration afin de vérifier si le memebre sur l'attente + #déja inscrit + for registration in registrations: + #compare le membre sur l'attente au membre sur l'inscription + if queue.member_id == registration.member_id: + #si membre trouvé inscrit sur l'activité on le supprime de la queue + record.activity_queue_ids = [(2, queue.id, 0)] + #si membre trouvé on mentionne enregistré, on passe au + #registration suivante + membre_registred = True + break + #à la sortie de la boucle si le membre nest pas sur inscription faire une + if not membre_registred: + #valeures pour creer une inscritpion apartir de la file + values = { + 'activity_id' : queue.activity_id, + 'member_id' : queue.member_id + } + # creation d'inscription + record.activity_registration_ids = [(0, 0, values)] + #suppression de l'element de la file d'attente + record.activity_queue_ids = [(2, queue.id, 0)] + #sortir de la boucle parcourissante la queue puisque inscription faite + break + + + + #calculer le nombre d'inscription sur la file d'attente + @api.multi + @api.depends('activity_queue_ids') + def _compute_queue_activity_number(self): + """ compute number of queue registration for activity""" + for activity in self: + activity.queue_activity_number = len(activity.activity_queue_ids) + + #lancer une fenetre pour inscritpion en file d'attente à partir du boutton + @api.multi + def queue_register(self): + """ launch a wizard to register in queue """ + self.ensure_one() + activity_id = self[0] + return { + 'name' : _('Register in the queue'), + 'type' : 'ir.actions.act_window', + 'res_model' : 'golem.activity.queue', + 'context' :{'default_activity_id' : activity_id.id}, + 'domain' : [('activity_id', '=', activity_id.id)], + 'view_mode': 'tree', + 'flags': {'action_buttons': True}, + 'target': 'new', + } + + #fonction enregistrement du premier element de la liste d'ttente en inscription : mode manuel + @api.multi + def register_from_queue(self): + """ register member from queue""" + for record in self: + #recupérer la liste en file d'attente + queues = record.activity_queue_ids + #trier la liste selon l'id : récupérer l'ancien element + queues_sorted = sorted(queues, key=lambda k: k['id']) + #suppose que le membre est enrigistré + membre_registred = True + #parcourir les element sur l'attente + for queue in queues_sorted: + #inverse l'etat du memebre + membre_registred = False + #recuperer la liste des registration + registrations = record.activity_registration_ids + #parcourir les registration afin de vérifier si le memebre sur + #l'attente déja inscrit + for registration in registrations: + #compare le membre sur l'attente au membre sur l'inscription + if queue.member_id == registration.member_id: + #si membre trouvé inscrit sur l'activité on le supprime de la queue + record.activity_queue_ids = [(2, queue.id, 0)] + # on mentionne enregistré, on passe au registration suivante + membre_registred = True + #on sort de la boucle de registration et on passe à + #l'element suivant de l'attente + break + #à la sortie de la boucle si le membre nest pas sur inscription faire une + if not membre_registred: + #valeures pour creer une inscritpion apartir de la file + values = { + 'activity_id' : queue.activity_id, + 'member_id' : queue.member_id + } + # creation d'inscription + record.activity_registration_ids = [(0, 0, values)] + #suppression de l'element de la file d'attente + record.activity_queue_ids = [(2, queue.id, 0)] + #sortir de la boucle parcourissante la queue puisque inscription faite + break + #si member_registred est true donc soit membre déja inscrit ou aucun membre sur queue + if membre_registred: + message = _('there is no member to register for this activity' + ' from queue.') + raise ValidationError(message) + # 1 verifie si ajout insctiption donc nombre d'inscription depasse place donc proposer queue + #2 verifier si desincription donc place disponible pour queue(automatique ou manuel) + @api.multi + @api.onchange('activity_registration_ids') + def _check_registration_number(self): + current_activity = self._origin + for record in self: + #warning au cas ou le nombre d'inscription depasse le nombre de place + if (len(record.activity_registration_ids) > record.places and + record.queue_allowed): + message = _('No remaining place for the activity : {}, please' + ' discard changes and register in the queue using' + ' the button bellow') + return { + 'warning' : { + 'title' : _('Warning'), + 'message': message.format(record.name), + } + } + elif (len(record.activity_registration_ids) < len(current_activity.activity_registration_ids) and + len(current_activity.activity_registration_ids) == record.places and + record.queue_activity_number > 0): + #si le mode d'inscription depuis attente est activé + if record.auto_registration_from_queue: + #lancer un warning informant que l'inscription automatique aura + #lieu apres sauvegarde + warning_message = _('There is a free place for the activity' + ' : {}, once you save it will be filled' + ' by the first membre from queue') + return { + 'warning' : { + 'title' : _('Warning'), + 'message': warning_message.format(record.name) + } + } + + #traitement manuel pour le passage de la file d'attente en inscription + # : button sur queue + else: + warning_message = _('There is a free place for the activity' + ' : {}, you can fill it from the queue' + ' using the button on queue tab') + return { + 'warning' : { + 'title' : _('Warning'), + 'message': warning_message.format(record.name) + } + } diff --git a/golem_activity_queue/models/golem_activity_queue.py b/golem_activity_queue/models/golem_activity_queue.py new file mode 100644 index 0000000..2591d56 --- /dev/null +++ b/golem_activity_queue/models/golem_activity_queue.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" GOLEM activities related models """ + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + +class GolemActivityQueue(models.Model): + """ GOLEM Activity Queue """ + _name = 'golem.activity.queue' + _order = "sequence" + _description = 'GOLEM Activity Queue' + + member_id = fields.Many2one('golem.member', required=True, + string='Member', ondelete='cascade', + index=True) + activity_id = fields.Many2one('golem.activity', required=True, + string='Activity', ondelete='cascade', + index=True) + season_id = fields.Many2one(related='activity_id.season_id') + + is_current = fields.Boolean('Current season?', + related='activity_id.is_current', store=True) + #nombre de place disponible sur activité liée + places_remain = fields.Integer(related='activity_id.places_remain') + #activité liée est plein ou non + is_activity_full = fields.Char(compute="_compute_is_activity_full", store=True) + + sequence = fields.Integer() + + _sql_constraints = [ + ('queue_uniq', 'UNIQUE (member_id, activity_id)', + _('This member has already been registered for the queue.'))] + + # decider si l'activity liée est pleine ou non : pour group by sur la liste + @api.depends('places_remain') + def _compute_is_activity_full(self): + """ Decide if activity is full or not """ + for record in self: + if record.places_remain == 0: + record.is_activity_full = "Full activity" + else: + record.is_activity_full = "Not full activity" + + @api.constrains('member_id', 'activity_id') + def _check_member_registration(self): + """ Forbid registration in queue when member is already registred in the + activity """ + for queue in self: + domain = [('member_id', '=', queue.member_id.id), + ('activity_id', '=', queue.activity_id.id)] + #verifier si un enrigistrement avec le meme membre et activité est déja fait + registrations = self.env['golem.activity.registration'].search(domain) + #si oui lancer un erreur + if len(registrations): + raise ValidationError(_('The member your trying to add to the queue' + ' is already registred for this activity')) diff --git a/golem_activity_queue/models/golem_member.py b/golem_activity_queue/models/golem_member.py new file mode 100644 index 0000000..31695d8 --- /dev/null +++ b/golem_activity_queue/models/golem_member.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from odoo import models, fields, api, _ + +class GolemMember(models.Model): + """ GOLEM Member adaptations """ + _inherit = 'golem.member' + + #ajout d'un champs O2M vers member_id de golem.activity.queue + activity_queue_ids = fields.One2many('golem.activity.queue', + 'member_id', 'Pending registration') + + @api.multi + def write(self, vals): + registrations_edited = vals.get('activity_registration_ids') + if registrations_edited: + for registration_edited in registrations_edited: + if registration_edited[0] == 2: + registration_removed = self.env['golem.activity.registration'].browse( + registration_edited[1]) + activity_removed = self.env['golem.activity'].browse( + registration_removed.activity_id.id) + if (activity_removed.queue_allowed and + activity_removed.auto_registration_from_queue and + activity_removed.queue_activity_number > 0): + #récupérer la liste des attente lié à l'activité + queues = activity_removed.activity_queue_ids + #trier la liste selon l'id : récupérer l'ancien element + queues_sorted = sorted(queues, key=lambda k: k['id']) + #suppose que le membre est enrigistré + membre_registred = True + #parcourir les element sur l'attente + for queue in queues_sorted: + #inverse l'etat du memebre + membre_registred = False + #recuperer la liste des registration + registrations = activity_removed.activity_registration_ids + #parcourir les registration afin de vérifier si le memebre sur + #l'attente déja inscrit + for registration in registrations: + #compare le membre sur l'attente au membre sur l'inscription + if queue.member_id == registration.member_id: + #si membre trouvé inscrit sur l'activité on le + #supprime de la queue + activity_removed.activity_queue_ids = [(2, queue.id, 0)] + #si membre trouvé on mentionne enregistré, on passe au + #registration suivante + membre_registred = True + break + #à la sortie de la boucle si le membre nest pas sur + #inscription faire une + if not membre_registred: + #valeures pour creer une inscritpion apartir de la file + values = { + 'activity_id' : queue.activity_id, + 'member_id' : queue.member_id + } + + super(GolemMember, self).write(vals) + # creation d'inscription + activity_removed.activity_registration_ids = [(0, 0, values)] + #suppression de l'element de la file d'attente + activity_removed.activity_queue_ids = [(2, queue.id, 0)] + #sortir de la boucle parcourissante la queue puisque + # inscription faite + break + return True + + #verifier si nombre d'inscription sur activité est supérieur au place disponible + #inviter l'utilisateur à s'inscrire sur l'attente + @api.multi + @api.onchange('activity_registration_ids') + def _check_registration_number(self): + """ check activity registration number """ + self.ensure_one() + member_id = self[0] + #recupérer la liste des inscription pour le membbre courant + for registration in member_id.activity_registration_ids: + #recupérer l'activité liée + activity = registration.activity_id + #vérifier si le nombre d'inscription depasse le nombre de places sur activité + if len(activity.activity_registration_ids) > activity.places and activity.queue_allowed: + warning_message = _('This activity : {} is already full, please' + ' discard changes and register in' + ' the queue using the bellow button') + return { + 'warning' : { + 'title' : _('Warning'), + 'message': warning_message.format(activity.name), + } + } + #lancer popup pour choisir activité à s'inscrire + @api.multi + def choose_queue_to_register(self): + """ launch wizard to choose activity queue to register in""" + self.ensure_one() + member_id = self[0] + #lancer le wizard avec le membre actuel + return { + 'name' : _('Choose the activity to register in'), + 'type' : 'ir.actions.act_window', + 'res_model' : 'golem.activity.queue.choose.wizard', + 'view_mode': 'form', + 'context' : {'default_member_id' : member_id.id}, + 'target': 'new', + } diff --git a/golem_activity_queue/security/ir.model.access.csv b/golem_activity_queue/security/ir.model.access.csv new file mode 100644 index 0000000..55c78a8 --- /dev/null +++ b/golem_activity_queue/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_golem_activity_queue_user,Access GOLEM Activity Queue User,model_golem_activity_queue,golem_base.group_golem_user,1,1,1,1 +access_golem_activity_queue_manager,Access GOLEM Activity Queue Manager,model_golem_activity_queue,golem_base.group_golem_manager,1,1,1,1 diff --git a/golem_activity_queue/static/description/icon.png b/golem_activity_queue/static/description/icon.png new file mode 100644 index 0000000..87f67b4 Binary files /dev/null and b/golem_activity_queue/static/description/icon.png differ diff --git a/golem_activity_queue/tests/__init__.py b/golem_activity_queue/tests/__init__.py new file mode 100644 index 0000000..721a338 --- /dev/null +++ b/golem_activity_queue/tests/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from . import test_golem_activity_queue, test_golem_activity, test_golem_member diff --git a/golem_activity_queue/tests/test_golem_activity.py b/golem_activity_queue/tests/test_golem_activity.py new file mode 100644 index 0000000..055a540 --- /dev/null +++ b/golem_activity_queue/tests/test_golem_activity.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from odoo.tests.common import TransactionCase +from odoo.models import ValidationError + + +class TestGolemActivity(TransactionCase): + """ GOLEM Activity Queue testing """ + + def setUp(self): + """ Bootstrap activity """ + super(TestGolemActivity, self).setUp() + #création du saison + self.season = self.env['golem.season'].sudo().create({'name': u'Season 1'}) + self.season.do_default_season() + #préparation des données pour la création des membres + self.data_member_1 = {'lastname': u'LAST1', + 'firstname': u'First1', + 'season_ids':[(4, self.season.id, False)] + } + self.data_member_2 = {'lastname': u'LAST2', + 'firstname': u'First2', + 'season_ids':[(4, self.season.id, False)] + } + self.member1 = self.env['golem.member'] + self.member2 = self.env['golem.member'] + #préparation des donnée pour la création de l'activité + type_id = self.ref("golem_activity.golem_activity_type_activity") + self.data_activity = { + 'name': u'Activity 1', + 'season_id': self.season.id, + 'type_id': type_id} + self.activity = self.env['golem.activity'] + + # test de creation d'activity et inistialisation des champs + def test_activity_creation(self): + """ Test creation of activity """ + activity = self.activity.create(self.data_activity) + self.assertTrue(activity.queue_allowed) + self.assertTrue(activity.auto_registration_from_queue) + self.assertEqual(activity.queue_activity_number, 0) + self.assertFalse(activity.activity_registration_ids) + self.assertFalse(activity.activity_queue_ids) + + + #test toggle auto_registration + def test_auto_registration_toggle(self): + """ Test Toggle Autoregistration from queue """ + activity = self.activity.create(self.data_activity) + self.assertTrue(activity.auto_registration_from_queue) + activity.auto_registration_toggle() + self.assertFalse(activity.auto_registration_from_queue) + activity.auto_registration_toggle() + self.assertTrue(activity.auto_registration_from_queue) + #test du queue_allowed toggle: en cas de désactivation queue doit etre vidé + def test_queue_allowed_toggle(self): + """ Test Toggle queue_allowed """ + #création de 2 membre est une activité + member1 = self.member1.create(self.data_member_1) + member2 = self.member2.create(self.data_member_2) + activity = self.activity.create(self.data_activity) + #membre 1 inscrit sur activity + registration = { + 'activity_id' : activity.id, + 'member_id' : member1.id + } + #memebre 2 inscrit sur attente + queue = { + 'activity_id' : activity.id, + 'member_id' : member2.id + } + #Verification que l'atente est aactivé + self.assertTrue(activity.queue_allowed) + #réduire le nombre de place sur activity à 1 + activity.write({'places': 1}) + #enregistrement du membre 1 sur activity et memebre 2 sur attente + activity.write({'activity_registration_ids': [(0, False, registration)]}) + activity.write({'activity_queue_ids': [(0, False, queue)]}) + #desactivation de l'attente + activity.queue_allowed_toggle() + #verification que l'attente est désactivé et vidé + self.assertFalse(activity.queue_allowed) + self.assertFalse(activity.activity_queue_ids) + #appel wizard pour activation de l'attente + queue_activate_wizard_model = self.env['golem.activity.automated.queue.activate.wizard'] + queue_activate_wizard = queue_activate_wizard_model.create({'activity_id': activity.id}) + queue_activate_wizard.activate_queue() + #verification de l'attente activité + self.assertTrue(activity.queue_allowed) + self.assertTrue(activity.auto_registration_from_queue) + + #test de basculement automatique depuis queue + def test_auto_registration(self): + """ Test auto registration from queue """ + #création de 2 membre est une activité + member1 = self.member1.create(self.data_member_1) + member2 = self.member2.create(self.data_member_2) + activity = self.activity.create(self.data_activity) + #réduire le nombre de place sur activity à 1 et activation de queue et autoregistrement + activity.write({'places': 1, + 'queue_allowed': True, + 'auto_registration_from_queue': True}) + #membre 1 inscrit sur activity + registration = { + 'activity_id' : activity.id, + 'member_id' : member1.id + } + #memebre 2 inscrit sur attente + queue = { + 'activity_id' : activity.id, + 'member_id' : member2.id + } + #enregistrement du membre 1 sur activity et memebre 2 sur attente + activity.write({'activity_registration_ids': [(0, False, registration)]}) + activity.write({'activity_queue_ids': [(0, False, queue)]}) + #vérification du membre 1 sur registration et membre 2 sur attente + self.assertEqual(activity.activity_registration_ids[0].member_id, member1) + self.assertEqual(activity.activity_queue_ids[0].member_id, member2) + #suppression du membre 1 de l'activity + activity.write({'activity_registration_ids': [(2, + activity.activity_registration_ids[0].id, + False)]}) + #verifcation de la presence du membre 2 sur activity : basculement depuis attente + self.assertEqual(activity.activity_registration_ids[0].member_id, member2) + #verification de l'attente est vide + self.assertFalse(activity.activity_queue_ids) + # suppression du membre sur l'attente s'il sinscrit directement sur l'activity + def test_delete_queue_member(self): + """ Test Delete Queue member if they directly register """ + #création de 2 membre est une activité + member1 = self.member1.create(self.data_member_1) + member2 = self.member2.create(self.data_member_2) + activity = self.activity.create(self.data_activity) + #réduire le nombre de place sur activity à 1 et activation de queue et autoregistrement + activity.write({'places': 1, + 'queue_allowed': True, + 'auto_registration_from_queue': False}) + #membre 1 inscrit sur activity + registration = { + 'activity_id' : activity.id, + 'member_id' : member1.id + } + #memebre 2 inscrit sur attente + queue = { + 'activity_id' : activity.id, + 'member_id' : member2.id + } + #enregistrement du membre 1 sur activity et memebre 2 sur attente + activity.write({'activity_registration_ids': [(0, False, registration)]}) + activity.write({'activity_queue_ids': [(0, False, queue)]}) + # vérification des inscriptions + self.assertEqual(activity.activity_registration_ids[0].member_id, member1) + self.assertEqual(activity.activity_queue_ids[0].member_id, member2) + #suppression du membre 1 de l'activity + activity.write({'activity_registration_ids': [(2, + activity.activity_registration_ids[0].id, + False)]}) + #inscription sur activity avec le membre sur queue + activity.write({'activity_registration_ids': [(0, False, queue)]}) + #vérification queue vide + self.assertFalse(activity.activity_queue_ids) + #verification du membre 2 sur inscriptions + self.assertEqual(activity.activity_registration_ids[0].member_id, member2) diff --git a/golem_activity_queue/tests/test_golem_activity_queue.py b/golem_activity_queue/tests/test_golem_activity_queue.py new file mode 100644 index 0000000..a32734e --- /dev/null +++ b/golem_activity_queue/tests/test_golem_activity_queue.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" GOLEM Activity Queue testing """ + +from odoo.tests.common import TransactionCase +from odoo.exceptions import ValidationError + + +class TestGolemActivityQueue(TransactionCase): + """ GOLEM Activity Queue testing """ + + def setUp(self, *args, **kwargs): + """ Bootstrap ActivityQueue """ + super(TestGolemActivityQueue, self).setUp(*args, **kwargs) + + self.season = self.env['golem.season'].sudo().create({'name': u'Season 1'}) + self.season.do_default_season() + #préparation des données pour la création des membres + self.data_member_1 = {'lastname': u'LAST1', + 'firstname': u'First1', + 'season_ids':[(4, self.season.id, False)] + } + self.data_member_2 = {'lastname': u'LAST2', + 'firstname': u'First2', + 'season_ids':[(4, self.season.id, False)] + } + self.member1 = self.env['golem.member'] + self.member2 = self.env['golem.member'] + #préparation des donnée pour la création de l'activité + type_id = self.ref("golem_activity.golem_activity_type_activity") + self.data_activity = { + 'name': u'Activity 1', + 'season_id': self.season.id, + 'type_id': type_id} + self.activity = self.env['golem.activity'] + self.activity_queue = self.env['golem.activity.queue'] + + def test_activity_queue_basic(self): + """ Test activity queue bases """ + member1 = self.member1.create(self.data_member_1) + activity = self.activity.create(self.data_activity) + activity_queue = self.activity_queue.create({'activity_id': activity.id, + 'member_id': member1.id}) + self.assertEqual(activity.activity_queue_ids[0], activity_queue) + self.assertEqual(member1.activity_queue_ids[0], activity_queue) + + #test interdiction d'inscrire sur attente si inscrit sur activité + def test_check_member_registration(self): + """ Test activity queue bases """ + #creation du membre et de l'activité + member1 = self.member1.create(self.data_member_1) + activity = self.activity.create(self.data_activity) + #enregistrement du membre 1 sur activity + activity.write({'activity_registration_ids': [(0, False, {'activity_id': activity.id, + 'member_id': member1.id})]}) + #vérification que le membre 1 est inscrit sur activité + self.assertEqual(activity.activity_registration_ids[0].member_id, member1) + #inscription du meme membre sur l'attente du meme activié: interdit + with self.assertRaises(ValidationError): + activity_queue = self.activity_queue.create({'activity_id': activity.id, + 'member_id': member1.id}) diff --git a/golem_activity_queue/tests/test_golem_member.py b/golem_activity_queue/tests/test_golem_member.py new file mode 100644 index 0000000..f7a20b9 --- /dev/null +++ b/golem_activity_queue/tests/test_golem_member.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 Youssef El Ouahby +# Copyright 2018 Fabien Bourgeois +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" GOLEM member testing """ + + +from odoo.tests.common import TransactionCase +# from psycopg2 import IntegrityError + + +class TestGolemMember(TransactionCase): + """ GOLEM member testing """ + + def setUp(self, *args, **kwargs): + """ Bootstrap member """ + super(TestGolemMember, self).setUp(*args, **kwargs) + #création du saison + self.season = self.env['golem.season'].sudo().create({'name': u'Season 1'}) + self.season.do_default_season() + #préparation des données pour la création des membres + self.data_member_1 = {'lastname': u'LAST1', + 'firstname': u'First1', + 'season_ids':[(4, self.season.id, False)] + } + self.data_member_2 = {'lastname': u'LAST2', + 'firstname': u'First2', + 'season_ids':[(4, self.season.id, False)] + } + self.member1 = self.env['golem.member'] + self.member2 = self.env['golem.member'] + #préparation des donnée pour la création de l'activité + type_id = self.ref("golem_activity.golem_activity_type_activity") + self.data_activity = { + 'name': u'Activity 1', + 'season_id': self.season.id, + 'type_id': type_id} + self.activity = self.env['golem.activity'] + + + def test_member_creation(self): + """ Test member creation """ + member1 = self.member1.create(self.data_member_1) + #verification que le membre n'est inscrit en aucune attente + self.assertFalse(member1.activity_queue_ids) + + + def test_automated_registration(self): + """ Test automated registration """ + #création de 2 membre est une activité + member1 = self.member1.create(self.data_member_1) + member2 = self.member2.create(self.data_member_2) + activity = self.activity.create(self.data_activity) + #membre 1 inscrit sur activity + registration = { + 'activity_id' : activity.id, + 'member_id' : member1.id + } + #memebre 2 inscrit sur attente + queue = { + 'activity_id' : activity.id, + 'member_id' : member2.id + } + #Verification que l'atente est le basculement automatique est aactivé + self.assertTrue(activity.queue_allowed) + self.assertTrue(activity.auto_registration_from_queue) + #réduire le nombre de place sur activity à 1 + activity.write({'places': 1}) + #enregistrement du membre 1 sur activity et memebre 2 sur attente + activity.write({'activity_registration_ids': [(0, False, registration)]}) + activity.write({'activity_queue_ids': [(0, False, queue)]}) + #vérification des membre 1 sur registration et membre 2 sur attente + self.assertEqual(activity.activity_registration_ids[0].member_id, member1) + self.assertEqual(activity.activity_queue_ids[0].member_id, member2) + #suppression du membre 1 de l'activity + member1.write({'activity_registration_ids': [(2, + member1.activity_registration_ids[0].id, + False)]}) + #verifcation de la presence du membre 2 sur activity : basculement depuis attente + self.assertEqual(activity.activity_registration_ids[0].member_id, member2) + #verification de l'attente est vide + self.assertFalse(activity.activity_queue_ids) + + #test de l'inscription d'un membre à travers l'assistan lancé par le bouton + def test_choose_queue_to_register(self): + """ Queue register through wizard """ + + #création de 2 membre est une activité + member1 = self.member1.create(self.data_member_1) + member2 = self.member2.create(self.data_member_2) + activity = self.activity.create(self.data_activity) + #membre 1 inscrit sur activity + registration = { + 'activity_id' : activity.id, + 'member_id' : member1.id + } + #réduire le nombre de place sur activity à 1 + activity.write({'places': 1}) + #enregistrement du membre 1 sur activity et memebre 2 sur attente + activity.write({'activity_registration_ids': [(0, False, registration)]}) + #enregistrement du membre 2 a travers l'assistant + queue_register_wizard = self.env['golem.activity.queue.choose.wizard'].create({ + 'activity_id': activity.id, + 'member_id': member2.id + }) + queue_register_wizard.register_in_queue() + + #verifcation de l'inscription en queue + self.assertEqual(activity.activity_queue_ids[0].member_id, member2) + self.assertEqual(member2.activity_queue_ids[0].activity_id, activity) diff --git a/golem_activity_queue/views/golem_activity_queue_views.xml b/golem_activity_queue/views/golem_activity_queue_views.xml new file mode 100644 index 0000000..8caecc8 --- /dev/null +++ b/golem_activity_queue/views/golem_activity_queue_views.xml @@ -0,0 +1,96 @@ + + + + + + + queue.form + golem.activity.queue + +
+ + + + + + + +
+
+
+ + + queue.search + golem.activity.queue + + + + + + + + + + + + + + + + + + + queue.tree + golem.activity.queue + + + + + + + + + + + + + + + GOLEM Activity Queue List + golem.activity.queue + tree,search + {'search_default_season_default': True} + + + +
+
diff --git a/golem_activity_queue/views/golem_activity_views.xml b/golem_activity_queue/views/golem_activity_views.xml new file mode 100644 index 0000000..5f03397 --- /dev/null +++ b/golem_activity_queue/views/golem_activity_views.xml @@ -0,0 +1,83 @@ + + + + + + Add the queue registration items to activity form + golem.activity + + + + + + + + + +