2012-05-15 08:41:29 +00:00
#-*- coding:utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# 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
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
2012-05-21 07:06:43 +00:00
import time
2012-05-24 11:24:12 +00:00
from datetime import datetime
2012-05-21 07:06:43 +00:00
from dateutil . relativedelta import relativedelta
2012-06-04 06:18:28 +00:00
from calendar import isleap
2012-05-23 12:12:10 +00:00
2012-12-06 14:56:32 +00:00
from openerp . tools . translate import _
from openerp . osv import fields , osv
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2012-05-21 07:06:43 +00:00
2012-06-12 05:57:10 +00:00
DATETIME_FORMAT = " % Y- % m- %d "
2012-06-15 12:29:01 +00:00
class hr_contract ( osv . osv ) :
2012-06-18 11:10:22 +00:00
"""
Employee contract allows to add different values in fields .
Fields are used in salary rule computation .
"""
2012-05-15 08:41:29 +00:00
_inherit = ' hr.contract '
2012-06-26 06:03:54 +00:00
_description = ' HR Contract '
2012-07-13 13:23:04 +00:00
2012-05-23 07:29:45 +00:00
_columns = {
2012-06-26 06:03:54 +00:00
' tds ' : fields . float ( ' TDS ' , digits_compute = dp . get_precision ( ' Payroll ' ) , help = " Amount for Tax Deduction at Source " ) ,
2012-07-26 06:29:17 +00:00
' driver_salay ' : fields . boolean ( ' Driver Salary ' , help = " Check this box if you provide allowance for driver " ) ,
2012-06-26 10:48:58 +00:00
' medical_insurance ' : fields . float ( ' Medical Insurance ' , digits_compute = dp . get_precision ( ' Payroll ' ) , help = " Deduction towards company provided medical insurance " ) ,
2012-08-29 12:19:12 +00:00
' voluntary_provident_fund ' : fields . float ( ' Voluntary Provident Fund ( % ) ' , digits_compute = dp . get_precision ( ' Payroll ' ) , help = " VPF is a safe option wherein you can contribute more than the PF ceiling of 12 % that has been mandated by the government and VPF computed as percentage( % ) " ) ,
2013-06-26 12:16:17 +00:00
' house_rent_allowance_metro_nonmetro ' : fields . float ( ' House Rent Allowance ( % ) ' , digits_compute = dp . get_precision ( ' Payroll ' ) , help = " HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses for metro city it is 50 % a nd for non metro 40 % . \n HRA computed as percentage( % ) " ) ,
2012-08-22 11:21:53 +00:00
' supplementary_allowance ' : fields . float ( ' Supplementary Allowance ' , digits_compute = dp . get_precision ( ' Payroll ' ) ) ,
2012-05-23 07:29:45 +00:00
}
2012-06-04 06:18:28 +00:00
class payroll_advice ( osv . osv ) :
'''
2012-06-15 12:29:01 +00:00
Bank Advice
2012-07-17 07:21:28 +00:00
'''
2012-06-04 06:18:28 +00:00
_name = ' hr.payroll.advice '
2012-06-15 12:29:01 +00:00
_description = ' Bank Advice '
2012-06-04 06:18:28 +00:00
_columns = {
2012-06-12 05:57:10 +00:00
' name ' : fields . char ( ' Name ' , size = 32 , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
2012-06-04 06:18:28 +00:00
' note ' : fields . text ( ' Description ' ) ,
2012-06-28 05:37:59 +00:00
' date ' : fields . date ( ' Date ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Advice Date is used to search Payslips " ) ,
2012-06-04 06:18:28 +00:00
' state ' : fields . selection ( [
2012-06-26 07:14:59 +00:00
( ' draft ' , ' Draft ' ) ,
2012-06-27 05:44:41 +00:00
( ' confirm ' , ' Confirmed ' ) ,
2012-06-26 07:14:59 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2012-10-12 11:42:58 +00:00
] , ' Status ' , select = True , readonly = True ) ,
2012-07-20 11:09:53 +00:00
' number ' : fields . char ( ' Reference ' , size = 16 , readonly = True ) ,
2012-06-13 06:58:07 +00:00
' line_ids ' : fields . one2many ( ' hr.payroll.advice.line ' , ' advice_id ' , ' Employee Salary ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
2012-06-27 09:11:01 +00:00
' chaque_nos ' : fields . char ( ' Cheque Numbers ' , size = 256 ) ,
2012-07-16 13:04:32 +00:00
' neft ' : fields . boolean ( ' NEFT Transaction ' , help = " Check this box if your company use online transfer for salary " ) ,
2012-06-14 12:45:33 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2012-06-27 09:00:04 +00:00
' bank_id ' : fields . many2one ( ' res.bank ' , ' Bank ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Select the Bank from which the salary is going to be paid " ) ,
2012-08-07 06:13:29 +00:00
' batch_id ' : fields . many2one ( ' hr.payslip.run ' , ' Batch ' , readonly = True )
2012-06-04 06:18:28 +00:00
}
2012-07-13 13:23:04 +00:00
2012-06-04 06:18:28 +00:00
_defaults = {
2012-06-26 07:14:59 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
' state ' : lambda * a : ' draft ' ,
2012-06-04 06:18:28 +00:00
' company_id ' : lambda self , cr , uid , context : \
self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ,
context = context ) . company_id . id ,
2012-06-13 12:26:05 +00:00
' note ' : " Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries: "
2012-06-04 06:18:28 +00:00
}
def compute_advice ( self , cr , uid , ids , context = None ) :
2012-06-18 11:10:22 +00:00
"""
2012-07-13 13:23:04 +00:00
Advice - Create Advice lines in Payment Advice and
2012-06-18 11:10:22 +00:00
compute Advice lines .
@param cr : the current row , from the database cursor ,
@param uid : the current user ’ s ID for security checks ,
@param ids : List of Advice ’ s IDs
@return : Advice lines
@param context : A standard dictionary for contextual values
"""
2012-06-04 06:18:28 +00:00
payslip_pool = self . pool . get ( ' hr.payslip ' )
advice_line_pool = self . pool . get ( ' hr.payroll.advice.line ' )
payslip_line_pool = self . pool . get ( ' hr.payslip.line ' )
for advice in self . browse ( cr , uid , ids , context = context ) :
2012-06-26 07:14:59 +00:00
old_line_ids = advice_line_pool . search ( cr , uid , [ ( ' advice_id ' , ' = ' , advice . id ) ] , context = context )
2012-06-06 09:55:08 +00:00
if old_line_ids :
advice_line_pool . unlink ( cr , uid , old_line_ids , context = context )
2012-06-26 07:14:59 +00:00
slip_ids = payslip_pool . search ( cr , uid , [ ( ' date_from ' , ' <= ' , advice . date ) , ( ' date_to ' , ' >= ' , advice . date ) , ( ' state ' , ' = ' , ' done ' ) ] , context = context )
2012-06-06 09:55:08 +00:00
for slip in payslip_pool . browse ( cr , uid , slip_ids , context = context ) :
2012-07-04 04:23:27 +00:00
if not slip . employee_id . bank_account_id and not slip . employee_id . bank_account_id . acc_number :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define bank account for the %s employee ' ) % ( slip . employee_id . name ) )
2012-06-26 07:14:59 +00:00
line_ids = payslip_line_pool . search ( cr , uid , [ ( ' slip_id ' , ' = ' , slip . id ) , ( ' code ' , ' = ' , ' NET ' ) ] , context = context )
2012-06-12 05:57:10 +00:00
if line_ids :
line = payslip_line_pool . browse ( cr , uid , line_ids , context = context ) [ 0 ]
2012-06-26 07:14:59 +00:00
advice_line = {
2012-06-12 05:57:10 +00:00
' advice_id ' : advice . id ,
' name ' : slip . employee_id . bank_account_id . acc_number ,
' employee_id ' : slip . employee_id . id ,
' bysal ' : line . total
2012-06-06 09:55:08 +00:00
}
2012-06-12 05:57:10 +00:00
advice_line_pool . create ( cr , uid , advice_line , context = context )
2012-06-26 10:48:58 +00:00
payslip_pool . write ( cr , uid , slip_ids , { ' advice_id ' : advice . id } , context = context )
2012-06-12 05:57:10 +00:00
return True
2012-06-04 06:18:28 +00:00
def confirm_sheet ( self , cr , uid , ids , context = None ) :
2012-06-18 11:10:22 +00:00
"""
confirm Advice - confirmed Advice after computing Advice Lines . .
@param cr : the current row , from the database cursor ,
@param uid : the current user ’ s ID for security checks ,
@param ids : List of confirm Advice ’ s IDs
@return : confirmed Advice lines and set sequence of Advice .
@param context : A standard dictionary for contextual values
"""
2012-06-15 12:29:01 +00:00
seq_obj = self . pool . get ( ' ir.sequence ' )
2012-06-13 06:59:30 +00:00
for advice in self . browse ( cr , uid , ids , context = context ) :
if not advice . line_ids :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You can not confirm Payment advice without advice lines. ' ) )
2012-06-26 07:14:59 +00:00
advice_date = datetime . strptime ( advice . date , DATETIME_FORMAT )
2012-06-15 12:29:01 +00:00
advice_year = advice_date . strftime ( ' % m ' ) + ' - ' + advice_date . strftime ( ' % Y ' )
number = seq_obj . get ( cr , uid , ' payment.advice ' )
sequence_num = ' PAY ' + ' / ' + advice_year + ' / ' + number
self . write ( cr , uid , [ advice . id ] , { ' number ' : sequence_num , ' state ' : ' confirm ' } , context = context )
2012-06-15 11:06:56 +00:00
return True
2012-06-04 06:18:28 +00:00
def set_to_draft ( self , cr , uid , ids , context = None ) :
2012-06-18 11:10:22 +00:00
""" Resets Advice as draft.
"""
2012-06-11 11:10:55 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' draft ' } , context = context )
2012-06-04 06:18:28 +00:00
def cancel_sheet ( self , cr , uid , ids , context = None ) :
2012-06-18 11:10:22 +00:00
""" Marks Advice as cancelled.
"""
2012-06-11 11:10:55 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
2012-06-04 06:18:28 +00:00
def onchange_company_id ( self , cr , uid , ids , company_id = False , context = None ) :
res = { }
if company_id :
2012-06-12 05:57:10 +00:00
company = self . pool . get ( ' res.company ' ) . browse ( cr , uid , [ company_id ] , context = context ) [ 0 ]
2012-06-04 06:18:28 +00:00
if company . partner_id . bank_ids :
2012-08-16 10:15:47 +00:00
res . update ( { ' bank_id ' : company . partner_id . bank_ids [ 0 ] . bank . id } )
2012-06-04 06:18:28 +00:00
return {
' value ' : res
2012-07-17 07:21:28 +00:00
}
2012-06-04 06:18:28 +00:00
2012-07-13 12:09:27 +00:00
class hr_payslip_run ( osv . osv ) :
_inherit = ' hr.payslip.run '
_description = ' Payslip Batches '
2012-07-17 07:21:28 +00:00
_columns = {
2012-07-26 06:33:14 +00:00
' available_advice ' : fields . boolean ( ' Made Payment Advice? ' , help = " If this box is checked which means that Payment Advice exists for current batch " , readonly = False ) ,
2012-07-17 07:21:28 +00:00
}
2012-07-27 08:16:59 +00:00
def copy ( self , cr , uid , id , default = { } , context = None ) :
2012-07-27 07:26:27 +00:00
if not default :
default = { }
2012-07-27 08:16:59 +00:00
default . update ( { ' available_advice ' : False } )
2012-07-27 07:26:27 +00:00
return super ( hr_payslip_run , self ) . copy ( cr , uid , id , default , context = context )
2012-07-17 07:21:28 +00:00
def draft_payslip_run ( self , cr , uid , ids , context = None ) :
2012-07-26 06:29:17 +00:00
res = super ( hr_payslip_run , self ) . draft_payslip_run ( cr , uid , ids , context = context )
2012-07-17 10:48:56 +00:00
self . write ( cr , uid , ids , { ' available_advice ' : False } , context = context )
2012-07-26 06:29:17 +00:00
return res
2012-07-16 09:05:15 +00:00
def create_advice ( self , cr , uid , ids , context = None ) :
2012-07-13 12:09:27 +00:00
payslip_pool = self . pool . get ( ' hr.payslip ' )
payslip_line_pool = self . pool . get ( ' hr.payslip.line ' )
advice_pool = self . pool . get ( ' hr.payroll.advice ' )
advice_line_pool = self . pool . get ( ' hr.payroll.advice.line ' )
users = self . pool . get ( ' res.users ' ) . browse ( cr , uid , [ uid ] , context = context )
for run in self . browse ( cr , uid , ids , context = context ) :
2012-07-17 10:48:56 +00:00
if run . available_advice :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " Payment advice already exists for %s , ' Set to Draft ' to create a new advice. " ) % ( run . name ) )
2012-07-13 12:09:27 +00:00
advice_data = {
2012-08-07 06:13:29 +00:00
' batch_id ' : run . id ,
2012-07-13 12:09:27 +00:00
' company_id ' : users [ 0 ] . company_id . id ,
' name ' : run . name ,
' date ' : run . date_end ,
' bank_id ' : users [ 0 ] . company_id . bank_ids and users [ 0 ] . company_id . bank_ids [ 0 ] . id or False
}
advice_id = advice_pool . create ( cr , uid , advice_data , context = context )
2012-07-16 11:12:44 +00:00
slip_ids = [ ]
for slip_id in run . slip_ids :
2013-01-28 15:29:16 +00:00
# TODO is it necessary to interleave the calls ?
payslip_pool . signal_hr_verify_sheet ( cr , uid , [ slip_id . id ] )
payslip_pool . signal_process_sheet ( cr , uid , [ slip_id . id ] )
2012-07-16 11:12:44 +00:00
slip_ids . append ( slip_id . id )
2012-07-20 09:57:49 +00:00
2012-07-13 12:09:27 +00:00
for slip in payslip_pool . browse ( cr , uid , slip_ids , context = context ) :
2012-08-16 08:48:21 +00:00
if not slip . employee_id . bank_account_id or not slip . employee_id . bank_account_id . acc_number :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define bank account for the %s employee ' ) % ( slip . employee_id . name ) )
2012-07-13 12:09:27 +00:00
line_ids = payslip_line_pool . search ( cr , uid , [ ( ' slip_id ' , ' = ' , slip . id ) , ( ' code ' , ' = ' , ' NET ' ) ] , context = context )
if line_ids :
line = payslip_line_pool . browse ( cr , uid , line_ids , context = context ) [ 0 ]
advice_line = {
' advice_id ' : advice_id ,
' name ' : slip . employee_id . bank_account_id . acc_number ,
' employee_id ' : slip . employee_id . id ,
' bysal ' : line . total
}
advice_line_pool . create ( cr , uid , advice_line , context = context )
2012-07-18 05:16:17 +00:00
return self . write ( cr , uid , ids , { ' available_advice ' : True } )
2012-07-13 12:09:27 +00:00
2012-06-04 06:18:28 +00:00
class payroll_advice_line ( osv . osv ) :
'''
Bank Advice Lines
2012-07-17 07:21:28 +00:00
'''
2012-07-13 06:38:31 +00:00
def onchange_employee_id ( self , cr , uid , ids , employee_id = False , context = None ) :
2012-07-10 05:48:11 +00:00
res = { }
2012-07-16 13:04:32 +00:00
hr_obj = self . pool . get ( ' hr.employee ' )
2012-07-13 10:52:18 +00:00
if not employee_id :
2012-07-16 13:04:32 +00:00
return { ' value ' : res }
employee = hr_obj . browse ( cr , uid , [ employee_id ] , context = context ) [ 0 ]
2012-08-13 09:35:35 +00:00
res . update ( { ' name ' : employee . bank_account_id . acc_number , ' ifsc_code ' : employee . bank_account_id . bank_bic or ' ' } )
2012-07-17 07:21:28 +00:00
return { ' value ' : res }
2012-06-04 06:18:28 +00:00
_name = ' hr.payroll.advice.line '
_description = ' Bank Advice Lines '
_columns = {
2012-06-26 10:48:58 +00:00
' advice_id ' : fields . many2one ( ' hr.payroll.advice ' , ' Bank Advice ' ) ,
2012-07-18 10:26:38 +00:00
' name ' : fields . char ( ' Bank Account No. ' , size = 25 , required = True ) ,
' ifsc_code ' : fields . char ( ' IFSC Code ' , size = 16 ) ,
2012-06-26 07:13:46 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , ' Employee ' , required = True ) ,
2012-06-04 06:18:28 +00:00
' bysal ' : fields . float ( ' By Salary ' , digits_compute = dp . get_precision ( ' Payroll ' ) ) ,
2012-07-18 10:26:38 +00:00
' debit_credit ' : fields . char ( ' C/D ' , size = 3 , required = False ) ,
2012-06-28 06:27:41 +00:00
' company_id ' : fields . related ( ' advice_id ' , ' company_id ' , type = ' many2one ' , required = False , relation = ' res.company ' , string = ' Company ' , store = True ) ,
2012-07-20 11:09:53 +00:00
' ifsc ' : fields . related ( ' advice_id ' , ' neft ' , type = ' boolean ' , string = ' IFSC ' ) ,
2012-06-04 06:18:28 +00:00
}
2012-07-09 06:29:05 +00:00
_defaults = {
2012-07-10 05:48:11 +00:00
' debit_credit ' : ' C ' ,
2012-07-17 07:21:28 +00:00
}
2012-06-04 06:18:28 +00:00
2012-06-26 10:48:58 +00:00
class hr_payslip ( osv . osv ) :
'''
Employee Pay Slip
'''
_inherit = ' hr.payslip '
2012-07-18 05:16:17 +00:00
_description = ' Pay Slips '
2012-06-26 10:48:58 +00:00
_columns = {
' advice_id ' : fields . many2one ( ' hr.payroll.advice ' , ' Bank Advice ' )
}
2012-06-26 10:51:40 +00:00
2012-07-27 08:16:59 +00:00
def copy ( self , cr , uid , id , default = { } , context = None ) :
2012-07-27 07:26:27 +00:00
if not default :
default = { }
2012-07-27 08:16:59 +00:00
default . update ( { ' advice_id ' : False } )
2012-07-27 07:26:27 +00:00
return super ( hr_payslip , self ) . copy ( cr , uid , id , default , context = context )
2012-06-26 10:51:40 +00:00
2012-06-29 05:41:18 +00:00
class res_company ( osv . osv ) :
_inherit = ' res.company '
_columns = {
2012-06-29 09:07:31 +00:00
' dearness_allowance ' : fields . boolean ( ' Dearness Allowance ' , help = " Check this box if your company provide Dearness Allowance to employee " )
2012-06-29 05:41:18 +00:00
}
2012-07-02 09:38:15 +00:00
_defaults = {
' dearness_allowance ' : True ,
2012-07-13 13:23:04 +00:00
}
2012-06-29 05:41:18 +00:00
2012-07-27 07:26:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: