2
# -*- coding: utf-8 -*-
3
##############################################################################
5
# OpenERP, Open Source Management Solution
6
# Copyright (C) 2012 TeMPO Consulting, MSF. All Rights Reserved
7
# Developer: Olivier DOSSMANN
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU Affero General Public License as
11
# published by the Free Software Foundation, either version 3 of the
12
# License, or (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU Affero General Public License for more details.
19
# You should have received a copy of the GNU Affero General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
##############################################################################
25
from osv import fields
26
from lxml import etree
27
from tools.translate import _
29
class hr_employee(osv.osv):
31
_inherit = 'hr.employee'
33
_order = 'name_resource'
35
def _get_allow_edition(self, cr, uid, ids, field_name=None, arg=None, context=None):
37
For given ids get True or False regarding payroll system configuration (activated or not).
38
If payroll_ok is True, so don't permit Local employee edition.
39
Otherwise permit user to edit them.
45
setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
46
if setup and not setup.payroll_ok:
52
def onchange_type(self, cr, uid, ids, e_type=None, context=None):
54
Update allow_edition field when changing employee_type
61
elif e_type == 'local':
62
if not 'value' in res:
65
setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
66
if setup and not setup.payroll_ok:
68
res['value'].update({'allow_edition': allowed,})
72
'employee_type': fields.selection([('', ''), ('local', 'Local Staff'), ('ex', 'Expatriate employee')], string="Type", required=True),
73
'cost_center_id': fields.many2one('account.analytic.account', string="Cost Center", required=False, domain="[('category','=','OC'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
74
'funding_pool_id': fields.many2one('account.analytic.account', string="Funding Pool", domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
75
'free1_id': fields.many2one('account.analytic.account', string="Free 1", domain="[('category', '=', 'FREE1'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
76
'free2_id': fields.many2one('account.analytic.account', string="Free 2", domain="[('category', '=', 'FREE2'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
77
'homere_codeterrain': fields.char(string='Homere field: codeterrain', size=20, readonly=True, required=False),
78
'homere_id_staff': fields.integer(string='Homere field: id_staff', size=10, readonly=True, required=False),
79
'homere_id_unique': fields.char(string='Homere field: id_unique', size=42, readonly=True, required=False),
80
'gender': fields.selection([('male', 'Male'),('female', 'Female'), ('unknown', 'Unknown')], 'Gender'),
81
'private_phone': fields.char(string='Private Phone', size=32),
82
'name_resource': fields.related('resource_id', 'name', string="Name", type='char', size=128, store=True),
83
'destination_id': fields.many2one('account.analytic.account', string="Destination", domain="[('category', '=', 'DEST'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
84
'allow_edition': fields.function(_get_allow_edition, method=True, type='boolean', store=False, string="Allow local employee edition?", readonly=True),
85
'photo': fields.binary('Photo', readonly=True),
89
'employee_type': lambda *a: 'ex',
90
'homere_codeterrain': lambda *a: '',
91
'homere_id_staff': lambda *a: 0.0,
92
'homere_id_unique': lambda *a: '',
93
'gender': lambda *a: 'unknown',
96
def _check_unicity(self, cr, uid, ids, context=None):
98
Check that identification_id is not used yet.
103
# Search if no one use this identification_id
104
for e in self.browse(cr, uid, ids):
105
if e.identification_id:
106
same = self.search(cr, uid, [('identification_id', '=', e.identification_id)])
107
if same and len(same) > 1:
112
(_check_unicity, "Another employee has the same unique code.", ['identification_id']),
115
def create(self, cr, uid, vals, context=None):
117
Block creation for local staff if no 'from' in context
122
allow_edition = False
123
if 'employee_type' in vals and vals.get('employee_type') == 'local':
124
# Search Payroll functionnality preference (activated or not)
125
# If payroll_ok is False, then we permit user to create local employees
126
setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
127
if setup and not setup.payroll_ok:
129
# Raise an error if employee is created manually
130
if (not context.get('from', False) or context.get('from') not in ['yaml', 'import']) and not context.get('sync_data', False) and not allow_edition:
131
raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.'))
132
# # Raise an error if no cost_center
133
# if not vals.get('cost_center_id', False):
134
# raise osv.except_osv(_('Warning'), _('You have to complete Cost Center field before employee creation!'))
135
# Add Nat. staff by default if not in vals
136
if not vals.get('destination_id', False):
138
ns_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_destination_national_staff')[1]
141
vals.update({'destination_id': ns_id})
143
return super(hr_employee, self).create(cr, uid, vals, context)
145
def write(self, cr, uid, ids, vals, context=None):
147
Block write for local staff if no 'from' in context.
148
Allow only analytic distribution changes (cost center, funding pool, free 1 and free 2)
153
# Prepare some values
158
setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
159
if setup and not setup.payroll_ok:
161
# Prepare some variable for process
162
if vals.get('employee_type', False):
163
if vals.get('employee_type') == 'local':
165
elif vals.get('employee_type') == 'ex':
167
if (context.get('from', False) and context.get('from') in ['yaml', 'import']) or context.get('sync_data', False):
169
# Browse all employees
170
for emp in self.browse(cr, uid, ids):
171
new_vals = dict(vals)
172
# Raise an error if attempt to change local into expat and expat into local
173
if emp.employee_type == 'ex' and local and not allowed:
174
raise osv.except_osv(_('Error'), _('You are not allowed to change an expatriate to local staff!'))
175
if emp.employee_type == 'local' and ex and not allowed:
176
raise osv.except_osv(_('Error'), _('You are not allowed to change a local staff to expatriate!'))
177
# Do some modifications for local employees
178
if local or emp.employee_type == 'local':
179
# Do not change any field except analytic distribution (if not allowed)
181
if el in ['cost_center_id', 'funding_pool_id', 'free1_id', 'free2_id']:
182
new_vals.update({el: vals[el]})
184
employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context)
186
res.append(employee_id)
189
def unlink(self, cr, uid, ids, context=None):
191
Delete local staff is not allowed except if:
192
- 'unlink' is in context and its value is 'auto'
193
- Payroll functionnality have been DESactivated
198
delete_local_staff = False
200
setup = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
201
if setup and not setup.payroll_ok:
203
if (context.get('unlink', False) and context.get('unlink') == 'auto') or allowed:
204
delete_local_staff = True
205
setup_id = self.pool.get('unifield.setup.configuration').get_config(cr, uid)
206
if not setup_id.payroll_ok:
207
delete_local_staff = True
208
# Browse all employee
209
for emp in self.browse(cr, uid, ids):
210
if emp.employee_type == 'local' and (not delete_local_staff or not allowed):
211
raise osv.except_osv(_('Warning'), _('You are not allowed to delete local staff manually!'))
212
return super(hr_employee, self).unlink(cr, uid, ids, context)
214
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
216
Change funding pool domain in order to include MSF Private fund
220
view = super(hr_employee, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
221
form = etree.fromstring(view['arch'])
222
data_obj = self.pool.get('ir.model.data')
224
oc_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project')[1]
228
fields = form.xpath('/' + view_type + '//field[@name="cost_center_id"]')
230
field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
233
fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
236
fp_fields = form.xpath('/' + view_type + '//field[@name="funding_pool_id"]')
237
for field in fp_fields:
238
field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('state', '=', 'open'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
239
view['arch'] = etree.tostring(form)
242
def onchange_cc(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False):
244
Update FP or CC regarding both.
246
# Prepare some values
248
if not cost_center_id or not funding_pool_id:
250
if cost_center_id and funding_pool_id:
251
fp = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id)
253
fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
256
# Exception for MSF Private Fund
257
if funding_pool_id == fp_id:
259
if cost_center_id not in [x.id for x in fp.cost_center_ids]:
260
vals.update({'funding_pool_id': False})
261
return {'value': vals}
263
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
268
# UTP-441: only see active employee execept if args also contains a search on 'active' field
270
if context.get('disrupt_inactive', False) and context.get('disrupt_inactive') == True:
273
if not ('active', '=', False) or not ('active', '=', True) in args:
274
args += [('active', '=', True)]
275
return super(hr_employee, self).name_search(cr, uid, name, args, operator, context, limit)
278
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: