diff options
| author | Ravi Gohil <rgo@tinyerp.com> | 2016-03-25 11:12:40 +0100 |
|---|---|---|
| committer | Jérome Maes <jem@openerp.com> | 2016-04-13 12:28:34 +0200 |
| commit | 44c62e1b7f02bb43ed75615076bcbbc7195f72d4 (patch) | |
| tree | 0ae1aa7c0b7444fc68a7b221d9966f5a5eef7239 | |
| parent | c232e3cbcbeb0f84a5bcec4d12557eacc1fbc65a (diff) | |
[MIGR] hr : migrate to new api
- Rewrite the code to new api without changes
business behavior
- Remove old v7 backward compatibility method
- Regroup view definitions in the same xml file
- Use read_group for computed fields
| -rw-r--r-- | addons/hr/data/hr_data.xml | 7 | ||||
| -rw-r--r-- | addons/hr/data/hr_demo.xml | 26 | ||||
| -rw-r--r-- | addons/hr/data/web_tip_data.xml | 8 | ||||
| -rw-r--r-- | addons/hr/models/hr.py | 474 | ||||
| -rw-r--r-- | addons/hr/models/res_partner.py | 4 | ||||
| -rw-r--r-- | addons/hr/models/res_users.py | 74 | ||||
| -rw-r--r-- | addons/hr/security/hr_security.xml | 16 | ||||
| -rw-r--r-- | addons/hr/views/hr_templates.xml | 18 | ||||
| -rw-r--r-- | addons/hr/views/hr_views.xml | 69 |
9 files changed, 339 insertions, 357 deletions
diff --git a/addons/hr/data/hr_data.xml b/addons/hr/data/hr_data.xml index 638071a77ac..699b25bf890 100644 --- a/addons/hr/data/hr_data.xml +++ b/addons/hr/data/hr_data.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> +<odoo> <data noupdate="1"> <!-- notify all employees of module installation --> - <record model="mail.message" id="module_install_notification"> + <record id="module_install_notification" model="mail.message"> <field name="model">mail.channel</field> <field name="res_id" ref="mail.channel_all_employees"/> <field name="message_type">notification</field> @@ -16,9 +16,10 @@ Leave Management (keep track of employee leaves), Expense Management (manage emp <record id="dep_administration" model="hr.department"> <field name="name">Administration</field> </record> + <record id="dep_sales" model="hr.department"> <field name="name">Sales</field> </record> </data> -</openerp> +</odoo> diff --git a/addons/hr/data/hr_demo.xml b/addons/hr/data/hr_demo.xml index c9e20c4cbb0..4557af3354b 100644 --- a/addons/hr/data/hr_demo.xml +++ b/addons/hr/data/hr_demo.xml @@ -1,9 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> +<odoo> <data noupdate="1"> - <!--Users--> - <!--Department--> <record id="dep_management" model="hr.department"> @@ -64,7 +62,7 @@ Good knowledge of Python Good knowledge of HTML and Javascript Knowledge of UML-like modeling Good language skills, other than English (Dutch and French preferred, others welcome) -</field> + </field> </record> <record id="job_consultant" model="hr.job"> @@ -291,7 +289,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_phone">+3282823500</field> <field name="image" type="base64" file="hr/static/img/employee_han-image.png"/> </record> - + <record id="employee_jve" model="hr.employee"> <field name="name">Jan Van Eyck</field> <field name="department_id" ref="dep_rd"/> @@ -301,7 +299,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_email">jve@openerp.com</field> <field name="image" type="base64" file="hr/static/img/employee_jve-image.jpg"/> </record> - + <record id="employee_jep" model="hr.employee"> <field name="name">Jean-Pierre Carnaud</field> <field name="department_id" ref="dep_ps"/> @@ -311,7 +309,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_email">jep@openerp.com</field> <field name="image" type="base64" file="hr/static/img/employee_jep-image.jpg"/> </record> - + <record id="employee_jod" model="hr.employee"> <field name="name">John Doe</field> <field name="department_id" ref="dep_sales"/> @@ -321,7 +319,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_email">jod@openerp.com</field> <field name="image" type="base64" file="hr/static/img/employee_jod-image.png"/> </record> - + <record id="employee_jog" model="hr.employee"> <field name="name">João Gomer</field> <field name="department_id" ref="dep_rd"/> @@ -331,7 +329,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_email">jog@openerp.com</field> <field name="image" type="base64" file="hr/static/img/employee_jog-image.jpg"/> </record> - + <record id="employee_jgo" model="hr.employee"> <field name="name">Juan Gomez</field> <field name="department_id" ref="dep_ps"/> @@ -340,7 +338,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_phone">+3282823500</field> <field name="image" type="base64" file="hr/static/img/employee_jgo-image.jpg"/> </record> - + <record id="employee_lur" model="hr.employee"> <field name="name">Luigi Rondi</field> <field name="department_id" ref="dep_sales"/> @@ -350,7 +348,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_email">lur@openerp.com</field> <field name="image" type="base64" file="hr/static/img/employee_lur-image.png"/> </record> - + <record id="employee_hne" model="hr.employee"> <field name="name">Николай Петра</field> <field name="department_id" ref="dep_ps"/> @@ -359,7 +357,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_phone">+3282823500</field> <field name="image" type="base64" file="hr/static/img/employee_hne-image.png"/> </record> - + <record id="employee_djj" model="hr.employee"> <field name="name">佐藤さくら (demo_ja_JP)</field> <field name="department_id" ref="dep_ps"/> @@ -368,7 +366,7 @@ If you have development competencies, we can propose you specific traineeships</ <field name="work_phone">+3282823500</field> <field name="image" type="base64" file="hr/static/img/employee_djj-image.png"/> </record> - + <record id="employee_dzc" model="hr.employee"> <field name="name">趙生 (demo_zh_CN)</field> <field name="department_id" ref="dep_rd"/> @@ -378,4 +376,4 @@ If you have development competencies, we can propose you specific traineeships</ <field name="image" type="base64" file="hr/static/img/employee_dzc-image.jpg"/> </record> </data> -</openerp> +</odoo> diff --git a/addons/hr/data/web_tip_data.xml b/addons/hr/data/web_tip_data.xml index 2c675d52b36..df0911fad25 100644 --- a/addons/hr/data/web_tip_data.xml +++ b/addons/hr/data/web_tip_data.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<openerp> +<odoo> <data> - <record model="web.tip" id="hr_tip_1"> + <record id="hr_tip_1" model="web.tip"> <field name="title"></field> <field name="description">Get all applications related to this job position.</field> <field name="model">hr.job</field> @@ -11,7 +11,7 @@ <field name="placement">bottom</field> </record> - <record model="web.tip" id="hr_tip_2"> + <record id="hr_tip_2" model="web.tip"> <field name="title"></field> <field name="description">Click here to change the recruitment goal!</field> <field name="model">hr.job</field> @@ -21,4 +21,4 @@ <field name="placement">bottom</field> </record> </data> -</openerp> +</odoo> diff --git a/addons/hr/models/hr.py b/addons/hr/models/hr.py index ae0215a92da..87f508828dd 100644 --- a/addons/hr/models/hr.py +++ b/addons/hr/models/hr.py @@ -3,132 +3,97 @@ import logging -import openerp -from openerp import api -from openerp import SUPERUSER_ID -from openerp import tools -from openerp.modules.module import get_module_resource -from openerp.osv import fields, osv -from openerp.tools.translate import _ +from odoo import api, fields, models +from odoo import tools, _ +from odoo.exceptions import ValidationError +from odoo.modules.module import get_module_resource + _logger = logging.getLogger(__name__) -class hr_employee_category(osv.Model): +class EmployeeCategory(models.Model): _name = "hr.employee.category" _description = "Employee Category" - _columns = { - 'name': fields.char("Employee Tag", required=True), - 'color': fields.integer('Color Index'), - 'employee_ids': fields.many2many('hr.employee', 'employee_category_rel', 'category_id', 'emp_id', 'Employees'), - } - _sql_constraints = [ - ('name_uniq', 'unique (name)', "Tag name already exists !"), - ] + name = fields.Char(string="Employee Tag", required=True) + color = fields.Integer(string='Color Index') + employee_ids = fields.Many2many('hr.employee', 'employee_category_rel', 'category_id', 'emp_id', string='Employees') -class hr_job(osv.Model): + _sql_constraints = [ + ('name_uniq', 'unique (name)', "Tag name already exists !"), + ] - def _get_nbr_employees(self, cr, uid, ids, name, args, context=None): - res = {} - for job in self.browse(cr, uid, ids, context=context): - nb_employees = len(job.employee_ids or []) - res[job.id] = { - 'no_of_employee': nb_employees, - 'expected_employees': nb_employees + job.no_of_recruitment, - } - return res - def _get_job_position(self, cr, uid, ids, context=None): - res = [] - for employee in self.pool.get('hr.employee').browse(cr, uid, ids, context=context): - if employee.job_id: - res.append(employee.job_id.id) - return res +class Job(models.Model): _name = "hr.job" _description = "Job Position" _inherit = ['mail.thread'] - _columns = { - 'name': fields.char('Job Name', required=True, select=True, translate=True), - 'expected_employees': fields.function(_get_nbr_employees, string='Total Forecasted Employees', - help='Expected number of employees for this job position after new recruitment.', - store = { - 'hr.job': (lambda self,cr,uid,ids,c=None: ids, ['no_of_recruitment'], 10), - 'hr.employee': (_get_job_position, ['job_id'], 10), - }, type='integer', - multi='_get_nbr_employees'), - 'no_of_employee': fields.function(_get_nbr_employees, string="Current Number of Employees", - help='Number of employees currently occupying this job position.', - store = { - 'hr.employee': (_get_job_position, ['job_id'], 10), - }, type='integer', - multi='_get_nbr_employees'), - 'no_of_recruitment': fields.integer('Expected New Employees', copy=False, - help='Number of new employees you expect to recruit.'), - 'no_of_hired_employee': fields.integer('Hired Employees', copy=False, - help='Number of hired employees for this job position during recruitment phase.'), - 'employee_ids': fields.one2many('hr.employee', 'job_id', 'Employees', groups='base.group_user'), - 'description': fields.text('Job Description'), - 'requirements': fields.text('Requirements'), - 'department_id': fields.many2one('hr.department', 'Department'), - 'company_id': fields.many2one('res.company', 'Company'), - 'state': fields.selection([('recruit', 'Recruitment in Progress'), ('open', 'Recruitment Closed')], - string='Status', readonly=True, required=True, - track_visibility='always', copy=False, - help="Set whether the recruitment process is open or closed for this job position."), - 'write_date': fields.datetime('Update Date', readonly=True), - } - _defaults = { - 'company_id': lambda self, cr, uid, ctx=None: self.pool.get('res.company')._company_default_get(cr, uid, 'hr.job', context=ctx), - 'state': 'recruit', - 'no_of_recruitment' : 1, - } + name = fields.Char(string='Job Name', required=True, index=True, translate=True) + expected_employees = fields.Integer(compute='_compute_employees', string='Total Forecasted Employees', store=True, + help='Expected number of employees for this job position after new recruitment.') + no_of_employee = fields.Integer(compute='_compute_employees', string="Current Number of Employees", store=True, + help='Number of employees currently occupying this job position.') + no_of_recruitment = fields.Integer(string='Expected New Employees', copy=False, + help='Number of new employees you expect to recruit.', default=1) + no_of_hired_employee = fields.Integer(string='Hired Employees', copy=False, + help='Number of hired employees for this job position during recruitment phase.') + employee_ids = fields.One2many('hr.employee', 'job_id', string='Employees', groups='base.group_user') + description = fields.Text(string='Job Description') + requirements = fields.Text('Requirements') + department_id = fields.Many2one('hr.department', string='Department') + company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id) + state = fields.Selection([ + ('recruit', 'Recruitment in Progress'), + ('open', 'Recruitment Closed') + ], string='Status', readonly=True, required=True, track_visibility='always', copy=False, default='recruit', help="Set whether the recruitment process is open or closed for this job position.") _sql_constraints = [ ('name_company_uniq', 'unique(name, company_id, department_id)', 'The name of the job position must be unique per department in company!'), - ] - def set_recruit(self, cr, uid, ids, context=None): - for job in self.browse(cr, uid, ids, context=context): - no_of_recruitment = job.no_of_recruitment == 0 and 1 or job.no_of_recruitment - self.write(cr, uid, [job.id], {'state': 'recruit', 'no_of_recruitment': no_of_recruitment}, context=context) - return True - - def set_open(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, { - 'state': 'open', - 'no_of_recruitment': 0, - 'no_of_hired_employee': 0 - }, context=context) - return True + @api.depends('no_of_recruitment', 'employee_ids.job_id') + def _compute_employees(self): + employee_data = self.env['hr.employee'].read_group([('job_id', 'in', self.ids)], ['job_id'], ['job_id']) + result = dict((data['job_id'][0], data['job_id_count']) for data in employee_data) + for job in self: + job.no_of_employee = result.get(job.id, 0) + job.expected_employees = result.get(job.id, 0) + job.no_of_recruitment - # TDE note: done in new api, because called with new api -> context is a - # frozendict -> error when tryign to manipulate it @api.model def create(self, values): - return super(hr_job, self.with_context(mail_create_nosubscribe=True)).create(values) + """ We don't want the current user to be follower of all created job """ + return super(Job, self.with_context(mail_create_nosubscribe=True)).create(values) - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} + @api.multi + def copy(self, default=None): + self.ensure_one() + default = dict(default or {}) if 'name' not in default: - job = self.browse(cr, uid, id, context=context) - default['name'] = _("%s (copy)") % (job.name) - return super(hr_job, self).copy(cr, uid, id, default=default, context=context) + default['name'] = _("%s (copy)") % (self.name) + return super(Job, self).copy(default=default) + + @api.multi + def set_recruit(self): + for record in self: + no_of_recruitment = 1 if record.no_of_recruitment == 0 else record.no_of_recruitment + record.write({'state': 'recruit', 'no_of_recruitment': no_of_recruitment}) + return True + + @api.multi + def set_open(self): + return self.write({ + 'state': 'open', + 'no_of_recruitment': 0, + 'no_of_hired_employee': 0 + }) - # ---------------------------------------- - # Compatibility methods - # ---------------------------------------- - _no_of_employee = _get_nbr_employees # v7 compatibility - job_open = set_open # v7 compatibility - job_recruitment = set_recruit # v7 compatibility +class Employee(models.Model): -class hr_employee(osv.osv): _name = "hr.employee" _description = "Employee" _order = 'name_related' @@ -137,46 +102,60 @@ class hr_employee(osv.osv): _mail_post_access = 'read' - _columns = { - #we need a related field in order to be able to sort the employee by name - 'name_related': fields.related('resource_id', 'name', type='char', string='Name', readonly=True, store=True), - 'country_id': fields.many2one('res.country', 'Nationality (Country)'), - 'birthday': fields.date("Date of Birth"), - 'ssnid': fields.char('SSN No', help='Social Security Number'), - 'sinid': fields.char('SIN No', help="Social Insurance Number"), - 'identification_id': fields.char('Identification No'), - 'gender': fields.selection([('male', 'Male'), ('female', 'Female'), ('other', 'Other')], 'Gender'), - 'marital': fields.selection([('single', 'Single'), ('married', 'Married'), ('widower', 'Widower'), ('divorced', 'Divorced')], 'Marital Status'), - 'department_id': fields.many2one('hr.department', 'Department'), - 'address_id': fields.many2one('res.partner', 'Working Address'), - 'address_home_id': fields.many2one('res.partner', 'Home Address'), - 'bank_account_id': fields.many2one('res.partner.bank', 'Bank Account Number', domain="[('partner_id','=',address_home_id)]", help="Employee bank salary account"), - 'work_phone': fields.char('Work Phone', readonly=False), - 'mobile_phone': fields.char('Work Mobile', readonly=False), - 'work_email': fields.char('Work Email', size=240), - 'work_location': fields.char('Work Location'), - 'notes': fields.text('Notes'), - 'parent_id': fields.many2one('hr.employee', 'Manager'), - 'category_ids': fields.many2many('hr.employee.category', 'employee_category_rel', 'emp_id', 'category_id', 'Tags'), - 'child_ids': fields.one2many('hr.employee', 'parent_id', 'Subordinates'), - 'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True, auto_join=True), - 'coach_id': fields.many2one('hr.employee', 'Coach'), - 'job_id': fields.many2one('hr.job', 'Job Title'), - 'passport_id': fields.char('Passport No'), - 'color': fields.integer('Color Index'), - 'city': fields.related('address_id', 'city', type='char', string='City'), - 'login': fields.related('user_id', 'login', type='char', string='Login', readonly=1), - 'last_login': fields.related('user_id', 'date', type='datetime', string='Latest Connection', readonly=1), - } + @api.model + def _default_image(self): + image_path = get_module_resource('hr', 'static/src/img', 'default_image.png') + return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) + + # we need a related field in order to be able to sort the employee by name + name_related = fields.Char('Name', related='resource_id.name', readonly=True, store=True) + country_id = fields.Many2one('res.country', string='Nationality (Country)') + birthday = fields.Date('Date of Birth') + ssnid = fields.Char('SSN No', help='Social Security Number') + sinid = fields.Char('SIN No', help='Social Insurance Number') + identification_id = fields.Char(string='Identification No') + gender = fields.Selection([ + ('male', 'Male'), + ('female', 'Female'), + ('other', 'Other') + ]) + marital = fields.Selection([ + ('single', 'Single'), + ('married', 'Married'), + ('widower', 'Widower'), + ('divorced', 'Divorced') + ], string='Marital Status') + department_id = fields.Many2one('hr.department', string='Department') + address_id = fields.Many2one('res.partner', string='Working Address') + address_home_id = fields.Many2one('res.partner', string='Home Address') + bank_account_id = fields.Many2one('res.partner.bank', string='Bank Account Number', + domain="[('partner_id', '=', address_home_id)]", help='Employee bank salary account') + work_phone = fields.Char('Work Phone') + mobile_phone = fields.Char('Work Mobile') + work_email = fields.Char('Work Email') + work_location = fields.Char('Work Location') + notes = fields.Text('Notes') + parent_id = fields.Many2one('hr.employee', string='Manager') + category_ids = fields.Many2many('hr.employee.category', 'employee_category_rel', 'emp_id', 'category_id', string='Tags') + child_ids = fields.One2many('hr.employee', 'parent_id', string='Subordinates') + resource_id = fields.Many2one('resource.resource', string='Resource', + ondelete='cascade', required=True, auto_join=True) + coach_id = fields.Many2one('hr.employee', string='Coach') + job_id = fields.Many2one('hr.job', string='Job Title') + passport_id = fields.Char('Passport No') + color = fields.Integer('Color Index', default=0) + city = fields.Char(related='address_id.city') + login = fields.Char(related='user_id.login', readonly=True) + last_login = fields.Datetime(related='user_id.login_date', string='Latest Connection', readonly=True) # image: all image fields are base64 encoded and PIL-supported - image = openerp.fields.Binary("Photo", attachment=True, + image = fields.Binary("Photo", default=_default_image, attachment=True, help="This field holds the image used as photo for the employee, limited to 1024x1024px.") - image_medium = openerp.fields.Binary("Medium-sized photo", attachment=True, + image_medium = fields.Binary("Medium-sized photo", attachment=True, help="Medium-sized photo of the employee. It is automatically "\ "resized as a 128x128px image, with aspect ratio preserved. "\ "Use this field in form views or some kanban views.") - image_small = openerp.fields.Binary("Small-sized photo", attachment=True, + image_small = fields.Binary("Small-sized photo", attachment=True, help="Small-sized photo of the employee. It is automatically "\ "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required.") @@ -191,67 +170,66 @@ class hr_employee(osv.osv): 'color': 0, } + @api.constrains('parent_id') + def _check_parent_id(self): + for employee in self: + if not employee._check_recursion(): + raise ValidationError(_('Error! You cannot create recursive hierarchy of Employee(s).')) + + @api.onchange('address_id') + def _onchange_address(self): + self.work_phone = self.address_id.phone + self.mobile_phone = self.address_id.mobile + + @api.onchange('company_id') + def _onchange_company(self): + address = self.company_id.partner_id.address_get(['default']) + self.address_id = address['default'] if address else False + + @api.onchange('department_id') + def _onchange_department(self): + self.parent_id = self.department_id.manager_id + + @api.onchange('user_id') + def _onchange_user(self): + self.work_email = self.user_id.email + self.name = self.user_id.name + self.image = self.user_id.image + @api.model def create(self, vals): tools.image_resize_images(vals) - return super(hr_employee, self).create(vals) + return super(Employee, self).create(vals) @api.multi def write(self, vals): tools.image_resize_images(vals) - return super(hr_employee, self).write(vals) - - def unlink(self, cr, uid, ids, context=None): - resource_ids = [] - for employee in self.browse(cr, uid, ids, context=context): - resource_ids.append(employee.resource_id.id) - super(hr_employee, self).unlink(cr, uid, ids, context=context) - return self.pool.get('resource.resource').unlink(cr, uid, resource_ids, context=context) - - def onchange_address_id(self, cr, uid, ids, address, context=None): - if address: - address = self.pool.get('res.partner').browse(cr, uid, address, context=context) - return {'value': {'work_phone': address.phone, 'mobile_phone': address.mobile}} - return {'value': {}} - - def onchange_company(self, cr, uid, ids, company, context=None): - address_id = False - if company: - company_id = self.pool.get('res.company').browse(cr, uid, company, context=context) - address = self.pool.get('res.partner').address_get(cr, uid, [company_id.partner_id.id], ['contact']) - address_id = address and address['contact'] or False - return {'value': {'address_id': address_id}} - - def onchange_department_id(self, cr, uid, ids, department_id, context=None): - value = {'parent_id': False} - if department_id: - department = self.pool.get('hr.department').browse(cr, uid, department_id) - value['parent_id'] = department.manager_id.id - return {'value': value} - - def onchange_user(self, cr, uid, ids, name, image, user_id, context=None): - if user_id: - user = self.pool['res.users'].browse(cr, uid, user_id, context=context) - values = { - 'name': name or user.name, - 'work_email': user.email, - 'image': image or user.image, - } - return {'value': values} - - def action_follow(self, cr, uid, ids, context=None): + return super(Employee, self).write(vals) + + @api.multi + def unlink(self): + resources = self.mapped('resource_id') + super(Employee, self).unlink() + return resources.unlink() + + @api.multi + def action_follow(self): """ Wrapper because message_subscribe_users take a user_ids=None - that receive the context without the wrapper. """ - return self.message_subscribe_users(cr, uid, ids, context=context) + that receive the context without the wrapper. + """ + return self.message_subscribe_users() - def action_unfollow(self, cr, uid, ids, context=None): + @api.multi + def action_unfollow(self): """ Wrapper because message_unsubscribe_users take a user_ids=None - that receive the context without the wrapper. """ - return self.message_unsubscribe_users(cr, uid, ids, context=context) + that receive the context without the wrapper. + """ + return self.message_unsubscribe_users() - def _message_get_auto_subscribe_fields(self, cr, uid, updated_fields, auto_follow_fields=None, context=None): + @api.model + def _message_get_auto_subscribe_fields(self, updated_fields, auto_follow_fields=None): """ Overwrite of the original method to always follow user_id field, - even when not track_visibility so that a user will follow it's employee + even when not track_visibility so that a user will follow it's employee """ if auto_follow_fields is None: auto_follow_fields = ['user_id'] @@ -261,103 +239,71 @@ class hr_employee(osv.osv): user_field_lst.append(name) return user_field_lst - _constraints = [(osv.osv._check_recursion, _('Error! You cannot create recursive hierarchy of Employee(s).'), ['parent_id']),] +class Department(models.Model): -class hr_department(osv.osv): _name = "hr.department" - _description = "Department" + _description = "Hr Department" _inherit = ['mail.thread', 'ir.needaction_mixin'] - _columns = { - 'name': fields.char('Department Name', required=True), - 'active': fields.boolean('Active'), - 'company_id': fields.many2one('res.company', 'Company', select=True, required=False), - 'parent_id': fields.many2one('hr.department', 'Parent Department', select=True), - 'child_ids': fields.one2many('hr.department', 'parent_id', 'Child Departments'), - 'manager_id': fields.many2one('hr.employee', 'Manager', track_visibility='onchange'), - 'member_ids': fields.one2many('hr.employee', 'department_id', 'Members', readonly=True), - 'jobs_ids': fields.one2many('hr.job', 'department_id', 'Jobs'), - 'note': fields.text('Note'), - 'color': fields.integer('Color Index'), - } - - _defaults = { - 'active': True, - 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'hr.department', context=c), - } + name = fields.Char('Department Name', required=True) + active = fields.Boolean('Active', default=True) + company_id = fields.Many2one('res.company', string='Company', index=True, default=lambda self: self.env.user.company_id) + parent_id = fields.Many2one('hr.department', string='Parent Department', index=True) + child_ids = fields.One2many('hr.department', 'parent_id', string='Child Departments') + manager_id = fields.Many2one('hr.employee', string='Manager', track_visibility='onchange') + member_ids = fields.One2many('hr.employee', 'department_id', string='Members', readonly=True) + jobs_ids = fields.One2many('hr.job', 'department_id', string='Jobs') + note = fields.Text('Note') + color = fields.Integer('Color Index') + + @api.constrains('parent_id') + def _check_parent_id(self): + if not self._check_recursion(): + raise ValidationError(_('Error! You cannot create recursive departments.')) - _constraints = [ - (osv.osv._check_recursion, _('Error! You cannot create recursive departments.'), ['parent_id']) - ] + @api.multi + def name_get(self): + result = [] + for record in self: + name = record.name + if record.parent_id: + name = "%s / %s" % (record.parent_id.name_get()[1], name) + result.append((record.id, name)) + return result - def name_get(self, cr, uid, ids, context=None): - if not ids: - return [] - if isinstance(ids, (int, long)): - ids = [ids] - if context is None: - context = {} - reads = self.read(cr, uid, ids, ['name','parent_id'], context=context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res - - def create(self, cr, uid, vals, context=None): - if context is None: - context = {} - context = dict(context, mail_create_nosubscribe=True) + @api.model + def create(self, vals): # TDE note: auto-subscription of manager done by hand, because currently # the tracking allows to track+subscribe fields linked to a res.user record # An update of the limited behavior should come, but not currently done. - manager_id = vals.get("manager_id") - new_id = super(hr_department, self).create(cr, uid, vals, context=context) - if manager_id: - employee = self.pool.get('hr.employee').browse(cr, uid, manager_id, context=context) - if employee.user_id: - self.message_subscribe_users(cr, uid, [new_id], user_ids=[employee.user_id.id], context=context) - return new_id - - def write(self, cr, uid, ids, vals, context=None): + department = super(Department, self.with_context(mail_create_nosubscribe=True)).create(vals) + manager = self.env['hr.employee'].browse(vals.get("manager_id")) + if manager.user_id: + department.message_subscribe_users(user_ids=manager.user_id.ids) + return department + + @api.multi + def write(self, vals): + """ If updating manager of a department, we need to update all the employees + of department hierarchy, and subscribe the new manager. + """ # TDE note: auto-subscription of manager done by hand, because currently # the tracking allows to track+subscribe fields linked to a res.user record # An update of the limited behavior should come, but not currently done. - if isinstance(ids, (int, long)): - ids = [ids] - employee_ids = [] if 'manager_id' in vals: manager_id = vals.get("manager_id") if manager_id: - employee = self.pool['hr.employee'].browse(cr, uid, manager_id, context=context) - if employee.user_id: - self.message_subscribe_users(cr, uid, ids, user_ids=[employee.user_id.id], context=context) - for department in self.browse(cr, uid, ids, context=context): - employee_ids += self.pool['hr.employee'].search( - cr, uid, [ - ('id', '!=', manager_id), - ('department_id', '=', department.id), - ('parent_id', '=', department.manager_id.id) - ], context=context) - self.pool['hr.employee'].write(cr, uid, employee_ids, {'parent_id': manager_id}, context=context) - return super(hr_department, self).write(cr, uid, ids, vals, context=context) - - -class res_users(osv.osv): - _name = 'res.users' - _inherit = 'res.users' - - def write(self, cr, uid, ids, vals, context=None): - if isinstance(ids, (int, long)): - ids = [ids] - result = super(res_users, self).write(cr, uid, ids, vals, context=context) - employee_obj = self.pool.get('hr.employee') - if vals.get('name'): - for user_id in ids: - if user_id == SUPERUSER_ID: - employee_ids = employee_obj.search(cr, uid, [('user_id', '=', user_id)]) - employee_obj.write(cr, uid, employee_ids, {'name': vals['name']}, context=context) - return result + manager = self.env['hr.employee'].browse(manager_id) + # subscribe the manager user + if manager.user_id: + self.message_subscribe_users(user_ids=manager.user_id.ids) + employees = self.env['hr.employee'] + for department in self: + employees = employees | self.env['hr.employee'].search([ + ('id', '!=', manager_id), + ('department_id', '=', department.id), + ('parent_id', '=', department.manager_id.id) + ]) + employees.write({'parent_id': manager_id}) + return super(Department, self).write(vals) diff --git a/addons/hr/models/res_partner.py b/addons/hr/models/res_partner.py index 962a0989e61..8c306f778da 100644 --- a/addons/hr/models/res_partner.py +++ b/addons/hr/models/res_partner.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -from openerp import api, models +from odoo import api, models class Partner(models.Model): - _name = "res.partner" + _inherit = ['res.partner'] @api.model diff --git a/addons/hr/models/res_users.py b/addons/hr/models/res_users.py index 0b1994f3ab5..f759aba30f5 100644 --- a/addons/hr/models/res_users.py +++ b/addons/hr/models/res_users.py @@ -1,40 +1,48 @@ -from openerp import api -from openerp.osv import fields, osv +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import api, models, fields +from odoo import SUPERUSER_ID -class res_users(osv.Model): - """ Update of res.users class - - add field for the related employee of the user - - if adding groups to an user, check if base.group_user is in it (member of - 'Employee'), create an employee form linked to it. """ - _name = 'res.users' +class User(models.Model): + _inherit = ['res.users'] - _columns = { - 'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'), - } - - def _message_post_get_eid(self, cr, uid, thread_id, context=None): - assert thread_id, "res.users does not support posting global messages" - if context and 'thread_model' in context: - context = dict(context or {}) - context['thread_model'] = 'hr.employee' - if isinstance(thread_id, (list, tuple)): - thread_id = thread_id[0] - return self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', thread_id)], context=context) - - @api.cr_uid_ids_context - def message_post(self, cr, uid, thread_id, context=None, **kwargs): - """ Redirect the posting of message on res.users to the related employee. + employee_ids = fields.One2many('hr.employee', 'user_id', string='Related employees') + + @api.multi + def write(self, vals): + """ When renaming admin user, we want its new name propagated to its related employees """ + result = super(User, self).write(vals) + Employee = self.env['hr.employee'] + if vals.get('name'): + for user in self.filtered(lambda user: user.id == SUPERUSER_ID): + employees = Employee.search([('user_id', '=', user.id)]) + employees.write({'name': vals['name']}) + return result + + @api.multi + def _get_related_employees(self): + self.ensure_one() + ctx = dict(self.env.context) + if 'thread_model' in ctx: + ctx['thread_model'] = 'hr.employee' + return self.env['hr.employee'].with_context(ctx).search([('user_id', '=', self.id)]) + + @api.multi + def message_post(self, **kwargs): + """ Redirect the posting of message on res.users to the related employees. This is done because when giving the context of Chatter on the - various mailboxes, we do not have access to the current partner_id. """ + various mailboxes, we do not have access to the current partner_id. + """ + self.ensure_one() if kwargs.get('message_type') == 'email': - return super(res_users, self).message_post(cr, uid, thread_id, context=context, **kwargs) - res = None - employee_ids = self._message_post_get_eid(cr, uid, thread_id, context=context) - if not employee_ids: # no employee: fall back on previous behavior - return super(res_users, self).message_post(cr, uid, thread_id, context=context, **kwargs) - for employee_id in employee_ids: - res = self.pool.get('hr.employee').message_post(cr, uid, employee_id, context=context, **kwargs) - return res + return super(User, self).message_post(**kwargs) + message_id = None + employees = self._get_related_employees() + if not employees: # no employee: fall back on previous behavior + return super(User, self).message_post(**kwargs) + for employee in employees: + message_id = employee.message_post(**kwargs) + return message_id diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml index 3a598bb4a70..320ca081325 100644 --- a/addons/hr/security/hr_security.xml +++ b/addons/hr/security/hr_security.xml @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> -<data noupdate="0"> +<odoo> - <record model="res.groups" id="base.group_user"> + <record id="base.group_user" model="res.groups"> <field name="comment">the user will be able to manage his own human resources stuff (leave request, timesheets, ...), if he is linked to an employee in the system.</field> </record> @@ -12,6 +11,7 @@ <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> <field name="comment">the user will be able to approve document created by employees.</field> </record> + <record id="base.group_hr_manager" model="res.groups"> <field name="name">Manager</field> <field name="comment">the user will have an access to the human resources configuration as well as statistic reports.</field> @@ -19,7 +19,7 @@ <field name="implied_ids" eval="[(4, ref('base.group_hr_user'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/> </record> - + <record id="base.default_user" model="res.users"> <field name="groups_id" eval="[(4,ref('base.group_hr_manager'))]"/> </record> @@ -29,21 +29,21 @@ <field name="category_id" ref="base.module_category_hidden"/> </record> -</data> <data noupdate="1"> <record id="hr_dept_comp_rule" model="ir.rule"> <field name="name">Department multi company rule</field> <field name="model_id" ref="model_hr_department"/> - <field eval="True" name="global"/> + <field name="global" eval="True"/> <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> </record> + <record id="hr_job_comp_rule" model="ir.rule"> <field name="name">Job multi company rule</field> <field name="model_id" ref="model_hr_job"/> - <field eval="True" name="global"/> + <field name="global" eval="True"/> <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> </record> </data> -</openerp> +</odoo> diff --git a/addons/hr/views/hr_templates.xml b/addons/hr/views/hr_templates.xml index b01f006f4e7..1d1ebd71267 100644 --- a/addons/hr/views/hr_templates.xml +++ b/addons/hr/views/hr_templates.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - <template id="assets_backend" name="hr assets" inherit_id="web.assets_backend"> - <xpath expr="." position="inside"> - <link rel="stylesheet" href="/hr/static/src/less/hr.less"/> - </xpath> - </template> - </data> -</openerp> +<odoo> + + <template id="assets_backend" name="hr assets" inherit_id="web.assets_backend"> + <xpath expr="." position="inside"> + <link rel="stylesheet" href="/hr/static/src/less/hr.less"/> + </xpath> + </template> + +</odoo> diff --git a/addons/hr/views/hr_views.xml b/addons/hr/views/hr_views.xml index 61a444a42b1..14024c5bea6 100644 --- a/addons/hr/views/hr_views.xml +++ b/addons/hr/views/hr_views.xml @@ -1,17 +1,35 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> +<odoo> <data> <!-- Top menu item --> - <menuitem name="Employees" + <menuitem id="menu_hr_root" + name="Employees" groups="base.group_hr_manager,base.group_hr_user,base.group_user" icon="fa-user" web_icon="hr,static/description/icon.png" sequence="75"/> - <menuitem id="menu_hr_main" parent="menu_hr_root" name="Human Resources" sequence="0"/> - <menuitem id="menu_hr_reporting_timesheet" name="Reports" parent="menu_hr_root" sequence="99" groups="base.group_hr_manager,base.group_hr_user"/> - <menuitem id="menu_human_resources_configuration" name="Configuration" parent="menu_hr_root" sequence="100" groups="base.group_hr_manager"/> + + <menuitem + id="menu_hr_main" + name="Human Resources" + parent="menu_hr_root" + sequence="0"/> + + <menuitem + id="menu_hr_reporting_timesheet" + name="Reports" + parent="menu_hr_root" + groups="base.group_hr_manager,base.group_hr_user" + sequence="99"/> + + <menuitem + id="menu_human_resources_configuration" + name="Configuration" + parent="menu_hr_root" + groups="base.group_hr_manager" + sequence="100"/> <!-- Employee --> <record id="view_employee_form" model="ir.ui.view"> @@ -39,7 +57,7 @@ <page name="public" string="Public Information"> <group> <group string="Contact Information"> - <field name="address_id" on_change="onchange_address_id(address_id)" + <field name="address_id" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/> <field name="mobile_phone"/> @@ -48,7 +66,7 @@ <field name="work_phone"/> </group> <group string="Position"> - <field name="department_id" on_change="onchange_department_id(department_id)"/> + <field name="department_id"/> <field name="job_id"/> <field name="parent_id"/> <field name="coach_id"/> @@ -82,8 +100,8 @@ <page string="HR Settings" groups="base.group_hr_user"> <group> <group string='Status' name="active_group"> - <field name="company_id" groups="base.group_multi_company" on_change="onchange_company(company_id)"/> - <field name="user_id" on_change="onchange_user(name, image, user_id)" string="Related User"/> + <field name="company_id" groups="base.group_multi_company"/> + <field name="user_id" string="Related User"/> </group> </group> </page> @@ -116,7 +134,7 @@ </record> <record id="view_employee_filter" model="ir.ui.view"> - <field name="name">Employees</field> + <field name="name">hr.employee.search</field> <field name="model">hr.employee</field> <field name="arch" type="xml"> <search string="Employees"> @@ -137,8 +155,8 @@ </field> </record> - <record model="ir.ui.view" id="hr_kanban_view_employees"> - <field name="name">HR - Employess Kanban</field> + <record id="hr_kanban_view_employees" model="ir.ui.view"> + <field name="name">hr.employee.kanban</field> <field name="model">hr.employee</field> <field name="priority">10</field> <field name="arch" type="xml"> @@ -229,14 +247,18 @@ </field> </record> - <menuitem action="open_view_employee_list_my" id="menu_open_view_employee_list_my" sequence="3" parent="menu_hr_root"/> + <menuitem + id="menu_open_view_employee_list_my" + action="open_view_employee_list_my" + parent="menu_hr_root" + sequence="3"/> <!-- Employee tree by manager --> <record id="view_partner_tree2" model="ir.ui.view"> <field name="name">hr.employee.tree</field> <field name="model">hr.employee</field> <field name="field_parent">child_ids</field> - <field eval="20" name="priority"/> + <field name="priority" eval="20"/> <field name="arch" type="xml"> <tree string="Employees"> <field name="name"/> @@ -281,6 +303,7 @@ </form> </field> </record> + <record id="view_employee_category_list" model="ir.ui.view"> <field name="name">hr.employee.category.list</field> <field name="model">hr.employee.category</field> @@ -291,14 +314,20 @@ </tree> </field> </record> + <record id="open_view_categ_form" model="ir.actions.act_window"> <field name="name">Employee Tags</field> <field name="res_model">hr.employee.category</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> </record> - <menuitem action="open_view_categ_form" id="menu_view_employee_category_form" - parent="menu_human_resources_configuration" sequence="1" groups="base.group_no_one"/> + + <menuitem + id="menu_view_employee_category_form" + action="open_view_categ_form" + parent="menu_human_resources_configuration" + groups="base.group_no_one" + sequence="1"/> <!-- hr.job --> <record id="view_hr_job_form" model="ir.ui.view"> @@ -354,7 +383,7 @@ </record> <record id="view_job_filter" model="ir.ui.view"> - <field name="name">Job</field> + <field name="name">hr.job.search</field> <field name="model">hr.job</field> <field name="arch" type="xml"> <search string="Jobs"> @@ -373,7 +402,7 @@ </field> </record> - <record model="ir.actions.act_window" id="action_hr_job"> + <record id="action_hr_job" model="ir.actions.act_window"> <field name="name">Job Positions</field> <field name="res_model">hr.job</field> <field name="view_type">form</field> @@ -396,7 +425,7 @@ </field> </record> - <record model="ir.actions.act_window" id="act_employee_from_department"> + <record id="act_employee_from_department" model="ir.actions.act_window"> <field name="name">Employees</field> <field name="res_model">hr.employee</field> <field name="view_type">form</field> @@ -561,4 +590,4 @@ groups="base.group_hr_user"/> </data> -</openerp> +</odoo> |
