2010-06-05 06:39:12 +00:00
#-*- coding:utf-8 -*-
##############################################################################
#
2010-07-01 07:31:46 +00:00
# OpenERP, Open Source Management Solution
2010-06-05 06:39:12 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# d$
#
# This program is free software: you can redistribute it and/or modify
2010-10-28 06:54:18 +00:00
# it under the terms of the GNU Affero General Public License as published by
2010-06-05 06:39:12 +00:00
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2010-10-28 06:54:18 +00:00
# GNU Affero General Public License for more details.
2010-06-05 06:39:12 +00:00
#
2010-10-28 06:54:18 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-06-05 06:39:12 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
2010-06-05 07:32:16 +00:00
import time
2010-10-06 12:44:54 +00:00
from datetime import date
from datetime import datetime
from datetime import timedelta
2011-04-05 16:46:17 +00:00
from dateutil import relativedelta
2010-07-27 07:11:45 +00:00
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp import tools
from openerp . tools . translate import _
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2010-06-05 07:32:16 +00:00
2012-12-06 14:56:32 +00:00
from openerp . tools . safe_eval import safe_eval as eval
2011-03-22 10:35:31 +00:00
2010-06-06 18:26:08 +00:00
class hr_payroll_structure ( osv . osv ) :
"""
Salary structure used to defined
2010-06-09 14:38:22 +00:00
- Basic
2011-02-25 13:43:14 +00:00
- Allowances
2010-06-09 14:38:22 +00:00
- Deductions
2010-06-06 18:26:08 +00:00
"""
2010-10-26 12:23:32 +00:00
2010-06-06 18:26:08 +00:00
_name = ' hr.payroll.structure '
_description = ' Salary Structure '
2010-06-05 07:32:16 +00:00
_columns = {
2011-03-08 06:36:37 +00:00
' name ' : fields . char ( ' Name ' , size = 256 , required = True ) ,
2011-04-06 12:28:45 +00:00
' code ' : fields . char ( ' Reference ' , size = 64 , required = True ) ,
2011-03-07 07:18:29 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2010-06-06 16:29:11 +00:00
' note ' : fields . text ( ' Description ' ) ,
2011-03-07 07:18:29 +00:00
' parent_id ' : fields . many2one ( ' hr.payroll.structure ' , ' Parent ' ) ,
2011-09-14 08:53:11 +00:00
' children_ids ' : fields . one2many ( ' hr.payroll.structure ' , ' parent_id ' , ' Children ' ) ,
2013-04-23 09:16:26 +00:00
' rule_ids ' : fields . many2many ( ' hr.salary.rule ' , ' hr_structure_salary_rule_rel ' , ' struct_id ' , ' rule_id ' , ' Salary Rules ' ) ,
2010-06-05 07:32:16 +00:00
}
2011-04-11 10:35:27 +00:00
def _get_parent ( self , cr , uid , context = None ) :
2011-04-12 05:23:49 +00:00
obj_model = self . pool . get ( ' ir.model.data ' )
res = False
data_id = obj_model . search ( cr , uid , [ ( ' model ' , ' = ' , ' hr.payroll.structure ' ) , ( ' name ' , ' = ' , ' structure_base ' ) ] )
if data_id :
res = obj_model . browse ( cr , uid , data_id [ 0 ] , context = context ) . res_id
return res
2011-04-11 10:35:27 +00:00
2010-06-05 07:32:16 +00:00
_defaults = {
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
2011-04-11 10:35:27 +00:00
' parent_id ' : _get_parent ,
2010-06-05 07:32:16 +00:00
}
2010-07-01 07:31:46 +00:00
2013-04-23 09:16:26 +00:00
_constraints = [
( osv . osv . _check_recursion , ' Error ! You cannot create a recursive Salary Structure. ' , [ ' parent_id ' ] )
]
2010-06-05 07:32:16 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2010-06-06 18:26:08 +00:00
"""
Create a new record in hr_payroll_structure model from existing one
@param cr : cursor to database
@param user : id of current user
@param id : list of record ids on which copy method executes
@param default : dict type contains the values to be override during copy of object
@param context : context arguments , like lang , time zone
2010-07-01 07:31:46 +00:00
2010-06-06 18:26:08 +00:00
@return : returns a id of newly created record
"""
2011-04-08 09:30:10 +00:00
if not default :
default = { }
2012-09-24 16:26:45 +00:00
default . update (
code = _ ( " %s (copy) " ) % ( self . browse ( cr , uid , id , context = context ) . code ) ,
company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . id )
2010-10-27 13:10:19 +00:00
return super ( hr_payroll_structure , self ) . copy ( cr , uid , id , default , context = context )
2010-07-01 07:31:46 +00:00
2011-04-04 21:28:11 +00:00
def get_all_rules ( self , cr , uid , structure_ids , context = None ) :
"""
@param structure_ids : list of structure
@return : returns a list of tuple ( id , sequence ) of rules that are maybe to apply
"""
all_rules = [ ]
for struct in self . browse ( cr , uid , structure_ids , context = context ) :
2011-04-08 12:17:59 +00:00
all_rules + = self . pool . get ( ' hr.salary.rule ' ) . _recursive_search_of_rules ( cr , uid , struct . rule_ids , context = context )
2011-04-04 21:28:11 +00:00
return all_rules
def _get_parent_structure ( self , cr , uid , struct_ids , context = None ) :
if not struct_ids :
return [ ]
parent = [ ]
for struct in self . browse ( cr , uid , struct_ids , context = context ) :
if struct . parent_id :
parent . append ( struct . parent_id . id )
if parent :
parent = self . _get_parent_structure ( cr , uid , parent , context )
return parent + struct_ids
2010-06-05 07:32:16 +00:00
class hr_contract ( osv . osv ) :
2010-06-06 19:09:53 +00:00
"""
Employee contract based on the visa , work permits
2011-02-25 13:43:14 +00:00
allows to configure different Salary structure
2010-06-06 19:09:53 +00:00
"""
2010-10-26 12:23:32 +00:00
2010-06-05 07:32:16 +00:00
_inherit = ' hr.contract '
_description = ' Employee Contract '
_columns = {
2011-06-30 09:15:33 +00:00
' struct_id ' : fields . many2one ( ' hr.payroll.structure ' , ' Salary Structure ' ) ,
2011-03-18 13:13:33 +00:00
' schedule_pay ' : fields . selection ( [
( ' monthly ' , ' Monthly ' ) ,
( ' quarterly ' , ' Quarterly ' ) ,
( ' semi-annually ' , ' Semi-annually ' ) ,
( ' annually ' , ' Annually ' ) ,
( ' weekly ' , ' Weekly ' ) ,
( ' bi-weekly ' , ' Bi-weekly ' ) ,
( ' bi-monthly ' , ' Bi-monthly ' ) ,
] , ' Scheduled Pay ' , select = True ) ,
2010-06-18 03:11:40 +00:00
}
2011-03-03 13:17:54 +00:00
2011-09-14 14:24:50 +00:00
_defaults = {
' schedule_pay ' : ' monthly ' ,
}
2011-04-04 21:28:11 +00:00
def get_all_structures ( self , cr , uid , contract_ids , context = None ) :
"""
@param contract_ids : list of contracts
@return : the structures linked to the given contracts , ordered by hierachy ( parent = False first , then first level children and so on ) and without duplicata
"""
2013-12-23 10:18:41 +00:00
structure_ids = [ contract . struct_id . id for contract in self . browse ( cr , uid , contract_ids , context = context ) if contract . struct_id ]
if not structure_ids :
return [ ]
2011-04-04 21:28:11 +00:00
return list ( set ( self . pool . get ( ' hr.payroll.structure ' ) . _get_parent_structure ( cr , uid , structure_ids , context = context ) ) )
2010-06-06 16:29:11 +00:00
class contrib_register ( osv . osv ) :
'''
Contribution Register
'''
2010-10-26 12:23:32 +00:00
2011-04-12 17:01:28 +00:00
_name = ' hr.contribution.register '
2010-06-06 16:29:11 +00:00
_description = ' Contribution Register '
2010-07-01 07:31:46 +00:00
2010-06-06 16:29:11 +00:00
_columns = {
2012-06-20 13:04:49 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' ) ,
2010-06-06 16:29:11 +00:00
' name ' : fields . char ( ' Name ' , size = 256 , required = True , readonly = False ) ,
2011-04-07 10:12:26 +00:00
' register_line_ids ' : fields . one2many ( ' hr.payslip.line ' , ' register_id ' , ' Register Line ' , readonly = True ) ,
2010-06-06 16:29:11 +00:00
' note ' : fields . text ( ' Description ' ) ,
}
_defaults = {
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
}
2011-03-08 06:36:37 +00:00
2010-06-06 16:29:11 +00:00
2011-05-11 07:18:54 +00:00
class hr_salary_rule_category ( osv . osv ) :
2010-06-09 14:38:22 +00:00
"""
2011-05-11 07:18:54 +00:00
HR Salary Rule Category
2010-06-09 14:38:22 +00:00
"""
2010-07-01 07:31:46 +00:00
2011-05-11 07:18:54 +00:00
_name = ' hr.salary.rule.category '
_description = ' Salary Rule Category '
2010-06-09 14:38:22 +00:00
_columns = {
2011-03-07 07:18:29 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , readonly = False ) ,
' code ' : fields . char ( ' Code ' , size = 64 , required = True , readonly = False ) ,
2011-05-11 07:18:54 +00:00
' parent_id ' : fields . many2one ( ' hr.salary.rule.category ' , ' Parent ' , help = " Linking a salary category to its parent is used only for the reporting purpose. " ) ,
2011-09-14 08:53:11 +00:00
' children_ids ' : fields . one2many ( ' hr.salary.rule.category ' , ' parent_id ' , ' Children ' ) ,
2010-06-09 14:54:48 +00:00
' note ' : fields . text ( ' Description ' ) ,
2010-06-09 14:38:22 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = False ) ,
2010-06-06 16:29:11 +00:00
}
2011-03-08 06:36:37 +00:00
2010-06-06 16:29:11 +00:00
_defaults = {
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
}
2011-03-08 06:36:37 +00:00
2010-06-06 16:29:11 +00:00
2011-04-12 13:35:42 +00:00
class one2many_mod2 ( fields . one2many ) :
def get ( self , cr , obj , ids , name , user = None , offset = 0 , context = None , values = None ) :
if context is None :
context = { }
if not values :
values = { }
res = { }
for id in ids :
res [ id ] = [ ]
2013-03-29 14:37:20 +00:00
ids2 = obj . pool [ self . _obj ] . search ( cr , user , [ ( self . _fields_id , ' in ' , ids ) , ( ' appears_on_payslip ' , ' = ' , True ) ] , limit = self . _limit )
for r in obj . pool [ self . _obj ] . _read_flat ( cr , user , ids2 , [ self . _fields_id ] , context = context , load = ' _classic_write ' ) :
2011-04-12 13:35:42 +00:00
res [ r [ self . _fields_id ] ] . append ( r [ ' id ' ] )
return res
2011-05-03 12:38:07 +00:00
class hr_payslip_run ( osv . osv ) :
2011-04-25 12:30:44 +00:00
2011-05-03 12:38:07 +00:00
_name = ' hr.payslip.run '
2011-11-08 23:00:27 +00:00
_description = ' Payslip Batches '
2011-04-25 12:30:44 +00:00
_columns = {
2011-05-12 07:49:18 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-05-12 07:02:53 +00:00
' slip_ids ' : fields . one2many ( ' hr.payslip ' , ' payslip_run_id ' , ' Payslips ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-05-11 09:10:09 +00:00
' state ' : fields . selection ( [
( ' draft ' , ' Draft ' ) ,
( ' close ' , ' Close ' ) ,
2012-05-04 11:57:48 +00:00
] , ' Status ' , select = True , readonly = True ) ,
2011-05-24 11:41:17 +00:00
' date_start ' : fields . date ( ' Date From ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' date_end ' : fields . date ( ' Date To ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-05-23 06:01:13 +00:00
' credit_note ' : fields . boolean ( ' Credit Note ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " If its checked, indicates that all payslips generated from here are refund payslips. " ) ,
2011-05-11 09:10:09 +00:00
}
_defaults = {
' state ' : ' draft ' ,
2011-05-24 11:41:17 +00:00
' date_start ' : lambda * a : time . strftime ( ' % Y- % m-01 ' ) ,
' date_end ' : lambda * a : str ( datetime . now ( ) + relativedelta . relativedelta ( months = + 1 , day = 1 , days = - 1 ) ) [ : 10 ] ,
2011-04-25 12:30:44 +00:00
}
2011-05-11 09:10:09 +00:00
def draft_payslip_run ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' state ' : ' draft ' } , context = context )
def close_payslip_run ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' state ' : ' close ' } , context = context )
2011-04-25 12:30:44 +00:00
2010-06-06 16:29:11 +00:00
class hr_payslip ( osv . osv ) :
'''
Pay Slip
'''
2010-10-26 12:23:32 +00:00
2010-06-06 16:29:11 +00:00
_name = ' hr.payslip '
_description = ' Pay Slip '
2010-07-01 07:31:46 +00:00
2011-05-11 07:18:54 +00:00
def _get_lines_salary_rule_category ( self , cr , uid , ids , field_names , arg = None , context = None ) :
2011-04-11 08:40:07 +00:00
result = { }
if not ids : return result
2011-04-25 12:30:44 +00:00
for id in ids :
result . setdefault ( id , [ ] )
2011-04-11 08:40:07 +00:00
cr . execute ( ''' SELECT pl.slip_id, pl.id FROM hr_payslip_line AS pl \
2011-05-11 07:18:54 +00:00
LEFT JOIN hr_salary_rule_category AS sh on ( pl . category_id = sh . id ) \
2011-04-11 08:40:07 +00:00
WHERE pl . slip_id in % s \
2011-05-04 11:13:48 +00:00
GROUP BY pl . slip_id , pl . sequence , pl . id ORDER BY pl . sequence ''' ,(tuple(ids),))
2011-04-11 08:40:07 +00:00
res = cr . fetchall ( )
for r in res :
result [ r [ 0 ] ] . append ( r [ 1 ] )
return result
2011-03-14 06:16:21 +00:00
2010-06-06 16:29:11 +00:00
_columns = {
2011-05-11 09:10:09 +00:00
' struct_id ' : fields . many2one ( ' hr.payroll.structure ' , ' Structure ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = ' Defines the rules that have to be applied to this payslip, accordingly to the contract chosen. If you let empty the field contract, this field isn \' t mandatory anymore and thus the rules applied will be all the rules set on the structure of all contracts of the employee valid for the chosen period ' ) ,
2013-03-20 13:31:37 +00:00
' name ' : fields . char ( ' Payslip Name ' , size = 64 , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-06 12:28:45 +00:00
' number ' : fields . char ( ' Reference ' , size = 64 , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-04 06:29:30 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , ' Employee ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-05 16:46:17 +00:00
' date_from ' : fields . date ( ' Date From ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , required = True ) ,
' date_to ' : fields . date ( ' Date To ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , required = True ) ,
2011-04-04 06:29:30 +00:00
' state ' : fields . selection ( [
2011-05-17 10:51:55 +00:00
( ' draft ' , ' Draft ' ) ,
2011-05-24 12:31:16 +00:00
( ' verify ' , ' Waiting ' ) ,
2011-05-17 10:51:55 +00:00
( ' done ' , ' Done ' ) ,
2011-05-24 12:31:16 +00:00
( ' cancel ' , ' Rejected ' ) ,
2012-05-04 11:57:48 +00:00
] , ' Status ' , select = True , readonly = True ,
2012-10-12 11:42:58 +00:00
help = ' * When the payslip is created the status is \' Draft \' . \
\n * If the payslip is under verification , the status is \' Waiting \' . \
\n * If the payslip is confirmed then status is set to \' Done \' . \
\n * When user cancel payslip the status is \' Rejected \' . ' ) ,
2011-04-12 13:35:42 +00:00
# 'line_ids': fields.one2many('hr.payslip.line', 'slip_id', 'Payslip Line', required=False, readonly=True, states={'draft': [('readonly', False)]}),
2011-05-12 04:59:02 +00:00
' line_ids ' : one2many_mod2 ( ' hr.payslip.line ' , ' slip_id ' , ' Payslip Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-04 06:29:30 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-05-09 08:50:02 +00:00
' worked_days_line_ids ' : fields . one2many ( ' hr.payslip.worked_days ' , ' payslip_id ' , ' Payslip Worked Days ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-04 11:32:19 +00:00
' input_line_ids ' : fields . one2many ( ' hr.payslip.input ' , ' payslip_id ' , ' Payslip Inputs ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-04 06:29:30 +00:00
' paid ' : fields . boolean ( ' Made Payment Order ? ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2013-03-12 13:33:09 +00:00
' note ' : fields . text ( ' Internal Note ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-04-04 06:29:30 +00:00
' contract_id ' : fields . many2one ( ' hr.contract ' , ' Contract ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-05-11 07:18:54 +00:00
' details_by_salary_rule_category ' : fields . function ( _get_lines_salary_rule_category , method = True , type = ' one2many ' , relation = ' hr.payslip.line ' , string = ' Details by Salary Rule Category ' ) ,
2011-06-29 09:03:53 +00:00
' credit_note ' : fields . boolean ( ' Credit Note ' , help = " Indicates this payslip has a refund of another " , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-11-08 23:00:27 +00:00
' payslip_run_id ' : fields . many2one ( ' hr.payslip.run ' , ' Payslip Batches ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-06-06 16:29:11 +00:00
}
_defaults = {
2011-04-05 16:46:17 +00:00
' date_from ' : lambda * a : time . strftime ( ' % Y- % m-01 ' ) ,
' date_to ' : lambda * a : str ( datetime . now ( ) + relativedelta . relativedelta ( months = + 1 , day = 1 , days = - 1 ) ) [ : 10 ] ,
2011-03-14 08:57:50 +00:00
' state ' : ' draft ' ,
2011-04-08 09:30:10 +00:00
' credit_note ' : False ,
2010-06-06 16:29:11 +00:00
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
}
2010-07-01 07:31:46 +00:00
2011-11-22 12:41:29 +00:00
def _check_dates ( self , cr , uid , ids , context = None ) :
2011-11-30 14:01:07 +00:00
for payslip in self . browse ( cr , uid , ids , context = context ) :
if payslip . date_from > payslip . date_to :
2011-11-23 05:31:54 +00:00
return False
return True
2012-09-24 16:26:45 +00:00
_constraints = [ ( _check_dates , " Payslip ' Date From ' must be before ' Date To ' . " , [ ' date_from ' , ' date_to ' ] ) ]
2010-07-01 07:31:46 +00:00
2010-06-06 16:29:11 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2011-04-08 09:30:10 +00:00
if not default :
default = { }
2010-06-06 16:29:11 +00:00
company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . id
2011-04-08 09:30:10 +00:00
default . update ( {
2011-03-29 11:28:13 +00:00
' line_ids ' : [ ] ,
2011-03-22 10:35:31 +00:00
' company_id ' : company_id ,
2012-07-26 11:11:13 +00:00
' number ' : ' ' ,
' payslip_run_id ' : False ,
2012-07-30 05:58:04 +00:00
' paid ' : False ,
2011-04-08 09:30:10 +00:00
} )
2011-03-14 11:22:27 +00:00
return super ( hr_payslip , self ) . copy ( cr , uid , id , default , context = context )
2010-07-01 07:31:46 +00:00
2010-07-27 07:11:45 +00:00
def cancel_sheet ( self , cr , uid , ids , context = None ) :
2011-04-04 09:06:56 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
2010-07-01 07:31:46 +00:00
2010-07-27 07:11:45 +00:00
def process_sheet ( self , cr , uid , ids , context = None ) :
2011-04-04 09:06:56 +00:00
return self . write ( cr , uid , ids , { ' paid ' : True , ' state ' : ' done ' } , context = context )
2010-10-26 12:23:32 +00:00
2011-05-17 10:51:55 +00:00
def hr_verify_sheet ( self , cr , uid , ids , context = None ) :
2012-11-01 05:09:24 +00:00
self . compute_sheet ( cr , uid , ids , context )
2011-05-17 10:51:55 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' verify ' } , context = context )
2011-05-27 07:32:56 +00:00
2011-04-08 09:30:10 +00:00
def refund_sheet ( self , cr , uid , ids , context = None ) :
2011-04-08 10:34:12 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2011-07-11 15:03:09 +00:00
for payslip in self . browse ( cr , uid , ids , context = context ) :
id_copy = self . copy ( cr , uid , payslip . id , { ' credit_note ' : True , ' name ' : _ ( ' Refund: ' ) + payslip . name } , context = context )
2011-04-08 09:30:10 +00:00
self . compute_sheet ( cr , uid , [ id_copy ] , context = context )
2013-01-31 06:33:33 +00:00
self . signal_hr_verify_sheet ( cr , uid , [ id_copy ] )
self . signal_process_sheet ( cr , uid , [ id_copy ] )
2011-04-08 10:34:12 +00:00
form_id = mod_obj . get_object_reference ( cr , uid , ' hr_payroll ' , ' view_hr_payslip_form ' )
form_res = form_id and form_id [ 1 ] or False
tree_id = mod_obj . get_object_reference ( cr , uid , ' hr_payroll ' , ' view_hr_payslip_tree ' )
tree_res = tree_id and tree_id [ 1 ] or False
return {
' name ' : _ ( " Refund Payslip " ) ,
' view_mode ' : ' tree, form ' ,
' view_id ' : False ,
' view_type ' : ' form ' ,
' res_model ' : ' hr.payslip ' ,
' type ' : ' ir.actions.act_window ' ,
' nodestroy ' : True ,
' target ' : ' current ' ,
' domain ' : " [( ' id ' , ' in ' , %s )] " % [ id_copy ] ,
' views ' : [ ( tree_res , ' tree ' ) , ( form_res , ' form ' ) ] ,
' context ' : { }
}
2011-04-08 09:30:10 +00:00
2011-05-20 11:55:14 +00:00
def check_done ( self , cr , uid , ids , context = None ) :
2011-05-17 10:51:55 +00:00
return True
2011-04-01 10:58:07 +00:00
2012-11-02 06:11:26 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
for payslip in self . browse ( cr , uid , ids , context = context ) :
if payslip . state not in [ ' draft ' , ' cancel ' ] :
2012-11-13 07:57:33 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot delete a payslip which is not draft or cancelled! ' ) )
2012-11-02 06:11:26 +00:00
return super ( hr_payslip , self ) . unlink ( cr , uid , ids , context )
2011-04-06 12:28:45 +00:00
#TODO move this function into hr_contract module, on hr.employee object
2011-04-05 16:46:17 +00:00
def get_contract ( self , cr , uid , employee , date_from , date_to , context = None ) :
"""
@param employee : browse record of employee
@param date_from : date field
@param date_to : date field
@return : returns the ids of all the contracts for the given employee that need to be considered for the given dates
"""
2011-03-29 13:32:43 +00:00
contract_obj = self . pool . get ( ' hr.contract ' )
2011-04-05 16:46:17 +00:00
clause = [ ]
2011-04-06 05:55:19 +00:00
#a contract is valid if it ends between the given dates
2011-04-05 16:46:17 +00:00
clause_1 = [ ' & ' , ( ' date_end ' , ' <= ' , date_to ) , ( ' date_end ' , ' >= ' , date_from ) ]
2011-04-06 05:55:19 +00:00
#OR if it starts between the given dates
2011-04-05 16:46:17 +00:00
clause_2 = [ ' & ' , ( ' date_start ' , ' <= ' , date_to ) , ( ' date_start ' , ' >= ' , date_from ) ]
#OR if it starts before the date_from and finish after the date_end (or never finish)
2014-07-18 05:58:44 +00:00
clause_3 = [ ' & ' , ( ' date_start ' , ' <= ' , date_from ) , ' | ' , ( ' date_end ' , ' = ' , False ) , ( ' date_end ' , ' >= ' , date_to ) ]
2011-04-05 16:46:17 +00:00
clause_final = [ ( ' employee_id ' , ' = ' , employee . id ) , ' | ' , ' | ' ] + clause_1 + clause_2 + clause_3
2011-05-12 09:00:09 +00:00
contract_ids = contract_obj . search ( cr , uid , clause_final , context = context )
2011-04-04 21:28:11 +00:00
return contract_ids
2010-07-01 07:31:46 +00:00
2011-04-05 16:46:17 +00:00
def compute_sheet ( self , cr , uid , ids , context = None ) :
2011-04-06 05:39:48 +00:00
slip_line_pool = self . pool . get ( ' hr.payslip.line ' )
2011-04-18 12:27:08 +00:00
sequence_obj = self . pool . get ( ' ir.sequence ' )
2011-04-05 16:46:17 +00:00
for payslip in self . browse ( cr , uid , ids , context = context ) :
2011-07-11 15:03:09 +00:00
number = payslip . number or sequence_obj . get ( cr , uid , ' salary.slip ' )
2011-04-06 05:55:19 +00:00
#delete old payslip lines
old_slipline_ids = slip_line_pool . search ( cr , uid , [ ( ' slip_id ' , ' = ' , payslip . id ) ] , context = context )
2011-05-02 12:18:22 +00:00
# old_slipline_ids
2011-04-06 05:55:19 +00:00
if old_slipline_ids :
slip_line_pool . unlink ( cr , uid , old_slipline_ids , context = context )
2011-04-05 16:46:17 +00:00
if payslip . contract_id :
#set the list of contract for which the rules have to be applied
contract_ids = [ payslip . contract_id . id ]
2011-03-28 10:21:53 +00:00
else :
2011-04-05 16:46:17 +00:00
#if we don't give the contract, then the rules to apply should be for all current contracts of the employee
2011-04-06 05:39:48 +00:00
contract_ids = self . get_contract ( cr , uid , payslip . employee_id , payslip . date_from , payslip . date_to , context = context )
2011-04-11 12:12:41 +00:00
lines = [ ( 0 , 0 , line ) for line in self . pool . get ( ' hr.payslip ' ) . get_payslip_lines ( cr , uid , contract_ids , payslip . id , context = context ) ]
2011-04-18 12:27:08 +00:00
self . write ( cr , uid , [ payslip . id ] , { ' line_ids ' : lines , ' number ' : number , } , context = context )
2011-04-05 16:46:17 +00:00
return True
2011-05-09 08:50:02 +00:00
def get_worked_day_lines ( self , cr , uid , contract_ids , date_from , date_to , context = None ) :
2011-04-05 16:46:17 +00:00
"""
@param contract_ids : list of contract id
@return : returns a list of dict containing the input that should be applied for the given contract between date_from and date_to
"""
2011-04-06 12:28:45 +00:00
def was_on_leave ( employee_id , datetime_day , context = None ) :
res = False
day = datetime_day . strftime ( " % Y- % m- %d " )
holiday_ids = self . pool . get ( ' hr.holidays ' ) . search ( cr , uid , [ ( ' state ' , ' = ' , ' validate ' ) , ( ' employee_id ' , ' = ' , employee_id ) , ( ' type ' , ' = ' , ' remove ' ) , ( ' date_from ' , ' <= ' , day ) , ( ' date_to ' , ' >= ' , day ) ] )
if holiday_ids :
res = self . pool . get ( ' hr.holidays ' ) . browse ( cr , uid , holiday_ids , context = context ) [ 0 ] . holiday_status_id . name
return res
2011-04-05 16:46:17 +00:00
res = [ ]
for contract in self . pool . get ( ' hr.contract ' ) . browse ( cr , uid , contract_ids , context = context ) :
if not contract . working_hours :
#fill only if the contract as a working schedule linked
continue
2011-04-06 12:28:45 +00:00
attendances = {
' name ' : _ ( " Normal Working Days paid at 100 % " ) ,
' sequence ' : 1 ,
' code ' : ' WORK100 ' ,
' number_of_days ' : 0.0 ,
' number_of_hours ' : 0.0 ,
' contract_id ' : contract . id ,
}
leaves = { }
2011-04-05 16:46:17 +00:00
day_from = datetime . strptime ( date_from , " % Y- % m- %d " )
day_to = datetime . strptime ( date_to , " % Y- % m- %d " )
2011-04-06 12:28:45 +00:00
nb_of_days = ( day_to - day_from ) . days + 1
for day in range ( 0 , nb_of_days ) :
working_hours_on_day = self . pool . get ( ' resource.calendar ' ) . working_hours_on_day ( cr , uid , contract . working_hours , day_from + timedelta ( days = day ) , context )
if working_hours_on_day :
#the employee had to work
leave_type = was_on_leave ( contract . employee_id . id , day_from + timedelta ( days = day ) , context = context )
if leave_type :
#if he was on leave, fill the leaves dict
if leave_type in leaves :
leaves [ leave_type ] [ ' number_of_days ' ] + = 1.0
leaves [ leave_type ] [ ' number_of_hours ' ] + = working_hours_on_day
else :
leaves [ leave_type ] = {
' name ' : leave_type ,
' sequence ' : 5 ,
' code ' : leave_type ,
' number_of_days ' : 1.0 ,
' number_of_hours ' : working_hours_on_day ,
' contract_id ' : contract . id ,
}
else :
#add the input vals to tmp (increment if existing)
attendances [ ' number_of_days ' ] + = 1.0
attendances [ ' number_of_hours ' ] + = working_hours_on_day
leaves = [ value for key , value in leaves . items ( ) ]
res + = [ attendances ] + leaves
2011-04-05 16:46:17 +00:00
return res
2011-04-01 12:21:32 +00:00
2011-05-10 11:19:31 +00:00
def get_inputs ( self , cr , uid , contract_ids , date_from , date_to , context = None ) :
2011-05-09 08:50:02 +00:00
res = [ ]
contract_obj = self . pool . get ( ' hr.contract ' )
rule_obj = self . pool . get ( ' hr.salary.rule ' )
structure_ids = contract_obj . get_all_structures ( cr , uid , contract_ids , context = context )
rule_ids = self . pool . get ( ' hr.payroll.structure ' ) . get_all_rules ( cr , uid , structure_ids , context = context )
sorted_rule_ids = [ id for id , sequence in sorted ( rule_ids , key = lambda x : x [ 1 ] ) ]
for contract in contract_obj . browse ( cr , uid , contract_ids , context = context ) :
for rule in rule_obj . browse ( cr , uid , sorted_rule_ids , context = context ) :
if rule . input_ids :
for input in rule . input_ids :
inputs = {
' name ' : input . name ,
' code ' : input . code ,
' contract_id ' : contract . id ,
}
res + = [ inputs ]
return res
2011-04-05 16:46:17 +00:00
def get_payslip_lines ( self , cr , uid , contract_ids , payslip_id , context ) :
2011-05-11 07:18:54 +00:00
def _sum_salary_rule_category ( localdict , category , amount ) :
2011-05-10 11:54:11 +00:00
if category . parent_id :
2011-05-11 07:18:54 +00:00
localdict = _sum_salary_rule_category ( localdict , category . parent_id , amount )
2011-05-27 10:13:22 +00:00
localdict [ ' categories ' ] . dict [ category . code ] = category . code in localdict [ ' categories ' ] . dict and localdict [ ' categories ' ] . dict [ category . code ] + amount or amount
2011-04-08 14:55:54 +00:00
return localdict
2011-05-23 14:09:26 +00:00
class BrowsableObject ( object ) :
2011-05-12 16:16:28 +00:00
def __init__ ( self , pool , cr , uid , employee_id , dict ) :
self . pool = pool
self . cr = cr
self . uid = uid
self . employee_id = employee_id
self . dict = dict
2011-05-13 06:36:39 +00:00
2011-05-12 16:16:28 +00:00
def __getattr__ ( self , attr ) :
2011-05-27 10:13:22 +00:00
return attr in self . dict and self . dict . __getitem__ ( attr ) or 0.0
2011-05-13 06:36:39 +00:00
2011-05-23 14:09:26 +00:00
class InputLine ( BrowsableObject ) :
""" a class that will be used into the python code, mainly for usability purposes """
2011-05-12 16:16:28 +00:00
def sum ( self , code , from_date , to_date = None ) :
if to_date is None :
to_date = datetime . now ( ) . strftime ( ' % Y- % m- %d ' )
result = 0.0
2011-05-24 06:02:02 +00:00
self . cr . execute ( " SELECT sum(amount) as sum \
2011-05-12 16:16:28 +00:00
FROM hr_payslip as hp , hr_payslip_input as pi \
2011-05-25 09:12:34 +00:00
WHERE hp . employee_id = % s AND hp . state = ' done ' \
2011-05-12 16:16:28 +00:00
AND hp . date_from > = % s AND hp . date_to < = % s AND hp . id = pi . payslip_id AND pi . code = % s " ,
( self . employee_id , from_date , to_date , code ) )
res = self . cr . fetchone ( ) [ 0 ]
return res or 0.0
2011-05-23 14:09:26 +00:00
class WorkedDays ( BrowsableObject ) :
2011-05-12 16:16:28 +00:00
""" a class that will be used into the python code, mainly for usability purposes """
def _sum ( self , code , from_date , to_date = None ) :
if to_date is None :
to_date = datetime . now ( ) . strftime ( ' % Y- % m- %d ' )
result = 0.0
self . cr . execute ( " SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours \
FROM hr_payslip as hp , hr_payslip_worked_days as pi \
2011-05-25 09:12:34 +00:00
WHERE hp . employee_id = % s AND hp . state = ' done ' \
2011-05-12 16:16:28 +00:00
AND hp . date_from > = % s AND hp . date_to < = % s AND hp . id = pi . payslip_id AND pi . code = % s " ,
( self . employee_id , from_date , to_date , code ) )
return self . cr . fetchone ( )
2011-05-13 06:36:39 +00:00
2011-05-12 16:16:28 +00:00
def sum ( self , code , from_date , to_date = None ) :
res = self . _sum ( code , from_date , to_date )
return res and res [ 0 ] or 0.0
2011-05-13 06:36:39 +00:00
2011-05-12 16:16:28 +00:00
def sum_hours ( self , code , from_date , to_date = None ) :
2011-05-13 06:36:39 +00:00
res = self . _sum ( code , from_date , to_date )
2011-05-12 16:16:28 +00:00
return res and res [ 1 ] or 0.0
2011-05-23 14:09:26 +00:00
class Payslips ( BrowsableObject ) :
2011-05-13 06:00:12 +00:00
""" a class that will be used into the python code, mainly for usability purposes """
def sum ( self , code , from_date , to_date = None ) :
if to_date is None :
to_date = datetime . now ( ) . strftime ( ' % Y- % m- %d ' )
2011-05-13 11:23:15 +00:00
self . cr . execute ( " SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end) \
2011-05-13 06:00:12 +00:00
FROM hr_payslip as hp , hr_payslip_line as pl \
2011-05-25 09:12:34 +00:00
WHERE hp . employee_id = % s AND hp . state = ' done ' \
2011-05-13 06:00:12 +00:00
AND hp . date_from > = % s AND hp . date_to < = % s AND hp . id = pl . slip_id AND pl . code = % s " ,
( self . employee_id , from_date , to_date , code ) )
2011-05-13 11:23:15 +00:00
res = self . cr . fetchone ( )
return res and res [ 0 ] or 0.0
2011-05-13 06:00:12 +00:00
2011-04-12 17:01:28 +00:00
#we keep a dict with the result because a value can be overwritten by another rule with the same code
result_dict = { }
2011-05-26 11:08:38 +00:00
rules = { }
2011-05-26 05:54:02 +00:00
categories_dict = { }
2011-04-08 12:19:36 +00:00
blacklist = [ ]
2011-05-12 12:36:19 +00:00
payslip_obj = self . pool . get ( ' hr.payslip ' )
2011-05-12 13:58:25 +00:00
inputs_obj = self . pool . get ( ' hr.payslip.worked_days ' )
2011-04-25 07:19:30 +00:00
obj_rule = self . pool . get ( ' hr.salary.rule ' )
2011-05-12 12:36:19 +00:00
payslip = payslip_obj . browse ( cr , uid , payslip_id , context = context )
2011-04-08 12:56:16 +00:00
worked_days = { }
2011-05-09 08:50:02 +00:00
for worked_days_line in payslip . worked_days_line_ids :
worked_days [ worked_days_line . code ] = worked_days_line
2011-05-10 11:19:31 +00:00
inputs = { }
2011-04-08 12:56:16 +00:00
for input_line in payslip . input_line_ids :
2011-05-10 11:19:31 +00:00
inputs [ input_line . code ] = input_line
2011-06-02 13:00:43 +00:00
2011-05-24 10:33:12 +00:00
categories_obj = BrowsableObject ( self . pool , cr , uid , payslip . employee_id . id , categories_dict )
2011-05-12 16:16:28 +00:00
input_obj = InputLine ( self . pool , cr , uid , payslip . employee_id . id , inputs )
2011-05-13 06:00:12 +00:00
worked_days_obj = WorkedDays ( self . pool , cr , uid , payslip . employee_id . id , worked_days )
payslip_obj = Payslips ( self . pool , cr , uid , payslip . employee_id . id , payslip )
2011-05-26 11:08:38 +00:00
rules_obj = BrowsableObject ( self . pool , cr , uid , payslip . employee_id . id , rules )
2011-05-13 06:00:12 +00:00
2014-04-02 08:29:54 +00:00
baselocaldict = { ' categories ' : categories_obj , ' rules ' : rules_obj , ' payslip ' : payslip_obj , ' worked_days ' : worked_days_obj , ' inputs ' : input_obj }
2011-04-06 05:55:19 +00:00
#get the ids of the structures on the contracts and their parent id as well
2011-04-04 21:28:11 +00:00
structure_ids = self . pool . get ( ' hr.contract ' ) . get_all_structures ( cr , uid , contract_ids , context = context )
#get the rules of the structure and thier children
rule_ids = self . pool . get ( ' hr.payroll.structure ' ) . get_all_rules ( cr , uid , structure_ids , context = context )
#run the rules by sequence
sorted_rule_ids = [ id for id , sequence in sorted ( rule_ids , key = lambda x : x [ 1 ] ) ]
2011-04-06 05:55:19 +00:00
2011-04-04 21:28:11 +00:00
for contract in self . pool . get ( ' hr.contract ' ) . browse ( cr , uid , contract_ids , context = context ) :
2011-04-06 12:28:45 +00:00
employee = contract . employee_id
2014-04-02 08:29:54 +00:00
localdict = dict ( baselocaldict , employee = employee , contract = contract )
2011-04-25 07:19:30 +00:00
for rule in obj_rule . browse ( cr , uid , sorted_rule_ids , context = context ) :
2011-04-12 17:01:28 +00:00
key = rule . code + ' - ' + str ( contract . id )
2011-04-08 14:55:54 +00:00
localdict [ ' result ' ] = None
2011-05-26 16:56:10 +00:00
localdict [ ' result_qty ' ] = 1.0
2014-03-31 05:01:22 +00:00
localdict [ ' result_rate ' ] = 100
2011-04-04 21:28:11 +00:00
#check if the rule can be applied
2011-04-25 07:19:30 +00:00
if obj_rule . satisfy_condition ( cr , uid , rule . id , localdict , context = context ) and rule . id not in blacklist :
2011-04-12 17:01:28 +00:00
#compute the amount of the rule
2012-01-05 08:13:25 +00:00
amount , qty , rate = obj_rule . compute_rule ( cr , uid , rule . id , localdict , context = context )
2011-04-12 17:01:28 +00:00
#check if there is already a rule computed with that code
2011-04-25 07:19:30 +00:00
previous_amount = rule . code in localdict and localdict [ rule . code ] or 0.0
2011-04-06 12:28:45 +00:00
#set/overwrite the amount computed for this rule in the localdict
2012-01-05 08:13:25 +00:00
tot_rule = amount * qty * rate / 100.0
localdict [ rule . code ] = tot_rule
2011-05-27 07:23:52 +00:00
rules [ rule . code ] = rule
2011-05-10 14:27:23 +00:00
#sum the amount for its salary category
2012-01-05 08:13:25 +00:00
localdict = _sum_salary_rule_category ( localdict , rule . category_id , tot_rule - previous_amount )
2011-04-12 17:01:28 +00:00
#create/overwrite the rule in the temporary results
result_dict [ key ] = {
2011-04-08 22:38:45 +00:00
' salary_rule_id ' : rule . id ,
2011-04-12 17:01:28 +00:00
' contract_id ' : contract . id ,
2011-04-04 21:28:11 +00:00
' name ' : rule . name ,
' code ' : rule . code ,
' category_id ' : rule . category_id . id ,
' sequence ' : rule . sequence ,
' appears_on_payslip ' : rule . appears_on_payslip ,
' condition_select ' : rule . condition_select ,
' condition_python ' : rule . condition_python ,
' condition_range ' : rule . condition_range ,
' condition_range_min ' : rule . condition_range_min ,
' condition_range_max ' : rule . condition_range_max ,
' amount_select ' : rule . amount_select ,
' amount_fix ' : rule . amount_fix ,
' amount_python_compute ' : rule . amount_python_compute ,
' amount_percentage ' : rule . amount_percentage ,
' amount_percentage_base ' : rule . amount_percentage_base ,
' register_id ' : rule . register_id . id ,
2011-05-26 16:56:10 +00:00
' amount ' : amount ,
2011-04-04 21:28:11 +00:00
' employee_id ' : contract . employee_id . id ,
2011-05-26 16:56:10 +00:00
' quantity ' : qty ,
2012-01-05 08:13:25 +00:00
' rate ' : rate ,
2011-04-04 21:28:11 +00:00
}
2011-04-08 12:19:36 +00:00
else :
#blacklist this rule and its children
blacklist + = [ id for id , seq in self . pool . get ( ' hr.salary.rule ' ) . _recursive_search_of_rules ( cr , uid , [ rule ] , context = context ) ]
2011-04-25 07:25:10 +00:00
2011-04-12 17:01:28 +00:00
result = [ value for code , value in result_dict . items ( ) ]
2011-04-04 21:28:11 +00:00
return result
2011-04-05 16:46:17 +00:00
def onchange_employee_id ( self , cr , uid , ids , date_from , date_to , employee_id = False , contract_id = False , context = None ) :
2011-02-25 13:43:14 +00:00
empolyee_obj = self . pool . get ( ' hr.employee ' )
2011-03-25 13:20:10 +00:00
contract_obj = self . pool . get ( ' hr.contract ' )
2011-05-09 08:50:02 +00:00
worked_days_obj = self . pool . get ( ' hr.payslip.worked_days ' )
2011-04-06 12:28:45 +00:00
input_obj = self . pool . get ( ' hr.payslip.input ' )
2011-03-14 11:22:27 +00:00
2011-02-25 13:43:14 +00:00
if context is None :
context = { }
2011-05-09 08:50:02 +00:00
#delete old worked days lines
old_worked_days_ids = ids and worked_days_obj . search ( cr , uid , [ ( ' payslip_id ' , ' = ' , ids [ 0 ] ) ] , context = context ) or False
if old_worked_days_ids :
worked_days_obj . unlink ( cr , uid , old_worked_days_ids , context = context )
2011-04-06 12:28:45 +00:00
#delete old input lines
old_input_ids = ids and input_obj . search ( cr , uid , [ ( ' payslip_id ' , ' = ' , ids [ 0 ] ) ] , context = context ) or False
if old_input_ids :
input_obj . unlink ( cr , uid , old_input_ids , context = context )
2011-04-04 21:28:11 +00:00
2011-05-09 08:50:02 +00:00
2011-04-04 21:28:11 +00:00
#defaults
res = { ' value ' : {
2011-04-06 05:55:19 +00:00
' line_ids ' : [ ] ,
2011-05-02 12:18:22 +00:00
' input_line_ids ' : [ ] ,
2011-05-12 05:07:03 +00:00
' worked_days_line_ids ' : [ ] ,
2011-04-06 12:28:45 +00:00
#'details_by_salary_head':[], TODO put me back
2011-04-04 21:28:11 +00:00
' name ' : ' ' ,
' contract_id ' : False ,
2011-04-06 05:55:19 +00:00
' struct_id ' : False ,
2011-04-04 21:28:11 +00:00
}
2011-05-02 12:18:22 +00:00
}
2011-11-14 22:42:13 +00:00
if ( not employee_id ) or ( not date_from ) or ( not date_to ) :
2011-04-04 21:28:11 +00:00
return res
2011-04-05 16:46:17 +00:00
ttyme = datetime . fromtimestamp ( time . mktime ( time . strptime ( date_from , " % Y- % m- %d " ) ) )
2011-04-04 21:28:11 +00:00
employee_id = empolyee_obj . browse ( cr , uid , employee_id , context = context )
res [ ' value ' ] . update ( {
2011-05-02 12:18:22 +00:00
' name ' : _ ( ' Salary Slip of %s for %s ' ) % ( employee_id . name , tools . ustr ( ttyme . strftime ( ' % B- % Y ' ) ) ) ,
' company_id ' : employee_id . company_id . id
} )
2011-04-04 21:28:11 +00:00
if not context . get ( ' contract ' , False ) :
#fill with the first contract of the employee
2011-04-05 16:46:17 +00:00
contract_ids = self . get_contract ( cr , uid , employee_id , date_from , date_to , context = context )
2011-04-04 21:28:11 +00:00
else :
2011-03-30 13:21:14 +00:00
if contract_id :
2011-04-05 16:46:17 +00:00
#set the list of contract for which the input have to be filled
2011-04-04 21:28:11 +00:00
contract_ids = [ contract_id ]
else :
2011-04-05 16:46:17 +00:00
#if we don't give the contract, then the input to fill should be for all current contracts of the employee
contract_ids = self . get_contract ( cr , uid , employee_id , date_from , date_to , context = context )
2011-04-04 21:28:11 +00:00
2012-09-14 11:18:47 +00:00
if not contract_ids :
return res
contract_record = contract_obj . browse ( cr , uid , contract_ids [ 0 ] , context = context )
res [ ' value ' ] . update ( {
' contract_id ' : contract_record and contract_record . id or False
} )
struct_record = contract_record and contract_record . struct_id or False
if not struct_record :
return res
res [ ' value ' ] . update ( {
' struct_id ' : struct_record . id ,
} )
2011-04-05 16:46:17 +00:00
#computation of the salary input
2011-05-09 08:50:02 +00:00
worked_days_line_ids = self . get_worked_day_lines ( cr , uid , contract_ids , date_from , date_to , context = context )
2011-05-10 11:19:31 +00:00
input_line_ids = self . get_inputs ( cr , uid , contract_ids , date_from , date_to , context = context )
2011-04-04 21:28:11 +00:00
res [ ' value ' ] . update ( {
2011-05-09 08:50:02 +00:00
' worked_days_line_ids ' : worked_days_line_ids ,
2011-04-05 16:46:17 +00:00
' input_line_ids ' : input_line_ids ,
2011-05-02 12:18:22 +00:00
} )
2011-04-04 21:28:11 +00:00
return res
2011-02-25 13:43:14 +00:00
2011-04-05 16:46:17 +00:00
def onchange_contract_id ( self , cr , uid , ids , date_from , date_to , employee_id = False , contract_id = False , context = None ) :
2011-05-12 11:54:58 +00:00
#TODO it seems to be the mess in the onchanges, we should have onchange_employee => onchange_contract => doing all the things
2011-03-30 13:21:14 +00:00
if context is None :
context = { }
2011-04-05 16:46:17 +00:00
res = { ' value ' : {
2011-04-06 05:55:19 +00:00
' line_ids ' : [ ] ,
' name ' : ' ' ,
2011-04-05 16:46:17 +00:00
}
}
2011-03-30 13:21:14 +00:00
context . update ( { ' contract ' : True } )
if not contract_id :
res [ ' value ' ] . update ( { ' struct_id ' : False } )
2011-04-05 16:46:17 +00:00
return self . onchange_employee_id ( cr , uid , ids , date_from = date_from , date_to = date_to , employee_id = employee_id , contract_id = contract_id , context = context )
2011-03-24 09:11:03 +00:00
2010-06-06 16:29:11 +00:00
2011-05-09 08:50:02 +00:00
class hr_payslip_worked_days ( osv . osv ) :
'''
Payslip Worked Days
'''
_name = ' hr.payslip.worked_days '
_description = ' Payslip Worked Days '
_columns = {
' name ' : fields . char ( ' Description ' , size = 256 , required = True ) ,
2011-12-09 06:03:08 +00:00
' payslip_id ' : fields . many2one ( ' hr.payslip ' , ' Pay Slip ' , required = True , ondelete = ' cascade ' , select = True ) ,
' sequence ' : fields . integer ( ' Sequence ' , required = True , select = True ) ,
2011-05-09 08:50:02 +00:00
' code ' : fields . char ( ' Code ' , size = 52 , required = True , help = " The code that can be used in the salary rules " ) ,
' number_of_days ' : fields . float ( ' Number of Days ' ) ,
' number_of_hours ' : fields . float ( ' Number of Hours ' ) ,
' contract_id ' : fields . many2one ( ' hr.contract ' , ' Contract ' , required = True , help = " The contract for which applied this input " ) ,
}
_order = ' payslip_id, sequence '
_defaults = {
' sequence ' : 10 ,
}
2011-04-04 11:32:19 +00:00
class hr_payslip_input ( osv . osv ) :
'''
Payslip Input
'''
_name = ' hr.payslip.input '
_description = ' Payslip Input '
_columns = {
2011-04-04 13:00:33 +00:00
' name ' : fields . char ( ' Description ' , size = 256 , required = True ) ,
2011-12-09 06:03:08 +00:00
' payslip_id ' : fields . many2one ( ' hr.payslip ' , ' Pay Slip ' , required = True , ondelete = ' cascade ' , select = True ) ,
' sequence ' : fields . integer ( ' Sequence ' , required = True , select = True ) ,
2011-04-04 11:32:19 +00:00
' code ' : fields . char ( ' Code ' , size = 52 , required = True , help = " The code that can be used in the salary rules " ) ,
2011-05-24 12:49:08 +00:00
' amount ' : fields . float ( ' Amount ' , help = " It is used in computation. For e.g. A rule for sales having 1 % c ommission of basic salary for per product can defined in expression like result = inputs.SALEURO.amount * contract.wage*0.01. " ) ,
2011-04-04 21:28:11 +00:00
' contract_id ' : fields . many2one ( ' hr.contract ' , ' Contract ' , required = True , help = " The contract for which applied this input " ) ,
2011-04-04 11:32:19 +00:00
}
2011-05-09 08:50:02 +00:00
_order = ' payslip_id, sequence '
2011-04-04 11:32:19 +00:00
_defaults = {
' sequence ' : 10 ,
2011-05-24 06:02:02 +00:00
' amount ' : 0.0 ,
2011-04-04 11:32:19 +00:00
}
2011-05-13 06:00:12 +00:00
2011-04-04 11:32:19 +00:00
2011-02-24 13:12:20 +00:00
class hr_salary_rule ( osv . osv ) :
2010-07-01 07:31:46 +00:00
2011-02-24 13:12:20 +00:00
_name = ' hr.salary.rule '
_columns = {
2011-04-04 21:28:11 +00:00
' name ' : fields . char ( ' Name ' , size = 256 , required = True , readonly = False ) ,
2011-04-28 13:30:56 +00:00
' code ' : fields . char ( ' Code ' , size = 64 , required = True , help = " The code of salary rules can be used as reference in computation of other rules. In that case, it is case sensitive. " ) ,
2011-12-09 06:03:08 +00:00
' sequence ' : fields . integer ( ' Sequence ' , required = True , help = ' Use to arrange calculation sequence ' , select = True ) ,
2011-05-13 06:24:05 +00:00
' quantity ' : fields . char ( ' Quantity ' , size = 256 , help = " It is used in computation for percentage and fixed amount.For e.g. A rule for Meal Voucher having fixed amount of 1€ per worked day can have its quantity defined in expression like worked_days.WORK100.number_of_days. " ) ,
2011-05-11 07:18:54 +00:00
' category_id ' : fields . many2one ( ' hr.salary.rule.category ' , ' Category ' , required = True ) ,
2011-04-04 21:28:11 +00:00
' active ' : fields . boolean ( ' Active ' , help = " If the active field is set to false, it will allow you to hide the salary rule without removing it. " ) ,
2012-07-13 10:17:51 +00:00
' appears_on_payslip ' : fields . boolean ( ' Appears on Payslip ' , help = " Used to display the salary rule on payslip. " ) ,
2011-04-04 21:28:11 +00:00
' parent_rule_id ' : fields . many2one ( ' hr.salary.rule ' , ' Parent Salary Rule ' , select = True ) ,
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = False ) ,
2011-04-06 12:28:45 +00:00
' condition_select ' : fields . selection ( [ ( ' none ' , ' Always True ' ) , ( ' range ' , ' Range ' ) , ( ' python ' , ' Python Expression ' ) ] , " Condition Based on " , required = True ) ,
2012-01-27 05:37:24 +00:00
' condition_range ' : fields . char ( ' Range Based on ' , size = 1024 , readonly = False , help = ' This will be used to compute the % f ields values; in general it is on basic, but you can also use categories code fields in lowercase as a variable names (hra, ma, lta, etc.) and the variable basic. ' ) ,
2012-06-20 13:04:49 +00:00
' condition_python ' : fields . text ( ' Python Condition ' , required = True , readonly = False , help = ' Applied this rule for calculation if condition is true. You can specify condition like basic > 1000. ' ) ,
2011-03-03 09:52:26 +00:00
' condition_range_min ' : fields . float ( ' Minimum Range ' , required = False , help = " The minimum amount, applied for this rule. " ) ,
' condition_range_max ' : fields . float ( ' Maximum Range ' , required = False , help = " The maximum amount, applied for this rule. " ) ,
2011-04-04 21:28:11 +00:00
' amount_select ' : fields . selection ( [
( ' percentage ' , ' Percentage ( % ) ' ) ,
( ' fix ' , ' Fixed Amount ' ) ,
( ' code ' , ' Python Code ' ) ,
] , ' Amount Type ' , select = True , required = True , help = " The computation method for the rule amount. " ) ,
2011-06-03 09:25:55 +00:00
' amount_fix ' : fields . float ( ' Fixed Amount ' , digits_compute = dp . get_precision ( ' Payroll ' ) , ) ,
2012-01-05 08:13:25 +00:00
' amount_percentage ' : fields . float ( ' Percentage ( % ) ' , digits_compute = dp . get_precision ( ' Payroll Rate ' ) , help = ' For example, enter 50.0 to apply a percentage of 50 % ' ) ,
2011-04-04 21:28:11 +00:00
' amount_python_compute ' : fields . text ( ' Python Code ' ) ,
2011-05-10 14:27:23 +00:00
' amount_percentage_base ' : fields . char ( ' Percentage based on ' , size = 1024 , required = False , readonly = False , help = ' result will be affected to a variable ' ) ,
2011-03-15 13:33:22 +00:00
' child_ids ' : fields . one2many ( ' hr.salary.rule ' , ' parent_rule_id ' , ' Child Salary Rule ' ) ,
2011-07-11 16:05:49 +00:00
' register_id ' : fields . many2one ( ' hr.contribution.register ' , ' Contribution Register ' , help = " Eventual third party involved in the salary payment of the employees. " ) ,
2011-05-09 08:50:02 +00:00
' input_ids ' : fields . one2many ( ' hr.rule.input ' , ' input_id ' , ' Inputs ' ) ,
2011-04-04 21:28:11 +00:00
' note ' : fields . text ( ' Description ' ) ,
2011-02-24 13:12:20 +00:00
}
_defaults = {
2011-04-06 12:28:45 +00:00
' amount_python_compute ' : '''
# Available variables:
#----------------------
2011-05-13 06:24:05 +00:00
# payslip: object containing the payslips
2011-04-06 12:28:45 +00:00
# employee: hr.employee object
# contract: hr.contract object
2011-05-26 11:08:38 +00:00
# rules: object containing the rules code (previously computed)
2011-06-02 13:00:43 +00:00
# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category).
2011-05-13 06:24:05 +00:00
# worked_days: object containing the computed worked days.
# inputs: object containing the computed inputs.
2011-04-06 12:28:45 +00:00
# Note: returned value have to be set in the variable 'result'
result = contract . wage * 0.10 ''' ,
2011-04-07 11:45:52 +00:00
' condition_python ' :
2011-04-06 12:28:45 +00:00
'''
# Available variables:
#----------------------
2011-05-13 06:24:05 +00:00
# payslip: object containing the payslips
2011-04-06 12:28:45 +00:00
# employee: hr.employee object
# contract: hr.contract object
2011-05-27 10:13:22 +00:00
# rules: object containing the rules code (previously computed)
2011-06-02 13:00:43 +00:00
# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category).
2011-05-13 06:24:05 +00:00
# worked_days: object containing the computed worked days
# inputs: object containing the computed inputs
2011-04-06 12:28:45 +00:00
# Note: returned value have to be set in the variable 'result'
2011-05-27 10:13:22 +00:00
result = rules . NET > categories . NET * 0.10 ''' ,
2011-04-06 12:28:45 +00:00
' condition_range ' : ' contract.wage ' ,
2011-03-03 12:44:06 +00:00
' sequence ' : 5 ,
' appears_on_payslip ' : True ,
' active ' : True ,
2011-03-07 09:47:10 +00:00
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
2011-04-04 11:32:19 +00:00
' condition_select ' : ' none ' ,
2011-04-04 21:28:11 +00:00
' amount_select ' : ' fix ' ,
' amount_fix ' : 0.0 ,
' amount_percentage ' : 0.0 ,
2011-05-26 16:56:10 +00:00
' quantity ' : ' 1.0 ' ,
2011-02-24 13:12:20 +00:00
}
2011-11-09 05:39:06 +00:00
2011-04-08 12:17:59 +00:00
def _recursive_search_of_rules ( self , cr , uid , rule_ids , context = None ) :
"""
@param rule_ids : list of browse record
@return : returns a list of tuple ( id , sequence ) which are all the children of the passed rule_ids
"""
children_rules = [ ]
for rule in rule_ids :
if rule . child_ids :
children_rules + = self . _recursive_search_of_rules ( cr , uid , rule . child_ids , context = context )
return [ ( r . id , r . sequence ) for r in rule_ids ] + children_rules
2011-04-06 12:28:45 +00:00
#TODO should add some checks on the type of result (should be float)
def compute_rule ( self , cr , uid , rule_id , localdict , context = None ) :
"""
2012-01-05 08:13:25 +00:00
: param rule_id : id of rule to compute
: param localdict : dictionary containing the environement in which to compute the rule
2012-09-24 16:26:45 +00:00
: return : returns a tuple build as the base / amount computed , the quantity and the rate
2012-01-05 08:13:25 +00:00
: rtype : ( float , float , float )
2011-04-06 12:28:45 +00:00
"""
2011-04-04 21:28:11 +00:00
rule = self . browse ( cr , uid , rule_id , context = context )
if rule . amount_select == ' fix ' :
2011-04-25 07:25:10 +00:00
try :
2015-02-08 21:30:40 +00:00
return rule . amount_fix , float ( eval ( rule . quantity , localdict ) ) , 100.0
2011-04-25 07:25:10 +00:00
except :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Wrong quantity defined for salary rule %s ( %s ). ' ) % ( rule . name , rule . code ) )
2011-04-04 21:28:11 +00:00
elif rule . amount_select == ' percentage ' :
2011-04-08 12:31:56 +00:00
try :
2015-02-08 21:30:40 +00:00
return ( float ( eval ( rule . amount_percentage_base , localdict ) ) ,
float ( eval ( rule . quantity , localdict ) ) ,
rule . amount_percentage )
2011-04-08 12:31:56 +00:00
except :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Wrong percentage base or quantity defined for salary rule %s ( %s ). ' ) % ( rule . name , rule . code ) )
2011-04-04 21:28:11 +00:00
else :
2011-04-08 12:31:56 +00:00
try :
eval ( rule . amount_python_compute , localdict , mode = ' exec ' , nocopy = True )
2015-02-08 21:30:40 +00:00
return float ( localdict [ ' result ' ] ) , ' result_qty ' in localdict and localdict [ ' result_qty ' ] or 1.0 , ' result_rate ' in localdict and localdict [ ' result_rate ' ] or 100.0
2011-04-08 12:31:56 +00:00
except :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Wrong python code defined for salary rule %s ( %s ). ' ) % ( rule . name , rule . code ) )
2011-04-04 21:28:11 +00:00
2011-04-07 12:40:50 +00:00
def satisfy_condition ( self , cr , uid , rule_id , localdict , context = None ) :
2011-04-04 21:28:11 +00:00
"""
@param rule_id : id of hr . salary . rule to be tested
@param contract_id : id of hr . contract to be tested
@return : returns True if the given rule match the condition for the given contract . Return False otherwise .
"""
rule = self . browse ( cr , uid , rule_id , context = context )
if rule . condition_select == ' none ' :
return True
elif rule . condition_select == ' range ' :
2011-04-08 12:56:16 +00:00
try :
result = eval ( rule . condition_range , localdict )
return rule . condition_range_min < = result and result < = rule . condition_range_max or False
except :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Wrong range condition defined for salary rule %s ( %s ). ' ) % ( rule . name , rule . code ) )
2011-04-04 21:28:11 +00:00
else : #python code
2011-04-08 12:31:56 +00:00
try :
eval ( rule . condition_python , localdict , mode = ' exec ' , nocopy = True )
return ' result ' in localdict and localdict [ ' result ' ] or False
except :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Wrong python condition defined for salary rule %s ( %s ). ' ) % ( rule . name , rule . code ) )
2011-04-08 12:31:56 +00:00
2010-10-26 12:23:32 +00:00
2011-05-09 08:50:02 +00:00
class hr_rule_input ( osv . osv ) :
'''
Salary Rule Input
'''
_name = ' hr.rule.input '
_description = ' Salary Rule Input '
_columns = {
' name ' : fields . char ( ' Description ' , size = 256 , required = True ) ,
' code ' : fields . char ( ' Code ' , size = 52 , required = True , help = " The code that can be used in the salary rules " ) ,
' input_id ' : fields . many2one ( ' hr.salary.rule ' , ' Salary Rule Input ' , required = True )
}
2011-04-04 21:28:11 +00:00
class hr_payslip_line ( osv . osv ) :
'''
Payslip Line
'''
_name = ' hr.payslip.line '
_inherit = ' hr.salary.rule '
_description = ' Payslip Line '
2011-04-12 17:01:28 +00:00
_order = ' contract_id, sequence '
2011-04-04 21:28:11 +00:00
2011-05-26 16:56:10 +00:00
def _calculate_total ( self , cr , uid , ids , name , args , context ) :
if not ids : return { }
res = { }
for line in self . browse ( cr , uid , ids , context = context ) :
2012-01-05 08:13:25 +00:00
res [ line . id ] = float ( line . quantity ) * line . amount * line . rate / 100
2011-05-26 16:56:10 +00:00
return res
2011-04-04 21:28:11 +00:00
_columns = {
2011-11-09 05:39:06 +00:00
' slip_id ' : fields . many2one ( ' hr.payslip ' , ' Pay Slip ' , required = True , ondelete = ' cascade ' ) ,
2011-04-08 22:38:45 +00:00
' salary_rule_id ' : fields . many2one ( ' hr.salary.rule ' , ' Rule ' , required = True ) ,
2011-04-04 21:28:11 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , ' Employee ' , required = True ) ,
2011-12-09 06:03:08 +00:00
' contract_id ' : fields . many2one ( ' hr.contract ' , ' Contract ' , required = True , select = True ) ,
2012-01-05 08:13:25 +00:00
' rate ' : fields . float ( ' Rate ( % ) ' , digits_compute = dp . get_precision ( ' Payroll Rate ' ) ) ,
2011-06-03 09:25:55 +00:00
' amount ' : fields . float ( ' Amount ' , digits_compute = dp . get_precision ( ' Payroll ' ) ) ,
' quantity ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Payroll ' ) ) ,
' total ' : fields . function ( _calculate_total , method = True , type = ' float ' , string = ' Total ' , digits_compute = dp . get_precision ( ' Payroll ' ) , store = True ) ,
2011-04-04 21:28:11 +00:00
}
2011-11-15 14:07:23 +00:00
_defaults = {
' quantity ' : 1.0 ,
2012-01-05 08:13:25 +00:00
' rate ' : 100.0 ,
2011-11-15 14:07:23 +00:00
}
2011-04-01 10:58:07 +00:00
2010-06-06 16:29:11 +00:00
class hr_employee ( osv . osv ) :
'''
Employee
'''
2010-10-26 12:23:32 +00:00
2011-03-14 08:58:54 +00:00
_inherit = ' hr.employee '
_description = ' Employee '
2011-04-06 12:28:45 +00:00
def _calculate_total_wage ( self , cr , uid , ids , name , args , context ) :
2011-03-09 10:10:41 +00:00
if not ids : return { }
2011-03-14 08:58:54 +00:00
res = { }
2011-03-09 10:10:41 +00:00
current_date = datetime . now ( ) . strftime ( ' % Y- % m- %d ' )
for employee in self . browse ( cr , uid , ids , context = context ) :
2011-03-14 08:58:54 +00:00
if not employee . contract_ids :
res [ employee . id ] = { ' basic ' : 0.0 }
2011-03-09 10:10:41 +00:00
continue
cr . execute ( ' SELECT SUM(wage) ' \
' FROM hr_contract ' \
' WHERE employee_id = %s ' \
2011-03-22 11:03:53 +00:00
' AND date_start <= %s ' \
2011-03-09 10:10:41 +00:00
' AND (date_end > %s OR date_end is NULL) ' ,
( employee . id , current_date , current_date ) )
result = dict ( cr . dictfetchone ( ) )
2011-03-14 08:58:54 +00:00
res [ employee . id ] = { ' basic ' : result [ ' sum ' ] }
return res
2010-10-26 12:23:32 +00:00
2010-06-06 16:29:11 +00:00
_columns = {
' slip_ids ' : fields . one2many ( ' hr.payslip ' , ' employee_id ' , ' Payslips ' , required = False , readonly = True ) ,
2011-06-03 09:25:55 +00:00
' total_wage ' : fields . function ( _calculate_total_wage , method = True , type = ' float ' , string = ' Total Basic Salary ' , digits_compute = dp . get_precision ( ' Payroll ' ) , help = " Sum of all current contract ' s wage of employee. " ) ,
2010-06-06 16:29:11 +00:00
}
2011-03-14 08:58:54 +00:00
2010-06-06 16:29:11 +00:00
2011-03-24 09:11:03 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: