[MERGE] merge from trunk-payroll main branch
bzr revid: mra@mra-laptop-20110512130410-5ybp0b0drz6iqg7f
This commit is contained in:
@@ -49,6 +49,7 @@ Generic Payroll system.
|
||||
],
|
||||
'update_xml': [
|
||||
'security/hr_security.xml',
|
||||
'wizard/hr_payroll_payslips_by_employees.xml',
|
||||
'hr_payroll_view.xml',
|
||||
'hr_payroll_workflow.xml',
|
||||
'hr_payroll_sequence.xml',
|
||||
|
||||
+146
-93
@@ -165,17 +165,17 @@ class contrib_register(osv.osv):
|
||||
|
||||
contrib_register()
|
||||
|
||||
class hr_salary_category(osv.osv):
|
||||
class hr_salary_rule_category(osv.osv):
|
||||
"""
|
||||
HR Salary Category
|
||||
HR Salary Rule Category
|
||||
"""
|
||||
|
||||
_name = 'hr.salary.category'
|
||||
_description = 'Salary Category'
|
||||
_name = 'hr.salary.rule.category'
|
||||
_description = 'Salary Rule Category'
|
||||
_columns = {
|
||||
'name':fields.char('Name', size=64, required=True, readonly=False),
|
||||
'code':fields.char('Code', size=64, required=True, readonly=False),
|
||||
'parent_id':fields.many2one('hr.salary.category', 'Parent', help="Linking a salary category to its parent is used only for the reporting purpose."),
|
||||
'parent_id':fields.many2one('hr.salary.rule.category', 'Parent', help="Linking a salary category to its parent is used only for the reporting purpose."),
|
||||
'note': fields.text('Description'),
|
||||
'company_id':fields.many2one('res.company', 'Company', required=False),
|
||||
}
|
||||
@@ -186,7 +186,7 @@ class hr_salary_category(osv.osv):
|
||||
context=context).company_id.id,
|
||||
}
|
||||
|
||||
hr_salary_category()
|
||||
hr_salary_rule_category()
|
||||
|
||||
class one2many_mod2(fields.one2many):
|
||||
|
||||
@@ -203,6 +203,29 @@ class one2many_mod2(fields.one2many):
|
||||
res[r[self._fields_id]].append( r['id'] )
|
||||
return res
|
||||
|
||||
class hr_payslip_run(osv.osv):
|
||||
|
||||
_name = 'hr.payslip.run'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'slip_ids': fields.one2many('hr.payslip', 'payslip_run_id', 'Payslips', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('close', 'Close'),
|
||||
], 'State', select=True, readonly=True)
|
||||
}
|
||||
_defaults = {
|
||||
'state': 'draft',
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
hr_payslip_run()
|
||||
|
||||
class hr_payslip(osv.osv):
|
||||
'''
|
||||
Pay Slip
|
||||
@@ -214,22 +237,23 @@ class hr_payslip(osv.osv):
|
||||
def __init__(self, pool, cr):
|
||||
super(hr_payslip, self).__init__(pool, cr)
|
||||
self.cr = cr
|
||||
|
||||
def _get_lines_salary_category(self, cr, uid, ids, field_names, arg=None, context=None):
|
||||
|
||||
def _get_lines_salary_rule_category(self, cr, uid, ids, field_names, arg=None, context=None):
|
||||
result = {}
|
||||
if not ids: return result
|
||||
for id in ids:
|
||||
result.setdefault(id, [])
|
||||
cr.execute('''SELECT pl.slip_id, pl.id FROM hr_payslip_line AS pl \
|
||||
LEFT JOIN hr_salary_category AS sh on (pl.category_id = sh.id) \
|
||||
LEFT JOIN hr_salary_rule_category AS sh on (pl.category_id = sh.id) \
|
||||
WHERE pl.slip_id in %s \
|
||||
GROUP BY pl.slip_id, pl.sequence, pl.id ORDER BY pl.sequence''',(tuple(ids),))
|
||||
res = cr.fetchall()
|
||||
for r in res:
|
||||
result.setdefault(r[0], [])
|
||||
result[r[0]].append(r[1])
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'struct_id': fields.many2one('hr.payroll.structure', 'Structure', 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'),
|
||||
'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'),
|
||||
'name': fields.char('Description', size=64, required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'number': fields.char('Reference', size=64, required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'employee_id': fields.many2one('hr.employee', 'Employee', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
@@ -250,14 +274,16 @@ class hr_payslip(osv.osv):
|
||||
\n* If the salary is paid then state is set to \'Paid Salary\'.\
|
||||
\n* The \'Reject\' state is used when user cancel payslip.'),
|
||||
# 'line_ids': fields.one2many('hr.payslip.line', 'slip_id', 'Payslip Line', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Line',readonly=True),
|
||||
'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Lines', readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'worked_days_line_ids': fields.one2many('hr.payslip.worked_days', 'payslip_id', 'Payslip Worked Days', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'input_line_ids': fields.one2many('hr.payslip.input', 'payslip_id', 'Payslip Inputs', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'paid': fields.boolean('Made Payment Order ? ', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'note': fields.text('Description'),
|
||||
'note': fields.text('Description', readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'contract_id': fields.many2one('hr.contract', 'Contract', required=False, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'details_by_salary_category': fields.function(_get_lines_salary_category, method=True, type='one2many', relation='hr.payslip.line', string='Details by Salary Head'),
|
||||
'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'),
|
||||
'credit_note': fields.boolean('Credit Note', help="Indicates this payslip has a refund of another"),
|
||||
'payslip_run_id': fields.many2one('hr.payslip.run', 'Payslip Run', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
}
|
||||
_defaults = {
|
||||
'date_from': lambda *a: time.strftime('%Y-%m-01'),
|
||||
@@ -325,59 +351,6 @@ class hr_payslip(osv.osv):
|
||||
}
|
||||
|
||||
def verify_sheet(self, cr, uid, ids, context=None):
|
||||
#TODO clean me: this function should create the register lines accordingly to the rules computed (run the compute_sheet first)
|
||||
# holiday_pool = self.pool.get('hr.holidays')
|
||||
# salary_rule_pool = self.pool.get('hr.salary.rule')
|
||||
# structure_pool = self.pool.get('hr.payroll.structure')
|
||||
# register_line_pool = self.pool.get('hr.contibution.register.line')
|
||||
# contracts = []
|
||||
# structures = []
|
||||
# rules = []
|
||||
# lines = []
|
||||
# sal_structures =[]
|
||||
# for slip in self.browse(cr, uid, ids, context=context):
|
||||
# if slip.contract_id:
|
||||
# contracts.append(slip.contract_id)
|
||||
# else:
|
||||
# contracts = self.get_contract(cr, uid, slip.employee_id, slip.date, context=context)
|
||||
# for contract in contracts:
|
||||
# structures.append(contract.struct_id.id)
|
||||
# leave_ids = self._get_leaves(cr, uid, slip.date, slip.employee_id, contract, context)
|
||||
# for hday in holiday_pool.browse(cr, uid, leave_ids, context=context):
|
||||
# salary_rules = salary_rule_pool.search(cr, uid, [('code', '=', hday.holiday_status_id.code)], context=context)
|
||||
# rules += salary_rule_pool.browse(cr, uid, salary_rules, context=context)
|
||||
# for structure in structures:
|
||||
# sal_structures = self._get_parent_structure(cr, uid, [structure], context=context)
|
||||
# for struct in sal_structures:
|
||||
# lines = structure_pool.browse(cr, uid, struct, context=context).rule_ids
|
||||
# for line in lines:
|
||||
# if line.child_ids:
|
||||
# for r in line.child_ids:
|
||||
# lines.append(r)
|
||||
# rules.append(line)
|
||||
# base = {
|
||||
# 'basic': slip.basic_amount,
|
||||
# }
|
||||
# if rules:
|
||||
# for rule in rules:
|
||||
# if rule.company_contribution:
|
||||
# base[rule.code.lower()] = rule.amount
|
||||
# if rule.register_id:
|
||||
# for slip in slip.line_ids:
|
||||
# if slip.category_id == rule.category_id:
|
||||
# line_tot = slip.total
|
||||
# value = eval(rule.computational_expression, base)
|
||||
# company_contrib = self._compute(cr, uid, rule.id, value, employee, contract, context)
|
||||
# reg_line = {
|
||||
# 'name': rule.name,
|
||||
# 'register_id': rule.register_id.id,
|
||||
# 'code': rule.code,
|
||||
# 'employee_id': slip.employee_id.id,
|
||||
# 'emp_deduction': line_tot,
|
||||
# 'comp_deduction': company_contrib,
|
||||
# 'total': rule.amount + line_tot
|
||||
# }
|
||||
# register_line_pool.create(cr, uid, reg_line, context=context)
|
||||
return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
|
||||
|
||||
#TODO move this function into hr_contract module, on hr.employee object
|
||||
@@ -395,9 +368,9 @@ class hr_payslip(osv.osv):
|
||||
#OR if it starts between the given dates
|
||||
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)
|
||||
clause_3 = [('date_start','<=', date_from),'|',('date_end', '=', False),('date_end','>=', date)]
|
||||
clause_3 = [('date_start','<=', date_from),'|',('date_end', '=', False),('date_end','>=', date_to)]
|
||||
clause_final = [('employee_id', '=', employee.id),'|','|'] + clause_1 + clause_2 + clause_3
|
||||
contract_ids = contract_obj.search(cr, uid, [('employee_id', '=', employee.id),], context=context)
|
||||
contract_ids = contract_obj.search(cr, uid, clause_final, context=context)
|
||||
return contract_ids
|
||||
|
||||
def compute_sheet(self, cr, uid, ids, context=None):
|
||||
@@ -405,7 +378,7 @@ class hr_payslip(osv.osv):
|
||||
for payslip in self.browse(cr, uid, ids, context=context):
|
||||
#delete old payslip lines
|
||||
old_slipline_ids = slip_line_pool.search(cr, uid, [('slip_id', '=', payslip.id)], context=context)
|
||||
old_slipline_ids
|
||||
# old_slipline_ids
|
||||
if old_slipline_ids:
|
||||
slip_line_pool.unlink(cr, uid, old_slipline_ids, context=context)
|
||||
if payslip.contract_id:
|
||||
@@ -418,7 +391,7 @@ class hr_payslip(osv.osv):
|
||||
self.write(cr, uid, [payslip.id], {'line_ids': lines}, context=context)
|
||||
return True
|
||||
|
||||
def get_input_lines(self, cr, uid, contract_ids, date_from, date_to, context=None):
|
||||
def get_worked_day_lines(self, cr, uid, contract_ids, date_from, date_to, context=None):
|
||||
"""
|
||||
@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
|
||||
@@ -475,10 +448,31 @@ class hr_payslip(osv.osv):
|
||||
res += [attendances] + leaves
|
||||
return res
|
||||
|
||||
def get_inputs(self, cr, uid, contract_ids, date_from, date_to, context=None):
|
||||
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
|
||||
|
||||
def get_payslip_lines(self, cr, uid, contract_ids, payslip_id, context):
|
||||
def _sum_salary_category(localdict, category, amount):
|
||||
def _sum_salary_rule_category(localdict, category, amount):
|
||||
if category.parent_id:
|
||||
localdict = _sum_salary_category(localdict, category.parent_id, amount)
|
||||
localdict = _sum_salary_rule_category(localdict, category.parent_id, amount)
|
||||
localdict['categories'][category.code] = category.code in localdict['categories'] and localdict['categories'][category.code] + amount or amount
|
||||
return localdict
|
||||
|
||||
@@ -489,9 +483,12 @@ class hr_payslip(osv.osv):
|
||||
obj_rule = self.pool.get('hr.salary.rule')
|
||||
payslip = payslip_obj.browse(cr, uid, payslip_id, context=context)
|
||||
worked_days = {}
|
||||
for worked_days_line in payslip.worked_days_line_ids:
|
||||
worked_days[worked_days_line.code] = worked_days_line
|
||||
inputs = {}
|
||||
for input_line in payslip.input_line_ids:
|
||||
worked_days[input_line.code] = input_line
|
||||
localdict = {'categories': {}, 'payslip': payslip, 'worked_days': worked_days, 'payslip_obj': payslip_obj}
|
||||
inputs[input_line.code] = input_line
|
||||
localdict = {'categories': {}, 'payslip': payslip, 'worked_days': worked_days, 'inputs': inputs, 'payslip_obj': payslip_obj}
|
||||
#get the ids of the structures on the contracts and their parent id as well
|
||||
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
|
||||
@@ -514,7 +511,7 @@ class hr_payslip(osv.osv):
|
||||
#set/overwrite the amount computed for this rule in the localdict
|
||||
localdict[rule.code] = amount
|
||||
#sum the amount for its salary category
|
||||
localdict = _sum_salary_category(localdict, rule.category_id, amount - previous_amount)
|
||||
localdict = _sum_salary_rule_category(localdict, rule.category_id, amount - previous_amount)
|
||||
#create/overwrite the rule in the temporary results
|
||||
result_dict[key] = {
|
||||
'salary_rule_id': rule.id,
|
||||
@@ -549,38 +546,48 @@ class hr_payslip(osv.osv):
|
||||
def onchange_employee_id(self, cr, uid, ids, date_from, date_to, employee_id=False, contract_id=False, context=None):
|
||||
empolyee_obj = self.pool.get('hr.employee')
|
||||
contract_obj = self.pool.get('hr.contract')
|
||||
worked_days_obj = self.pool.get('hr.payslip.worked_days')
|
||||
input_obj = self.pool.get('hr.payslip.input')
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
#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)
|
||||
|
||||
#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)
|
||||
|
||||
|
||||
#defaults
|
||||
res = {'value':{
|
||||
'line_ids':[],
|
||||
'input_line_ids': [],
|
||||
'worked_days_line_ids': [],
|
||||
#'details_by_salary_head':[], TODO put me back
|
||||
'name':'',
|
||||
'contract_id': False,
|
||||
'struct_id': False,
|
||||
}
|
||||
}
|
||||
}
|
||||
if not employee_id:
|
||||
return res
|
||||
ttyme = datetime.fromtimestamp(time.mktime(time.strptime(date_from, "%Y-%m-%d")))
|
||||
employee_id = empolyee_obj.browse(cr, uid, employee_id, context=context)
|
||||
res['value'].update({
|
||||
'name': _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(ttyme.strftime('%B-%Y'))),
|
||||
'company_id': employee_id.company_id.id
|
||||
})
|
||||
'name': _('Salary Slip of %s for %s') % (employee_id.name, tools.ustr(ttyme.strftime('%B-%Y'))),
|
||||
'company_id': employee_id.company_id.id
|
||||
})
|
||||
|
||||
if not context.get('contract', False):
|
||||
#fill with the first contract of the employee
|
||||
contract_ids = self.get_contract(cr, uid, employee_id, date_from, date_to, context=context)
|
||||
res['value'].update({
|
||||
'struct_id': contract_ids and contract_obj.read(cr, uid, contract_ids[0], ['struct_id'], context=context)['struct_id'][0] or False,
|
||||
'contract_id': contract_ids and contract_ids[0] or False,
|
||||
'struct_id': contract_ids and contract_obj.read(cr, uid, contract_ids[0], ['struct_id'], context=context)['struct_id'][0] or False,
|
||||
'contract_id': contract_ids and contract_ids[0] or False,
|
||||
})
|
||||
else:
|
||||
if contract_id:
|
||||
@@ -588,7 +595,10 @@ class hr_payslip(osv.osv):
|
||||
contract_ids = [contract_id]
|
||||
#fill the structure with the one on the selected contract
|
||||
contract_record = contract_obj.browse(cr, uid, contract_id, context=context)
|
||||
res['value'].update({'struct_id': contract_record.struct_id.id, 'contract_id': contract_id})
|
||||
res['value'].update({
|
||||
'struct_id': contract_record.struct_id.id,
|
||||
'contract_id': contract_id
|
||||
})
|
||||
else:
|
||||
#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)
|
||||
@@ -596,13 +606,16 @@ class hr_payslip(osv.osv):
|
||||
return res
|
||||
|
||||
#computation of the salary input
|
||||
input_line_ids = self.get_input_lines(cr, uid, contract_ids, date_from, date_to, context=context)
|
||||
worked_days_line_ids = self.get_worked_day_lines(cr, uid, contract_ids, date_from, date_to, context=context)
|
||||
input_line_ids = self.get_inputs(cr, uid, contract_ids, date_from, date_to, context=context)
|
||||
res['value'].update({
|
||||
'worked_days_line_ids': worked_days_line_ids,
|
||||
'input_line_ids': input_line_ids,
|
||||
})
|
||||
})
|
||||
return res
|
||||
|
||||
def onchange_contract_id(self, cr, uid, ids, date_from, date_to, employee_id=False, contract_id=False, context=None):
|
||||
#TODO it seems to be the mess in the onchanges, we should have onchange_employee => onchange_contract => doing all the things
|
||||
if context is None:
|
||||
context = {}
|
||||
res = {'value':{
|
||||
@@ -634,9 +647,31 @@ class hr_payslip(osv.osv):
|
||||
sum -= r['total']
|
||||
cr.close()
|
||||
return sum
|
||||
|
||||
|
||||
hr_payslip()
|
||||
|
||||
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),
|
||||
'payslip_id': fields.many2one('hr.payslip', 'Pay Slip', required=True),
|
||||
'sequence': fields.integer('Sequence', required=True,),
|
||||
'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,
|
||||
}
|
||||
hr_payslip_worked_days()
|
||||
|
||||
class hr_payslip_input(osv.osv):
|
||||
'''
|
||||
Payslip Input
|
||||
@@ -649,15 +684,15 @@ class hr_payslip_input(osv.osv):
|
||||
'payslip_id': fields.many2one('hr.payslip', 'Pay Slip', required=True),
|
||||
'sequence': fields.integer('Sequence', required=True,),
|
||||
'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'),
|
||||
'quantity': fields.float('Quantity', help="It is used in computation. For e.g. A rule for sales having 1% commission of basic salary for per product can defined in expression like result = inputs['S-ASUS']['qunatity']*contract.wage*0.01."),
|
||||
'contract_id': fields.many2one('hr.contract', 'Contract', required=True, help="The contract for which applied this input"),
|
||||
}
|
||||
_order = 'payslip_id,sequence'
|
||||
_order = 'payslip_id, sequence'
|
||||
_defaults = {
|
||||
'sequence': 10,
|
||||
'quantity': 0.0,
|
||||
}
|
||||
|
||||
|
||||
def sum(self, cr, uid, code, field, from_date, to_date=None, employee=False, context=None):
|
||||
if not employee:
|
||||
return 0.0
|
||||
@@ -685,7 +720,7 @@ class hr_salary_rule(osv.osv):
|
||||
'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."),
|
||||
'sequence': fields.integer('Sequence', required=True, help='Use to arrange calculation sequence'),
|
||||
'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']."),
|
||||
'category_id':fields.many2one('hr.salary.category', 'Category', required=True),
|
||||
'category_id':fields.many2one('hr.salary.rule.category', 'Category', required=True),
|
||||
'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."),
|
||||
'appears_on_payslip': fields.boolean('Appears on Payslip', help="Used for the display of rule on payslip"),
|
||||
'parent_rule_id':fields.many2one('hr.salary.rule', 'Parent Salary Rule', select=True),
|
||||
@@ -715,6 +750,7 @@ class hr_salary_rule(osv.osv):
|
||||
help="Contribution register based on company",
|
||||
required=False
|
||||
),
|
||||
'input_ids': fields.one2many('hr.rule.input', 'input_id', 'Inputs'),
|
||||
'note':fields.text('Description'),
|
||||
}
|
||||
_defaults = {
|
||||
@@ -725,8 +761,9 @@ class hr_salary_rule(osv.osv):
|
||||
# employee: hr.employee object
|
||||
# contract: hr.contract object
|
||||
# rules: rules code (previously computed)
|
||||
# categories: dictionary containing the computed categories (sum of amount of all rules belonging to that category). Keys are the category codes.
|
||||
# categories: dictionary containing the computed salary rule categories (sum of amount of all rules belonging to that category). Keys are the category codes.
|
||||
# worked_days: dictionary containing the computed worked days. Keys are the worked days codes.
|
||||
# inputs: dictionary containing the computed inputs. Keys are the inputs codes.
|
||||
|
||||
# Note: returned value have to be set in the variable 'result'
|
||||
|
||||
@@ -739,8 +776,9 @@ result = contract.wage * 0.10''',
|
||||
# employee: hr.employee object
|
||||
# contract: hr.contract object
|
||||
# rules: rules code (previously computed)
|
||||
# categories: dictionary containing the computed categories (sum of amount of all rules belonging to that category). Keys are the category codes.
|
||||
# categories: dictionary containing the computed salary rule categories (sum of amount of all rules belonging to that category). Keys are the category codes.
|
||||
# worked_days: dictionary containing the computed worked days. Keys are the worked days codes.
|
||||
# inputs: dictionary containing the computed inputs. Keys are the inputs codes.
|
||||
|
||||
# Note: returned value have to be set in the variable 'result'
|
||||
|
||||
@@ -821,6 +859,21 @@ result = rules['NET'] > categories['NET'] * 0.10''',
|
||||
|
||||
hr_salary_rule()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
hr_rule_input()
|
||||
|
||||
class hr_payslip_line(osv.osv):
|
||||
'''
|
||||
Payslip Line
|
||||
|
||||
@@ -2,32 +2,32 @@
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="BASIC" model="hr.salary.category">
|
||||
<record id="BASIC" model="hr.salary.rule.category">
|
||||
<field name="name">Basic</field>
|
||||
<field name="code">BASIC</field>
|
||||
</record>
|
||||
|
||||
<record id="ALW" model="hr.salary.category">
|
||||
<record id="ALW" model="hr.salary.rule.category">
|
||||
<field name="name">Allowance</field>
|
||||
<field name="code">ALW</field>
|
||||
</record>
|
||||
|
||||
<record id="DED" model="hr.salary.category">
|
||||
<record id="DED" model="hr.salary.rule.category">
|
||||
<field name="name">Deduction</field>
|
||||
<field name="code">DED</field>
|
||||
</record>
|
||||
|
||||
<record id="GROSS" model="hr.salary.category">
|
||||
<record id="GROSS" model="hr.salary.rule.category">
|
||||
<field name="name">Gross</field>
|
||||
<field name="code">GROSS</field>
|
||||
</record>
|
||||
|
||||
<record id="NET" model="hr.salary.category">
|
||||
<record id="NET" model="hr.salary.rule.category">
|
||||
<field name="name">Net</field>
|
||||
<field name="code">NET</field>
|
||||
</record>
|
||||
|
||||
<record id="COMP" model="hr.salary.category">
|
||||
<record id="COMP" model="hr.salary.rule.category">
|
||||
<field name="name">Company Contribution</field>
|
||||
<field name="code">COMP</field>
|
||||
</record>
|
||||
|
||||
@@ -60,6 +60,30 @@
|
||||
<field name="name">Meal Voucher</field>
|
||||
<field name="sequence" eval="16"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_salary_rule_sales_commission" model="hr.salary.rule">
|
||||
<field name="amount_select">code</field>
|
||||
<field name="code">SALE</field>
|
||||
<field name="category_id" ref="hr_payroll.ALW"/>
|
||||
<field name="name">Get 1% of sales</field>
|
||||
<field name="sequence" eval="17"/>
|
||||
<field name="amount_python_compute">result = (inputs['S-ASUS']['quantity'] + inputs['S-CHINA']['quantity']) * contract.wage * 0.01</field>
|
||||
</record>
|
||||
|
||||
<!-- Rule Inputs -->
|
||||
|
||||
<record id="hr_rule_input_sale_a" model="hr.rule.input">
|
||||
<field name="code">S-ASUS</field>
|
||||
<field name="name">Sale to ASUStek</field>
|
||||
<field name="input_id" ref="hr_salary_rule_sales_commission"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_rule_input_sale_b" model="hr.rule.input">
|
||||
<field name="code">S-CHINA</field>
|
||||
<field name="name">Sale to China Export</field>
|
||||
<field name="input_id" ref="hr_salary_rule_sales_commission"/>
|
||||
</record>
|
||||
|
||||
<!-- Salary Structure -->
|
||||
|
||||
<record id="structure_001" model="hr.payroll.structure">
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
<field name="model">hr.payslip</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree colors="blue:state in ('confirm','hr_check','accont_check');black:state in ('new');gray:state in('cancel')" string="Payslip">
|
||||
<tree colors="blue:state in ('confirm','hr_check','accont_check');black:state in ('new');gray:state in('cancel')" string="Payslips">
|
||||
<field name="number"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
@@ -260,8 +260,8 @@
|
||||
</field>
|
||||
</page>
|
||||
<!-- TODO: put me back -->
|
||||
<page string="Details By Salary Category">
|
||||
<field name="details_by_salary_category" context="{'group_by':'category_id'}" domain="[('appears_on_payslip', '=', True)]" nolabel="1">
|
||||
<page string="Details By Salary Rule Category">
|
||||
<field name="details_by_salary_rule_category" context="{'group_by':'category_id'}" domain="[('appears_on_payslip', '=', True)]" nolabel="1">
|
||||
<tree string="Payslip Lines" colors="blue:total == 0">
|
||||
<field name="category_id"/>
|
||||
<field name="name"/>
|
||||
@@ -272,7 +272,7 @@
|
||||
</page>
|
||||
|
||||
<page string="Worked Days">
|
||||
<field name="input_line_ids" colspan="4" nolabel="1" mode="tree">
|
||||
<field name="worked_days_line_ids" colspan="4" nolabel="1">
|
||||
<tree string="Worked Days" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
@@ -281,6 +281,36 @@
|
||||
<field name="contract_id" groups="base.group_extended"/>
|
||||
<field name="sequence" invisible="True"/>
|
||||
</tree>
|
||||
<form string="Worked Day">
|
||||
<group>
|
||||
<field name="name" select="1"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="payslip_id"/>
|
||||
<field name="sequence" groups="base.group_extended"/>
|
||||
<field name="number_of_days"/>
|
||||
<field name="number_of_hours"/>
|
||||
<field name="contract_id" groups="base.group_extended"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<field name="input_line_ids" colspan="4" nolabel="1">
|
||||
<tree string="Input Data" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="quantity"/>
|
||||
<field name="contract_id" groups="base.group_extended"/>
|
||||
<field name="sequence" invisible="True"/>
|
||||
</tree>
|
||||
<form string="Payslip Line">
|
||||
<group>
|
||||
<field name="name" select="1"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="payslip_id"/>
|
||||
<field name="sequence" groups="base.group_extended"/>
|
||||
<field name="quantity"/>
|
||||
<field name="contract_id" groups="base.group_extended"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
@@ -288,6 +318,7 @@
|
||||
<separator colspan="4" string="Other Information"/>
|
||||
<group col="5" colspan="2">
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
|
||||
<field name="payslip_run_id" domain="[('state','=','draft')]"/>
|
||||
<field name="paid" readonly="1"/>
|
||||
</group>
|
||||
<newline/>
|
||||
@@ -347,9 +378,9 @@
|
||||
<menuitem action="action_view_hr_payslip_form" id="menu_department_tree" parent="menu_hr_root_payroll"/>
|
||||
|
||||
<!-- Salary Category-->
|
||||
<record id="hr_salary_category_form" model="ir.ui.view">
|
||||
<field name="name">hr.salary.category.form</field>
|
||||
<field name="model">hr.salary.category</field>
|
||||
<record id="hr_salary_rule_category_form" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.form</field>
|
||||
<field name="model">hr.salary.rule.category</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Salary Categories">
|
||||
@@ -364,9 +395,9 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_salary_category_tree" model="ir.ui.view">
|
||||
<field name="name">hr.salary.category.tree</field>
|
||||
<field name="model">hr.salary.category</field>
|
||||
<record id="hr_salary_rule_category_tree" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.tree</field>
|
||||
<field name="model">hr.salary.rule.category</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Salary Rule Categories">
|
||||
@@ -377,9 +408,9 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_salary_category_filter" model="ir.ui.view">
|
||||
<field name="name">hr.salary.category.select</field>
|
||||
<field name="model">hr.salary.category</field>
|
||||
<record id="view_hr_salary_rule_category_filter" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.select</field>
|
||||
<field name="model">hr.salary.rule.category</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Salary Rule Categories">
|
||||
@@ -389,16 +420,16 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_hr_salary_category" model="ir.actions.act_window">
|
||||
<field name="name">Salary Categories</field>
|
||||
<field name="res_model">hr.salary.category</field>
|
||||
<record id="action_hr_salary_rule_category" model="ir.actions.act_window">
|
||||
<field name="name">Salary Rule Categories</field>
|
||||
<field name="res_model">hr.salary.rule.category</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="hr_salary_category_tree"/>
|
||||
<field name="search_view_id" ref="view_hr_salary_category_filter"/>
|
||||
<field name="view_id" ref="hr_salary_rule_category_tree"/>
|
||||
<field name="search_view_id" ref="view_hr_salary_rule_category_filter"/>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_hr_salary_category"
|
||||
action="action_hr_salary_category"
|
||||
id="menu_hr_salary_rule_category"
|
||||
action="action_hr_salary_rule_category"
|
||||
parent="payroll_configure"
|
||||
sequence="11"
|
||||
/>
|
||||
@@ -487,7 +518,7 @@
|
||||
|
||||
<!-- Salary Rules -->
|
||||
|
||||
<record id="hr_salary_rule_tree" model="ir.ui.view">
|
||||
<record id="hr_salary_rule_tree" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.tree</field>
|
||||
<field name="model">hr.salary.rule</field>
|
||||
<field name="type">tree</field>
|
||||
@@ -501,86 +532,149 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_form" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.form</field>
|
||||
<field name="model">hr.salary.rule</field>
|
||||
<record id="hr_salary_rule_form" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.form</field>
|
||||
<field name="model">hr.salary.rule</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Salary Rules">
|
||||
<group col="6" colspan="6">
|
||||
<field name="name"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="category_id"/>
|
||||
<field name="sequence" />
|
||||
<field name="active"/>
|
||||
<field name="appears_on_payslip" attrs="{'readonly':[('company_contribution','=', True)]}" />
|
||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<notebook colspan="6">
|
||||
<page string="General">
|
||||
<group col="4" colspan="6">
|
||||
<separator colspan="4" string="Conditions"/>
|
||||
<field name="condition_select"/><newline/>
|
||||
<field name="condition_python" attrs="{'invisible':[('condition_select','<>','python')], 'required': [('condition_select','=','python')]}" colspan="4"/> <newline/>
|
||||
<field name="condition_range" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/><newline/>
|
||||
<field name="condition_range_min" colspan="2" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/>
|
||||
<field name="condition_range_max" colspan="2" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/>
|
||||
<separator colspan="4" string="Computation"/>
|
||||
<field name="amount_select"/>
|
||||
<field name="quantity" attrs="{'invisible':[('amount_select','=','code')], 'required':[('amount_select','!=','code')]}"/><newline/>
|
||||
<field name="amount_fix" attrs="{'invisible':[('amount_select','<>','fix')], 'required':[('amount_select','=','fix')]}"/>
|
||||
<field colspan="4" name="amount_python_compute" attrs="{'invisible':[('amount_select','<>','code')], 'required':[('amount_select','=','code')]}"/>
|
||||
<field name="amount_percentage_base" attrs="{'invisible':[('amount_select','<>','percentage')], 'required': [('amount_select','=','percentage')]}"/>
|
||||
<field name="amount_percentage" attrs="{'invisible':[('amount_select','<>','percentage')], 'required':[('amount_select','=','percentage')]}"/>
|
||||
<separator colspan="4" string="Company contribution"/>
|
||||
<field name="register_id"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Child Rules">
|
||||
<field name="parent_rule_id"/>
|
||||
<separator colspan="4" string="Children definition"/>
|
||||
<field colspan="4" name="child_ids" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Inputs">
|
||||
<field name="input_ids" colspan="4" nolabel="1" mode="tree, form">
|
||||
<tree string="Input Data" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Description">
|
||||
<field name="note" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_rule_filter" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.select</field>
|
||||
<field name="model">hr.salary.rule</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Salary Rule">
|
||||
<group col="8" colspan="4">
|
||||
<field name="name"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="category_id"/>
|
||||
<field name="condition_range_min"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<group col="8" colspan="4" expand="0" string="Group By...">
|
||||
<filter string="Head" icon="terp-folder-yellow" name="head" context="{'group_by':'category_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_salary_rule_form" model="ir.actions.act_window">
|
||||
<field name="name">Salary Rules</field>
|
||||
<field name="res_model">hr.salary.rule</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="hr_salary_rule_tree"/>
|
||||
<field name="domain">[('parent_rule_id','=',False)]</field>
|
||||
<field name="search_view_id" ref="view_hr_rule_filter"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_hr_salary_rule_form" action="action_salary_rule_form" parent="payroll_configure" sequence="12"/>
|
||||
|
||||
<act_window domain="[('slip_id', '=', active_id)]" id="act_payslip_lines" name="Payslip Lines" res_model="hr.payslip.line" src_model="hr.payslip"/>
|
||||
|
||||
<record id="hr_payslip_run_filter" model="ir.ui.view">
|
||||
<field name="name">hr.payslip.run.search</field>
|
||||
<field name="model">hr.payslip.run</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Payslips Group">
|
||||
<field name="name"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payslip_run_tree" model="ir.ui.view">
|
||||
<field name="name">hr.payslip.run.tree</field>
|
||||
<field name="model">hr.payslip.run</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Payslips Run">
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_payslip_run_form" model="ir.ui.view">
|
||||
<field name="name">hr.payslip.run.form</field>
|
||||
<field name="model">hr.payslip.run</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Salary Rules">
|
||||
<form string="Payslips Run">
|
||||
<group col="6" colspan="6">
|
||||
<field name="name"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="category_id"/>
|
||||
<field name="sequence" />
|
||||
<field name="active"/>
|
||||
<field name="appears_on_payslip" attrs="{'readonly':[('company_contribution','=', True)]}" />
|
||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
<notebook colspan="6">
|
||||
<page string="General">
|
||||
<group col="4" colspan="6">
|
||||
<separator colspan="4" string="Conditions"/>
|
||||
<field name="condition_select"/><newline/>
|
||||
<field name="condition_python" attrs="{'invisible':[('condition_select','<>','python')], 'required': [('condition_select','=','python')]}" colspan="4"/> <newline/>
|
||||
<field name="condition_range" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/><newline/>
|
||||
<field name="condition_range_min" colspan="2" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/>
|
||||
<field name="condition_range_max" colspan="2" attrs="{'invisible':[('condition_select','<>','range')], 'required':[('condition_select','=','range')]}"/>
|
||||
<separator colspan="4" string="Computation"/>
|
||||
<field name="amount_select"/>
|
||||
<field name="quantity" attrs="{'invisible':[('amount_select','=','code')], 'required':[('amount_select','!=','code')]}"/><newline/>
|
||||
<field name="amount_fix" attrs="{'invisible':[('amount_select','<>','fix')], 'required':[('amount_select','=','fix')]}"/>
|
||||
<field colspan="4" name="amount_python_compute" attrs="{'invisible':[('amount_select','<>','code')], 'required':[('amount_select','=','code')]}"/>
|
||||
<field name="amount_percentage_base" attrs="{'invisible':[('amount_select','<>','percentage')], 'required': [('amount_select','=','percentage')]}"/>
|
||||
<field name="amount_percentage" attrs="{'invisible':[('amount_select','<>','percentage')], 'required':[('amount_select','=','percentage')]}"/>
|
||||
<separator colspan="4" string="Company contribution"/>
|
||||
<field name="register_id"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Child Rules">
|
||||
<field name="parent_rule_id"/>
|
||||
<separator colspan="4" string="Children definition"/>
|
||||
<field colspan="4" name="child_ids" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Description">
|
||||
<field name="note" colspan="4" nolabel="1"/>
|
||||
<page string="Payslips">
|
||||
<field name="slip_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<group col="6" colspan="6">
|
||||
<field name="state"/>
|
||||
<button name="%(action_hr_payslip_by_employees)d" type="action" states="draft" icon="gtk-execute" string="Generate Payslips" />
|
||||
<button name="close_payslip_run" type="object" icon="gtk-jump-to" string="Close" states="draft"/>
|
||||
<button string="Set to Draft" icon="terp-stock_effects-object-colorize" name="draft_payslip_run" type="object" states="close"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_rule_filter" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.select</field>
|
||||
<field name="model">hr.salary.rule</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Salary Rule">
|
||||
<group col="8" colspan="4">
|
||||
<field name="name"/>
|
||||
<field name="code" select="1"/>
|
||||
<field name="category_id"/>
|
||||
<field name="condition_range_min"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<group col="8" colspan="4" expand="0" string="Group By...">
|
||||
<filter string="Category" icon="terp-folder-yellow" name="category" context="{'group_by':'category_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_salary_rule_form" model="ir.actions.act_window">
|
||||
<field name="name">Salary Rules</field>
|
||||
<field name="res_model">hr.salary.rule</field>
|
||||
<record id="action_hr_payslip_run_tree" model="ir.actions.act_window">
|
||||
<field name="name">Payslips Run</field>
|
||||
<field name="res_model">hr.payslip.run</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="hr_salary_rule_tree"/>
|
||||
<field name="domain">[('parent_rule_id','=',False)]</field>
|
||||
<field name="search_view_id" ref="view_hr_rule_filter"/>
|
||||
<field name="view_id" ref="hr_payslip_run_tree"/>
|
||||
<field name="search_view_id" ref="hr_payslip_run_filter"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_hr_salary_rule_form" action="action_salary_rule_form" parent="payroll_configure" sequence="12"/>
|
||||
|
||||
<act_window domain="[('slip_id', '=', active_id)]" id="act_payslip_lines" name="Payslip Lines" res_model="hr.payslip.line" src_model="hr.payslip"/>
|
||||
<menuitem action="action_hr_payslip_run_tree" id="menu_hr_payslip_run" parent="menu_hr_root_payroll"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"access_hr_payroll_structure","hr.payroll.structure","model_hr_payroll_structure","base.group_hr_user",1,1,1,1
|
||||
"access_hr_contribution_register","hr.contribution.register","model_hr_contribution_register","base.group_hr_user",1,1,1,1
|
||||
"access_hr_salary_category","hr.salary.category","model_hr_salary_category","base.group_hr_user",1,1,1,1
|
||||
"access_hr_salary_rule_category","hr.salary.rule.category","model_hr_salary_rule_category","base.group_hr_user",1,1,1,1
|
||||
"access_hr_payslip","hr.payslip","model_hr_payslip","base.group_hr_user",1,1,1,1
|
||||
"access_hr_payslip_line","hr.payslip.line","model_hr_payslip_line","base.group_hr_user",1,1,1,1
|
||||
"access_hr_payslip_input_user","hr.payslip.input.user","model_hr_payslip_input","base.group_hr_user",1,1,1,1
|
||||
"access_hr_payslip_manager","hr.payslip.manager","model_hr_payslip","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payslip_line_manager","hr.payslip.line.manager","model_hr_payslip_line","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payroll_structure_manager","hr.payroll.structure.manager","model_hr_payroll_structure","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payslip_input ","hr.payslip.input","model_hr_payslip_input","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payslip_worked_days ","hr.payslip.worked_days","model_hr_payslip_worked_days","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_salary_rule","hr.salary.rule","model_hr_salary_rule","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payslip_run","hr.payslip.run","model_hr_payslip_run","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_payslip_input","hr.payslip.input","model_hr_payslip_input","base.group_hr_manager",1,1,1,1
|
||||
"access_hr_rule_input","hr.rule.input","model_hr_rule_input","base.group_hr_manager",1,1,1,1
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
|
||||
# d$
|
||||
#
|
||||
@@ -24,5 +24,5 @@
|
||||
import hr_payroll_employees_detail
|
||||
#import hr_payroll_create_analytic
|
||||
import hr_payroll_year_salary
|
||||
|
||||
import hr_payroll_payslips_by_employees
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from dateutil import relativedelta
|
||||
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
|
||||
class hr_payslip_employees(osv.osv_memory):
|
||||
|
||||
_name ='hr.payslip.employees'
|
||||
_description = 'Generate payslips for all selected employees'
|
||||
_columns = {
|
||||
'employee_ids': fields.many2many('hr.employee', 'hr_employee_group_rel', 'payslip_id', 'employee_id', 'Employees'),
|
||||
}
|
||||
|
||||
def compute_sheet(self, cr, uid, ids, context=None):
|
||||
emp_pool = self.pool.get('hr.employee')
|
||||
slip_pool = self.pool.get('hr.payslip')
|
||||
slip_ids = []
|
||||
if context is None:
|
||||
context = {}
|
||||
data = self.read(cr, uid, ids, context=context)[0]
|
||||
if not data['employee_ids']:
|
||||
raise osv.except_osv(_("Warning !"), _("You must select employee(s) to generate payslip(s)"))
|
||||
for emp in emp_pool.browse(cr, uid, data['employee_ids'], context=context):
|
||||
slip_data = slip_pool.onchange_employee_id(cr, uid, [], time.strftime('%Y-%m-01'), str(datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1))[:10], emp.id, contract_id=False, context=context)
|
||||
res = {
|
||||
'employee_id': emp.id,
|
||||
'name': slip_data['value'].get('name', False),
|
||||
'struct_id': slip_data['value'].get('struct_id', False),
|
||||
'contract_id': slip_data['value'].get('contract_id', False),
|
||||
'payslip_run_id': context.get('active_id', False),
|
||||
'input_line_ids': [(0, 0, x) for x in slip_data['value'].get('input_line_ids', False)],
|
||||
'worked_days_line_ids': [(0, 0, x) for x in slip_data['value'].get('worked_days_line_ids', False)]
|
||||
}
|
||||
slip_ids.append(slip_pool.create(cr, uid, res, context=context))
|
||||
slip_pool.compute_sheet(cr, uid, slip_ids, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
hr_payslip_employees()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_hr_payslip_by_employees" model="ir.ui.view">
|
||||
<field name="name">hr_payroll_payslip_employees</field>
|
||||
<field name="model">hr.payslip.employees</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Payslips by Employees">
|
||||
<group colspan="4" >
|
||||
<separator string="Payslips by Employees" colspan="4"/>
|
||||
<label colspan="4" nolabel="1" string="This wizard will generate payslips for all selected employee(s)" />
|
||||
<newline/>
|
||||
<field name="employee_ids" nolabel="1"/>
|
||||
</group>
|
||||
<separator string="" colspan="4" />
|
||||
<group colspan="4" col="6">
|
||||
<label string ="" colspan="2"/>
|
||||
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
|
||||
<button icon="gtk-execute" string="Generate" name="compute_sheet" type="object"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_hr_payslip_by_employees" model="ir.actions.act_window">
|
||||
<field name="name">Generate Payslips</field>
|
||||
<field name="res_model">hr.payslip.employees</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_hr_payslip_by_employees"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user