From a4fa72fe14ef200992cf8e84dabe9bfaa8e460b1 Mon Sep 17 00:00:00 2001 From: Jaime Arroyo Date: Tue, 19 Mar 2019 13:51:44 +0100 Subject: [PATCH 01/45] [11.0][ADD] hr_course --- hr_course/README.rst | 85 ++++ hr_course/__init__.py | 2 + hr_course/__manifest__.py | 23 + hr_course/i18n/es.po | 343 +++++++++++++++ hr_course/models/__init__.py | 2 + hr_course/models/hr_course.py | 229 ++++++++++ hr_course/models/hr_employee.py | 28 ++ hr_course/readme/CONTRIBUTORS.rst | 2 + hr_course/readme/DESCRIPTION.rst | 2 + hr_course/readme/USAGE.rst | 6 + hr_course/security/course_security.xml | 31 ++ hr_course/security/ir.model.access.csv | 7 + hr_course/static/description/icon.png | Bin 0 -> 9455 bytes hr_course/static/description/index.html | 429 +++++++++++++++++++ hr_course/tests/__init__.py | 1 + hr_course/tests/test_hr_course.py | 71 +++ hr_course/views/hr_course_category_views.xml | 49 +++ hr_course/views/hr_course_views.xml | 175 ++++++++ hr_course/views/hr_employee_views.xml | 50 +++ 19 files changed, 1535 insertions(+) create mode 100644 hr_course/README.rst create mode 100644 hr_course/__init__.py create mode 100644 hr_course/__manifest__.py create mode 100644 hr_course/i18n/es.po create mode 100644 hr_course/models/__init__.py create mode 100644 hr_course/models/hr_course.py create mode 100644 hr_course/models/hr_employee.py create mode 100644 hr_course/readme/CONTRIBUTORS.rst create mode 100644 hr_course/readme/DESCRIPTION.rst create mode 100644 hr_course/readme/USAGE.rst create mode 100644 hr_course/security/course_security.xml create mode 100644 hr_course/security/ir.model.access.csv create mode 100644 hr_course/static/description/icon.png create mode 100644 hr_course/static/description/index.html create mode 100644 hr_course/tests/__init__.py create mode 100644 hr_course/tests/test_hr_course.py create mode 100644 hr_course/views/hr_course_category_views.xml create mode 100644 hr_course/views/hr_course_views.xml create mode 100644 hr_course/views/hr_employee_views.xml diff --git a/hr_course/README.rst b/hr_course/README.rst new file mode 100644 index 00000000000..358a076be38 --- /dev/null +++ b/hr_course/README.rst @@ -0,0 +1,85 @@ +========= +Hr Course +========= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github + :target: https://github.com/OCA/hr/tree/11.0/hr_course + :alt: OCA/hr +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/hr-11-0/hr-11-0-hr_course + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/116/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows your to manage employee's training courses and all its +validation process. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To create a Course go to Employees -> Courses. + +Fill the information and click assign attendees to continue. Assign them and +then start the course. + +One it has finished you must enter the results and finish the course. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella +* Jaime Arroyo + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/hr `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_course/__init__.py b/hr_course/__init__.py new file mode 100644 index 00000000000..0ee8b5073e2 --- /dev/null +++ b/hr_course/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import tests diff --git a/hr_course/__manifest__.py b/hr_course/__manifest__.py new file mode 100644 index 00000000000..7ea8618b5e7 --- /dev/null +++ b/hr_course/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Hr Course', + 'summary': """ + This module allows your to manage employee's training courses""", + 'version': '11.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Creu Blanca,Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/hr/', + 'depends': [ + 'hr', + 'mail', + ], + 'data': [ + 'security/course_security.xml', + 'security/ir.model.access.csv', + 'views/hr_course_category_views.xml', + 'views/hr_course_views.xml', + 'views/hr_employee_views.xml', + ], +} diff --git a/hr_course/i18n/es.po b/hr_course/i18n/es.po new file mode 100644 index 00000000000..d9da6ea2a6c --- /dev/null +++ b/hr_course/i18n/es.po @@ -0,0 +1,343 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_course +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-03-21 09:30+0000\n" +"PO-Revision-Date: 2019-03-21 10:41+0100\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: es\n" +"X-Generator: Poedit 2.0.6\n" + +#. module: hr_course +#: selection:hr.course.attendee,result:0 +msgid "Absent" +msgstr "Ausente" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_active +msgid "Active" +msgstr "Activo" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Assign attendees" +msgstr "Asignar asistentes" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendant_ids +msgid "Attendant" +msgstr "Asistente" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Attendees" +msgstr "Asistentes" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_authorized_by +msgid "Authorized by" +msgstr "Autorizado por" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Back to Draft" +msgstr "Devolver a borrador" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Cancel Course" +msgstr "Cancelar curso" + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "Cancelled" +msgstr "Cancelado" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Cancelled Course" +msgstr "Curso cancelado" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_id_9820 +#: model:ir.ui.view,arch_db:hr_course.hr_course_search_view +#: model:ir.ui.view,arch_db:hr_course.view_course_category_search +msgid "Category" +msgstr "Categoría" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Complete Course" +msgstr "Curso finalizado" + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "Completed" +msgstr "Finalizado" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_course_id +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Course" +msgstr "Curso" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_course_attendee_ids +msgid "Course Attendee" +msgstr "Asistente al curso" + +#. module: hr_course +#: model:ir.actions.act_window,name:hr_course.open_view_course_category_form +#: model:ir.ui.menu,name:hr_course.menu_view_course_category_form +#: model:ir.ui.view,arch_db:hr_course.view_course_category_form +#: model:ir.ui.view,arch_db:hr_course.view_course_category_list +#: model:ir.ui.view,arch_db:hr_course.view_course_category_search +msgid "Course Categories" +msgstr "Tipos de cursos" + +#. module: hr_course +#: model:ir.model,name:hr_course.model_hr_course_category +msgid "Course Category" +msgstr "Tipo de curso" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_cost +msgid "Course Cost" +msgstr "Coste del curso" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_search_view +msgid "Course Name" +msgstr "Nombre del curso" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Course Results" +msgstr "Resultado del curso" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_name +msgid "Course category" +msgstr "Tipo del curso" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Course name" +msgstr "Nombre del curso" + +#. module: hr_course +#: model:ir.actions.act_window,name:hr_course.action_hr_course +#: model:ir.actions.act_window,name:hr_course.action_view_course +#: model:ir.model.fields,field_description:hr_course.field_hr_employee_courses_ids +#: model:ir.ui.menu,name:hr_course.menu_hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +#: model:ir.ui.view,arch_db:hr_course.view_hr_employee_form +msgid "Courses" +msgstr "Cursos" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_create_uid +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_create_uid +#: model:ir.model.fields,field_description:hr_course.field_hr_course_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_create_date +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_create_date +#: model:ir.model.fields,field_description:hr_course.field_hr_course_create_date +msgid "Created on" +msgstr "Creado en" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_currency_id +msgid "Currency" +msgstr "Moneda" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_display_name +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_display_name +#: model:ir.model.fields,field_description:hr_course.field_hr_course_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "Draft" +msgstr "Borrador" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Draft Course" +msgstr "Curso en borrador" + +#. module: hr_course +#: model:ir.model,name:hr_course.model_hr_employee +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_employee_id +msgid "Employee" +msgstr "Empleado" + +#. module: hr_course +#: code:addons/hr_course/models/hr_course.py:119 +#, python-format +msgid "Employees removed from this course:

