2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
26 from osv import fields
27 from tools import config
28 from tools.translate import _
30 from datetime import date
31 from datetime import datetime
32 from datetime import timedelta
34 def prev_bounds(cdate=False):
35 when = date.fromtimestamp(time.mktime(time.strptime(cdate,"%Y-%m-%d")))
36 this_first = date(when.year, when.month, 1)
37 month = when.month + 1
42 next_month = date(year, month, 1)
43 prev_end = next_month - timedelta(days=1)
44 return this_first, prev_end
46 class hr_contract_wage_type(osv.osv):
50 Grows = Basic + Allowances
51 New = Grows - Deductions
54 _inherit = 'hr.contract.wage.type'
56 'type' : fields.selection([('basic','Basic'), ('gross','Gross'), ('net','Net')], 'Type', required=True),
58 hr_contract_wage_type()
60 class hr_passport(osv.osv):
63 Passport based Contratacts for Employees
67 _description = 'Passport Detail'
70 'employee_id':fields.many2one('hr.employee', 'Employee', required=True),
71 'name':fields.char('Passport No', size=64, required=True, readonly=False),
72 'country_id':fields.many2one('res.country', 'Country of Issue', required=True),
73 'address_id':fields.many2one('res.partner.address', 'Address', required=False),
74 'date_issue': fields.date('Passport Issue Date', required=True),
75 'date_expire': fields.date('Passport Expire Date', required=True),
76 'contracts_ids':fields.one2many('hr.contract', 'passport_id', 'Contracts', required=False, readonly=True),
77 'note': fields.text('Description'),
81 class hr_payroll_structure(osv.osv):
83 Salary structure used to defined
89 _name = 'hr.payroll.structure'
90 _description = 'Salary Structure'
93 'name':fields.char('Name', size=256, required=True, readonly=False),
94 'code':fields.char('Code', size=64, required=True, readonly=False),
95 'line_ids':fields.one2many('hr.payslip.line', 'function_id', 'Salary Structure', required=False),
96 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', required=False),
97 'company_id':fields.many2one('res.company', 'Company', required=False),
98 'note': fields.text('Description'),
101 'company_id': lambda self, cr, uid, context: \
102 self.pool.get('res.users').browse(cr, uid, uid,
103 context=context).company_id.id,
106 def copy(self, cr, uid, id, default=None, context=None):
108 Create a new record in hr_payroll_structure model from existing one
109 @param cr: cursor to database
110 @param user: id of current user
111 @param id: list of record ids on which copy method executes
112 @param default: dict type contains the values to be override during copy of object
113 @param context: context arguments, like lang, time zone
115 @return: returns a id of newly created record
117 code = self.browse(cr, uid, id).code
119 'code':code+"(copy)",
120 'company_id':self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
122 res_id = super(hr_payroll_structure, self).copy(cr, uid, id, default, context)
125 hr_payroll_structure()
127 class hr_contract(osv.osv):
129 Employee contract based on the visa, work permits
130 allowas to configure different Salary structure
133 _inherit = 'hr.contract'
134 _description = 'Employee Contract'
137 'permit_no':fields.char('Work Permit No', size=256, required=False, readonly=False),
138 'passport_id':fields.many2one('hr.passport', 'Passport', required=False),
139 'visa_no':fields.char('Visa No', size=64, required=False, readonly=False),
140 'visa_expire': fields.date('Visa Expire Date'),
141 'struct_id' : fields.many2one('hr.payroll.structure', 'Salary Structure'),
142 'working_days_per_week': fields.integer('Working Days', help="No of Working days / week for an employee")
145 'working_days_per_week': lambda *a: 5,
149 class payroll_register(osv.osv):
153 _name = 'hr.payroll.register'
154 _description = 'Payroll Register'
156 def _calculate(self, cr, uid, ids, field_names, arg, context):
162 for register in self.browse(cr, uid, ids, context):
163 for slip in register.line_ids:
164 allounce += slip.allounce
165 deduction += slip.deduction
171 'deduction':deduction,
178 'name':fields.char('Name', size=64, required=True, readonly=False),
179 'date': fields.date('Date', required=True),
180 'number':fields.char('Number', size=64, required=False, readonly=True),
181 'line_ids':fields.one2many('hr.payslip', 'register_id', 'Payslips', required=False),
182 'state':fields.selection([
184 ('draft','Wating for Verification'),
185 ('hr_check','Wating for HR Verification'),
186 ('accont_check','Wating for Account Verification'),
187 ('confirm','Confirm Sheet'),
188 ('done','Paid Salary'),
190 ],'State', select=True, readonly=True),
191 'journal_id': fields.many2one('account.journal', 'Expanse Journal', required=True),
192 'bank_journal_id': fields.many2one('account.journal', 'Bank Journal', required=True),
193 'active':fields.boolean('Active', required=False),
194 # 'advice_ids':fields.one2many('hr.payroll.advice', 'register_id', 'Bank Advice'),
195 'company_id':fields.many2one('res.company', 'Company', required=False),
196 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(Payslip) date."),
197 'grows': fields.function(_calculate, method=True, store=True, multi='dc', string='Gross Salary', type='float', digits=(16, int(config['price_accuracy']))),
198 'net': fields.function(_calculate, method=True, store=True, multi='dc', string='Net Salary', digits=(16, int(config['price_accuracy']))),
199 'allounce': fields.function(_calculate, method=True, store=True, multi='dc', string='Allowance', digits=(16, int(config['price_accuracy']))),
200 'deduction': fields.function(_calculate, method=True, store=True, multi='dc', string='Deduction', digits=(16, int(config['price_accuracy']))),
201 'note': fields.text('Description'),
205 'date': lambda *a: time.strftime('%Y-%m-%d'),
206 'state': lambda *a: 'new',
207 'active': lambda *a: True,
208 'company_id': lambda self, cr, uid, context: \
209 self.pool.get('res.users').browse(cr, uid, uid,
210 context=context).company_id.id,
213 def compute_sheet(self, cr, uid, ids, context={}):
214 emp_pool = self.pool.get('hr.employee')
215 slip_pool = self.pool.get('hr.payslip')
216 func_pool = self.pool.get('hr.payroll.structure')
217 slip_line_pool = self.pool.get('hr.payslip.line')
218 wf_service = netsvc.LocalService("workflow")
220 vals = self.read(cr, uid, ids)[0]
222 emp_ids = emp_pool.search(cr, uid, [])
224 for emp in emp_pool.browse(cr, uid, emp_ids):
225 old_slips = slip_pool.search(cr, uid, [('employee_id','=',emp.id), ('date','=',vals['date'])])
227 slip_pool.write(cr, uid, old_slips, {'register_id':ids[0]})
228 for sid in old_slips:
229 wf_service.trg_validate(uid, 'hr.payslip', sid, 'compute_sheet', cr)
233 SELECT c.wage as wage, struct_id as function
235 LEFT JOIN hr_employee emp on (c.employee_id=emp.id)
236 LEFT JOIN hr_contract_wage_type cwt on (cwt.id = c.wage_type_id)
237 LEFT JOIN hr_contract_wage_type_period p on (cwt.period_id = p.id)
240 (date_start <= %s) AND
241 (date_end IS NULL OR date_end >= %s)
244 cr.execute(sql_req, (emp.id, vals['date'], vals['date']))
245 contract_info = cr.dictfetchone()
247 if not contract_info:
250 function = contract_info['struct_id']
253 func = func_pool.read(cr, uid, function, ['line_ids'])
254 lines = slip_line_pool.browse(cr, uid, func['line_ids'])
257 'employee_id':emp.id,
258 'basic':contract_info['wage'],
259 'register_id':ids[0],
262 'journal_id':vals['journal_id'][0],
263 'bank_journal_id':vals['bank_journal_id'][0]
265 slip_id = slip_pool.create(cr, uid, res)
267 old_slip_id = slip_line_pool.search(cr, uid, [('slip_id','=',slip_id)])
268 slip_line_pool.unlink(cr, uid, old_slip_id)
271 slip_line_pool.copy(cr, uid, line.id, {'slip_id':slip_id, 'employee_id':False, 'function_id':False}, {})
273 for line in emp.line_ids:
274 slip_line_pool.copy(cr, uid, line.id, {'slip_id':slip_id, 'employee_id':False, 'function_id':False}, {})
276 wf_service.trg_validate(uid, 'hr.payslip', slip_id, 'compute_sheet', cr)
278 number = self.pool.get('ir.sequence').get(cr, uid, 'salary.register')
279 self.write(cr, uid, ids, {'state':'draft', 'number':number})
282 def verify_sheet(self, cr, uid, ids, context={}):
283 slip_pool = self.pool.get('hr.payslip')
286 sids = slip_pool.search(cr, uid, [('register_id','=',id)])
287 wf_service = netsvc.LocalService("workflow")
289 wf_service.trg_validate(uid, 'hr.payslip', sid, 'verify_sheet', cr)
291 self.write(cr, uid, ids, {'state':'hr_check'})
294 def verify_twice_sheet(self, cr, uid, ids, context={}):
295 slip_pool = self.pool.get('hr.payslip')
298 sids = slip_pool.search(cr, uid, [('register_id','=',id), ('state','=','hr_check')])
299 wf_service = netsvc.LocalService("workflow")
301 wf_service.trg_validate(uid, 'hr.payslip', sid, 'verify_twice_sheet', cr)
303 self.write(cr, uid, ids, {'state':'accont_check'})
306 def final_verify_sheet(self, cr, uid, ids, context={}):
307 slip_pool = self.pool.get('hr.payslip')
308 advice_pool = self.pool.get('hr.payroll.advice')
309 advice_line_pool = self.pool.get('hr.payroll.advice.line')
312 sids = slip_pool.search(cr, uid, [('register_id','=',id), ('state','=','accont_check')])
313 wf_service = netsvc.LocalService("workflow")
315 wf_service.trg_validate(uid, 'hr.payslip', sid, 'final_verify_sheet', cr)
318 for reg in self.browse(cr, uid, ids):
320 for slip in reg.line_ids:
322 if accs.get(slip.employee_id.property_bank_account.code, False) == False:
324 'name': 'Payment Advice from %s / Bank Account %s' % (self.pool.get('res.users').browse(cr, uid, uid).company_id.name, slip.employee_id.property_bank_account.name),
325 'number': self.pool.get('ir.sequence').get(cr, uid, 'payment.advice'),
326 'register_id':reg.id,
327 'account_id':slip.employee_id.property_bank_account.id
329 pid = advice_pool.create(cr, uid, advice)
330 accs[slip.employee_id.property_bank_account.code] = pid
332 pid = accs[slip.employee_id.property_bank_account.code]
336 'name':slip.employee_id.otherid,
337 'employee_id':slip.employee_id.id,
338 'amount':slip.other_pay + slip.net,
341 id = advice_line_pool.create(cr, uid, pline)
343 #, 'advice_ids':[(6, 0, [pid])]
344 self.write(cr, uid, ids, {'state':'confirm'})
347 def process_sheet(self, cr, uid, ids, context={}):
348 slip_pool = self.pool.get('hr.payslip')
350 sids = slip_pool.search(cr, uid, [('register_id','=',id), ('state','=','confirm')])
351 wf_service = netsvc.LocalService("workflow")
353 wf_service.trg_validate(uid, 'hr.payslip', sid, 'process_sheet', cr)
355 self.write(cr, uid, ids, {'state':'done'})
360 class payroll_advice(osv.osv):
364 _name = 'hr.payroll.advice'
365 _description = 'Bank Advice Note'
368 'register_id':fields.many2one('hr.payroll.register', 'Payroll Register', required=False),
369 'name':fields.char('Name', size=2048, required=True, readonly=False),
370 'note': fields.text('Description'),
371 'date': fields.date('Date'),
372 'state':fields.selection([
373 ('draft','Draft Sheet'),
374 ('confirm','Confirm Sheet'),
376 ],'State', select=True, readonly=True),
377 'number':fields.char('Number', size=64, required=False, readonly=True),
378 'line_ids':fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', required=False),
379 'chaque_nos':fields.char('Chaque Nos', size=256, required=False, readonly=False),
380 'account_id': fields.many2one('account.account', 'Account', required=True),
381 'company_id':fields.many2one('res.company', 'Company', required=False),
385 'date': lambda *a: time.strftime('%Y-%m-%d'),
386 'state': lambda *a: 'draft',
387 'company_id': lambda self, cr, uid, context: \
388 self.pool.get('res.users').browse(cr, uid, uid,
389 context=context).company_id.id,
392 def confirm_sheet(self, cr, uid, ids, context={}):
393 self.write(cr, uid, ids, {'state':'confirm'})
396 def set_to_draft(self, cr, uid, ids, context={}):
397 self.write(cr, uid, ids, {'state':'draft'})
400 def cancel_sheet(self, cr, uid, ids, context={}):
401 self.write(cr, uid, ids, {'state':'cancel'})
406 class payroll_advice_line(osv.osv):
410 _name = 'hr.payroll.advice.line'
411 _description = 'Bank Advice Lines'
414 'advice_id':fields.many2one('hr.payroll.advice', 'Bank Advice', required=False),
415 'name':fields.char('Bank Account A/C', size=64, required=True, readonly=False),
416 'employee_id':fields.many2one('hr.employee', 'Employee', required=True),
417 'amount': fields.float('Amount', digits=(16, int(config['price_accuracy']))),
418 'bysal': fields.float('By Salary', digits=(16, int(config['price_accuracy']))),
419 'flag':fields.char('D/C', size=8, required=True, readonly=False),
422 'flag': lambda *a: 'C',
425 def onchange_employee_id(self, cr, uid, ids, ddate, employee_id, context={}):
427 slip_pool = self.pool.get('hr.payslip')
429 dates = prev_bounds(ddate)
431 sids = slip_pool.search(cr, uid, [('paid','=',False),('state','=','confirm'),('date','>=',dates[0]), ('employee_id','=',employee_id), ('date','<=',dates[1])])
433 slip = slip_pool.browse(cr, uid, sids[0])
434 vals['name'] = slip.employee_id.otherid
435 vals['amount'] = slip.net + slip.other_pay
436 vals['bysal'] = slip.net
440 payroll_advice_line()
442 class contrib_register(osv.osv):
444 Contribution Register
446 _name = 'hr.contibution.register'
447 _description = 'Contribution Register'
449 def _total_contrib(self, cr, uid, ids, field_names, arg, context={}):
450 line_pool = self.pool.get('hr.contibution.register.line')
451 period_id = self.pool.get('account.period').search(cr,uid,[('date_start','<=',time.strftime('%Y-%m-%d')),('date_stop','>=',time.strftime('%Y-%m-%d'))])[0]
452 fiscalyear_id = self.pool.get('account.period').browse(cr, uid, period_id).fiscalyear_id
454 for cur in self.browse(cr, uid, ids):
455 current = line_pool.search(cr, uid, [('period_id','=',period_id),('register_id','=',cur.id)])
456 years = line_pool.search(cr, uid, [('period_id.fiscalyear_id','=',fiscalyear_id.id), ('register_id','=',cur.id)])
460 for i in line_pool.browse(cr, uid, current):
461 e_month += i.emp_deduction
462 c_month += i.comp_deduction
466 for j in line_pool.browse(cr, uid, years):
467 e_year += i.emp_deduction
468 c_year += i.comp_deduction
471 'monthly_total_by_emp':e_month,
472 'monthly_total_by_comp':c_month,
473 'yearly_total_by_emp':e_year,
474 'yearly_total_by_comp':c_year
480 'company_id':fields.many2one('res.company', 'Company', required=False),
481 'account_id': fields.many2one('account.account', 'Account', required=True),
482 'analytic_account_id':fields.many2one('account.analytic.account', 'Analytic Account', required=False),
483 'name':fields.char('Name', size=256, required=True, readonly=False),
484 'register_line_ids':fields.one2many('hr.contibution.register.line', 'register_id', 'Register Line', readonly=True),
485 'yearly_total_by_emp': fields.function(_total_contrib, method=True, multi='dc', store=True, string='Total By Employee', digits=(16, int(config['price_accuracy']))),
486 'yearly_total_by_comp': fields.function(_total_contrib, method=True, multi='dc', store=True, string='Total By Company', digits=(16, int(config['price_accuracy']))),
487 'monthly_total_by_emp': fields.function(_total_contrib, method=True, multi='dc', store=True, string='Total By Employee', digits=(16, int(config['price_accuracy']))),
488 'monthly_total_by_comp': fields.function(_total_contrib, method=True, multi='dc', store=True, string='Total By Company', digits=(16, int(config['price_accuracy']))),
489 'note': fields.text('Description'),
492 'company_id': lambda self, cr, uid, context: \
493 self.pool.get('res.users').browse(cr, uid, uid,
494 context=context).company_id.id,
498 class contrib_register_line(osv.osv):
500 Contribution Register Line
502 _name = 'hr.contibution.register.line'
503 _description = 'Contribution Register Line'
505 def _total(self, cr, uid, ids, field_names, arg, context):
507 for line in self.browse(cr, uid, ids, context):
508 res[line.id] = line.emp_deduction + line.comp_deduction
512 'name':fields.char('Name', size=256, required=True, readonly=False),
513 'register_id':fields.many2one('hr.contibution.register', 'Register', required=False),
514 'code':fields.char('Code', size=64, required=False, readonly=False),
515 'employee_id':fields.many2one('hr.employee', 'Employee', required=True),
516 'period_id': fields.many2one('account.period', 'Period'),
517 'emp_deduction': fields.float('Employee Deduction', digits=(16, int(config['price_accuracy']))),
518 'comp_deduction': fields.float('Company Deduction', digits=(16, int(config['price_accuracy']))),
519 'total': fields.function(_total, method=True, store=True, string='Total', digits=(16, int(config['price_accuracy']))),
521 contrib_register_line()
523 class payment_category(osv.osv):
525 Allowance, Deduction Heads
526 House Rent Allowance, Medical Allowance, Food Allowance
527 Professional Tax, Advance TDS, Providend Funds, etc
530 _name = 'hr.allounce.deduction.categoty'
531 _description = 'Allowance Deduction Heads'
534 'name':fields.char('Categoty Name', size=64, required=True, readonly=False),
535 'code':fields.char('Categoty Code', size=64, required=True, readonly=False),
536 'type':fields.selection([
537 ('allowance','Allowance'),
538 ('deduction','Deduction'),
540 ],'Type', select=True),
541 'base':fields.char('Based on', size=64, required=True, readonly=False, help='This will use to computer the % fields values, in general its on basic, but You can use all heads code field in small letter as a variable name i.e. hra, ma, lta, etc...., also you can use, static varible basic'),
542 'condition':fields.char('Condition', size=1024, required=True, readonly=False, help='Applied this head for calculation if condition is true'),
543 'sequence': fields.integer('Sequence', required=True, help='Use to arrange calculation sequence'),
544 'note': fields.text('Description'),
545 'user_id':fields.char('User', size=64, required=False, readonly=False),
546 'state':fields.char('Label', size=64, required=False, readonly=False),
547 'company_id':fields.many2one('res.company', 'Company', required=False),
550 'condition': lambda *a: 'True',
551 'base': lambda *a:'basic',
552 'sequence': lambda *a:5,
553 'company_id': lambda self, cr, uid, context: \
554 self.pool.get('res.users').browse(cr, uid, uid,
555 context=context).company_id.id,
559 class company_contribution(osv.osv):
562 Allows to configure company contribution for some taxes
565 _name = 'company.contribution'
566 _description = "Company Contribution"
569 'category_id':fields.many2one('hr.allounce.deduction.categoty', 'Heads', required=False),
570 'name':fields.char('Name', size=256, required=True, readonly=False),
571 'code':fields.char('Code', size=64, required=True, readonly=False),
572 'include_in_salary':fields.boolean('Included in Salary ?', help='If company contribute on this deduction then should company contribution is also deducted from Employee Salary'),
573 'gratuity':fields.boolean('Use for Gratuity ?', required=False),
574 'line_ids':fields.one2many('company.contribution.line', 'contribution_id', 'Calculations', required=False),
575 'register_id':fields.property(
576 'hr.contibution.register',
578 relation='hr.contibution.register',
579 string="Contribution Register",
582 help="Contribution register based on company",
585 'amount_type':fields.selection([
586 ('fix','Fixed Amount'),
587 ('func','Function Calculation'),
588 ],'Amount Type', select=True),
589 'contribute_per':fields.float('Contribution', digits=(16, int(config['price_accuracy'])), help='Define Company contribution ratio 1.00=100% contribution, If Employee Contribute 5% then company will and here 0.50 defined then company will contribute 50% on employee 5% contribution'),
590 'account_id':fields.property(
593 relation='account.account',
597 help="Expanse account where company expanse will be encoded",
600 'company_id':fields.many2one('res.company', 'Company', required=False),
601 'active':fields.boolean('Active', required=False),
602 'note': fields.text('Description'),
606 'amount_type': lambda *a:'fix',
607 'active': lambda *a:True,
608 'company_id': lambda self, cr, uid, context: \
609 self.pool.get('res.users').browse(cr, uid, uid,
610 context=context).company_id.id,
613 def execute_function(self, cr, uid, id, value, context):
615 self: pointer to self object
616 cr: cursor to database
617 uid: user id of current executer
620 line_pool = self.pool.get('company.contribution.line')
622 ids = line_pool.search(cr, uid, [('category_id','=',id), ('to_val','>=',value),('from_val','<=',value)])
624 ids = line_pool.search(cr, uid, [('category_id','=',id), ('from','<=',value)])
628 res = line_pool.browse(cr, uid, ids)[0].value
631 company_contribution()
633 class company_contribution_line(osv.osv):
635 Company contribution lines
638 _name = 'company.contribution.line'
639 _description = 'Allowance Deduction Categoty'
643 'contribution_id':fields.many2one('company.contribution', 'Contribution', required=False),
644 'name':fields.char('Name', size=64, required=False, readonly=False),
645 'umo_id':fields.many2one('product.uom', 'Unite', required=False),
646 'from_val': fields.float('From', digits=(16, int(config['price_accuracy']))),
647 'to_val': fields.float('To', digits=(16, int(config['price_accuracy']))),
648 'amount_type':fields.selection([
649 ('fix','Fixed Amount'),
650 ],'Amount Type', select=True),
651 'sequence':fields.integer('Sequence'),
652 'value': fields.float('Value', digits=(16, int(config['price_accuracy']))),
654 company_contribution_line()
656 class hr_holidays_status(osv.osv):
657 _inherit = "hr.holidays.status"
660 'company_id':fields.many2one('res.company', 'Company', required=False),
661 'type':fields.selection([
662 ('paid','Paid Holiday'),
663 ('unpaid','Un-Paid Holiday'),
664 ('halfpaid','Half-Pay Holiday')
665 ], string='Payment'),
666 'account_id': fields.many2one('account.account', 'Account', required=False),
667 'analytic_account_id':fields.many2one('account.analytic.account', 'Analytic Account', required=False),
668 'head_id': fields.many2one('hr.allounce.deduction.categoty', 'Payroll Head', domain=[('type','=','deduction')]),
669 'code':fields.char('Code', size=64, required=False, readonly=False),
672 'type': lambda *args: 'unpaid',
673 'company_id': lambda self, cr, uid, context: \
674 self.pool.get('res.users').browse(cr, uid, uid,
675 context=context).company_id.id,
679 class hr_expense_expense(osv.osv):
680 _inherit = "hr.expense.expense"
681 _description = "Expense"
683 'category_id':fields.many2one('hr.allounce.deduction.categoty', 'Payroll Head', domain=[('type','=','other')]),
687 class hr_payslip(osv.osv):
692 _description = 'Pay Slip'
694 def _calculate(self, cr, uid, ids, field_names, arg, context):
696 for rs in self.browse(cr, uid, ids, context):
713 for line in rs.line_ids:
716 if line.amount_type == 'per':
718 amount = line.amount * eval(str(line.category_id.base), obj)
720 raise osv.except_osv(_('Variable Error !'), _('Variable Error : %s ' % (e)))
722 elif line.amount_type in ('fix', 'func'):
725 cd = line.category_id.code.lower()
729 # if line.category_id.include_in_salary:
730 # contrib = line.company_contrib
732 if line.type == 'allowance':
736 elif line.type == 'deduction':
740 elif line.type == 'advance':
742 elif line.type == 'loan':
744 elif line.type == 'otherpay':
747 self.pool.get('hr.payslip.line').write(cr, uid, [line.id], {'total':amount})
750 'allounce':round(allow),
751 'deduction':round(deduct),
752 'grows':round(rs.basic + allow),
753 'net':round(rs.basic + allow - deduct),
755 'total_pay':round(rs.basic + allow - deduct)
762 'deg_id':fields.many2one('hr.payroll.structure', 'Designation', required=False),
763 'register_id':fields.many2one('hr.payroll.register', 'Register', required=False),
764 'journal_id': fields.many2one('account.journal', 'Expanse Journal', required=True),
765 'bank_journal_id': fields.many2one('account.journal', 'Bank Journal', required=True),
766 'name':fields.char('Name', size=64, required=False, readonly=True, states={'draft': [('readonly', False)]}),
767 'number':fields.char('Number', size=64, required=False, readonly=True),
768 'employee_id':fields.many2one('hr.employee', 'Employee', required=True),
769 'date': fields.date('Date'),
770 'state':fields.selection([
772 ('draft','Wating for Verification'),
773 ('hr_check','Wating for HR Verification'),
774 ('accont_check','Wating for Account Verification'),
775 ('confirm','Confirm Sheet'),
776 ('done','Paid Salary'),
778 ],'State', select=True, readonly=True),
779 'basic_before_leaves': fields.float('Basic Salary', readonly=True, digits=(16, 2)),
780 'leaves': fields.float('Leaved Deduction', readonly=True, digits=(16, 2)),
781 'basic': fields.float('Basic Salary - Leaves', readonly=True, digits=(16, 2)),
782 'grows': fields.function(_calculate, method=True, store=True, multi='dc', string='Gross Salary', type='float', digits=(16, 2)),
783 'net': fields.function(_calculate, method=True, store=True, multi='dc', string='Net Salary', digits=(16, 2)),
784 'allounce': fields.function(_calculate, method=True, store=True, multi='dc', string='Allowance', digits=(16, 2)),
785 'deduction': fields.function(_calculate, method=True, store=True, multi='dc', string='Deduction', digits=(16, 2)),
786 'other_pay': fields.function(_calculate, method=True, store=True, multi='dc', string='Others', digits=(16, 2)),
787 'total_pay': fields.function(_calculate, method=True, store=True, multi='dc', string='Total Payment', digits=(16, 2)),
788 'line_ids':fields.one2many('hr.payslip.line', 'slip_id', 'Payslip Line', required=False, readonly=True, states={'draft': [('readonly', False)]}),
789 'move_ids':fields.one2many('hr.payslip.account.move', 'slip_id', 'Accounting vouchers', required=False),
790 'move_line_ids':fields.many2many('account.move.line', 'payslip_lines_rel', 'slip_id', 'line_id', 'Accounting Lines', readonly=True),
791 'move_payment_ids':fields.many2many('account.move.line', 'payslip_payment_rel', 'slip_id', 'payment_id', 'Payment Lines', readonly=True),
792 'company_id':fields.many2one('res.company', 'Company', required=False),
793 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(Payslip) date."),
794 'holiday_days': fields.integer('No of Leaves', readonly=True),
795 'worked_days': fields.integer('Worked Day', readonly=True),
796 'working_days': fields.integer('Working Days', readonly=True),
797 'paid':fields.boolean('Paid ? ', required=False),
798 'note':fields.text('Description'),
799 'contract_id':fields.many2one('hr.contract', 'Contract', required=False),
800 'igross': fields.float('Calculaton Field', readonly=True, digits=(16, 2), help="Calculation field used for internal calculation, do not place this on form"),
801 'inet': fields.float('Calculaton Field', readonly=True, digits=(16, 2), help="Calculation field used for internal calculation, do not place this on form"),
804 'date': lambda *a: time.strftime('%Y-%m-%d'),
805 'state': lambda *a: 'new',
806 'company_id': lambda self, cr, uid, context: \
807 self.pool.get('res.users').browse(cr, uid, uid,
808 context=context).company_id.id,
811 def copy(self, cr, uid, id, default=None, context=None):
812 company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
816 'move_line_ids': False,
817 'move_payment_ids': False,
818 'company_id':company_id,
820 'basic_before_leaves':0,
823 res_id = super(hr_payslip, self).copy(cr, uid, id, default, context)
826 def create_voucher(self, cr, uid, ids, name, voucher, sequence=5):
827 slip_move = self.pool.get('hr.payslip.account.move')
835 slip_move.create(cr, uid, res)
837 def set_to_draft(self, cr, uid, ids, context={}):
838 self.write(cr, uid, ids, {'state':'draft'})
841 def cancel_sheet(self, cr, uid, ids, context={}):
842 move_pool = self.pool.get('account.move')
844 for slip in self.browse(cr, uid, ids, context):
845 for line in slip.move_ids:
847 if slip.move_id.state == 'posted':
848 move_pool.button_cancel(cr, uid [slip.move_id.id], context)
849 move_pool.unlink(cr, uid, [slip.move_id.id])
851 self.write(cr, uid, ids, {'state':'cancel'})
854 def process_sheet(self, cr, uid, ids, context={}):
855 move_pool = self.pool.get('account.move')
856 movel_pool = self.pool.get('account.move.line')
857 invoice_pool = self.pool.get('account.invoice')
859 for slip in self.browse(cr,uid,ids):
865 partner = slip.employee_id.address_home_id.partner_id
866 partner_id = partner.id
868 fiscal_year_ids = self.pool.get('account.fiscalyear').search(cr, uid, [])
869 if not fiscal_year_ids:
870 raise osv.except_osv(_('Warning !'), _('Please define fiscal year for perticular contract'))
871 fiscal_year_objs = self.pool.get('account.fiscalyear').read(cr, uid, fiscal_year_ids, ['date_start','date_stop'])
873 for fiscal_year in fiscal_year_objs:
874 if ((fiscal_year['date_start'] <= slip.date) and (fiscal_year['date_stop'] >= slip.date)):
877 raise osv.except_osv(_('Warning !'), _('Fiscal Year is not defined for slip date %s'%slip.date))
878 search_period = self.pool.get('account.period').search(cr,uid,[('date_start','<=',slip.date),('date_stop','>=',slip.date)])
879 if not search_period:
880 raise osv.except_osv(_('Warning !'), _('Period is not defined for slip date %s'%slip.date))
881 period_id = search_period[0]
882 name = 'Payment of Salary to %s' % (slip.employee_id.name)
884 'journal_id': slip.bank_journal_id.id,
885 'period_id': period_id,
887 'type':'bank_pay_voucher',
891 move_id = move_pool.create(cr, uid, move)
892 self.create_voucher(cr, uid, [slip.id], name, move_id)
894 name = "To %s account" % (slip.employee_id.name)
898 #'partner_id': partner_id,
900 'account_id': slip.employee_id.property_bank_account.id,
902 'credit' : slip.total_pay,
903 'journal_id' : slip.journal_id.id,
904 'period_id' :period_id,
907 line_ids += [movel_pool.create(cr, uid, ded_rec)]
908 name = "By %s account" % (slip.employee_id.property_bank_account.name)
912 'partner_id': partner_id,
914 'account_id': partner.property_account_payable.id,
915 'debit': slip.total_pay,
917 'journal_id' : slip.journal_id.id,
918 'period_id' :period_id,
921 line_ids += [movel_pool.create(cr, uid, cre_rec)]
923 other_pay = slip.other_pay
924 #Process all Reambuse Entries
925 for line in slip.line_ids:
926 if line.type == 'otherpay' and line.expanse_id.invoice_id:
927 if not line.expanse_id.invoice_id.move_id:
928 raise osv.except_osv(_('Warning !'), _('Please Confirm all Expanse Invoice appear for Reimbursement'))
929 invids = [line.expanse_id.invoice_id.id]
931 acc_id = slip.bank_journal_id.default_credit_account_id and slip.bank_journal_id.default_credit_account_id.id
932 period_id = slip.period_id.id
933 journal_id = slip.bank_journal_id.id
934 name = '[%s]-%s' % (slip.number, line.name)
935 invoice_pool.pay_and_reconcile(cr, uid, invids, amount, acc_id, period_id, journal_id, False, period_id, False, context, name)
937 #TODO: link this account entries to the Payment Lines also Expanse Entries to Account Lines
938 l_ids = movel_pool.search(cr, uid, [('name','=',name)])
941 l_ids = movel_pool.search(cr, uid, [('invoice','=',line.expanse_id.invoice_id.id)])
944 #Process for Other payment if any
945 other_move_id = False
946 if slip.other_pay > 0:
947 narration = 'Payment of Other Payeble amounts to %s' % (slip.employee_id.name)
949 'journal_id': slip.bank_journal_id.id,
950 'period_id': period_id,
952 'type':'bank_pay_voucher',
954 'narration': narration
956 other_move_id = move_pool.create(cr, uid, move)
957 self.create_voucher(cr, uid, [slip.id], narration, move_id)
959 name = "To %s account" % (slip.employee_id.name)
961 'move_id':other_move_id,
964 'account_id':slip.employee_id.property_bank_account.id,
967 'journal_id':slip.journal_id.id,
968 'period_id':period_id,
971 line_ids += [movel_pool.create(cr, uid, ded_rec)]
972 name = "By %s account" % (slip.employee_id.property_bank_account.name)
974 'move_id':other_move_id,
976 'partner_id':partner_id,
978 'account_id':partner.property_account_payable.id,
981 'journal_id':slip.journal_id.id,
982 'period_id':period_id,
985 line_ids += [movel_pool.create(cr, uid, cre_rec)]
989 'move_payment_ids':[(6, 0, line_ids)],
992 self.write(cr, uid, [slip.id], rec)
993 for exp_id in exp_ids:
994 self.write(cr, uid, [slip.id], {'move_line_ids':[(4, exp_id)]})
998 def account_check_sheet(self, cr, uid, ids, context={}):
999 self.write(cr, uid, ids, {'state':'accont_check'})
1002 def hr_check_sheet(self, cr, uid, ids, context={}):
1003 self.write(cr, uid, ids, {'state':'hr_check'})
1006 def verify_sheet(self, cr, uid, ids, context={}):
1008 move_pool = self.pool.get('account.move')
1009 movel_pool = self.pool.get('account.move.line')
1010 exp_pool = self.pool.get('hr.expense.expense')
1012 for slip in self.browse(cr,uid,ids):
1019 if not slip.employee_id.address_home_id:
1020 raise osv.except_osv(_('Integrity Error !'), _('Please defined the Employee Home Address Along with Partners !!'))
1022 if not slip.employee_id.address_home_id.partner_id:
1023 raise osv.except_osv(_('Integrity Error !'), _('Please defined the Partner in Home Address !!'))
1025 partner = slip.employee_id.address_home_id.partner_id
1026 partner_id = slip.employee_id.address_home_id.partner_id.id
1031 period_id = slip.period_id.id
1033 fiscal_year_ids = self.pool.get('account.fiscalyear').search(cr, uid, [])
1034 if not fiscal_year_ids:
1035 raise osv.except_osv(_('Warning !'), _('Please define fiscal year for perticular contract'))
1036 fiscal_year_objs = self.pool.get('account.fiscalyear').read(cr, uid, fiscal_year_ids, ['date_start','date_stop'])
1038 for fiscal_year in fiscal_year_objs:
1039 if ((fiscal_year['date_start'] <= slip.date) and (fiscal_year['date_stop'] >= slip.date)):
1042 raise osv.except_osv(_('Warning !'), _('Fiscal Year is not defined for slip date %s'%slip.date))
1043 search_period = self.pool.get('account.period').search(cr,uid,[('date_start','<=',slip.date),('date_stop','>=',slip.date)])
1044 if not search_period:
1045 raise osv.except_osv(_('Warning !'), _('Period is not defined for slip date %s'%slip.date))
1046 period_id = search_period[0]
1050 'journal_id': slip.journal_id.id,
1051 'period_id': period_id,
1054 'narration': slip.name
1056 move_id = move_pool.create(cr, uid, move)
1057 self.create_voucher(cr, uid, [slip.id], slip.name, move_id)
1061 'name': "By Basic Salary / " + slip.employee_id.name,
1063 'account_id': slip.employee_id.salary_account.id,
1064 'debit': slip.basic,
1066 'quantity':slip.working_days,
1067 'journal_id': slip.journal_id.id,
1068 'period_id': period_id,
1069 'analytic_account_id': False,
1073 #Setting Analysis Account for Basic Salary
1074 if slip.employee_id.analytic_account:
1075 line['analytic_account_id'] = slip.employee_id.analytic_account.id
1077 move_line_id = movel_pool.create(cr, uid, line)
1078 line_ids += [move_line_id]
1082 'name': "To Basic Paysble Salary / " + slip.employee_id.name,
1083 'partner_id': partner_id,
1085 'account_id': slip.employee_id.employee_account.id,
1087 'quantity':slip.working_days,
1088 'credit': slip.basic,
1089 'journal_id': slip.journal_id.id,
1090 'period_id': period_id,
1093 line_ids += [movel_pool.create(cr, uid, line)]
1095 for line in slip.line_ids:
1096 name = "[%s] - %s / %s" % (line.code, line.name, slip.employee_id.name)
1099 if line.type == 'leaves':
1106 'account_id': line.account_id.id,
1109 'journal_id' : slip.journal_id.id,
1110 'period_id' :period_id,
1111 'analytic_account_id':False,
1116 #Setting Analysis Account for Salary Slip Lines
1117 if line.analytic_account_id:
1118 rec['analytic_account_id'] = line.analytic_account_id.id
1120 rec['analytic_account_id'] = slip.deg_id.account_id.id
1122 if line.type == 'allounce' or line.type == 'otherpay':
1123 rec['debit'] = amount
1124 if not partner.property_account_payable:
1125 raise osv.except_osv(_('Integrity Error !'), _('Please Configure Partners Payable Account!!'))
1129 'partner_id': partner_id,
1131 'account_id': partner.property_account_payable.id,
1135 'journal_id' : slip.journal_id.id,
1136 'period_id' :period_id,
1139 line_ids += [movel_pool.create(cr, uid, ded_rec)]
1140 elif line.type == 'deduction' or line.type == 'otherdeduct':
1141 if not partner.property_account_receivable:
1142 raise osv.except_osv(_('Integrity Error !'), _('Please Configure Partners Receivable Account!!'))
1143 rec['credit'] = amount
1144 total_deduct += amount
1148 'partner_id': partner_id,
1151 'account_id': partner.property_account_receivable.id,
1154 'journal_id' : slip.journal_id.id,
1155 'period_id' :period_id,
1158 line_ids += [movel_pool.create(cr, uid, ded_rec)]
1160 line_ids += [movel_pool.create(cr, uid, rec)]
1162 if line.company_contrib > 0:
1163 company_contrib = line.company_contrib
1164 # if line.category_id.amount_type == 'per':
1165 # company_contrib = (amount * line.category_id.contribute_per)
1167 narration = """Company Contribution of %s Encode same as a Company Expanse @ %s""" % (line.name, company_contrib)
1170 'journal_id': slip.journal_id.id,
1171 'period_id': period_id,
1174 'narration': narration
1176 company_contrib_move_id = move_pool.create(cr, uid, move)
1177 name = "[%s] - %s / %s - Company Contribution" % (line.code, line.name, slip.employee_id.name)
1178 self.create_voucher(cr, uid, [slip.id], name, company_contrib_move_id)
1181 'move_id':company_contrib_move_id,
1185 'account_id': line.category_id.account_id.id,
1186 'debit': company_contrib,
1188 'journal_id': slip.journal_id.id,
1189 'period_id': period_id,
1192 line_ids += [movel_pool.create(cr, uid, ded_deb)]
1194 'move_id':company_contrib_move_id,
1198 'account_id': line.category_id.register_id.account_id.id,
1200 'credit' : company_contrib,
1201 'journal_id': slip.journal_id.id,
1202 'period_id': period_id,
1205 line_ids += [movel_pool.create(cr, uid, ded_cre)]
1207 if line.category_id.include_in_salary:
1208 narration = """Company Contribution of %s Deducted from Employee %s""" % (line.name, company_contrib)
1211 'journal_id': slip.journal_id.id,
1212 'period_id': period_id,
1215 'narration': narration
1217 include_in_salary_move_id = move_pool.create(cr, uid, move)
1218 self.create_voucher(cr, uid, [slip.id], narration, include_in_salary_move_id)
1220 total_deduct += company_contrib
1222 'move_id':include_in_salary_move_id,
1224 'partner_id': partner_id,
1227 'account_id': partner.property_account_receivable.id,
1228 'debit': company_contrib,
1230 'journal_id': slip.journal_id.id,
1231 'period_id': period_id,
1234 line_ids += [movel_pool.create(cr, uid, ded_deb)]
1236 'move_id':include_in_salary_move_id,
1240 'account_id': line.category_id.account_id.id,
1242 'credit' : company_contrib,
1243 'journal_id': slip.journal_id.id,
1244 'period_id': period_id,
1247 line_ids += [movel_pool.create(cr, uid, ded_cre)]
1249 #make an entry line to contribution register
1250 if line.category_id.register_id:
1252 'register_id':line.category_id.register_id.id,
1255 'employee_id':slip.employee_id.id,
1256 'period_id':period_id,
1257 'emp_deduction':amount,
1259 if line.category_id.contribute:
1260 ctr['comp_deduction'] = amount
1264 if line.category_id.contribute and line.category_id.include_in_salary and line.category_id.amount_type == 'per':
1265 new_amount = (amount * (line.category_id.contribute_per / (1+line.category_id.contribute_per)))
1266 company = new_amount
1267 employee = amount - company
1269 elif line.category_id.contribute and line.category_id.include_in_salary and line.category_id.amount_type == 'fix':
1270 company = line.category_id.contribute_per
1271 employee = amount - company
1273 elif line.category_id.contribute and line.category_id.include_in_salary and line.category_id.amount_type == 'func':
1274 company = self.pool.get('hr.allounce.deduction.categoty').execute_function(cr, uid, line.category_id.id, line.slip_id.basic, context)
1277 elif line.category_id.contribute and not line.category_id.include_in_salary and line.category_id.amount_type == 'per':
1278 company = amount * line.category_id.contribute_per
1281 elif line.category_id.contribute and not line.category_id.include_in_salary and line.category_id.amount_type == 'fix':
1282 company = line.category_id.contribute_per
1285 elif line.category_id.contribute and not line.category_id.include_in_salary and line.category_id.amount_type == 'func':
1286 company = self.pool.get('hr.allounce.deduction.categoty').execute_function(cr, uid, line.category_id.id, line.slip_id.basic, context)
1289 ctr['emp_deduction'] = employee
1290 ctr['comp_deduction'] = company
1292 self.pool.get('hr.contibution.register.line').create(cr, uid, ctr)
1295 if total_deduct > 0:
1297 'journal_id': slip.journal_id.id,
1298 'period_id': period_id,
1301 'narration': 'Adjustment : %s' % (slip.name)
1303 adj_move_id = move_pool.create(cr, uid, move)
1304 name = "Adjustment Entry - %s" % (slip.employee_id.name)
1305 self.create_voucher(cr, uid, [slip.id], name, adj_move_id)
1308 'move_id':adj_move_id,
1310 'partner_id': partner_id,
1312 'account_id': partner.property_account_receivable.id,
1315 'credit' : total_deduct,
1316 'journal_id' : slip.journal_id.id,
1317 'period_id' :period_id,
1320 line_ids += [movel_pool.create(cr, uid, ded_rec)]
1322 'move_id':adj_move_id,
1324 'partner_id': partner_id,
1326 'account_id': partner.property_account_payable.id,
1327 'debit': total_deduct,
1330 'journal_id' : slip.journal_id.id,
1331 'period_id' :period_id,
1334 line_ids += [movel_pool.create(cr, uid, cre_rec)]
1338 'move_line_ids':[(6, 0,line_ids)],
1340 if not slip.period_id:
1341 rec['period_id'] = period_id
1343 dates = prev_bounds(slip.date)
1344 exp_ids = exp_pool.search(cr, uid, [('date_valid','>=',dates[0]), ('date_valid','<=',dates[1]), ('state','=','invoiced')])
1346 acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
1347 for exp in exp_pool.browse(cr, uid, exp_ids):
1350 'amount_type':'fix',
1352 'category_id':exp.category_id.id,
1353 'amount':exp.amount,
1355 'expanse_id':exp.id,
1358 self.pool.get('hr.payslip.line').create(cr, uid, exp_res)
1360 self.write(cr, uid, [slip.id], rec)
1364 def get_contract(self, cr, uid, employee, date, context={}):
1366 SELECT c.id as id, c.wage as wage, struct_id as function
1368 LEFT JOIN hr_employee emp on (c.employee_id=emp.id)
1369 LEFT JOIN hr_contract_wage_type cwt on (cwt.id = c.wage_type_id)
1370 LEFT JOIN hr_contract_wage_type_period p on (cwt.period_id = p.id)
1373 (date_start <= %s) AND
1374 (date_end IS NULL OR date_end >= %s)
1377 cr.execute(sql_req, (employee.id, date, date))
1378 contract = cr.dictfetchone()
1380 contract = contract and contract or {}
1384 def _get_leaves(self, cr, user, slip, employee, context={}):
1386 Compute leaves for an employee
1388 @param cr: cursor to database
1389 @param user: id of current user
1390 @param slip: object of the hr.payroll.slip model
1391 @param employee: object of the hr.employee model
1392 @param context: context arguments, like lang, time zone
1394 @return: return a result
1399 dates = prev_bounds(slip.date)
1400 sql = '''select id from hr_holidays
1401 where date_from >= '%s' and date_to <= '%s'
1402 and employee_id = %s
1403 and state = 'validate' ''' % (dates[0], dates[1], slip.employee_id.id)
1408 result = [x[0] for x in res]
1412 def compute_sheet(self, cr, uid, ids, context={}):
1413 emp_pool = self.pool.get('hr.employee')
1414 slip_pool = self.pool.get('hr.payslip')
1415 func_pool = self.pool.get('hr.payroll.structure')
1416 slip_line_pool = self.pool.get('hr.payslip.line')
1417 holiday_pool = self.pool.get('hr.holidays')
1419 date = self.read(cr, uid, ids, ['date'])[0]['date']
1421 #Check for the Holidays
1422 def get_days(start, end, month, year, calc_day):
1425 for day in range(start, end):
1426 if datetime.date(year, month, day).weekday() == calc_day:
1430 for slip in self.browse(cr, uid, ids):
1431 contracts = self.get_contract(cr, uid, slip.employee_id, date, context)
1433 if contracts.get('id', False) == False:
1436 contract = self.pool.get('hr.contract').browse(cr, uid, contracts.get('id'))
1437 sal_type = contract.wage_type_id.type
1438 function = contract.struct_id.id
1442 func = func_pool.read(cr, uid, function, ['line_ids'])
1443 lines = slip_line_pool.browse(cr, uid, func['line_ids'])
1445 lines += slip.employee_id.line_ids
1447 old_slip_id = slip_line_pool.search(cr, uid, [('slip_id','=',slip.id)])
1448 slip_line_pool.unlink(cr, uid, old_slip_id)
1464 if contract.wage_type_id.type == 'gross':
1465 obj['gross'] = contract.wage
1466 update['igross'] = contract.wage
1467 if contract.wage_type_id.type == 'net':
1468 obj['net'] = contract.wage
1469 update['inet'] = contract.wage
1470 if contract.wage_type_id.type == 'basic':
1471 obj['basic'] = contract.wage
1472 update['basic'] = contract.wage
1479 cd = line.code.lower()
1480 obj[cd] = line.amount or 0.0
1484 if line.category_id.code in ad:
1487 ad.append(line.category_id.code)
1488 cd = line.category_id.code.lower()
1492 exp = line.category_id.condition
1493 calculate = eval(exp, obj)
1494 except Exception, e:
1495 raise osv.except_osv(_('Variable Error !'), _('Variable Error : %s ' % (e)))
1503 company_contrib = 0.0
1504 base = line.category_id.base
1507 # Please have a look at the configuration guide for rules and restrictions
1508 amt = eval(base, obj)
1509 except Exception, e:
1510 raise osv.except_osv(_('Variable Error !'), _('Variable Error : %s ' % (e)))
1512 if sal_type in ('gross', 'net'):
1513 if line.amount_type == 'per':
1514 percent = line.amount
1517 value = percent * amt
1518 elif amt > 0 and amt <= 1:
1519 percent = percent * amt
1524 elif line.amount_type == 'fix':
1527 elif line.amount_type == 'func':
1528 value = self.pool.get('hr.payslip.line').execute_function(cr, uid, line.id, amt, context)
1531 if line.amount_type in ('fix', 'per'):
1533 elif line.amount_type == 'func':
1534 value = self.pool.get('hr.payslip.line').execute_function(cr, uid, line.id, amt, context)
1537 if line.type == 'allowance':
1540 elif line.type == 'deduction':
1545 'amount':line.amount,
1547 'employee_id':False,
1548 'function_id':False,
1551 slip_line_pool.copy(cr, uid, line.id, vals, {})
1553 if sal_type in ('gross', 'net'):
1555 if sal_type == 'net':
1559 if sal_type == 'net':
1560 per = (all_per - ded_per)
1565 final = (per * 100) + 100
1566 basic = (sal * 100) / final
1568 basic = contract.wage
1570 number = self.pool.get('ir.sequence').get(cr, uid, 'salary.slip')
1571 ttyme = datetime.fromtimestamp(time.mktime(time.strptime(slip.date,"%Y-%m-%d")))
1575 'basic': round(basic),
1576 'basic_before_leaves': round(basic),
1577 'name':'Salary Slip of %s for %s' % (slip.employee_id.name, ttyme.strftime('%B-%Y')),
1579 'contract_id':contract.id,
1580 'company_id':slip.employee_id.company_id.id
1582 self.write(cr, uid, [slip.id], update)
1584 for slip in self.browse(cr, uid, ids):
1586 if not slip.contract_id :
1589 basic_before_leaves = slip.basic
1593 dates = prev_bounds(slip.date)
1595 days_arr = [0, 1, 2, 3, 4, 5, 6]
1596 for dy in range(contract.working_days_per_week, 7):
1597 off_days += get_days(1, dates[1].day, dates[1].month, dates[1].year, days_arr[dy])
1599 total_off = off_days
1600 working_day = dates[1].day - total_off
1601 perday = slip.net / working_day
1606 leave_ids = self._get_leaves(cr, uid, slip, slip.employee_id, context)
1610 for hday in holiday_pool.browse(cr, uid, leave_ids):
1613 'name':hday.holiday_status_id.name + '-%s' % (hday.number_of_days),
1614 'code':hday.holiday_status_id.code,
1615 'amount_type':'fix',
1616 'category_id':hday.holiday_status_id.head_id.id,
1617 'account_id':hday.holiday_status_id.account_id.id,
1618 'analytic_account_id':hday.holiday_status_id.analytic_account_id.id
1621 days = hday.number_of_days
1622 if hday.number_of_days < 0:
1623 days = hday.number_of_days * -1
1626 if hday.holiday_status_id.type == 'paid':
1630 elif hday.holiday_status_id.type == 'halfpaid':
1631 paid_leave += (days / 2)
1632 res['name'] = hday.holiday_status_id.name + '-%s/2' % (days)
1633 res['amount'] = perday * (days/2)
1634 total += perday * (days/2)
1636 res['type'] = 'deduction'
1638 res['name'] = hday.holiday_status_id.name + '-%s' % (days)
1639 res['amount'] = perday * days
1640 res['type'] = 'deduction'
1642 total += perday * days
1644 slip_line_pool.create(cr, uid, res)
1645 basic = basic - total
1649 'basic_before_leaves': round(basic_before_leaves),
1651 'holiday_days':leave,
1652 'worked_days':working_day - leave,
1653 'working_days':working_day,
1655 self.write(cr, uid, [slip.id], update)
1661 class account_move_link_slip(osv.osv):
1663 Account Move Link to Pay Slip
1665 _name = 'hr.payslip.account.move'
1666 _description = 'Account Move Link to Pay Slip'
1668 'name':fields.char('Name', size=256, required=True, readonly=False),
1669 'move_id':fields.many2one('account.move', 'Expanse Entries', required=False, readonly=True),
1670 'slip_id':fields.many2one('hr.payslip', 'Pay Slip', required=False),
1671 'sequence': fields.integer('Sequence'),
1673 account_move_link_slip()
1675 class line_condition(osv.osv):
1679 _name = 'hr.payslip.line.condition'
1680 _description = 'Line Condition'
1683 'name':fields.char('Name', size=64, required=False, readonly=False),
1684 'date_start': fields.date('Start Date'),
1685 'date_end': fields.date('End Date'),
1686 'state':fields.selection([
1687 ('total','Override By'),
1688 ('add','Add to Structure')
1689 ],'Condition', select=True, readonly=False),
1693 class hr_payslip_line(osv.osv):
1697 _name = 'hr.payslip.line'
1698 _description = 'Payslip Line'
1700 # def _calculate(self, cr, uid, ids, field_names, arg, context):
1703 # for line in self.browse(cr, uid, ids, context):
1704 # obj['basic'] = line.slip_id.basic
1707 # if line.amount_type == 'per' and line.base:
1708 # print 'XXXXXXXXXXXXXXXX : ', obj
1709 # amount = line.amount * eval(line.base, obj)
1710 # elif line.amount_type in ('fix', 'func'):
1711 # amount = line.amount
1713 # cd = line.category_id.code.lower()
1715 # print 'XXXXXXXXXXXXXXXXXX : ', cd
1717 # res[line.id] = amount
1718 # print 'XXXXXXXXXXXX : ', obj
1721 def onchange_category(self, cr, uid, ids, category_id):
1726 category = self.pool.get('hr.allounce.deduction.categoty').browse(cr, uid, category_id)
1728 'sequence':category.sequence,
1729 'name':category.name,
1730 'code':category.code,
1731 'type':category.type
1733 return {'value':res}
1735 def onchange_amount(self, cr, uid, ids, amount, typ):
1737 if typ and typ == 'per':
1740 return {'value':{'amount':amt}}
1743 'slip_id':fields.many2one('hr.payslip', 'Pay Slip', required=False),
1744 'condition_id':fields.many2one('hr.payslip.line.condition', 'Condition', required=False),
1745 'function_id':fields.many2one('hr.payroll.structure', 'Function', required=False),
1746 'employee_id':fields.many2one('hr.employee', 'Employee', required=False),
1747 'name':fields.char('Name', size=256, required=True, readonly=False),
1748 'base':fields.char('Formula', size=1024, required=False, readonly=False),
1749 'code':fields.char('Code', size=64, required=False, readonly=False),
1750 'type':fields.selection([
1751 ('allowance','Allowance'),
1752 ('deduction','Deduction'),
1753 ('leaves','Leaves'),
1754 ('advance','Advance'),
1756 ('installment','Loan Installment'),
1757 ('otherpay','Other Payment'),
1758 ('otherdeduct','Other Deduction'),
1759 ],'Type', select=True, required=True),
1760 'category_id':fields.many2one('hr.allounce.deduction.categoty', 'Category', required=True),
1761 'amount_type':fields.selection([
1762 ('per','Percentage (%)'),
1763 ('fix','Fixed Amount'),
1764 ('func','Function Value'),
1765 ],'Amount Type', select=True),
1766 'amount': fields.float('Amount / Percentage', digits=(16, 4)),
1767 'analytic_account_id':fields.many2one('account.analytic.account', 'Analytic Account', required=False),
1768 'account_id':fields.many2one('account.account', 'General Account', required=True),
1769 'total': fields.float('Sub Total', readonly=True, digits=(16, int(config['price_accuracy']))),
1770 #'total': fields.function(_calculate, method=True, type='float', string='Label', store=True),
1771 'company_contrib': fields.float('Company Contribution', readonly=True, digits=(16, int(config['price_accuracy']))),
1772 'expanse_id': fields.many2one('hr.expense.expense', 'Expense'),
1773 'sequence': fields.integer('Sequence'),
1774 'note':fields.text('Description'),
1775 'line_ids':fields.one2many('hr.payslip.line.line', 'slipline_id', 'Calculations', required=False)
1779 def execute_function(self, cr, uid, id, value, context):
1780 line_pool = self.pool.get('hr.payslip.line.line')
1782 ids = line_pool.search(cr, uid, [('slipline_id','=',id), ('from_val','<=',value), ('to_val','>=',value)])
1785 ids = line_pool.search(cr, uid, [('slipline_id','=',id), ('from_val','<=',value)])
1790 res = line_pool.browse(cr, uid, ids)[-1].value
1795 class hr_payslip_line_line(osv.osv):
1799 _name = 'hr.payslip.line.line'
1800 _description = 'Function Line'
1804 'slipline_id':fields.many2one('hr.payslip.line', 'Slip Line', required=False),
1805 'name':fields.char('Name', size=64, required=False, readonly=False),
1806 'umo_id':fields.many2one('product.uom', 'Unite', required=False),
1807 'from_val': fields.float('From', digits=(16, int(config['price_accuracy']))),
1808 'to_val': fields.float('To', digits=(16, int(config['price_accuracy']))),
1809 'amount_type':fields.selection([
1810 ('fix','Fixed Amount'),
1811 ],'Amount Type', select=True),
1812 'sequence':fields.integer('Sequence'),
1813 'value': fields.float('Value', digits=(16, int(config['price_accuracy']))),
1815 hr_payslip_line_line()
1817 class hr_employee(osv.osv):
1821 _inherit = 'hr.employee'
1822 _description = 'Employee'
1825 'pan_no':fields.char('PAN No', size=64, required=False, readonly=False),
1826 'esp_account':fields.char('EPS Account', size=64, required=False, readonly=False),
1827 'pf_account':fields.char('PF Account', size=64, required=False, readonly=False),
1828 'pg_joining': fields.date('PF Join Date'),
1829 'esi_account':fields.char('ESI Account', size=64, required=False, readonly=False),
1830 'hospital_id':fields.many2one('res.partner.address', 'ESI Hospital', required=False),
1831 'passport_id':fields.many2one('hr.passport', 'Passport', required=False),
1832 'otherid':fields.char('Other Id', size=64, required=False),
1833 'line_ids':fields.one2many('hr.payslip.line', 'employee_id', 'Salary Structure', required=False),
1834 'slip_ids':fields.one2many('hr.payslip', 'employee_id', 'Payslips', required=False, readonly=True),
1835 'property_bank_account': fields.property(
1838 relation='account.account',
1839 string="Bank Account",
1842 help="Select Bank Account from where Salary Expanse will be Paid",
1844 'salary_account':fields.property(
1847 relation='account.account',
1848 string="Salary Account",
1851 help="Expanse account when Salary Expanse will be recorded",
1853 'employee_account':fields.property(
1856 relation='account.account',
1857 string="Employee Account",
1860 help="Employee Payable Account",
1862 'analytic_account':fields.property(
1863 'account.analytic.account',
1865 relation='account.analytic.account',
1866 string="Analytic Account",
1869 help="Analytic Account for Salary Analysis",
1874 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: