diff --git a/addons/hr_payroll/__openerp__.py b/addons/hr_payroll/__openerp__.py
index 87fe437a1db..44c0fb93506 100644
--- a/addons/hr_payroll/__openerp__.py
+++ b/addons/hr_payroll/__openerp__.py
@@ -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',
diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py
index b15cb8ff03a..3bc47053a3b 100644
--- a/addons/hr_payroll/hr_payroll.py
+++ b/addons/hr_payroll/hr_payroll.py
@@ -202,6 +202,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
@@ -213,18 +236,19 @@ class hr_payslip(osv.osv):
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_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)]}),
@@ -245,15 +269,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_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'),
@@ -401,7 +426,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:
@@ -587,26 +612,29 @@ class hr_payslip(osv.osv):
#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:
@@ -614,7 +642,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)
@@ -627,7 +658,7 @@ class hr_payslip(osv.osv):
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):
diff --git a/addons/hr_payroll/hr_payroll_view.xml b/addons/hr_payroll/hr_payroll_view.xml
index 28605cb1825..4d422d708de 100644
--- a/addons/hr_payroll/hr_payroll_view.xml
+++ b/addons/hr_payroll/hr_payroll_view.xml
@@ -209,7 +209,7 @@
hr.payslip
tree
-
+
@@ -260,7 +260,7 @@
-
+
@@ -318,6 +318,7 @@
+
@@ -620,6 +621,61 @@
+
+ hr.payslip.run.search
+ hr.payslip.run
+ search
+
+
+
+
+
+
+
+
+ hr.payslip.run.tree
+ hr.payslip.run
+ tree
+
+
+
+
+
+
+
+
+ hr.payslip.run.form
+ hr.payslip.run
+ form
+
+
+
+
+
+
+ Payslips Run
+ hr.payslip.run
+ form
+
+
+
+
+
diff --git a/addons/hr_payroll/security/ir.model.access.csv b/addons/hr_payroll/security/ir.model.access.csv
index 7616af52cc3..d19ec411d5d 100644
--- a/addons/hr_payroll/security/ir.model.access.csv
+++ b/addons/hr_payroll/security/ir.model.access.csv
@@ -10,5 +10,6 @@
"access_hr_payroll_structure_manager","hr.payroll.structure.manager","model_hr_payroll_structure","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
diff --git a/addons/hr_payroll/wizard/__init__.py b/addons/hr_payroll/wizard/__init__.py
index 2cf78e17b1c..e0882d2d6da 100644
--- a/addons/hr_payroll/wizard/__init__.py
+++ b/addons/hr_payroll/wizard/__init__.py
@@ -1,7 +1,7 @@
#-*- coding:utf-8 -*-
##############################################################################
#
-# OpenERP, Open Source Management Solution
+# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (). 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:
\ No newline at end of file
diff --git a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
new file mode 100644
index 00000000000..ebb9bed26c1
--- /dev/null
+++ b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+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:
\ No newline at end of file
diff --git a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.xml b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.xml
new file mode 100644
index 00000000000..c8bd142dbf9
--- /dev/null
+++ b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ hr_payroll_payslip_employees
+ hr.payslip.employees
+ form
+
+
+
+
+
+
+ Generate Payslips
+ hr.payslip.employees
+ form
+ tree,form
+
+ new
+
+
+
+