" +msgstr "Empleados eliminados de este curso:

" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_course_end +#: model:ir.model.fields,field_description:hr_course.field_hr_course_end_date +msgid "End date" +msgstr "Fecha final" + +#. module: hr_course +#: selection:hr.course.attendee,result:0 +msgid "Failed" +msgstr "Suspendido" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_permanence +msgid "Has Permanence" +msgstr "Tiene permanencia" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_id +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_id +#: model:ir.model.fields,field_description:hr_course.field_hr_course_id +msgid "ID" +msgstr "ID" + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "In progress" +msgstr "En progreso" + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "In validation" +msgstr "En corrección" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course___last_update +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee___last_update +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_write_uid +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_write_uid +#: model:ir.model.fields,field_description:hr_course.field_hr_course_write_uid +msgid "Last Updated by" +msgstr "Última actualización de" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_write_date +#: model:ir.model.fields,field_description:hr_course.field_hr_course_category_write_date +#: model:ir.model.fields,field_description:hr_course.field_hr_course_write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_name +#: model:ir.model.fields,field_description:hr_course.field_hr_course_name +msgid "Name" +msgstr "Nombre" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_employee_count_courses +msgid "Number of courses" +msgstr "Número de cursos" + +#. module: hr_course +#: selection:hr.course.attendee,result:0 +msgid "Passed" +msgstr "Aprobado" + +#. module: hr_course +#: selection:hr.course.attendee,result:0 +msgid "Pending" +msgstr "Pendiente" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_permanence_time +msgid "Permanence time" +msgstr "Tiempo de permanencia" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Rate Course" +msgstr "Corregir curso" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_result +msgid "Result" +msgstr "Resultado" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "Start Course" +msgstr "Empezar curso" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_course_start +#: model:ir.model.fields,field_description:hr_course.field_hr_course_start_date +msgid "Start date" +msgstr "Fecha de inicio" + +#. module: hr_course +#: model:ir.model.fields,field_description:hr_course.field_hr_course_attendee_state +#: model:ir.model.fields,field_description:hr_course.field_hr_course_state +#: model:ir.ui.view,arch_db:hr_course.hr_course_search_view +msgid "State" +msgstr "Estado" + +#. module: hr_course +#: sql_constraint:hr.course.category:0 +msgid "Category already exists !" +msgstr "Esta categoría ya existe !" + +#. module: hr_course +#: code:addons/hr_course/models/hr_course.py:85 +#, python-format +msgid "The start date cannot be later than the end date." +msgstr "La fecha de inicio no puede ser posterior a la de fin." + +#. module: hr_course +#: selection:hr.course,state:0 +msgid "Waiting attendees" +msgstr "Esperando asistentes" + +#. module: hr_course +#: code:addons/hr_course/models/hr_course.py:167 +#, python-format +msgid "You cannot complete the course with pending results" +msgstr "No se puede finalizar el curso con resultados pendientes" + +#. module: hr_course +#: model:ir.ui.view,arch_db:hr_course.hr_course_form_view +msgid "You should set a course back to draft only if you cancelled it by mistake or if some of its information is incorrect. Do you want to continue?" +msgstr "Solo deberías devolver un curso a borrador si se ha cancelado por error o si alguna de su información es incorrecta. Quieres continuar?" + +#. module: hr_course +#: model:ir.model,name:hr_course.model_hr_course +msgid "hr.course" +msgstr "hr.course" + +#. module: hr_course +#: model:ir.model,name:hr_course.model_hr_course_attendee +msgid "hr.course.attendee" +msgstr "hr.course.attendee" + +#. module: hr_course +#: model:ir.model,name:hr_course.model_hr_course_result +msgid "hr.course.result" +msgstr "hr.course.result" diff --git a/hr_course/models/__init__.py b/hr_course/models/__init__.py new file mode 100644 index 00000000000..dc9d3b1748a --- /dev/null +++ b/hr_course/models/__init__.py @@ -0,0 +1,2 @@ +from . import hr_employee +from . import hr_course diff --git a/hr_course/models/hr_course.py b/hr_course/models/hr_course.py new file mode 100644 index 00000000000..46396d0215b --- /dev/null +++ b/hr_course/models/hr_course.py @@ -0,0 +1,229 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class HrCourse(models.Model): + _name = 'hr.course' + _inherit = 'mail.thread' + + name = fields.Char( + string='Name', required=True, + track_visibility="onchange" + ) + category_id = fields.Many2one( + 'hr.course.category', + string='Category', + required=True, + ) + + start_date = fields.Date( + string="Start date", readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility="onchange", + ) + end_date = fields.Date( + string="End date", readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility="onchange", + ) + currency_id = fields.Many2one( + 'res.currency', string='Currency', + default=lambda self: self.env.user.company_id.currency_id, + ) + cost = fields.Monetary( + string='Course Cost', required=True, + track_visibility="onchange" + ) + authorized_by = fields.Many2one( + string="Authorized by", + comodel_name='hr.employee', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility="onchange" + ) + permanence = fields.Boolean( + string="Has Permanence", + readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility="onchange" + ) + permanence_time = fields.Char( + string='Permanence time', + readonly=True, + states={'draft': [('readonly', False)]}, + track_visibility="onchange" + ) + state = fields.Selection( + [ + ('draft', 'Draft'), + ('waiting_attendees', 'Waiting attendees'), + ('in_progress', 'In progress'), + ('in_validation', 'In validation'), + ('completed', 'Completed'), + ('cancelled', 'Cancelled'), + ], + required=True, + readonly=True, + default='draft', + track_visibility="onchange" + ) + attendant_ids = fields.Many2many( + 'hr.employee', + readonly=True, + states={'waiting_attendees': [('readonly', False)]}, + ) + course_attendee_ids = fields.One2many( + 'hr.course.attendee', + inverse_name='course_id', + readonly=True, + states={'in_validation': [('readonly', False)]}, + ) + + @api.constrains('start_date', 'end_date') + def _check_start_end_dates(self): + self.ensure_one() + if self.start_date and self.end_date and ( + self.start_date > self.end_date + ): + raise ValidationError(_('The start date cannot' + ' be later than the end date.')) + + def all_passed(self): + for attendee in self.course_attendee_ids: + attendee.result = 'passed' + + @api.onchange('permanence') + def _onchange_permanence(self): + self.permanence_time = False + + def _draft2waiting_values(self): + return { + 'state': 'waiting_attendees', + } + + def _attendee_values(self, attendee): + return { + 'employee_id': attendee.id, + 'course_id': self.id + } + + def _waiting2inprogress_values(self): + attendants = [] + employee_attendants = self.course_attendee_ids.mapped('employee_id') + for attendee in self.attendant_ids.filtered( + lambda r: r not in employee_attendants + ): + attendants.append((0, 0, self._attendee_values(attendee))) + deleted_attendees = '' + for course_attendee in self.course_attendee_ids.filtered( + lambda r: r.employee_id not in self.attendant_ids + ): + attendants += course_attendee._remove_from_course() + deleted_attendees += '- %s

