[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
This commit is contained in:
Ravi Gohil
2016-04-13 12:28:34 +02:00
committed by Jérome Maes
parent c232e3cbcb
commit 44c62e1b7f
9 changed files with 334 additions and 352 deletions
+4 -3
View File
@@ -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>
+12 -14
View File
@@ -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>
+4 -4
View File
@@ -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>
+207 -261
View File
@@ -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'),
}
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')
_sql_constraints = [
('name_uniq', 'unique (name)', "Tag name already exists !"),
('name_uniq', 'unique (name)', "Tag name already exists !"),
]
class hr_job(osv.Model):
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)
@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
@api.model
def create(self, 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)
@api.multi
def copy(self, default=None):
self.ensure_one()
default = dict(default or {})
if 'name' not in default:
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
def set_open(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {
@api.multi
def set_open(self):
return self.write({
'state': 'open',
'no_of_recruitment': 0,
'no_of_hired_employee': 0
}, context=context)
return True
# 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)
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
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)
# ----------------------------------------
# Compatibility methods
# ----------------------------------------
_no_of_employee = _get_nbr_employees # v7 compatibility
job_open = set_open # v7 compatibility
job_recruitment = set_recruit # v7 compatibility
})
class hr_employee(osv.osv):
class Employee(models.Model):
_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)
return super(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)
@api.multi
def unlink(self):
resources = self.mapped('resource_id')
super(Employee, self).unlink()
return resources.unlink()
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):
@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'),
}
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')
_defaults = {
'active': True,
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'hr.department', context=c),
}
@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
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
def write(self, cr, uid, ids, vals, context=None):
@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)
+2 -2
View File
@@ -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
+39 -31
View File
@@ -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
class User(models.Model):
- 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'
_inherit = ['res.users']
_columns = {
'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
}
employee_ids = fields.One2many('hr.employee', 'user_id', string='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.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.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.
@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
+8 -8
View File
@@ -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>
+9 -9
View File
@@ -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>
+49 -20
View File
@@ -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>