diff --git a/golem_resource/__manifest__.py b/golem_resource/__manifest__.py index 10b32de..eb6f91e 100644 --- a/golem_resource/__manifest__.py +++ b/golem_resource/__manifest__.py @@ -20,7 +20,7 @@ 'name': 'GOLEM non-profit resources', 'summary': 'GOLEM resources management', 'description': ''' GOLEM resources management ''', - 'version': '10.0.1.1.1', + 'version': '10.0.1.2.0', 'category': 'GOLEM', 'author': 'Youssef El Ouahby, Fabien Bourgeois', 'license': 'AGPL-3', @@ -28,7 +28,8 @@ 'installable': True, 'depends': ['product'], 'data': ['views/golem_resource_views.xml', - 'views/golem_reservation_views.xml', + 'views/golem_resource_type_views.xml', + 'views/golem_resource_reservation_views.xml', 'wizard/golem_reservation_rejection_views.xml', 'security/ir.model.access.csv'] } diff --git a/golem_resource/models/__init__.py b/golem_resource/models/__init__.py index 6d47599..d3aab06 100644 --- a/golem_resource/models/__init__.py +++ b/golem_resource/models/__init__.py @@ -16,4 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from . import golem_resource +from . import golem_resource_type, \ + golem_resource_timetable, \ + golem_resource, \ + golem_resource_reservation diff --git a/golem_resource/models/golem_resource.py b/golem_resource/models/golem_resource.py index 672f484..06bbfa8 100644 --- a/golem_resource/models/golem_resource.py +++ b/golem_resource/models/golem_resource.py @@ -18,9 +18,7 @@ """ GOLEM Resources management """ -from math import modf -from odoo import models, fields, api, _ -from odoo.exceptions import UserError, ValidationError +from odoo import models, fields, api class GolemResource(models.Model): @@ -50,180 +48,3 @@ class GolemResource(models.Model): """ Toggles active boolean """ for resource in self: resource.active = not resource.active - - -class GolemResourceReservation(models.Model): - """ GOLEM Resource Reservation Model """ - _name = 'golem.resource.reservation' - _description = 'GOLEM Reservation Model' - - name = fields.Char(compute='_compute_name', store=True) - # TODO: handle multiple days reservation - date = fields.Date(required=True, index=True) - hour_start = fields.Float('Start hour', required=True) - hour_stop = fields.Float('Stop hour', required=True) - date_start = fields.Datetime(compute='_compute_date_start', store=True, index=True) - date_stop = fields.Datetime(compute='_compute_date_stop', store=True, index=True) - - resource_id = fields.Many2one('golem.resource', required=True, index=True, - string='Resource') - user_id = fields.Many2one('res.users', required=True, index=True, - string='User', - default=lambda self: self.env.user) - partner_id = fields.Many2one('res.partner', string='On behalf of', - required=True, index=True) - status = fields.Selection([ - ('draft', 'Draft'), - ('confirmed', 'Confirmed'), - ('canceled', 'Canceled'), - ('validated', 'Validated'), - ('rejected', 'Rejected'), - ], default='draft') - - rejection_reason = fields.Text() - - @api.depends('resource_id', 'date') - def _compute_name(self): - """ Computes reservation name """ - for reservation in self: - reservation.name = u'{}/{}'.format(reservation.resource_id.name, - reservation.date) - - @api.depends('date', 'hour_start') - def _compute_date_start(self): - """ Computes Date start """ - for reservation in self: - hour_start, minute_start = modf(reservation.hour_start) - minute_start = int(round(minute_start * 60)) - reservation.date_start = u'{} {}:{}'.format(reservation.date, - hour_start, minute_start) - - @api.depends('date', 'hour_stop') - def _compute_date_stop(self): - """ Computes Date stop """ - for reservation in self: - hour_stop, minute_stop = modf(reservation.hour_stop) - minute_stop = int(round(minute_stop * 60)) - reservation.date_stop = u'{} {}:{}'.format(reservation.date, - hour_stop, minute_stop) - - @api.multi - def status_draft(self): - """ Status to draft """ - self.write({'status': 'draft'}) - - @api.multi - def status_confirm(self): - """ Confirms reservation, or validates it if not workflow is involved """ - for reservation in self: - if reservation.resource_id.validation_required: - reservation.status = 'confirmed' - else: - reservation.status_validated() - - - @api.multi - def status_canceled(self): - """ Status to cancel """ - self.write({'status': 'canceled'}) - - @api.multi - def status_validated(self): - """ Status to validated """ - self.write({'status': 'validated'}) - - @api.multi - def status_rejected(self): - """ Wizard call for reservation reject """ - self.ensure_one() - reservation_id = self[0] - return {'name' : _('Please enter the rejection reason'), - 'type' : 'ir.actions.act_window', - 'res_model' : 'golem.reservation.rejection.wizard', - 'context': {'default_reservation_id': reservation_id.id}, - 'view_mode': 'form', - 'target': 'new'} - - - @api.constrains('status') - def check_confirmed(self): - """ Check date coherence on reservation confirmation """ - for reservation in self: - if reservation.status == 'confirmed': - # Check is reservation is not taking place out of the resource avaibility period - if reservation.date < reservation.resource_id.avaibility_start or \ - reservation.date > reservation.resource_id.avaibility_stop: - uerr = _('Not allowed, the resource is not available in ' - 'this period, please choose another périod before ' - 'confirming') - raise UserError(uerr) - # Check if reservation is not taking place out the avaibility timetables - is_day_allowed = False - for timetable in reservation.resource_id.timetable_ids: - # Check for the time according to resource timetable avaibility - date = fields.Datetime.from_string(reservation.date) - if int(timetable.weekday) == date.weekday(): - is_day_allowed = True - if reservation.hour_start < timetable.date_start or \ - reservation.hour_stop > timetable.date_stop: - uerr = _('Not allowed, the resource is not available ' - 'during this period, please choose another ' - 'time before confirming.') - raise UserError(uerr) - if not is_day_allowed: - uerr = _('Not allowed, the resource is not available ' - 'this day. Please choose another date.') - raise UserError(uerr) - # Check if the resource is already taken during this period - # PERF : check the date, not iterate over all reservations - domain = [('resource_id', '=', reservation.resource_id.id), - ('date', '=', reservation.date), - ('status', '=', 'confirmed'), - ('id', '!=', reservation.id)] - reservations = self.env['golem.resource.reservation'].search(domain) - for other_res in reservations: - if (other_res.hour_start < reservation.hour_start < other_res.hour_stop) or \ - (other_res.hour_start < reservation.hour_stop < other_res.hour_stop): - uerr = _('Not allowed, the resource is already taken ' - 'during this period : from {} to {} this day, ' - 'please choose another périod before confirming.') - raise UserError(uerr.format(reservation.date_start, - reservation.date_stop)) - # Finally, validate the reservation if all checks have passed - if reservation.resource_id.validation_required: - reservation.status = 'validated' - - -class GolemResourceType(models.Model): - """ GOLEM Resource Type """ - _name = 'golem.resource.type' - _description = 'GOLEM Resource Type' - _sql_constraints = [('golem_resource_type_name_uniq', - 'UNIQUE (name)', - 'Resource type must be unique.')] - - name = fields.Char(string='Resource Type', required=True, index=True) - -class GolemTimetable(models.Model): - """ Golem Timetable """ - _name = "golem.resource.timetable" - _description = "Golem Timetable" - _rec_name = 'weekday' - - resource_id = fields.Many2one('golem.resource', required=True, - string='Linked resource') - weekday = fields.Selection([('0', _('Monday')), - ('1', _('Tuesday')), - ('2', _('Wednesday')), - ('3', _('Thursday')), - ('4', _('Friday')), - ('5', _('Saturday')), - ('6', _('Sunday'))], copy=False) - time_start = fields.Float(required=True, string='Start') - time_stop = fields.Float(required=True, string='Stop') - - @api.constrains('time_start', 'time_stop') - def _check_time_consistency(self): - for timetable in self: - if timetable.time_stop < timetable.time_start: - raise ValidationError(_('End time should be after than start time')) diff --git a/golem_resource/models/golem_resource_reservation.py b/golem_resource/models/golem_resource_reservation.py new file mode 100644 index 0000000..b54a8d5 --- /dev/null +++ b/golem_resource/models/golem_resource_reservation.py @@ -0,0 +1,165 @@ +# -*- 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 Resource Reservation """ + +from math import modf +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class GolemResourceReservation(models.Model): + """ GOLEM Resource Reservation Model """ + _name = 'golem.resource.reservation' + _description = 'GOLEM Reservation Model' + + name = fields.Char(compute='_compute_name', store=True) + # TODO: handle multiple days reservation + date = fields.Date(required=True, index=True) + hour_start = fields.Float('Start hour', required=True) + hour_stop = fields.Float('Stop hour', required=True) + date_start = fields.Datetime(compute='_compute_date_start', store=True, index=True) + date_stop = fields.Datetime(compute='_compute_date_stop', store=True, index=True) + + resource_id = fields.Many2one('golem.resource', required=True, index=True, + string='Resource') + user_id = fields.Many2one('res.users', required=True, index=True, + string='User', + default=lambda self: self.env.user) + partner_id = fields.Many2one('res.partner', string='On behalf of', + required=True, index=True) + status = fields.Selection([ + ('draft', 'Draft'), + ('confirmed', 'Confirmed'), + ('canceled', 'Canceled'), + ('validated', 'Validated'), + ('rejected', 'Rejected'), + ], default='draft') + + rejection_reason = fields.Text() + + @api.depends('resource_id', 'date') + def _compute_name(self): + """ Computes reservation name """ + for reservation in self: + reservation.name = u'{}/{}'.format(reservation.resource_id.name, + reservation.date) + + @api.depends('date', 'hour_start') + def _compute_date_start(self): + """ Computes Date start """ + for reservation in self: + hour_start, minute_start = modf(reservation.hour_start) + minute_start = int(round(minute_start * 60)) + reservation.date_start = u'{} {}:{}'.format(reservation.date, + hour_start, minute_start) + + @api.depends('date', 'hour_stop') + def _compute_date_stop(self): + """ Computes Date stop """ + for reservation in self: + hour_stop, minute_stop = modf(reservation.hour_stop) + minute_stop = int(round(minute_stop * 60)) + reservation.date_stop = u'{} {}:{}'.format(reservation.date, + hour_stop, minute_stop) + + @api.multi + def status_draft(self): + """ Status to draft """ + self.write({'status': 'draft'}) + + @api.multi + def status_confirm(self): + """ Confirms reservation, or validates it if not workflow is involved """ + for reservation in self: + if reservation.resource_id.validation_required: + reservation.status = 'confirmed' + else: + reservation.status_validated() + + + @api.multi + def status_canceled(self): + """ Status to cancel """ + self.write({'status': 'canceled'}) + + @api.multi + def status_validated(self): + """ Status to validated """ + self.write({'status': 'validated'}) + + @api.multi + def status_rejected(self): + """ Wizard call for reservation reject """ + self.ensure_one() + reservation_id = self[0] + return {'name' : _('Please enter the rejection reason'), + 'type' : 'ir.actions.act_window', + 'res_model' : 'golem.reservation.rejection.wizard', + 'context': {'default_reservation_id': reservation_id.id}, + 'view_mode': 'form', + 'target': 'new'} + + + @api.constrains('status') + def check_confirmed(self): + """ Check date coherence on reservation confirmation """ + for reservation in self: + if reservation.status == 'confirmed': + # Check is reservation is not taking place out of the resource avaibility period + if reservation.date < reservation.resource_id.avaibility_start or \ + reservation.date > reservation.resource_id.avaibility_stop: + uerr = _('Not allowed, the resource is not available in ' + 'this period, please choose another périod before ' + 'confirming') + raise UserError(uerr) + # Check if reservation is not taking place out the avaibility timetables + is_day_allowed = False + for timetable in reservation.resource_id.timetable_ids: + # Check for the time according to resource timetable avaibility + date = fields.Datetime.from_string(reservation.date) + if int(timetable.weekday) == date.weekday(): + is_day_allowed = True + if reservation.hour_start < timetable.date_start or \ + reservation.hour_stop > timetable.date_stop: + uerr = _('Not allowed, the resource is not available ' + 'during this period, please choose another ' + 'time before confirming.') + raise UserError(uerr) + if not is_day_allowed: + uerr = _('Not allowed, the resource is not available ' + 'this day. Please choose another date.') + raise UserError(uerr) + # Check if the resource is already taken during this period + # PERF : check the date, not iterate over all reservations + domain = [('resource_id', '=', reservation.resource_id.id), + ('date', '=', reservation.date), + ('status', '=', 'confirmed'), + ('id', '!=', reservation.id)] + reservations = self.env['golem.resource.reservation'].search(domain) + for other_res in reservations: + if (other_res.hour_start < reservation.hour_start < other_res.hour_stop) or \ + (other_res.hour_start < reservation.hour_stop < other_res.hour_stop): + uerr = _('Not allowed, the resource is already taken ' + 'during this period : from {} to {} this day, ' + 'please choose another périod before confirming.') + raise UserError(uerr.format(reservation.date_start, + reservation.date_stop)) + # Finally, validate the reservation if all checks have passed + if reservation.resource_id.validation_required: + reservation.status = 'validated' diff --git a/golem_resource/models/golem_resource_timetable.py b/golem_resource/models/golem_resource_timetable.py new file mode 100644 index 0000000..54e63a2 --- /dev/null +++ b/golem_resource/models/golem_resource_timetable.py @@ -0,0 +1,46 @@ +# -*- 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 Resource Timetable """ + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError + +class GolemTimetable(models.Model): + """ Golem Timetable """ + _name = "golem.resource.timetable" + _description = "Golem Timetable" + _rec_name = 'weekday' + + resource_id = fields.Many2one('golem.resource', required=True, + string='Linked resource') + weekday = fields.Selection([('0', _('Monday')), + ('1', _('Tuesday')), + ('2', _('Wednesday')), + ('3', _('Thursday')), + ('4', _('Friday')), + ('5', _('Saturday')), + ('6', _('Sunday'))], copy=False) + time_start = fields.Float(required=True, string='Start') + time_stop = fields.Float(required=True, string='Stop') + + @api.constrains('time_start', 'time_stop') + def _check_time_consistency(self): + for timetable in self: + if timetable.time_stop < timetable.time_start: + raise ValidationError(_('End time should be after than start time')) diff --git a/golem_resource/models/golem_resource_type.py b/golem_resource/models/golem_resource_type.py new file mode 100644 index 0000000..229b059 --- /dev/null +++ b/golem_resource/models/golem_resource_type.py @@ -0,0 +1,31 @@ +# -*- 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 Resource Type """ + +from odoo import models, fields + +class GolemResourceType(models.Model): + """ GOLEM Resource Type """ + _name = 'golem.resource.type' + _description = 'GOLEM Resource Type' + _sql_constraints = [('golem_resource_type_name_uniq', + 'UNIQUE (name)', + 'Resource type must be unique.')] + + name = fields.Char(string='Resource Type', required=True, index=True) diff --git a/golem_resource/views/golem_reservation_views.xml b/golem_resource/views/golem_resource_reservation_views.xml similarity index 100% rename from golem_resource/views/golem_reservation_views.xml rename to golem_resource/views/golem_resource_reservation_views.xml diff --git a/golem_resource/views/golem_resource_type_views.xml b/golem_resource/views/golem_resource_type_views.xml new file mode 100644 index 0000000..ca21927 --- /dev/null +++ b/golem_resource/views/golem_resource_type_views.xml @@ -0,0 +1,43 @@ + + + + + + + + GOLEM Resource Type Tree + golem.resource.type + + + + + + + + + + + + + + + diff --git a/golem_resource/views/golem_resource_views.xml b/golem_resource/views/golem_resource_views.xml index 30a1a20..369d482 100644 --- a/golem_resource/views/golem_resource_views.xml +++ b/golem_resource/views/golem_resource_views.xml @@ -36,16 +36,6 @@ along with this program. If not, see . - - GOLEM Resource Type Tree - golem.resource.type - - - - - - - GOLEM Resource Form @@ -110,8 +100,6 @@ along with this program. If not, see . - . -