'\ + % course_attendee.employee_id.name + if deleted_attendees != '': + self.message_post(_('Employees removed from' + ' this course:

' + deleted_attendees)) + return { + 'state': 'in_progress', + 'course_attendee_ids': attendants + } + + def _inprogress2validation_values(self): + return { + 'state': 'in_validation', + } + + def _validation2complete_values(self): + return { + 'state': 'completed', + } + + def _back2draft_values(self): + return { + 'state': 'draft', + } + + def _cancel_course_values(self): + return { + 'state': 'cancelled', + } + + @api.multi + def draft2waiting(self): + for record in self: + record.write(record._draft2waiting_values()) + + @api.multi + def waiting2inprogress(self): + for record in self: + record.write(record._waiting2inprogress_values()) + + @api.multi + def inprogress2validation(self): + for record in self: + record.write(record._inprogress2validation_values()) + + @api.multi + def validation2complete(self): + for record in self: + if self.course_attendee_ids.filtered( + lambda r: r.result == 'pending' and r.active): + raise ValidationError( + _('You cannot complete the course with pending results') + ) + else: + record.write(record._validation2complete_values()) + + @api.multi + def back2draft(self): + for record in self: + record.write(record._back2draft_values()) + + @api.multi + def cancel_course(self): + for record in self: + record.write(record._cancel_course_values()) + + +class HRCourseAttendee(models.Model): + _name = 'hr.course.attendee' + + course_id = fields.Many2one( + 'hr.course', + ondelete='cascade', + readonly=True, + required=True, + ) + name = fields.Char(related='course_id.name', readonly=True) + employee_id = fields.Many2one('hr.employee', readonly=True) + course_start = fields.Date(related='course_id.start_date', readonly=True) + course_end = fields.Date(related='course_id.end_date', readonly=True) + state = fields.Selection(related='course_id.state', readonly=True) + result = fields.Selection( + [ + ('passed', 'Passed'), + ('failed', 'Failed'), + ('absent', 'Absent'), + ('pending', 'Pending'), + ], + string="Result", default='pending', + ) + active = fields.Boolean(default=True, readonly=True) + + def _remove_from_course(self): + return [(1, self.id, {'active': False})] + + +class HRCourseCategory(models.Model): + _name = 'hr.course.category' + _description = "Course Category" + name = fields.Char(string="Course category", required=True) + + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Category already exists !"), + ] diff --git a/hr_course/models/hr_employee.py b/hr_course/models/hr_employee.py new file mode 100644 index 00000000000..41a2754a04f --- /dev/null +++ b/hr_course/models/hr_employee.py @@ -0,0 +1,28 @@ +from odoo import api, fields, models + + +class HrEmployee(models.Model): + _inherit = 'hr.employee' + + count_courses = fields.Integer( + 'Number of courses', + compute='_compute_count_courses' + ) + + courses_ids = fields.One2many( + 'hr.course.attendee', 'employee_id', + string='Courses', readonly=True, + ) + + @api.depends('courses_ids') + def _compute_count_courses(self): + for r in self: + r.count_courses = len(r.courses_ids) + + @api.multi + def action_view_course(self): + action = self.env.ref( + 'hr_course.action_view_course') + result = action.read()[0] + result['domain'] = [('employee_id', '=', self.id)] + return result diff --git a/hr_course/readme/CONTRIBUTORS.rst b/hr_course/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..94e7b0a4038 --- /dev/null +++ b/hr_course/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Enric Tobella +* Jaime Arroyo diff --git a/hr_course/readme/DESCRIPTION.rst b/hr_course/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..5710074fac2 --- /dev/null +++ b/hr_course/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows your to manage employee's training courses and all its +validation process. diff --git a/hr_course/readme/USAGE.rst b/hr_course/readme/USAGE.rst new file mode 100644 index 00000000000..1bf6eb122db --- /dev/null +++ b/hr_course/readme/USAGE.rst @@ -0,0 +1,6 @@ +To create a Course go to Employees -> Courses. + +Fill the information and click assign attendees to continue. Assign them and +then start the course. + +One it has finished you must enter the results and finish the course. diff --git a/hr_course/security/course_security.xml b/hr_course/security/course_security.xml new file mode 100644 index 00000000000..de8fd15505b --- /dev/null +++ b/hr_course/security/course_security.xml @@ -0,0 +1,31 @@ + + + + hr.employee.course.rule + + [('attendant_ids', 'in', user.employee_ids.ids)] + + + + + hr.employee.course.rule + + [(1, '=', 1)] + + + + + hr.employee.attendee.course.rule + + [('employee_id', 'in', user.employee_ids.ids)] + + + + + hr.employee.attendee.course.rule + + [(1, '=', 1)] + + + + diff --git a/hr_course/security/ir.model.access.csv b/hr_course/security/ir.model.access.csv new file mode 100644 index 00000000000..f483c6820d0 --- /dev/null +++ b/hr_course/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_course,access_hr_course,model_hr_course,base.group_user,1,0,0,0 +access_hr_course_manager,access_hr_course_manager,model_hr_course,hr.group_hr_manager,1,1,1,1 +access_hr_course_attendee,access_hr_course_attendee,model_hr_course_attendee,base.group_user,1,0,0,0 +access_hr_course_attendee_manager,access_hr_course_attendee_manager,model_hr_course_attendee,hr.group_hr_manager,1,1,1,1 +access_hr_course_category,access_hr_course_category,model_hr_course_category,base.group_user,1,0,0,0 +access_hr_course_category_manager,access_hr_course_category_manager,model_hr_course_category,hr.group_hr_manager,1,1,1,1 diff --git a/hr_course/static/description/icon.png b/hr_course/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/hr_course/static/description/index.html b/hr_course/static/description/index.html new file mode 100644 index 00000000000..18e94407ebe --- /dev/null +++ b/hr_course/static/description/index.html @@ -0,0 +1,429 @@ + + + + + + +Hr Course + + + +
+

