From 366e4dc2a1fd432b4f80b4cd9a84d1b6abf08fd3 Mon Sep 17 00:00:00 2001 From: Thibault Francois Date: Mon, 2 Jan 2017 23:16:46 +0100 Subject: [PATCH] [ADD] beesdoo_shift module, first version of the module to manage shifts --- beesdoo_shift/__init__.py | 3 + beesdoo_shift/__openerp__.py | 28 ++ beesdoo_shift/models/__init__.py | 3 + beesdoo_shift/models/planning.py | 118 +++++++++ beesdoo_shift/models/task.py | 36 +++ beesdoo_shift/security/ir.model.access.csv | 6 + beesdoo_shift/views/planning.xml | 80 ++++++ beesdoo_shift/views/task.xml | 77 ++++++ beesdoo_shift/views/task_template.xml | 246 ++++++++++++++++++ beesdoo_shift/wizard/__init__.py | 2 + beesdoo_shift/wizard/batch_template.py | 53 ++++ beesdoo_shift/wizard/batch_template.xml | 41 +++ beesdoo_shift/wizard/instanciate_planning.py | 18 ++ beesdoo_shift/wizard/instanciate_planning.xml | 20 ++ 14 files changed, 731 insertions(+) create mode 100644 beesdoo_shift/__init__.py create mode 100644 beesdoo_shift/__openerp__.py create mode 100644 beesdoo_shift/models/__init__.py create mode 100644 beesdoo_shift/models/planning.py create mode 100644 beesdoo_shift/models/task.py create mode 100644 beesdoo_shift/security/ir.model.access.csv create mode 100644 beesdoo_shift/views/planning.xml create mode 100644 beesdoo_shift/views/task.xml create mode 100644 beesdoo_shift/views/task_template.xml create mode 100644 beesdoo_shift/wizard/__init__.py create mode 100644 beesdoo_shift/wizard/batch_template.py create mode 100644 beesdoo_shift/wizard/batch_template.xml create mode 100644 beesdoo_shift/wizard/instanciate_planning.py create mode 100644 beesdoo_shift/wizard/instanciate_planning.xml diff --git a/beesdoo_shift/__init__.py b/beesdoo_shift/__init__.py new file mode 100644 index 0000000..8d752fb --- /dev/null +++ b/beesdoo_shift/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +import models +import wizard diff --git a/beesdoo_shift/__openerp__.py b/beesdoo_shift/__openerp__.py new file mode 100644 index 0000000..4a2a996 --- /dev/null +++ b/beesdoo_shift/__openerp__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Beescoop Shift Management", + + 'summary': """ + Volonteer Timetable Management""", + + 'description': """ + + """, + + 'author': "Thibault Francois", + 'website': "https://github.com/beescoop/Obeesdoo", + + 'category': 'Coop', + 'version': '0.1', + + 'depends': ['beesdoo_base'], + + 'data': [ + "security/ir.model.access.csv", + "views/task_template.xml", + "views/task.xml", + "views/planning.xml", + "wizard/instanciate_planning.xml", + "wizard/batch_template.xml", + ], +} diff --git a/beesdoo_shift/models/__init__.py b/beesdoo_shift/models/__init__.py new file mode 100644 index 0000000..14cfb0e --- /dev/null +++ b/beesdoo_shift/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +import planning +import task \ No newline at end of file diff --git a/beesdoo_shift/models/planning.py b/beesdoo_shift/models/planning.py new file mode 100644 index 0000000..421c6bc --- /dev/null +++ b/beesdoo_shift/models/planning.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError + +from pytz import UTC +import math +from datetime import datetime, timedelta + +def float_to_time(f): + decimal, integer = math.modf(f) + return "%s:%s" % (str(int(integer)).zfill(2), str(int(round(decimal * 60))).zfill(2)) + +def floatime_to_hour_minute(f): + decimal, integer = math.modf(f) + return int(integer), int(round(decimal * 60)) + +def get_first_day_of_week(): + today = datetime.now() + return datetime.now() - timedelta(days=today.weekday()) + +class TaskType(models.Model): + _name = 'beesdoo.shift.type' + + name = fields.Char() + description = fields.Text() + active = fields.Boolean(default=True) + +class DayNumber(models.Model): + _name = 'beesdoo.shift.daynumber' + + _order = 'number asc' + + name = fields.Char() + number = fields.Integer("Day Number", help="From 1 to N, When you will instanciate your planning, Day 1 will be the start date of the instance, Day 2 the second, etc...") + active = fields.Boolean(default=True) + +class Planning(models.Model): + _name = 'beesdoo.shift.planning' + + name = fields.Char() + task_template_ids = fields.One2many('beesdoo.shift.template', 'planning_id') + +class TaskTemplate(models.Model): + _name = 'beesdoo.shift.template' + + name = fields.Char(required=True) + planning_id = fields.Many2one('beesdoo.shift.planning', required=True) + day_nb_id = fields.Many2one('beesdoo.shift.daynumber', string='Day', required=True) + task_type_id = fields.Many2one('beesdoo.shift.type', string="Type") + start_time = fields.Float(required=True) + end_time = fields.Float(required=True) + + duration = fields.Float(help="Duration in Hour") + worker_nb = fields.Integer(string="Number of worker", help="Max number of worker for this task", default=1) + worker_ids = fields.Many2many('res.partner', string="Recurrent worker assigned", domain=[('eater', '=', 'worker_eater')]) + remaining_worker = fields.Integer(compute="_get_remaining", store=True, string="Remaining Place") + active = fields.Boolean(default=True) + #For Kanban View Only + color = fields.Integer('Color Index') + worker_name = fields.Char(compute="_get_worker_name") + #For calendar View + start_date = fields.Datetime(compute="_get_fake_date", search="_dummy_search") + end_date = fields.Datetime(compute="_get_fake_date", search="_dummy_search") + + @api.depends('start_time', 'end_time') + def _get_fake_date(self): + today = datetime.strptime(self._context.get('visualize_date'), '%Y-%m-%d') if self._context.get('visualize_date') else get_first_day_of_week() + for rec in self: + day = today + timedelta(days=rec.day_nb_id.number - 1) + h_begin, m_begin = floatime_to_hour_minute(rec.start_time) + h_end, m_end = floatime_to_hour_minute(rec.end_time) + rec.start_date = fields.Datetime.context_timestamp(self, day).replace(hour=h_begin, minute=m_begin, second=0).astimezone(UTC) + rec.end_date = fields.Datetime.context_timestamp(self, day).replace(hour=h_end, minute=m_end, second=0).astimezone(UTC) + + def _dummy_search(self, operator, value): + return [] + + @api.depends('worker_ids', 'worker_nb') + def _get_remaining(self): + for rec in self: + rec.remaining_worker = rec.worker_nb - len(rec.worker_ids) + + @api.depends("worker_ids") + def _get_worker_name(self): + for rec in self: + rec.worker_name = ','.join(rec.worker_ids.mapped('display_name')) + + @api.constrains('worker_nb', 'worker_ids') + def _nb_worker_max(self): + for rec in self: + if len(rec.worker_ids) > rec.worker_nb: + raise UserError(_('you cannot assign more worker then the number maximal define on the template')) + + + @api.onchange('start_time', 'end_time') + def _get_duration(self): + if self.start_time and self.end_time: + self.duration = self.end_time - self.start_time + + @api.onchange('duration') + def _set_duration(self): + if self.start_time: + self.end_time = self.start_time +self.duration + + def _generate_task_day(self): + tasks = self.env['beesdoo.shift.shift'] + for rec in self: + for i in xrange(0, rec.worker_nb): + tasks |= tasks.create({ + 'name' : "%s (%s) - (%s) [%s]" % (rec.name, float_to_time(rec.start_time), float_to_time(rec.end_time), i), + 'task_template_id' : rec.id, + 'task_type_id' : rec.task_type_id.id, + 'worker_id' : rec.worker_ids[i].id if len(rec.worker_ids) > i else False, + 'start_time' : rec.start_date, + 'end_time' : rec.end_date, + }) + return tasks \ No newline at end of file diff --git a/beesdoo_shift/models/task.py b/beesdoo_shift/models/task.py new file mode 100644 index 0000000..d603b7e --- /dev/null +++ b/beesdoo_shift/models/task.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields + +STATES = [ + ('draft', 'Unconfirmed'), + ('open', 'Confirmed'), + ('done', 'Attended'), + ('absent', 'Absent'), + ('excused', 'Excused'), + ('replaced', 'Replaced'), + ('cancel', 'Cancelled'), +] + +class Task(models.Model): + _name = 'beesdoo.shift.shift' + + #EX01 ADD inheritance + _inherit = ['mail.thread'] + + name = fields.Char(track_visibility='always') + task_template_id = fields.Many2one('beesdoo.shift.template') + planning_id = fields.Many2one(related='task_template_id.planning_id', store=True) + task_type_id = fields.Many2one('beesdoo.shift.type', string="Task Type") + worker_id = fields.Many2one('res.partner', track_visibility='onchange', domain=[('eater', '=', 'worker_eater')]) + start_time = fields.Datetime(track_visibility='always') + end_time = fields.Datetime(track_visibility='always') + state = fields.Selection(STATES, default='draft', track_visibility='onchange') + + def message_auto_subscribe(self, updated_fields, values=None): + self._add_follower(values) + return super(Task, self).message_auto_subscribe(updated_fields, values=values) + + def _add_follower(self, vals): + if vals.get('worker_id'): + worker = self.env['res.partner'].browse(vals['worker_id']) + self.message_subscribe(partner_ids=worker.ids) \ No newline at end of file diff --git a/beesdoo_shift/security/ir.model.access.csv b/beesdoo_shift/security/ir.model.access.csv new file mode 100644 index 0000000..a63e74b --- /dev/null +++ b/beesdoo_shift/security/ir.model.access.csv @@ -0,0 +1,6 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_coopplanning_task_type,access_coopplanning_task_type,model_beesdoo_shift_type,,1,1,1,1 +access_coopplanning_daynumber,access_coopplanning_daynumber,model_beesdoo_shift_daynumber,,1,1,1,1 +access_coopplanning_planning,access_coopplanning_planning,model_beesdoo_shift_planning,,1,1,1,1 +access_coopplanning_task_template,access_coopplanning_task_template,model_beesdoo_shift_template,,1,1,1,1 +access_coopplanning_task,access_coopplanning_task,model_beesdoo_shift_shift,,1,1,1,1 diff --git a/beesdoo_shift/views/planning.xml b/beesdoo_shift/views/planning.xml new file mode 100644 index 0000000..1ed7edb --- /dev/null +++ b/beesdoo_shift/views/planning.xml @@ -0,0 +1,80 @@ + + + Planning List + beesdoo.shift.planning + + + + + + + + + Planning Action + beesdoo.shift.template + kanban,tree,form,calendar,pivot + {'group_by': 'day_nb_id', + 'search_default_planning_id': active_id, + 'default_planning_id': active_id} + + + + Instanciate Planning Action + beesddoo.shift.generate_planning + form + new + + + + Planning Form + beesdoo.shift.planning + +
+
+ +
+ +
+ +
+
+

+ +

+
+ +