Hr Course

+ + +

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

+

This module allows your to manage employee’s training courses and all its +validation process.

+

Table of contents

+ +
+

Usage

+

To create a Course go to Employees -> Courses.

+

Fill the information and click assign attendees to continue. Assign them and +then start the course.

+

One it has finished you must enter the results and finish the course.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Creu Blanca
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/hr project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/hr_course/tests/__init__.py b/hr_course/tests/__init__.py new file mode 100644 index 00000000000..208b53bcdbb --- /dev/null +++ b/hr_course/tests/__init__.py @@ -0,0 +1 @@ +from . import test_hr_course diff --git a/hr_course/tests/test_hr_course.py b/hr_course/tests/test_hr_course.py new file mode 100644 index 00000000000..36247fb2138 --- /dev/null +++ b/hr_course/tests/test_hr_course.py @@ -0,0 +1,71 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import odoo.tests.common as common +from odoo.exceptions import ValidationError + + +class TestHrCourse(common.TransactionCase): + + def setUp(self): + super(TestHrCourse, self).setUp() + self.course_categ = self.env['hr.course.category'].create({ + 'name': 'Category 1' + }) + self.employee1 = self.env['hr.employee'].create({ + 'name': 'Employee 1' + }) + self.employee2 = self.env['hr.employee'].create({ + 'name': 'Employee 2' + }) + self.course_id = self.env['hr.course'].create({ + 'name': 'Course name', + 'category_id': self.course_categ.id, + 'cost': 100, + 'authorized_by': self.employee1.id, + 'permanence': True, + 'permanence_time': '1 month', + 'start_date': '2019-02-15', + 'end_date': '2019-02-20', + }) + + def test_hr_course(self): + with self.assertRaises(ValidationError): + self.course_id.write({'end_date': '2019-02-10'}) + + self.assertEqual(self.course_id.state, 'draft') + self.course_id.permanence = False + self.course_id._onchange_permanence() + self.assertFalse(self.course_id.permanence_time) + self.course_id.cancel_course() + self.assertEqual(self.course_id.state, 'cancelled') + self.course_id.back2draft() + + self.course_id.draft2waiting() + self.assertEqual(self.course_id.state, 'waiting_attendees') + self.course_id.attendant_ids = [ + (6, 0, [self.employee1.id, self.employee2.id]) + ] + self.assertTrue(self.course_id.attendant_ids) + self.assertEqual(len(self.course_id.attendant_ids), 2) + + self.course_id.waiting2inprogress() + self.assertEqual(self.course_id.state, 'in_progress') + self.assertEqual(len(self.course_id.course_attendee_ids), 2) + self.course_id.attendant_ids = [(2, self.employee2.id, 0)] + self.course_id.waiting2inprogress() + self.assertEqual(len(self.course_id.attendant_ids), 1) + self.assertEqual(len(self.course_id.course_attendee_ids), 1) + self.employee1._compute_count_courses() + self.assertEqual(self.employee1.count_courses, 1) + self.employee1.action_view_course() + + self.course_id.inprogress2validation() + self.assertEqual(self.course_id.state, 'in_validation') + with self.assertRaises(ValidationError): + self.course_id.validation2complete() + self.course_id.all_passed() + self.assertEqual( + self.course_id.course_attendee_ids[0].result, 'passed') + self.course_id.validation2complete() + self.assertEqual(self.course_id.state, 'completed') diff --git a/hr_course/views/hr_course_category_views.xml b/hr_course/views/hr_course_category_views.xml new file mode 100644 index 00000000000..da77bb753d0 --- /dev/null +++ b/hr_course/views/hr_course_category_views.xml @@ -0,0 +1,49 @@ + + + + hr.course.category.form + hr.course.category + +
+ + + +
+
+
+ + + hr.course.category.search + hr.course.category + + + + + + + + + hr.course.category.list + hr.course.category + + + + + + + + + + Course Categories + hr.course.category + form + tree,form + + + +
diff --git a/hr_course/views/hr_course_views.xml b/hr_course/views/hr_course_views.xml new file mode 100644 index 00000000000..17540e28ec8 --- /dev/null +++ b/hr_course/views/hr_course_views.xml @@ -0,0 +1,175 @@ + + + + + + + hr.course.form (in hr_courses) + hr.course + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + +
+

+
+ +
+
+
+
+ + +
+
+
+
+ + + hr.course.search + hr.course + + + + + + + + + + + hr.course.tree + hr.course + + + + + + + + + + + + + hr.course.attendee.tree + hr.course.attendee + 30 + + + + + + + + + + hr.course.attendee.form + hr.course.attendee + +
+ + + + + + + + + + +
+
+
+ + + + Courses + hr.course + form + tree,form + + + + +
diff --git a/hr_course/views/hr_employee_views.xml b/hr_course/views/hr_employee_views.xml new file mode 100644 index 00000000000..6ea5bc62897 --- /dev/null +++ b/hr_course/views/hr_employee_views.xml @@ -0,0 +1,50 @@ + + + + + hr.employee.course.form + hr.employee + + +
+ +
+
+
+ + + hr.course.attendee.tree.employee + hr.course.attendee + 100 + + + + + + + + + + + + + Courses + hr.course.attendee + tree,form + form + + + +
From dc2b7c7413367755947ef9f88fe353938a06c023 Mon Sep 17 00:00:00 2001 From: Jaime Arroyo Date: Tue, 22 Oct 2019 14:25:31 +0200 Subject: [PATCH 02/45] [12.0][MIG] hr_course --- hr_course/README.rst | 10 +++++----- hr_course/__manifest__.py | 2 +- hr_course/models/hr_course.py | 7 +++++-- hr_course/static/description/index.html | 6 +++--- hr_course/views/hr_course_views.xml | 4 ++-- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/hr_course/README.rst b/hr_course/README.rst index 358a076be38..5dd8216df00 100644 --- a/hr_course/README.rst +++ b/hr_course/README.rst @@ -14,13 +14,13 @@ Hr Course :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github - :target: https://github.com/OCA/hr/tree/11.0/hr_course + :target: https://github.com/OCA/hr/tree/12.0/hr_course :alt: OCA/hr .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/hr-11-0/hr-11-0-hr_course + :target: https://translation.odoo-community.org/projects/hr-12-0/hr-12-0-hr_course :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/116/11.0 + :target: https://runbot.odoo-community.org/runbot/116/12.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -49,7 +49,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -80,6 +80,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/hr `_ project on GitHub. +This module is part of the `OCA/hr `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_course/__manifest__.py b/hr_course/__manifest__.py index 7ea8618b5e7..b329a8e5d29 100644 --- a/hr_course/__manifest__.py +++ b/hr_course/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Hr Course', 'summary': """ This module allows your to manage employee's training courses""", - 'version': '11.0.1.0.0', + 'version': '12.0.1.0.0', 'license': 'AGPL-3', 'author': 'Creu Blanca,Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/hr/', diff --git a/hr_course/models/hr_course.py b/hr_course/models/hr_course.py index 46396d0215b..855c909be32 100644 --- a/hr_course/models/hr_course.py +++ b/hr_course/models/hr_course.py @@ -7,6 +7,7 @@ class HrCourse(models.Model): _name = 'hr.course' + _description = 'Course' _inherit = 'mail.thread' name = fields.Char( @@ -126,8 +127,9 @@ def _waiting2inprogress_values(self): deleted_attendees += '- %s

'\ % course_attendee.employee_id.name if deleted_attendees != '': - self.message_post(_('Employees removed from' - ' this course:

' + deleted_attendees)) + message = _('Employees removed from this course:

%s' + ) % deleted_attendees + self.message_post(body=message) return { 'state': 'in_progress', 'course_attendee_ids': attendants @@ -192,6 +194,7 @@ def cancel_course(self): class HRCourseAttendee(models.Model): _name = 'hr.course.attendee' + _description = "Course Attendee" course_id = fields.Many2one( 'hr.course', diff --git a/hr_course/static/description/index.html b/hr_course/static/description/index.html index 18e94407ebe..d746f7ae2f5 100644 --- a/hr_course/static/description/index.html +++ b/hr_course/static/description/index.html @@ -367,7 +367,7 @@

Hr Course

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

This module allows your to manage employee’s training courses and all its validation process.

Table of contents

@@ -395,7 +395,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -420,7 +420,7 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/hr project on GitHub.

+

This module is part of the OCA/hr project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/hr_course/views/hr_course_views.xml b/hr_course/views/hr_course_views.xml index 17540e28ec8..c1bbf726ee0 100644 --- a/hr_course/views/hr_course_views.xml +++ b/hr_course/views/hr_course_views.xml @@ -40,11 +40,11 @@ statusbar_visible="draft,waiting_attendees,in_progress,in_validation,completed"/> - - From 76460934d9772ad22641ee1fb9677cb204612b14 Mon Sep 17 00:00:00 2001 From: Jaime Arroyo Date: Tue, 29 Sep 2020 12:54:51 +0200 Subject: [PATCH 03/45] [IMP] hr_course: black, isort, prettier --- hr_course/__manifest__.py | 29 +-- hr_course/models/hr_course.py | 163 ++++++------- hr_course/models/hr_employee.py | 15 +- hr_course/security/course_security.xml | 34 +-- hr_course/tests/test_hr_course.py | 58 ++--- hr_course/views/hr_course_category_views.xml | 26 +- hr_course/views/hr_course_views.xml | 239 +++++++++++-------- hr_course/views/hr_employee_views.xml | 49 ++-- 8 files changed, 319 insertions(+), 294 deletions(-) diff --git a/hr_course/__manifest__.py b/hr_course/__manifest__.py index b329a8e5d29..2713b0bb088 100644 --- a/hr_course/__manifest__.py +++ b/hr_course/__manifest__.py @@ -2,22 +2,19 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Hr Course', - 'summary': """ + "name": "Hr Course", + "summary": """ This module allows your to manage employee's training courses""", - 'version': '12.0.1.0.0', - 'license': 'AGPL-3', - 'author': 'Creu Blanca,Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/hr/', - 'depends': [ - 'hr', - 'mail', - ], - 'data': [ - 'security/course_security.xml', - 'security/ir.model.access.csv', - 'views/hr_course_category_views.xml', - 'views/hr_course_views.xml', - 'views/hr_employee_views.xml', + "version": "12.0.1.0.0", + "license": "AGPL-3", + "author": "Creu Blanca,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/hr/", + "depends": ["hr", "mail"], + "data": [ + "security/course_security.xml", + "security/ir.model.access.csv", + "views/hr_course_category_views.xml", + "views/hr_course_views.xml", + "views/hr_employee_views.xml", ], } diff --git a/hr_course/models/hr_course.py b/hr_course/models/hr_course.py index 855c909be32..a3cc17a3eb2 100644 --- a/hr_course/models/hr_course.py +++ b/hr_course/models/hr_course.py @@ -1,158 +1,148 @@ # Copyright 2019 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class HrCourse(models.Model): - _name = 'hr.course' - _description = 'Course' - _inherit = 'mail.thread' + _name = "hr.course" + _description = "Course" + _inherit = "mail.thread" - name = fields.Char( - string='Name', required=True, - track_visibility="onchange" - ) + name = fields.Char(string="Name", required=True, track_visibility="onchange") category_id = fields.Many2one( - 'hr.course.category', - string='Category', - required=True, + "hr.course.category", string="Category", required=True, ) start_date = fields.Date( - string="Start date", readonly=True, - states={'draft': [('readonly', False)]}, + string="Start date", + readonly=True, + states={"draft": [("readonly", False)]}, track_visibility="onchange", ) end_date = fields.Date( - string="End date", readonly=True, - states={'draft': [('readonly', False)]}, + string="End date", + readonly=True, + states={"draft": [("readonly", False)]}, track_visibility="onchange", ) currency_id = fields.Many2one( - 'res.currency', string='Currency', + "res.currency", + string="Currency", default=lambda self: self.env.user.company_id.currency_id, ) cost = fields.Monetary( - string='Course Cost', required=True, - track_visibility="onchange" + string="Course Cost", required=True, track_visibility="onchange" ) authorized_by = fields.Many2one( string="Authorized by", - comodel_name='hr.employee', + comodel_name="hr.employee", required=True, readonly=True, - states={'draft': [('readonly', False)]}, - track_visibility="onchange" + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) permanence = fields.Boolean( string="Has Permanence", readonly=True, - states={'draft': [('readonly', False)]}, - track_visibility="onchange" + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) permanence_time = fields.Char( - string='Permanence time', + string="Permanence time", readonly=True, - states={'draft': [('readonly', False)]}, - track_visibility="onchange" + states={"draft": [("readonly", False)]}, + track_visibility="onchange", ) state = fields.Selection( [ - ('draft', 'Draft'), - ('waiting_attendees', 'Waiting attendees'), - ('in_progress', 'In progress'), - ('in_validation', 'In validation'), - ('completed', 'Completed'), - ('cancelled', 'Cancelled'), + ("draft", "Draft"), + ("waiting_attendees", "Waiting attendees"), + ("in_progress", "In progress"), + ("in_validation", "In validation"), + ("completed", "Completed"), + ("cancelled", "Cancelled"), ], required=True, readonly=True, - default='draft', - track_visibility="onchange" + default="draft", + track_visibility="onchange", ) attendant_ids = fields.Many2many( - 'hr.employee', + "hr.employee", readonly=True, - states={'waiting_attendees': [('readonly', False)]}, + states={"waiting_attendees": [("readonly", False)]}, ) course_attendee_ids = fields.One2many( - 'hr.course.attendee', - inverse_name='course_id', + "hr.course.attendee", + inverse_name="course_id", readonly=True, - states={'in_validation': [('readonly', False)]}, + states={"in_validation": [("readonly", False)]}, ) - @api.constrains('start_date', 'end_date') + @api.constrains("start_date", "end_date") def _check_start_end_dates(self): self.ensure_one() - if self.start_date and self.end_date and ( - self.start_date > self.end_date - ): - raise ValidationError(_('The start date cannot' - ' be later than the end date.')) + if self.start_date and self.end_date and (self.start_date > self.end_date): + raise ValidationError( + _("The start date cannot" " be later than the end date.") + ) def all_passed(self): for attendee in self.course_attendee_ids: - attendee.result = 'passed' + attendee.result = "passed" - @api.onchange('permanence') + @api.onchange("permanence") def _onchange_permanence(self): self.permanence_time = False def _draft2waiting_values(self): return { - 'state': 'waiting_attendees', + "state": "waiting_attendees", } def _attendee_values(self, attendee): - return { - 'employee_id': attendee.id, - 'course_id': self.id - } + return {"employee_id": attendee.id, "course_id": self.id} def _waiting2inprogress_values(self): attendants = [] - employee_attendants = self.course_attendee_ids.mapped('employee_id') + employee_attendants = self.course_attendee_ids.mapped("employee_id") for attendee in self.attendant_ids.filtered( lambda r: r not in employee_attendants ): attendants.append((0, 0, self._attendee_values(attendee))) - deleted_attendees = '' + deleted_attendees = "" for course_attendee in self.course_attendee_ids.filtered( lambda r: r.employee_id not in self.attendant_ids ): attendants += course_attendee._remove_from_course() - deleted_attendees += '- %s

'\ - % course_attendee.employee_id.name - if deleted_attendees != '': - message = _('Employees removed from this course:

%s' - ) % deleted_attendees + deleted_attendees += "- %s

" % course_attendee.employee_id.name + if deleted_attendees != "": + message = ( + _("Employees removed from this course:

%s") % deleted_attendees + ) self.message_post(body=message) - return { - 'state': 'in_progress', - 'course_attendee_ids': attendants - } + return {"state": "in_progress", "course_attendee_ids": attendants} def _inprogress2validation_values(self): return { - 'state': 'in_validation', + "state": "in_validation", } def _validation2complete_values(self): return { - 'state': 'completed', + "state": "completed", } def _back2draft_values(self): return { - 'state': 'draft', + "state": "draft", } def _cancel_course_values(self): return { - 'state': 'cancelled', + "state": "cancelled", } @api.multi @@ -174,9 +164,10 @@ def inprogress2validation(self): def validation2complete(self): for record in self: if self.course_attendee_ids.filtered( - lambda r: r.result == 'pending' and r.active): + lambda r: r.result == "pending" and r.active + ): raise ValidationError( - _('You cannot complete the course with pending results') + _("You cannot complete the course with pending results") ) else: record.write(record._validation2complete_values()) @@ -193,40 +184,38 @@ def cancel_course(self): class HRCourseAttendee(models.Model): - _name = 'hr.course.attendee' + _name = "hr.course.attendee" _description = "Course Attendee" course_id = fields.Many2one( - 'hr.course', - ondelete='cascade', - readonly=True, - required=True, + "hr.course", ondelete="cascade", readonly=True, required=True, ) - name = fields.Char(related='course_id.name', readonly=True) - employee_id = fields.Many2one('hr.employee', readonly=True) - course_start = fields.Date(related='course_id.start_date', readonly=True) - course_end = fields.Date(related='course_id.end_date', readonly=True) - state = fields.Selection(related='course_id.state', readonly=True) + name = fields.Char(related="course_id.name", readonly=True) + employee_id = fields.Many2one("hr.employee", readonly=True) + course_start = fields.Date(related="course_id.start_date", readonly=True) + course_end = fields.Date(related="course_id.end_date", readonly=True) + state = fields.Selection(related="course_id.state", readonly=True) result = fields.Selection( [ - ('passed', 'Passed'), - ('failed', 'Failed'), - ('absent', 'Absent'), - ('pending', 'Pending'), + ("passed", "Passed"), + ("failed", "Failed"), + ("absent", "Absent"), + ("pending", "Pending"), ], - string="Result", default='pending', + string="Result", + default="pending", ) active = fields.Boolean(default=True, readonly=True) def _remove_from_course(self): - return [(1, self.id, {'active': False})] + return [(1, self.id, {"active": False})] class HRCourseCategory(models.Model): - _name = 'hr.course.category' + _name = "hr.course.category" _description = "Course Category" name = fields.Char(string="Course category", required=True) _sql_constraints = [ - ('name_uniq', 'unique (name)', "Category already exists !"), + ("name_uniq", "unique (name)", "Category already exists !"), ] diff --git a/hr_course/models/hr_employee.py b/hr_course/models/hr_employee.py index 41a2754a04f..f6bcc1affbd 100644 --- a/hr_course/models/hr_employee.py +++ b/hr_course/models/hr_employee.py @@ -2,27 +2,24 @@ class HrEmployee(models.Model): - _inherit = 'hr.employee' + _inherit = "hr.employee" count_courses = fields.Integer( - 'Number of courses', - compute='_compute_count_courses' + "Number of courses", compute="_compute_count_courses" ) courses_ids = fields.One2many( - 'hr.course.attendee', 'employee_id', - string='Courses', readonly=True, + "hr.course.attendee", "employee_id", string="Courses", readonly=True, ) - @api.depends('courses_ids') + @api.depends("courses_ids") def _compute_count_courses(self): for r in self: r.count_courses = len(r.courses_ids) @api.multi def action_view_course(self): - action = self.env.ref( - 'hr_course.action_view_course') + action = self.env.ref("hr_course.action_view_course") result = action.read()[0] - result['domain'] = [('employee_id', '=', self.id)] + result["domain"] = [("employee_id", "=", self.id)] return result diff --git a/hr_course/security/course_security.xml b/hr_course/security/course_security.xml index de8fd15505b..1d922b58917 100644 --- a/hr_course/security/course_security.xml +++ b/hr_course/security/course_security.xml @@ -1,31 +1,35 @@ - + hr.employee.course.rule - - [('attendant_ids', 'in', user.employee_ids.ids)] - - + + [('attendant_ids', 'in', user.employee_ids.ids)] + + hr.employee.course.rule - + [(1, '=', 1)] - - + + hr.employee.attendee.course.rule - - [('employee_id', 'in', user.employee_ids.ids)] - - + + [('employee_id', 'in', user.employee_ids.ids)] + + hr.employee.attendee.course.rule - + [(1, '=', 1)] - - + + diff --git a/hr_course/tests/test_hr_course.py b/hr_course/tests/test_hr_course.py index 36247fb2138..001a5117001 100644 --- a/hr_course/tests/test_hr_course.py +++ b/hr_course/tests/test_hr_course.py @@ -6,51 +6,46 @@ class TestHrCourse(common.TransactionCase): - def setUp(self): super(TestHrCourse, self).setUp() - self.course_categ = self.env['hr.course.category'].create({ - 'name': 'Category 1' - }) - self.employee1 = self.env['hr.employee'].create({ - 'name': 'Employee 1' - }) - self.employee2 = self.env['hr.employee'].create({ - 'name': 'Employee 2' - }) - self.course_id = self.env['hr.course'].create({ - 'name': 'Course name', - 'category_id': self.course_categ.id, - 'cost': 100, - 'authorized_by': self.employee1.id, - 'permanence': True, - 'permanence_time': '1 month', - 'start_date': '2019-02-15', - 'end_date': '2019-02-20', - }) + self.course_categ = self.env["hr.course.category"].create( + {"name": "Category 1"} + ) + self.employee1 = self.env["hr.employee"].create({"name": "Employee 1"}) + self.employee2 = self.env["hr.employee"].create({"name": "Employee 2"}) + self.course_id = self.env["hr.course"].create( + { + "name": "Course name", + "category_id": self.course_categ.id, + "cost": 100, + "authorized_by": self.employee1.id, + "permanence": True, + "permanence_time": "1 month", + "start_date": "2019-02-15", + "end_date": "2019-02-20", + } + ) def test_hr_course(self): with self.assertRaises(ValidationError): - self.course_id.write({'end_date': '2019-02-10'}) + self.course_id.write({"end_date": "2019-02-10"}) - self.assertEqual(self.course_id.state, 'draft') + self.assertEqual(self.course_id.state, "draft") self.course_id.permanence = False self.course_id._onchange_permanence() self.assertFalse(self.course_id.permanence_time) self.course_id.cancel_course() - self.assertEqual(self.course_id.state, 'cancelled') + self.assertEqual(self.course_id.state, "cancelled") self.course_id.back2draft() self.course_id.draft2waiting() - self.assertEqual(self.course_id.state, 'waiting_attendees') - self.course_id.attendant_ids = [ - (6, 0, [self.employee1.id, self.employee2.id]) - ] + self.assertEqual(self.course_id.state, "waiting_attendees") + self.course_id.attendant_ids = [(6, 0, [self.employee1.id, self.employee2.id])] self.assertTrue(self.course_id.attendant_ids) self.assertEqual(len(self.course_id.attendant_ids), 2) self.course_id.waiting2inprogress() - self.assertEqual(self.course_id.state, 'in_progress') + self.assertEqual(self.course_id.state, "in_progress") self.assertEqual(len(self.course_id.course_attendee_ids), 2) self.course_id.attendant_ids = [(2, self.employee2.id, 0)] self.course_id.waiting2inprogress() @@ -61,11 +56,10 @@ def test_hr_course(self): self.employee1.action_view_course() self.course_id.inprogress2validation() - self.assertEqual(self.course_id.state, 'in_validation') + self.assertEqual(self.course_id.state, "in_validation") with self.assertRaises(ValidationError): self.course_id.validation2complete() self.course_id.all_passed() - self.assertEqual( - self.course_id.course_attendee_ids[0].result, 'passed') + self.assertEqual(self.course_id.course_attendee_ids[0].result, "passed") self.course_id.validation2complete() - self.assertEqual(self.course_id.state, 'completed') + self.assertEqual(self.course_id.state, "completed") diff --git a/hr_course/views/hr_course_category_views.xml b/hr_course/views/hr_course_category_views.xml index da77bb753d0..a6866fa1c79 100644 --- a/hr_course/views/hr_course_category_views.xml +++ b/hr_course/views/hr_course_category_views.xml @@ -1,4 +1,4 @@ - + hr.course.category.form @@ -6,44 +6,40 @@
- +
- hr.course.category.search hr.course.category - + - hr.course.category.list hr.course.category - + - + - - + Course Categories hr.course.category form tree,form - + id="menu_view_course_category_form" + action="open_view_course_category_form" + parent="hr.menu_human_resources_configuration" + sequence="10" + />
diff --git a/hr_course/views/hr_course_views.xml b/hr_course/views/hr_course_views.xml index c1bbf726ee0..dcfe02eb007 100644 --- a/hr_course/views/hr_course_views.xml +++ b/hr_course/views/hr_course_views.xml @@ -1,175 +1,220 @@ - + - - hr.course.form (in hr_courses) hr.course
-
-
- - + +
- hr.course.search hr.course - - - + + + - hr.course.tree hr.course - - - - - + + + + + - - hr.course.attendee.tree hr.course.attendee 30 - - - + + + - hr.course.attendee.form hr.course.attendee
- - - - + + + + - - + +
- - Courses hr.course form tree,form - - - +
diff --git a/hr_course/views/hr_employee_views.xml b/hr_course/views/hr_employee_views.xml index 6ea5bc62897..6c0446f8a84 100644 --- a/hr_course/views/hr_employee_views.xml +++ b/hr_course/views/hr_employee_views.xml @@ -1,50 +1,53 @@ - hr.employee.course.form hr.employee - +
-
- hr.course.attendee.tree.employee hr.course.attendee 100 - - - - - - + + + + + + - Courses hr.course.attendee tree,form form - + (0,0,{'view_mode':'form'})]" + /> -
From 06b335e040c74f0a01443e991216b1dedae349a8 Mon Sep 17 00:00:00 2001 From: Jaime Arroyo Date: Tue, 29 Sep 2020 13:02:50 +0200 Subject: [PATCH 04/45] [13.0][MIG] hr_course --- hr_course/README.rst | 10 +++---- hr_course/__manifest__.py | 2 +- hr_course/models/hr_course.py | 30 +++++++++----------- hr_course/models/hr_employee.py | 1 - hr_course/static/description/index.html | 6 ++-- hr_course/views/hr_course_category_views.xml | 1 - hr_course/views/hr_course_views.xml | 25 ++++++++-------- hr_course/views/hr_employee_views.xml | 1 - 8 files changed, 35 insertions(+), 41 deletions(-) diff --git a/hr_course/README.rst b/hr_course/README.rst index 5dd8216df00..12807bc45fe 100644 --- a/hr_course/README.rst +++ b/hr_course/README.rst @@ -14,13 +14,13 @@ Hr Course :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github - :target: https://github.com/OCA/hr/tree/12.0/hr_course + :target: https://github.com/OCA/hr/tree/13.0/hr_course :alt: OCA/hr .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/hr-12-0/hr-12-0-hr_course + :target: https://translation.odoo-community.org/projects/hr-13-0/hr-13-0-hr_course :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/116/12.0 + :target: https://runbot.odoo-community.org/runbot/116/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -49,7 +49,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -80,6 +80,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/hr `_ project on GitHub. +This module is part of the `OCA/hr `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_course/__manifest__.py b/hr_course/__manifest__.py index 2713b0bb088..1b5df6b4297 100644 --- a/hr_course/__manifest__.py +++ b/hr_course/__manifest__.py @@ -5,7 +5,7 @@ "name": "Hr Course", "summary": """ This module allows your to manage employee's training courses""", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "license": "AGPL-3", "author": "Creu Blanca,Odoo Community Association (OCA)", "website": "https://github.com/OCA/hr/", diff --git a/hr_course/models/hr_course.py b/hr_course/models/hr_course.py index a3cc17a3eb2..77057a020bc 100644 --- a/hr_course/models/hr_course.py +++ b/hr_course/models/hr_course.py @@ -10,7 +10,7 @@ class HrCourse(models.Model): _description = "Course" _inherit = "mail.thread" - name = fields.Char(string="Name", required=True, track_visibility="onchange") + name = fields.Char(string="Name", required=True, tracking=True) category_id = fields.Many2one( "hr.course.category", string="Category", required=True, ) @@ -19,41 +19,43 @@ class HrCourse(models.Model): string="Start date", readonly=True, states={"draft": [("readonly", False)]}, - track_visibility="onchange", + tracking=True, ) end_date = fields.Date( string="End date", readonly=True, states={"draft": [("readonly", False)]}, - track_visibility="onchange", + tracking=True, ) currency_id = fields.Many2one( "res.currency", string="Currency", default=lambda self: self.env.user.company_id.currency_id, ) - cost = fields.Monetary( - string="Course Cost", required=True, track_visibility="onchange" - ) + cost = fields.Monetary(string="Course Cost", required=True, tracking=True) authorized_by = fields.Many2one( string="Authorized by", comodel_name="hr.employee", required=True, readonly=True, states={"draft": [("readonly", False)]}, - track_visibility="onchange", + tracking=True, ) permanence = fields.Boolean( string="Has Permanence", readonly=True, states={"draft": [("readonly", False)]}, - track_visibility="onchange", + tracking=True, + help="Check if the participants of this course are restricted to" + " stay in the company for a certain period of time.", ) permanence_time = fields.Char( string="Permanence time", readonly=True, states={"draft": [("readonly", False)]}, - track_visibility="onchange", + tracking=True, + help="Amount of time the employee is restricted to stay in the" + " company by participating to this course.", ) state = fields.Selection( [ @@ -67,7 +69,7 @@ class HrCourse(models.Model): required=True, readonly=True, default="draft", - track_visibility="onchange", + tracking=True, ) attendant_ids = fields.Many2many( "hr.employee", @@ -86,7 +88,7 @@ def _check_start_end_dates(self): self.ensure_one() if self.start_date and self.end_date and (self.start_date > self.end_date): raise ValidationError( - _("The start date cannot" " be later than the end date.") + _("The start date cannot be later than the end date.") ) def all_passed(self): @@ -145,22 +147,18 @@ def _cancel_course_values(self): "state": "cancelled", } - @api.multi def draft2waiting(self): for record in self: record.write(record._draft2waiting_values()) - @api.multi def waiting2inprogress(self): for record in self: record.write(record._waiting2inprogress_values()) - @api.multi def inprogress2validation(self): for record in self: record.write(record._inprogress2validation_values()) - @api.multi def validation2complete(self): for record in self: if self.course_attendee_ids.filtered( @@ -172,12 +170,10 @@ def validation2complete(self): else: record.write(record._validation2complete_values()) - @api.multi def back2draft(self): for record in self: record.write(record._back2draft_values()) - @api.multi def cancel_course(self): for record in self: record.write(record._cancel_course_values()) diff --git a/hr_course/models/hr_employee.py b/hr_course/models/hr_employee.py index f6bcc1affbd..5250e9fee11 100644 --- a/hr_course/models/hr_employee.py +++ b/hr_course/models/hr_employee.py @@ -17,7 +17,6 @@ def _compute_count_courses(self): for r in self: r.count_courses = len(r.courses_ids) - @api.multi def action_view_course(self): action = self.env.ref("hr_course.action_view_course") result = action.read()[0] diff --git a/hr_course/static/description/index.html b/hr_course/static/description/index.html index d746f7ae2f5..12b84162bf0 100644 --- a/hr_course/static/description/index.html +++ b/hr_course/static/description/index.html @@ -367,7 +367,7 @@

Hr Course

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

This module allows your to manage employee’s training courses and all its validation process.

Table of contents

@@ -395,7 +395,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -420,7 +420,7 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/hr project on GitHub.

+

This module is part of the OCA/hr project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/hr_course/views/hr_course_category_views.xml b/hr_course/views/hr_course_category_views.xml index a6866fa1c79..e10851d0f19 100644 --- a/hr_course/views/hr_course_category_views.xml +++ b/hr_course/views/hr_course_category_views.xml @@ -33,7 +33,6 @@ Course Categories hr.course.category - form tree,form